Agency Flow · Prezentare de flux

C · Supplier fixed — pass-through cu profit zero

Prima întâlnire cu familia supplier: fără muncă internă, fără pontaje. Agenția plătește un furnizor o sumă fixă și facturează clientului exact aceeași sumă. Profit 0 — și e corect așa. Noutatea: fiecare lună lasă o linie de cost, ancora viitoarei facturi de furnizor. Într-o lună parțială, costul și factura se înmulțesc cu același billable_days / calendar_days, deci invariantul de profit zero se păstrează în calculul normal. Un override manual al perioadei devine suma finală către client fără să schimbe costul contractual, deci poate produce intenționat un profit diferit de zero.

Account manager — setup Engine — automat Finance — facturi (AR + AP)
1

O singură dată · la vânzarea serviciului

Setup: supplier obligatoriu, cost fix

Account manager

La tipurile supplier, drawerul cere un furnizor din lista fixă — devine parte din numele compus. CHECK-ul din DB întoarce regula față de A1: aici supplier-ul nu are voie să lipsească.

Drawer · New service
Northwind Retail — Magento · northwind.ro
Webmastering
grup derivat: SEO
Supplier — fixed cost (C)
Genezio
obligatoriu — intră în numele compus
2.000 RON
nume compus → Northwind Retail — SEO — Webmastering — Genezio
În baza de date · la Save
INSERT service + service_pricing_supplier_fixed
billing_type: 'supplier_fixed' supplier_id: → Genezio supplier_cost: 2000.00
CHK_SERVICE_SUPPLIER_BY_TYPE ✓ — C e single-supplier, supplier_id e obligatoriu. Alt serviciu Webmastering pe același asset dar cu alt supplier ar fi un serviciu distinct (supplierul e în cheia unică).
2

1 august · automat

Luna se deschide: perioadă + linia de cost

Engine

Ca la A1, totul e cunoscut din setup, deci perioada se naște ready. Noutatea: engine-ul materializează și linia de cost a lunii — rândul din service_cost_line pe care îl va revendica factura furnizorului. Nimeni nu editează linia direct; e oglinda perioadei.

În baza de date
INSERT service_billing_period
period: 2024-08-01 state: 'ready' ready_at: 2024-08-01 06:00 snapshot_pricing_supplier_cost: 2000.00 snapshot_client_amount: 2000.00 snapshot_supplier_cost: 2000.00 snapshot_labour_cost: 0.00
INSERT service_cost_line · materializată de engine
line_type: 'fixed_supplier' supplier_id: → Genezio quantity: 1 unit_cost: 2000.00 unit_fee: 0.00 client_amount: 2000.00 state: 'ready' — oglindește perioada
GENERATED calculate de DB
snapshot_gross_profit: 0.00 snapshot_net_profit: 0.00
0, nu NULL — „calculat și e zero" ≠ „necalculat". Pass-through-ul cu profit 0 e un rezultat legitim, nu o eroare.
Invariantul de verificat oricând: snapshot_supplier_cost al perioadei = Σ supplier_cost_amount al liniilor ei. Aici: 2.000 = 2.000, o singură linie.
3

1 septembrie · batch lunar

Factura către client (AR — Accounts Receivable)

Finance modulul Billing — urmează

Linia de factură = exact snapshot-ul: 2.000, fără niciun adaos. Perioada și linia de cost trec împreună pe invoiced.

Client invoice · DR-2024-0140
linieamountTVA 21%total
Northwind Retail — SEO — Webmastering — Genezio · aug2.000,00420,002.420,00
4

Când sosește factura Genezio

Factura de la furnizor (AP — Accounts Payable) — bucla se închide

Finance modulul Billing — urmează

Aici muncește service_cost_line: când finance înregistrează factura primită de la Genezio, linia ei se leagă de linia de cost a lunii printr-un singur FK — același mecanism pentru orice billing type cu furnizor. De acum, AR și AP ale serviciului sunt reconciliabile rând cu rând.

În baza de date · la înregistrarea facturii furnizorului
INSERT supplier_invoice + supplier_invoice_line
supplier_id: → Genezio amount: 2000.00 service_cost_line_id: → linia lunii august
FK-ul din spec §1.14 — motivul pentru care linia de cost există și la tipurile „plictisitoare" ca C.
client_invoice
2.000
AR — emisă clientului
supplier_cost
2.000
AP — factura Genezio
net_profit
0
pass-through legitim
De ce ar exista un serviciu cu profit 0? Pentru că agenția intermediază contractual (clientul vrea o singură factură, de la agenție), iar valoarea se face în alte servicii ale aceluiași client. Modelul nu forțează profit unde nu există — asta ține rapoartele oneste.
DB

service_billing_period — ce se scrie și ce se actualizează

C se naște ready — totul e cunoscut din setup; rândul lunar aproape nu se mai atinge după naștere.

service_billing_period · ciclul de scriere al rândului lunar
INSERT service_billing_period · la generare (naștere)
state: 'ready' ready_at: setat acum input_supplier_cost: NULL — nu aparțin C input_supplier_hours: NULL — nu aparțin C input_amount_spend: NULL — nu aparțin C snapshot_pricing_supplier_cost: = supplier_cost fix folosit la calcul snapshot_client_amount: = supplier_cost (2.000) snapshot_supplier_cost: = supplier_cost (2.000) snapshot_labour_cost: 0 snapshot_labour_hours: 0 snapshot_direct_profit: 0 snapshot_fee_profit: 0 snapshot_labour_recovery: 0 snapshot_gross_profit: generat de DB → 0 snapshot_net_profit: generat de DB → 0 snapshot_recovery_net: generat de DB → 0
În paralel engine-ul materializează 1 linie în service_cost_line (line_type='fixed_supplier', qty 1).
UPDATE service_billing_period · la emitere
state: 'ready' → 'invoiced' invoiced_at: setat
Fără UPDATE-uri pe parcursul lunii (nimic nu se schimbă). Editarea lui supplier_cost în setup NU atinge lunile cu ready_at setat — doar lunile încă writable. Aplicarea default-ului curent unei luni ready, nefacturate, cere reopen + recompute explicit și autorizat.
DB

service_cost_line — linia materializată de engine

La C, engine-ul materializează o singură linie de cost pe lună (nu omul), o îngheață odată cu perioada și o folosește ca ancoră AP. Într-o lună parțială, unit_cost și amounts țin valoarea prorată a perioadei, la qty 1. Override-ul manual înlocuiește doar client_amount; costul proratat rămâne contractual, iar gross-ul poate deveni nenul. Descrierea comună C/D este tăiată UTF-8-safe la maximum 255 de caractere și păstrează obligatoriu sufixul — YYYY-MM. Un refresh obișnuit pe o perioadă deja ready nu rescrie suma; doar un reopen explicit regenerează același rând. Nu se editează direct — starea ei oglindește perioada.

service_cost_line · linia lunii (materializată)
INSERT service_cost_line · la nașterea perioadei (materializare de engine)
billing_period_id: → perioada lunii supplier_id: → service.supplier_id line_type: 'fixed_supplier' description: auto — numele compus + luna quantity: 1 unit_cost: = supplier_cost (2.000) unit_fee: 0 supplier_cost_amount: 2.000 (= qty × unit_cost) fee_amount: 0 client_amount: 2.000 (= cost + fee) state: 'ready' — oglindește perioada position: 1
UPDATE service_cost_line · la emiterea facturii
state: 'ready' → 'invoiced' (Locked)
1 linie/lună, materializată de engine, NU editabilă direct; e ținta unică AP — supplier_invoice_line.service_cost_line_id pointează aici. Invariant: snapshot_supplier_cost = Σ supplier_cost_amount al liniilor perioadei. Editarea lui supplier_cost în setup pe o lună neînghețată → UPDATE unit_cost + amounts.

Simulare · pass-through-ul cu profit zero

interactiv · doar vizual

C n-are input lunar: totul cade din setup. Mișcă default-ul supplier_cost/lună: perioada ready își păstrează snapshot-ul; abia acțiunea explicită de reopen îl aplică identic facturii clientului și liniei de cost. Profitul rămâne 0, deliberat: agenția facturează exact cât plătește. Nimic nu se salvează — e o schiță.

Account manager — setează costul fix Finance — emite factura (AR + AP)
Setup · Supplier cost fix rol: Account manager
C e single-supplier, cost fix: client_invoice = supplier_cost, fără adaos. Editarea recalculează doar lunile cu ready_at IS NULL; luna ready de mai jos își păstrează snapshot-ul până la reopen explicit.
Linia de cost a lunii starea perioadei: ready
line_typeqtyunit_cost unit_feeclientstate
client_invoice
2.000
AR — = supplier_cost
supplier_cost
2.000
AP — factura furnizorului
gross_profit
0
client − supplier = 0
net_profit
0
pass-through, labour 0
Profitul 0 e CORECT — C e pass-through pur; linia de cost există totuși fiindcă e ancora facturii furnizorului (AP). 0 ≠ NULL: 0 = calculat și e zero, nu „necalculat". Editarea setup-ului recalculează doar lunile cu ready_at IS NULL — o lună deja ready cere reopen/recompute explicit, iar una invoiced rămâne neatinsă.