The purchasing_quotations module in src/cluster_erp/modules/purchasing_quotations/ is the register of supplier quotations. A supplier quotation (PurchasingQuotation) records an offer from a vendor, with or without a purchasing RFQ, priced per line with discount, surcharge, and freight conditions. Awarding a quotation can create a purchase order or a purchase contract in procurement and updates the purchasing info records. The register also lists the vendor quotations kept inside the purchasing RFQ sub-module.
At a glance¶
| Item | Value | Source |
|---|---|---|
| Package | src/cluster_erp/modules/purchasing_quotations/ |
folder |
| Module class | Module |
src/cluster_erp/modules/purchasing_quotations/module.py:15 |
name / version |
purchasing_quotations / 0.1.0 |
src/cluster_erp/modules/purchasing_quotations/module.py:20-26 |
| API prefix | /api/v1/purchasing_quotations (ModuleBase.api_prefix) |
src/cluster_erp/core/module_base.py:37-38 |
| Sub-modules | 1: quotations |
folder listing |
| Tables | 4: purq_quotations, purq_quotation_lines, purq_quotation_conditions, purq_quotation_scales |
__tablename__ in quotations/models.py |
| Entities in metadata | 6 keys in entities |
src/cluster_erp/modules/purchasing_quotations/module.py:173-184 |
| Routes | 27 route decorators in the package | grep for @...router.get/post/put/patch/delete |
| Actions | 7 ActionMeta entries |
grep "ActionMeta(" in the package |
| Migrations | 8 files | files in migrations/versions/ whose text contains one of the four table names |
| Web app presence | Catalogue app purchasing-quotations |
src/cluster_erp/modules/app_store/catalog.json (entry purchasing-quotations) |
| Desktop presence | src/cluster_erp/desktop/modules/purchasing_quotations/ holds only __init__.py; screens use the generic widgets |
folder |
What the module does¶
- Records supplier quotations with four
sourcevalues:rfq,unsolicited(default),spot, andprice_list(src/cluster_erp/modules/purchasing_quotations/quotations/models.py:40-46,src/cluster_erp/modules/purchasing_quotations/quotations/schemas.py:16). - Prices each line through a sequence of conditions into an effective unit price (
src/cluster_erp/modules/purchasing_quotations/quotations/services.py:797-831). - Keeps revisions:
reviseclones a quotation into a new version and marks the old one superseded (src/cluster_erp/modules/purchasing_quotations/quotations/services.py:509-594). - Awards lines and, on request, creates a purchase order or contract through the EventBus (
src/cluster_erp/modules/purchasing_quotations/quotations/services.py:285-346). - Compares all live quotation lines for one product by effective price, and lists a register of native and RFQ quotations (
src/cluster_erp/modules/purchasing_quotations/quotations/services.py:990-1116).
Sub-modules¶
| Sub-module | Purpose | Main entities | Router | Source |
|---|---|---|---|---|
quotations |
Quotations, lines, conditions, scales, compare, register | PurchasingQuotation, PurchasingQuotationLine, PurchasingQuotationCondition, PurchasingQuotationScale |
/purchasing-quotations and line, condition, and scale paths |
src/cluster_erp/modules/purchasing_quotations/quotations/routes.py:39-61 |
The router is mounted under the module prefix with require_app("purchasing_quotations") (src/cluster_erp/modules/purchasing_quotations/module.py:39-49).
Data model¶
| Entity | Table | Key fields | Notes | Source |
|---|---|---|---|---|
PurchasingQuotation |
purq_quotations |
code, source, purchase_rfq_id, vendor_party_id, status, version_no, superseded_by_id, vendor_quotation_no, currency, valid_from, valid_until, binding_until, total_amount, project_id, follow_on_type, follow_on_id, follow_on_code |
lifecycle stamps for submit, award, reject, complete, cancel | src/cluster_erp/modules/purchasing_quotations/quotations/models.py:24-104 |
PurchasingQuotationLine |
purq_quotation_lines |
line_no, product breakdown, description, qty, net_unit_price, effective_unit_price, line_total, lead_time_days, moq, rejection_indicator, is_awarded |
free-text lines have no product | src/cluster_erp/modules/purchasing_quotations/quotations/models.py:107-151 |
PurchasingQuotationCondition |
purq_quotation_conditions |
condition_type, calc_type, value, base, sequence, label |
src/cluster_erp/modules/purchasing_quotations/quotations/models.py:154-186 |
|
PurchasingQuotationScale |
purq_quotation_scales |
from_qty, unit_price |
src/cluster_erp/modules/purchasing_quotations/quotations/models.py:189-205 |
Money is Numeric: total and line total Numeric(19, 4), prices and condition values Numeric(18, 4) (src/cluster_erp/modules/purchasing_quotations/quotations/models.py:81, src/cluster_erp/modules/purchasing_quotations/quotations/models.py:127-131, src/cluster_erp/modules/purchasing_quotations/quotations/models.py:172). Line breakdowns are checked with ensure_product_breakdown (src/cluster_erp/modules/purchasing_quotations/quotations/services.py:713, src/cluster_erp/modules/purchasing_quotations/quotations/services.py:745).
Screens and metadata¶
get_metadata declares a sidebar group Supplier Quotations (Turkish Tedarikçi Teklifleri) with three list sections: Supplier Quotations, All Quotations (Register), and Compare Supplier Quotations (src/cluster_erp/modules/purchasing_quotations/module.py:130-170). The register and compare views are their own entities, purchasing_quotation_register and purchasing_quotation_compare (src/cluster_erp/modules/purchasing_quotations/module.py:173-184). See generic list.
Status and actions¶
Statuses are in_preparation (default), submitted, awarded, completed, rejected, and cancelled (src/cluster_erp/modules/purchasing_quotations/quotations/models.py:54-62). The service maps each action to a target status and allowed source statuses (src/cluster_erp/modules/purchasing_quotations/quotations/services.py:57-64).
stateDiagram-v2 [*] --> in_preparation in_preparation --> submitted: submit submitted --> awarded: award in_preparation --> rejected: reject submitted --> rejected: reject awarded --> completed: complete in_preparation --> cancelled: cancel submitted --> cancelled: cancel awarded --> cancelled: cancel rejected --> in_preparation: reopen cancelled --> in_preparation: reopen
In prose: a quotation is prepared and submitted, then awarded or rejected. An awarded quotation is completed or cancelled. A rejected or cancelled quotation can be reopened. revise works from submitted, rejected, or cancelled and starts a new in_preparation version (src/cluster_erp/modules/purchasing_quotations/quotations/services.py:509-525).
| Action | Label | Condition | Endpoint | Permission | Source |
|---|---|---|---|---|---|
submit |
Submit | status is in_preparation |
POST /purchasing-quotations/{id}/submit |
purchasing_quotation.submit |
src/cluster_erp/modules/purchasing_quotations/quotations/metadata.py:636-643, src/cluster_erp/modules/purchasing_quotations/quotations/routes.py:178 |
award |
Award | status is submitted |
POST /purchasing-quotations/{id}/award |
purchasing_quotation.award |
src/cluster_erp/modules/purchasing_quotations/quotations/metadata.py:644-695, src/cluster_erp/modules/purchasing_quotations/quotations/routes.py:192 |
reject |
Reject | status is in_preparation or submitted |
POST /purchasing-quotations/{id}/reject |
purchasing_quotation.reject |
src/cluster_erp/modules/purchasing_quotations/quotations/metadata.py:696-712, src/cluster_erp/modules/purchasing_quotations/quotations/routes.py:214 |
complete |
Complete | status is awarded |
POST /purchasing-quotations/{id}/complete |
purchasing_quotation.complete |
src/cluster_erp/modules/purchasing_quotations/quotations/metadata.py:713-720, src/cluster_erp/modules/purchasing_quotations/quotations/routes.py:231 |
cancel |
Cancel | status is in_preparation, submitted, or awarded |
POST /purchasing-quotations/{id}/cancel |
purchasing_quotation.update |
src/cluster_erp/modules/purchasing_quotations/quotations/metadata.py:721-737, src/cluster_erp/modules/purchasing_quotations/quotations/routes.py:245 |
reopen |
Reopen | status is rejected or cancelled |
POST /purchasing-quotations/{id}/reopen |
purchasing_quotation.update |
src/cluster_erp/modules/purchasing_quotations/quotations/metadata.py:738-745, src/cluster_erp/modules/purchasing_quotations/quotations/routes.py:262 |
revise |
Revise (new version) | status is submitted, rejected, or cancelled |
POST /purchasing-quotations/{id}/revise |
purchasing_quotation.revise |
src/cluster_erp/modules/purchasing_quotations/quotations/metadata.py:746-753, src/cluster_erp/modules/purchasing_quotations/quotations/routes.py:276 |
Business rules¶
- A quotation can be edited only in
in_preparationorsubmitted(src/cluster_erp/modules/purchasing_quotations/quotations/services.py:54,src/cluster_erp/modules/purchasing_quotations/quotations/services.py:634-640). - Only
in_preparation,cancelled, orrejectedquotations can be deleted; delete setsdeleted_at(src/cluster_erp/modules/purchasing_quotations/quotations/services.py:224-238). - Submitting needs at least one line, at least one line that is not rejected, a currency, and a validity date (
src/cluster_erp/modules/purchasing_quotations/quotations/services.py:599-633). - The effective unit price walks the conditions in
sequenceorder. A percent applies to the running subtotal or to the net price;per_unitadds a fixed amount per unit;absolutespreads a fixed amount over the quantity. Discounts subtract, surcharges and freight add, and the result is floored at zero (src/cluster_erp/modules/purchasing_quotations/quotations/services.py:797-831). - The line total is quantity times effective price, rounded to four places; the header total is recomputed on every line or condition change (
src/cluster_erp/modules/purchasing_quotations/quotations/services.py:848-874). - Award stores the decision first, then creates a follow-on purchase order or contract when asked, and skips this if a follow-on already exists (
src/cluster_erp/modules/purchasing_quotations/quotations/services.py:321-339,src/cluster_erp/modules/purchasing_quotations/quotations/services.py:389-449). codecomes from the yearly sequencepurchasing_quotation; a revision gets a new code andversion_noplus one (src/cluster_erp/modules/purchasing_quotations/quotations/services.py:168,src/cluster_erp/modules/purchasing_quotations/quotations/services.py:522).- Scales are stored per line and copied on revision; the effective price calculation does not read them (
src/cluster_erp/modules/purchasing_quotations/quotations/services.py:580-582,src/cluster_erp/modules/purchasing_quotations/quotations/services.py:848-851).
Events and queries¶
The module consumes 2 contracts and produces 7, counted as on the parties module page. The full list is on the contracts reference.
| Direction | Contract class | Handler or emitter | Source |
|---|---|---|---|
| emits | SupplierQuotationAwarded |
_emit_awarded_event, for awarded lines with a product |
src/cluster_erp/modules/purchasing_quotations/quotations/services.py:347-388 |
| queries | CreateFollowOnFromSupplierQuotation |
_create_follow_on |
src/cluster_erp/modules/purchasing_quotations/quotations/services.py:389-449 |
| queries | GetPurchaseRfqQuotations |
register, for the RFQ quotations |
src/cluster_erp/modules/purchasing_quotations/quotations/services.py:1048-1116 |
| answers query | GetPurchasingQuotationById |
_handle_get_quotation |
src/cluster_erp/modules/purchasing_quotations/module.py:51-102 |
| subscribes | DescribeAnalyticsFacts |
describe_analytics_facts |
src/cluster_erp/modules/purchasing_quotations/module.py:60-66 |
The procurement module handles SupplierQuotationAwarded and CreateFollowOnFromSupplierQuotation; see purchase contracts and info records.
Permissions¶
| Permission key | Used by | Source |
|---|---|---|
purchasing_quotation.read |
lists, detail, completeness, lines, conditions, scales, compare, register | src/cluster_erp/modules/purchasing_quotations/quotations/routes.py:61-473 |
purchasing_quotation.create, .update, .delete, .bulk_delete |
record and child changes, cancel, reopen | src/cluster_erp/modules/purchasing_quotations/quotations/routes.py:99-436 |
purchasing_quotation.submit, .award, .reject, .complete, .revise |
lifecycle routes | src/cluster_erp/modules/purchasing_quotations/quotations/routes.py:178-276 |
Privacy, retention, and history¶
The module has no privacy_handlers.py. Quotations are soft-deleted (src/cluster_erp/modules/purchasing_quotations/quotations/services.py:236). Pricing conditions and scales are child rows that are replaced together with their line; they are removed through the ORM so that each removal is recorded (see Notes below). Changes are captured by change history.
Analytics facts¶
analytics_facts.py contributes the fact set purchasing_quotation with the measures quotation_count, total_amount, median_amount, and average_lead_time_days, read with purchasing_quotation.read (src/cluster_erp/modules/purchasing_quotations/analytics_facts.py:17-117). See analytics facts.
Settings and reference data¶
The module has no settings section. Not determined from code: whether the condition types are meant to come from reference data; they are a fixed Literal in the schema (src/cluster_erp/modules/purchasing_quotations/quotations/schemas.py:16-19).
Tests¶
Found by a grep for modules.purchasing_quotations in tests/ (no hit) and by file name and the path /purchasing_quotations/ in web-client/.
| Kind | File | Covers |
|---|---|---|
| e2e | web-client/_e2e_purchasing_quotations_app.mjs |
the purchasing quotations app |
| e2e | web-client/_e2e_tenant_fields_phase4b.mjs |
tenant fields on the quotation |
Discrepancies¶
Terms introduced¶
| Term | Code identifier | Definition | Turkish term | Source |
|---|---|---|---|---|
| supplier quotation | PurchasingQuotation |
An offer from a vendor recorded in the register, with or without an RFQ. | Tedarikçi teklifi | src/cluster_erp/modules/purchasing_quotations/quotations/models.py:24 |
| pricing condition | PurchasingQuotationCondition |
A discount, surcharge, freight, or cash discount applied to a line price. | Fiyat koşulu | src/cluster_erp/modules/purchasing_quotations/quotations/models.py:154 |
| effective unit price | effective_unit_price |
The line price after all conditions in sequence. | Etkin birim fiyat | src/cluster_erp/modules/purchasing_quotations/quotations/models.py:130 |
| follow-on document | follow_on_type, follow_on_id |
The purchase order or contract created from an awarded quotation. | İzleyen belge | src/cluster_erp/modules/purchasing_quotations/quotations/models.py:91-93 |
Related pages¶
- Modules index
- Procurement module
- Purchasing RFQs
- Purchase contracts and info records
- API reference for this module
Verified against commit 92f8051 on 2026-11-13.