Skip to content

Commit e7c3404

Browse files
Dawid Makowskiclaude
andcommitted
docs: add architecture, sequence, ERD and resource diagrams to README
- Generic architecture flowchart right after the intro - Runtime import sequence diagram - Full ERD with every table and field - Separate Eloquent models class diagram + list - Separate Nova resources diagram + list (replaces the screenshot placeholder) Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
1 parent 238ac00 commit e7c3404

1 file changed

Lines changed: 302 additions & 4 deletions

File tree

README.md

Lines changed: 302 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,42 @@
1111

1212
You run one command. You get a fully populated `invoices` table with line items, tax details, seller and buyer parties, bank details, and the complete raw payload, all related and ready to query.
1313

14+
## Architecture at a glance
15+
16+
```mermaid
17+
flowchart TB
18+
FILE["Invoice file<br/>PDF / JPG / PNG / TIFF / DOC / DOCX"]
19+
CLI["Artisan command<br/>invoice-manager:import"]
20+
JOB["Queued job<br/>ParseAndStoreInvoiceJob"]
21+
PS["InvoiceParserService<br/>(sharpapi/laravel-invoice-parser)"]
22+
API[("SharpAPI<br/>Invoice Parser API")]
23+
IMP["InvoiceImporter<br/>map + persist<br/>(DB transaction, idempotent)"]
24+
DB[("Database<br/>invoice_* tables")]
25+
EVT(["InvoiceImported event"])
26+
NOVA["Laravel Nova admin<br/>(optional, auto-registered)"]
27+
APP["Your code:<br/>storeMany(result)"]
28+
29+
FILE --> CLI
30+
FILE --> JOB
31+
CLI --> PS
32+
JOB --> PS
33+
PS -->|"parseInvoice()"| API
34+
API -->|"fetchResults(): result JSON"| PS
35+
CLI --> IMP
36+
JOB --> IMP
37+
APP --> IMP
38+
IMP --> DB
39+
IMP --> EVT
40+
DB --> NOVA
41+
42+
classDef pkg fill:#eef6ff,stroke:#3b82f6,color:#0b3a82;
43+
classDef ext fill:#fff7ed,stroke:#fb923c,color:#7c2d12;
44+
class CLI,JOB,IMP,EVT,NOVA pkg;
45+
class PS,API ext;
46+
```
47+
48+
This package owns the blue boxes: the import command and job, the `InvoiceImporter`, the `InvoiceImported` event, and the optional Nova admin. The parsing itself is delegated to `sharpapi/laravel-invoice-parser`, which calls the SharpAPI endpoint. See [How an import flows](#how-an-import-flows) for the runtime sequence and [Data model](#data-model-all-tables-and-fields) for the full schema.
49+
1450
---
1551

1652
## Why this exists (the value)
@@ -192,9 +228,42 @@ public function handle(InvoiceImported $event): void
192228

193229
---
194230

195-
## Database schema overview
231+
## How an import flows
232+
233+
```mermaid
234+
sequenceDiagram
235+
autonumber
236+
participant App as Your app (command or job)
237+
participant Parser as InvoiceParserService
238+
participant API as SharpAPI
239+
participant Importer as InvoiceImporter
240+
participant DB as Database
241+
participant Event as InvoiceImported
242+
243+
App->>Parser: parseInvoice(filePath)
244+
Parser->>API: POST /finance/parse_invoice
245+
API-->>Parser: status_url
246+
App->>Parser: fetchResults(status_url)
247+
Parser->>API: poll until job done
248+
API-->>Parser: result JSON (array of invoices)
249+
App->>Importer: storeMany(result, meta)
250+
loop each invoice element
251+
Importer->>DB: begin transaction
252+
Importer->>DB: upsert invoice (idempotent on sharp_api_job_id)
253+
Importer->>DB: insert parties, bank details, line items, tax details
254+
Importer->>DB: commit
255+
Importer-)Event: dispatch(InvoiceImported)
256+
end
257+
Importer-->>App: Collection of Invoice models
258+
```
259+
260+
On import the mapper coerces empty strings to `null`, numeric strings to numbers, and date strings to Carbon dates. The whole element is also stored verbatim in `raw_payload` for lossless re-processing.
261+
262+
---
263+
264+
## Data model (all tables and fields)
196265

197-
All tables use the configurable `invoice_` prefix.
266+
All tables use the configurable `invoice_` prefix (shown without the prefix below). Money columns are `decimal(15,2)`, dates are real `date` columns, and empty strings from the API are coerced to `null`.
198267

199268
| Table | Holds |
200269
|-------|-------|
@@ -204,7 +273,212 @@ All tables use the configurable `invoice_` prefix.
204273
| `invoice_line_items` | Every line item, with money and quantities as decimals and date fields as real dates. |
205274
| `invoice_tax_details` | Tax breakdown rows (type, rate, taxable amount, tax amount). |
206275

207-
Money columns are `decimal(15,2)`, dates are real `date` columns, and empty strings from the API are coerced to `null`.
276+
```mermaid
277+
erDiagram
278+
invoices ||--o{ invoice_parties : "parties (seller, buyer)"
279+
invoices ||--o{ invoice_line_items : lineItems
280+
invoices ||--o{ invoice_tax_details : taxDetails
281+
invoice_parties ||--o{ invoice_bank_details : bankDetails
282+
283+
invoices {
284+
bigint id PK
285+
uuid uuid "indexed"
286+
string sharp_api_job_id UK "nullable; idempotency key"
287+
string source_file_path "nullable"
288+
string source_file_name "nullable"
289+
string document_type
290+
string original_type_label
291+
boolean is_invoice
292+
boolean is_copy
293+
string copy_type
294+
string invoice_number "indexed"
295+
date issue_date "indexed"
296+
date due_date
297+
date document_date
298+
date order_date
299+
date delivery_date
300+
date shipping_date
301+
date pricing_date
302+
string currency "3 chars"
303+
decimal exchange_rate "15,6"
304+
string page_info
305+
text amount_in_words
306+
text notes
307+
text remarks
308+
text delivery_instructions
309+
decimal late_payment_interest_rate "8,4"
310+
json terms_and_conditions
311+
decimal subtotal "15,2"
312+
decimal gross_amount "15,2"
313+
decimal total_discount_amount "15,2"
314+
decimal shipping_charge "15,2"
315+
decimal delivery_fee "15,2"
316+
decimal total_excl_tax "15,2"
317+
decimal total_tax_amount "15,2"
318+
decimal service_tax_amount "15,2"
319+
decimal total_incl_tax "15,2"
320+
decimal rounding_adjustment "15,2"
321+
decimal total_payable "15,2"
322+
decimal amount_paid "15,2"
323+
decimal amount_due "15,2"
324+
json references
325+
json e_invoice
326+
json sales_info
327+
json payment
328+
json logistics
329+
json source_pages
330+
json raw_payload
331+
timestamp created_at
332+
timestamp updated_at
333+
}
334+
335+
invoice_parties {
336+
bigint id PK
337+
bigint invoice_id FK
338+
string role "seller or buyer"
339+
string name
340+
string trade_name
341+
string registration_number
342+
string tin
343+
string brn
344+
string sst_id
345+
string gst_id
346+
string vat_id
347+
string msic_code
348+
string business_activity
349+
string customer_account_number
350+
string billing_location_name
351+
string billing_recipient_name
352+
string billing_street_line_1
353+
string billing_street_line_2
354+
string billing_city
355+
string billing_state
356+
string billing_postcode
357+
string billing_country
358+
string delivery_recipient_name
359+
string delivery_location_name
360+
string delivery_street_line_1
361+
string delivery_street_line_2
362+
string delivery_city
363+
string delivery_state
364+
string delivery_postcode
365+
string delivery_country
366+
boolean delivery_address_same_as_billing
367+
string phone
368+
string fax
369+
string email
370+
string website
371+
json contact_person
372+
json attention_to
373+
timestamp created_at
374+
timestamp updated_at
375+
}
376+
377+
invoice_bank_details {
378+
bigint id PK
379+
bigint invoice_party_id FK
380+
string bank_name
381+
string account_name
382+
string account_number
383+
string sort_code
384+
string swift_code
385+
string iban
386+
timestamp created_at
387+
timestamp updated_at
388+
}
389+
390+
invoice_line_items {
391+
bigint id PK
392+
bigint invoice_id FK
393+
integer line_number "indexed"
394+
string item_code
395+
string stock_code
396+
string barcode
397+
text description
398+
string classification_code
399+
string country_of_origin
400+
decimal quantity "15,4"
401+
decimal free_quantity "15,4"
402+
string unit_of_measure
403+
string unit_of_measure_raw
404+
string pack_size
405+
decimal total_units "15,4"
406+
decimal weight "15,4"
407+
string weight_uom
408+
decimal unit_price "15,4"
409+
decimal discount_percent "8,4"
410+
decimal discount_amount "15,2"
411+
decimal subtotal "15,2"
412+
decimal tax_rate "8,4"
413+
string tax_type
414+
decimal tax_amount "15,2"
415+
decimal total_excl_tax "15,2"
416+
decimal total_incl_tax "15,2"
417+
date expiry_date
418+
string batch_lot_number
419+
date service_start_date
420+
date service_end_date
421+
timestamp created_at
422+
timestamp updated_at
423+
}
424+
425+
invoice_tax_details {
426+
bigint id PK
427+
bigint invoice_id FK
428+
string tax_type
429+
decimal tax_rate "8,4"
430+
decimal taxable_amount "15,2"
431+
decimal tax_amount "15,2"
432+
timestamp created_at
433+
timestamp updated_at
434+
}
435+
```
436+
437+
---
438+
439+
## Eloquent models
440+
441+
The package ships five Eloquent models (namespace `SharpAPI\InvoiceManager\Models`). Each can be swapped for your own subclass via the `models.*` config keys.
442+
443+
```mermaid
444+
classDiagram
445+
class Invoice {
446+
+lineItems() HasMany
447+
+taxDetails() HasMany
448+
+parties() HasMany
449+
+seller() HasOne
450+
+buyer() HasOne
451+
+getTotalAttribute() string
452+
}
453+
class InvoiceParty {
454+
+invoice() BelongsTo
455+
+bankDetails() HasMany
456+
+scopeSellers() Builder
457+
+scopeBuyers() Builder
458+
}
459+
class InvoiceBankDetail {
460+
+party() BelongsTo
461+
}
462+
class InvoiceLineItem {
463+
+invoice() BelongsTo
464+
}
465+
class InvoiceTaxDetail {
466+
+invoice() BelongsTo
467+
}
468+
Invoice "1" --> "*" InvoiceLineItem : lineItems
469+
Invoice "1" --> "*" InvoiceTaxDetail : taxDetails
470+
Invoice "1" --> "*" InvoiceParty : parties
471+
Invoice "1" --> "1" InvoiceParty : seller / buyer
472+
InvoiceParty "1" --> "*" InvoiceBankDetail : bankDetails
473+
```
474+
475+
| Model | Backing table | Config key | Relationships and helpers |
476+
|-------|---------------|------------|---------------------------|
477+
| `Invoice` | `invoices` | `models.invoice` | `lineItems`, `taxDetails`, `parties`, `seller`, `buyer`, `total` accessor |
478+
| `InvoiceParty` | `invoice_parties` | `models.party` | `invoice`, `bankDetails`, `sellers()` / `buyers()` scopes |
479+
| `InvoiceBankDetail` | `invoice_bank_details` | `models.bank_detail` | `party` |
480+
| `InvoiceLineItem` | `invoice_line_items` | `models.line_item` | `invoice` |
481+
| `InvoiceTaxDetail` | `invoice_tax_details` | `models.tax_detail` | `invoice` |
208482

209483
---
210484

@@ -251,7 +525,31 @@ If [Laravel Nova](https://nova.laravel.com/) is installed and `nova.enabled` is
251525

252526
If Nova is not installed, nothing happens and the rest of the package works exactly the same.
253527

254-
> Nova admin screenshot placeholder. Add a screenshot of the Invoice detail view here.
528+
The package ships five Nova resources (namespace `SharpAPI\InvoiceManager\Nova`), listed separately from the Eloquent models above. Only the top-level **Invoice** resource appears in the navigation; the rest are exposed as related (HasMany) sub-resources from the invoice detail view.
529+
530+
```mermaid
531+
flowchart TB
532+
INV["Invoice resource<br/>navigation group: Invoices<br/>index: invoice #, seller, buyer, currency, total, issue date<br/>panels: Document, Invoice, Financials, Raw Groups"]
533+
PARTY["InvoiceParty resource<br/>hidden from navigation"]
534+
LI["InvoiceLineItem resource<br/>hidden from navigation"]
535+
TAX["InvoiceTaxDetail resource<br/>hidden from navigation"]
536+
BANK["InvoiceBankDetail resource<br/>hidden from navigation"]
537+
538+
INV -->|"HasMany lineItems"| LI
539+
INV -->|"HasMany taxDetails"| TAX
540+
INV -->|"HasMany parties"| PARTY
541+
PARTY -->|"HasMany bankDetails"| BANK
542+
```
543+
544+
| Nova resource | Backing model | In navigation | Key fields and panels |
545+
|---------------|---------------|---------------|-----------------------|
546+
| `Nova\Invoice` | `Invoice` | Yes (group "Invoices") | Index: invoice number, seller, buyer, currency, total, issue date. Detail panels: Document, Invoice, Financials, Raw Groups (references, e-invoice, payment, logistics, sales info, raw payload as read-only code fields). HasMany: line items, tax details, parties. |
547+
| `Nova\InvoiceParty` | `InvoiceParty` | No (sub-resource) | Role, name, VAT id, registration number, addresses, contact JSON. HasMany bank details. |
548+
| `Nova\InvoiceLineItem` | `InvoiceLineItem` | No (sub-resource) | Line number, description, quantity, unit price, subtotal, tax, totals, codes, expiry date. |
549+
| `Nova\InvoiceTaxDetail` | `InvoiceTaxDetail` | No (sub-resource) | Tax type, rate, taxable amount, tax amount. |
550+
| `Nova\InvoiceBankDetail` | `InvoiceBankDetail` | No (sub-resource) | Bank name, account name, account number, SWIFT, IBAN. |
551+
552+
To point the admin at your own customized resource, set `nova.resource` in the config to your subclass. The sub-resources are registered automatically alongside it.
255553

256554
---
257555

0 commit comments

Comments
 (0)