Factorizer/Cluster ERP WikiCluster ERP Türkçe
Home / Business modules

Module

Quotations module (quotations)

Module page for quotations, the sales quotes for customer RFQs with feasibility, BOM and costing steps, management approval, versions, and line roll-up.

The quotations module in src/cluster_erp/modules/quotations/ holds sales quotations. A quote (Quote) answers a customer RFQ of the crm module and moves through feasibility, design, BOM, costing, and a management approval before it is sent. Its lines carry cost and price, and the header totals and margin are rolled up from them. Sales orders reference quote lines, which can mark a quote as won.

At a glance

Item Value Source
Package src/cluster_erp/modules/quotations/ folder
Module class Module src/cluster_erp/modules/quotations/module.py:18
name / version quotations / 0.1.0 src/cluster_erp/modules/quotations/module.py:20-25
API prefix /api/v1/quotations (ModuleBase.api_prefix) src/cluster_erp/core/module_base.py:37-38
Sub-modules 2: quotes, line_items folder listing
Tables 5: quot_quotes, quot_mgmt_approvals, quot_quote_line_items, quot_rejection_reasons, quot_document_flow __tablename__ in the two models.py files
Entities in metadata 5 keys in entities src/cluster_erp/modules/quotations/module.py:224-230
Routes 34 route decorators in the package grep for @...router.get/post/put/patch/delete
Actions 15 ActionMeta entries grep "ActionMeta(" in the package
Migrations 20 files files in migrations/versions/ whose text contains one of the five table names
Web app presence Catalogue app quotations src/cluster_erp/modules/app_store/catalog.json (entry quotations)
Desktop presence generic widgets only src/cluster_erp/desktop/modules/ has no quotations folder

What the module does

  • Creates a quote for a customer RFQ, one active quote per RFQ, and takes commercial defaults from the customer (src/cluster_erp/modules/quotations/quotes/services.py:175-239).
  • Moves the quote through feasibility, optional design, BOM, and costing (src/cluster_erp/modules/quotations/quotes/services.py:59-71).
  • Records a management approval meeting and its decision (src/cluster_erp/modules/quotations/quotes/services.py:467-549).
  • Sends the quote after an approval and a completeness check, then marks it won, lost, or expired (src/cluster_erp/modules/quotations/quotes/services.py:550-686).
  • Rolls up total price, total cost, and margin from the lines (src/cluster_erp/modules/quotations/rollup.py:1-17).
  • Records which sales order lines reference which quote lines (src/cluster_erp/modules/quotations/quotes/services.py:925-1042).

Sub-modules

Sub-module Purpose Main entities Router Source
quotes Quotes, approvals, lifecycle, versions, history, document flow Quote, MgmtApproval /quotes src/cluster_erp/modules/quotations/quotes/routes.py:37-52
line_items Lines, rejection reasons, document flow rows QuoteLineItem, QuoteRejectionReason, QuoteDocumentFlow /quotes/.../line-items src/cluster_erp/modules/quotations/line_items/routes.py:21-24

Data model

Entity Table Key fields Notes Source
Quote quot_quotes quote_no, rfq_id, version_no, status, feasibility_done, requires_new_design, bom_id, nesting_id, costing_id, mgmt_approval_id, superseded_by_id, total_price, total_cost, overall_margin_pct, currency, validity_days, payment_terms, incoterm, sent_at, won_at, lost_at, lost_reason totals Numeric(19, 4), margin Numeric(5, 2) src/cluster_erp/modules/quotations/quotes/models.py:37-169
MgmtApproval quot_mgmt_approvals quote_id, scheduled_at, held_at, attendees, decision, adjustments, notes, recorded_by decision is approved, conditional, or rejected src/cluster_erp/modules/quotations/quotes/models.py:171-199, src/cluster_erp/modules/quotations/quotes/schemas.py:24
QuoteLineItem quot_quote_line_items quote_id, line_no, ordered_qty, part_id, part_model_id, part_version_id, customer_part_id, qty, uom, unit_cost, margin_pct, unit_price, line_total, currency, bom_id, costing_id, reason_for_rejection money Numeric(18, 4) src/cluster_erp/modules/quotations/line_items/models.py:49-137
QuoteRejectionReason quot_rejection_reasons code, label_en, label_tr, is_active reference data src/cluster_erp/modules/quotations/line_items/models.py:23-46
QuoteDocumentFlow quot_document_flow quote_id, quote_line_item_id, order_id, order_line_id, referenced_qty, referenced_value, currency one row per sales order reference src/cluster_erp/modules/quotations/line_items/models.py:139-171

Line breakdowns are checked with ensure_product_breakdown (src/cluster_erp/modules/quotations/line_items/services.py:102-106, src/cluster_erp/modules/quotations/line_items/services.py:131-136).

Screens and metadata

get_metadata declares a sidebar group Sales Quotations (Turkish Satış Teklifleri) with one list section of the same name (src/cluster_erp/modules/quotations/module.py:203-222). The entities are quote, quote_line_item, mgmt_approval, quote_document_flow, and quote_history (src/cluster_erp/modules/quotations/module.py:224-230). The quote card reads its completeness, document flow, history, versions, and similar projects from dedicated routes (src/cluster_erp/modules/quotations/quotes/routes.py:384-439). See generic detail.

Status and actions

The service allows these transitions (src/cluster_erp/modules/quotations/quotes/services.py:59-74).

stateDiagram-v2
 [*] --> draft
 draft --> feasibility: start_feasibility
 feasibility --> design_required: mark_design_required
 feasibility --> bom_pending: complete_feasibility
 design_required --> bom_pending: complete_design
 bom_pending --> costing: attach_bom
 costing --> bom_pending: return_to_bom
 costing --> pending_mgmt_approval: attach_costing
 pending_mgmt_approval --> mgmt_rejected: decision rejected
 pending_mgmt_approval --> sent: send
 sent --> won: mark_won
 sent --> lost: mark_lost
 sent --> expired: mark_expired

In prose: a quote starts as draft and goes through feasibility. If a new design is needed it passes design_required. A BOM moves it to costing, and a costing to pending_mgmt_approval. A rejected decision ends it as mgmt_rejected. Sending needs an approved or conditional decision (src/cluster_erp/modules/quotations/quotes/services.py:550-616). won, lost, expired, and mgmt_rejected are terminal; revise creates a new version from pending_mgmt_approval, mgmt_rejected, expired, or lost (src/cluster_erp/modules/quotations/quotes/services.py:73, src/cluster_erp/modules/quotations/quotes/services.py:687-743). feasibility_failed soft-deletes the quote and emits RfqDeclined (src/cluster_erp/modules/quotations/quotes/services.py:744-774).

Action Label Condition Endpoint Permission Source
start_feasibility Start Feasibility status is draft POST /quotes/{id}/start-feasibility quote.start_feasibility src/cluster_erp/modules/quotations/quotes/metadata.py:103-113
mark_design_required Design Required status is feasibility POST /quotes/{id}/mark-design-required quote.mark_design_required src/cluster_erp/modules/quotations/quotes/metadata.py:114-127
complete_feasibility Complete Feasibility status is feasibility POST /quotes/{id}/complete-feasibility quote.complete_feasibility src/cluster_erp/modules/quotations/quotes/metadata.py:128-152
complete_design Design Complete status is design_required POST /quotes/{id}/complete-design quote.complete_design src/cluster_erp/modules/quotations/quotes/metadata.py:153-166
attach_bom Attach BOM status is bom_pending POST /quotes/{id}/attach-bom quote.attach_bom src/cluster_erp/modules/quotations/quotes/metadata.py:167-202
attach_costing Attach Costing status is costing POST /quotes/{id}/attach-costing quote.attach_costing src/cluster_erp/modules/quotations/quotes/metadata.py:203-249
schedule_mgmt_approval Schedule Approval status is pending_mgmt_approval POST /quotes/{id}/schedule-mgmt-approval quote.schedule_mgmt_approval src/cluster_erp/modules/quotations/quotes/metadata.py:250-285
record_decision Record Decision on an approval row POST /quotes/{parent_id}/record-mgmt-decision mgmt_approval.record_decision src/cluster_erp/modules/quotations/quotes/metadata.py:790-819
send Send to Customer status is pending_mgmt_approval POST /quotes/{id}/send quote.send src/cluster_erp/modules/quotations/quotes/metadata.py:286-307
mark_won, mark_lost, mark_expired Mark as Won, Mark as Lost, Mark as Expired status is sent POST /quotes/{id}/mark-won, /mark-lost, /mark-expired quote.mark_won, quote.mark_lost, quote.mark_expired src/cluster_erp/modules/quotations/quotes/metadata.py:308-338, src/cluster_erp/modules/quotations/quotes/metadata.py:394-407
revise Revise status is pending_mgmt_approval, mgmt_rejected, expired, or lost POST /quotes/{id}/revise quote.revise src/cluster_erp/modules/quotations/quotes/metadata.py:339-354
feasibility_failed Feasibility Failed status is feasibility POST /quotes/{id}/feasibility-failed quote.feasibility_failed src/cluster_erp/modules/quotations/quotes/metadata.py:357-379
return_to_bom Return to BOM status is costing POST /quotes/{id}/return-to-bom quote.return_to_bom src/cluster_erp/modules/quotations/quotes/metadata.py:380-393

Routes and permissions are in src/cluster_erp/modules/quotations/quotes/routes.py:156-366.

Business rules

  • A quote needs an existing RFQ, read with GetRfqById; only one active quote per RFQ is allowed (src/cluster_erp/modules/quotations/quotes/services.py:175-190, src/cluster_erp/modules/quotations/quotes/services.py:1297-1317).
  • Currency and payment terms default from the customer through GetCustomerById when not given (src/cluster_erp/modules/quotations/quotes/services.py:190-205).
  • quote_no comes from the yearly sequence quotation; versions count per RFQ (src/cluster_erp/modules/quotations/quotes/services.py:209, src/cluster_erp/modules/quotations/quotes/services.py:1318-1326).
  • Attaching a costing reads GetCostingById; the header cost falls back to the costing's total cost when no line carries a unit cost (src/cluster_erp/modules/quotations/quotes/services.py:399-466).
  • The roll-up sets total price to the sum of line totals, total cost to the sum of quantity times unit cost, and margin to price minus cost over cost times 100. Amounts are rounded with ROUND_HALF_EVEN to the decimal places of the currency in the Currency reference table (src/cluster_erp/modules/quotations/rollup.py:35-117).
  • A management decision can be recorded once per approval; a price adjustment overrides the net value (src/cluster_erp/modules/quotations/quotes/services.py:492-549).
  • Sending is blocked while the completeness log has unmet items: a non-rejected line, a total price, a currency, and payment terms (src/cluster_erp/modules/quotations/quotes/services.py:574-588, src/cluster_erp/modules/quotations/quotes/services.py:828-876).
  • Lines cannot change after the quote is sent; a rejection reason must exist and be active (src/cluster_erp/modules/quotations/line_items/services.py:150-175, src/cluster_erp/modules/quotations/line_items/services.py:255-270).
  • A sent quote cannot be deleted; delete sets deleted_at (src/cluster_erp/modules/quotations/quotes/services.py:273-287).
  • When sales order references cover every referenceable line, a sent quote becomes won and QuoteWon is emitted (src/cluster_erp/modules/quotations/quotes/services.py:925-997).

Events and queries

The module consumes 6 contracts and produces 18, counted as on the parties module page. The full list is on the contracts reference.

Direction Contract class Handler or emitter Source
emits QuoteCreated, QuoteFeasibilityCompleted, QuoteBomAttached, QuoteCostingCompleted, QuoteMgmtApprovalRecorded, QuoteSent, QuoteWon, QuoteLost, QuoteExpired, RfqDeclined QuoteService lifecycle methods src/cluster_erp/modules/quotations/quotes/services.py:230, src/cluster_erp/modules/quotations/quotes/services.py:340, src/cluster_erp/modules/quotations/quotes/services.py:631, src/cluster_erp/modules/quotations/quotes/services.py:766, src/cluster_erp/modules/quotations/quotes/services.py:989
queries GetRfqById, GetCustomerById, GetCostingById, GetRfqIdsByCustomer services src/cluster_erp/modules/quotations/quotes/services.py:178, src/cluster_erp/modules/quotations/quotes/services.py:414
answers query GetQuoteById, GetQuoteLineItems, ReferenceQuoteLines, ReleaseQuoteLineReferences handlers in module.py src/cluster_erp/modules/quotations/module.py:63-68
subscribes DescribeAnalyticsFacts, DescribeFieldConsumers analytics and tenant fields src/cluster_erp/modules/quotations/module.py:69-84

Permissions

Permission key Used by Source
quote.read, .create, .update, .delete, .bulk_delete quote records and read routes src/cluster_erp/modules/quotations/quotes/routes.py:52-141, src/cluster_erp/modules/quotations/quotes/routes.py:384-439
one key per lifecycle route, for example quote.send and quote.revise lifecycle routes src/cluster_erp/modules/quotations/quotes/routes.py:156-366
mgmt_approval.read, mgmt_approval.record_decision approval routes src/cluster_erp/modules/quotations/quotes/routes.py:236, src/cluster_erp/modules/quotations/quotes/routes.py:267
quote_line_item.read, .create, .update, .delete, .bulk_delete line routes src/cluster_erp/modules/quotations/line_items/routes.py:24-142

Privacy, retention, and history

The module has no privacy_handlers.py. Quotes and lines are soft-deleted (src/cluster_erp/modules/quotations/quotes/services.py:273-287). The route GET /quotes/{quote_id}/history reads the quote's change history (src/cluster_erp/modules/quotations/quotes/services.py:1094-1183); see change history.

Analytics facts

analytics_facts.py contributes the fact set sales_quote over quot_quotes, with the measures quote_count, total_price, total_cost, and median_price in the field currency, read with quote.read (src/cluster_erp/modules/quotations/analytics_facts.py:25-108). See analytics facts.

Settings and reference data

Rejection reasons are reference data in quot_rejection_reasons, listed by GET /quotes/rejection-reasons (src/cluster_erp/modules/quotations/line_items/routes.py:24). The currency's decimal places come from the core currency table (src/cluster_erp/modules/quotations/rollup.py:35-45); see money and decimal.

Tests

Found by a grep for modules.quotations in tests/ (no hit) and for the path /quotations/ and file names in web-client/.

Kind File Covers
e2e web-client/_e2e_quotations_app.mjs the quotations app
e2e web-client/_e2e_quote_line_bom.mjs quote line to order line to BOM chain
e2e web-client/_e2e_order_shipment_chain.mjs quote to order to shipment
e2e web-client/_e2e_project_otd.mjs, web-client/_e2e_mrp_pir_faz1.mjs quotes as demand context

Discrepancies

Terms introduced

Term Code identifier Definition Turkish term Source
sales quote Quote A priced offer to a customer that answers a customer RFQ. Satış teklifi src/cluster_erp/modules/quotations/quotes/models.py:37
management approval MgmtApproval The meeting record and decision that releases a quote for sending. Yönetim onayı src/cluster_erp/modules/quotations/quotes/models.py:171
document flow QuoteDocumentFlow The record of which sales order lines reference which quote lines. Belge akışı src/cluster_erp/modules/quotations/line_items/models.py:139
completeness log QuoteRequirement The list of checks that must pass before a quote is sent. Eksiksizlik günlüğü src/cluster_erp/modules/quotations/quotes/services.py:77-88

Verified against commit 92f8051 on 2026-11-13.

Verified against commit 92f8051 · 2026-11-13