diff --git a/README.md b/README.md index 512a540..f715b68 100644 --- a/README.md +++ b/README.md @@ -285,6 +285,22 @@ Suppliers are `entity` rows in the company `customergroup` named `Lieferanten` ( --- +## `articlesupplier` + +Maps articles to suppliers (`entity`) with purchase pricing, stock, packaging unit, and supplier SKU: + +| Column | Type | Constraints / Description | +| --- | --- | --- | +| `id` | `INT` | Primary Key (`IDENTITY(1,1)`) | +| `article_id` | `INT` | FK → `article(id)` (`ON DELETE CASCADE`) | +| `entity_id` | `INT` | FK → `entity(id)` (`ON DELETE CASCADE`) | +| `price_id` | `INT` | FK → `price(id)` (`ON DELETE NO ACTION`), nullable | +| `SupplierSKU` | `NVARCHAR(100)` | Supplier's SKU code, nullable | +| `SupplierStock` | `FLOAT` | Stock available at supplier, nullable | +| `pu` | `FLOAT` | Packaging unit (Verpackungseinheit), default `1` | + +--- + ## `delivery` Company-scoped goods receipts on `manifest` / `manifestarticle` with fixed `type = 'delivery'`. Auth via selected company (`google_auth.selected_company` + membership). `manifest.company_id` scopes the list (`ON DELETE NO ACTION` — avoid SQL Server multiple cascade paths with `entity_id`). @@ -311,6 +327,8 @@ type DeliveryLine = { id: number, articleId: number | null, sku: string | null, + customsku: string | null, + customname: string | null, amount: number, net: number, rebate: number, @@ -321,7 +339,7 @@ type DeliveryLine = { type Delivery = DeliverySummary & { lines: DeliveryLine[] } ``` -`create` inserts header + lines in one transaction. The `number` is auto-allocated via `getnextnumberwithprefixandpadding` (`numbers` type `delivery`); supply `externalNumber` for an external (supplier) reference. `supplierId` null or a Lieferanten `entity` of the company (else `Supplier not found`). Each line requires `sku` (resolved in the company category tree → `Article not found`), `amount`, `net`, `tax` (rate → `tax.ensure`); `rebate` defaults `0`. Empty/invalid `date` → `Invalid delivery`; bad line payload → `Invalid lines`. +`create` inserts header + lines in one transaction. The `number` is auto-allocated via `getnextnumberwithprefixandpadding` (`numbers` type `delivery`); supply `externalNumber` for an external (supplier) reference. `supplierId` null or a Lieferanten `entity` of the company (else `Supplier not found`). Each line requires either `sku` (resolved in the company category tree → `Article not found`; `customsku` and `customname` forced `null`) or custom line fields (`customsku` / `customname` with `articleId` set to `null`). If `article_id` is set, `customsku` and `customname` must be null (`CK_manifestarticle_article_or_custom`). `amount`, `net`, `tax` (rate → `tax.ensure`); `rebate` defaults `0`. Empty/invalid `date` → `Invalid delivery`; bad line payload → `Invalid lines`. `update` patches header fields that are present. If `lines` is present, replace-all lines; if omitted, leave lines unchanged. Returns full `Delivery`. @@ -635,7 +653,7 @@ Auth via the article's `categoryarticle` category + `resolveCategoryAccess`. Err `orderImages` sets `articlepic.sort` to `0..n-1` in the given order. `fullHashes` must list every image of the article exactly once. Errors: `Article not found`, `Invalid image order`, `Not authorized`. -`delete` removes the article when it has no child articles (`fatherarticle_id` FK is `ON DELETE NO ACTION`). Cascades: `articlehistory`, `articleean`, `categoryarticle`, `price`, `articletext`, `articlelistarticle`, `optionslistarticle`, `articlepic`; deletes linked `pic` rows and owned `articlelist` / `optionslist`. `manifestarticle.article_id` is set to `NULL` and `comment` gets `article deleted` appended. Errors: `Article not found`, `Article has children`, `Not authorized`. +`delete` removes the article when it has no child articles (`fatherarticle_id` FK is `ON DELETE NO ACTION`). Cascades: `articlehistory`, `articleean`, `categoryarticle`, `price`, `articlesupplier`, `articletext`, `articlelistarticle`, `optionslistarticle`, `articlepic`; deletes linked `pic` rows and owned `articlelist` / `optionslist`. `manifestarticle.article_id` is set to `NULL` and `comment` gets `article deleted` appended. Errors: `Article not found`, `Article has children`, `Not authorized`. --- diff --git a/generate-schema-graph.js b/generate-schema-graph.js index 8271415..3918e68 100644 --- a/generate-schema-graph.js +++ b/generate-schema-graph.js @@ -355,6 +355,7 @@ const CLUSTERS = [ 'articlelistarticle', 'optionslist', 'optionslistarticle', + 'articlesupplier', ], }, { @@ -368,6 +369,7 @@ const CLUSTERS = [ tables: ['tax', 'price', 'manifest', 'manifestarticle', 'ledger'], }, { id: 'units', label: 'Units', tables: ['unit'] }, + { id: 'pos', label: 'POS', tables: ['posterminal', 'posterminalcategory'] }, ]; const COLORS = { diff --git a/schema-graph.dot b/schema-graph.dot index 9242711..f7d955b 100644 --- a/schema-graph.dot +++ b/schema-graph.dot @@ -9,14 +9,14 @@ digraph schema { label="Identity & Company"; labeljust="l"; fontsize=15; style="rounded,filled"; fillcolor="#202a3a"; color="#3f4c5f"; fontcolor="#e2e8f0"; margin=14; - customergroup [label=<
customergroup
idINT PK
company_id?INT FK
nameNVARCHAR(255)
sortINT
>]; meta [label=<
meta
keyNVARCHAR(255)
valueNVARCHAR(MAX)
>]; + customergroup [label=<
customergroup
idINT PK
company_id?INT FK
nameNVARCHAR(255)
sortINT
>]; + company [label=<
company
idINT PK
taxrate?INT FK
reducedtaxrate?INT FK
taxfreerate?INT FK
root_category_id?INT FK
default_language_id?INT FK
poscustomergroup_idINT FK
nameNVARCHAR(255)
>]; numbers [label=<
numbers
idINT PK
company_idINT FK
typeNVARCHAR(100)
prefixNVARCHAR(50)
paddingINT
nextINT
>]; entityaddress [label=<
entityaddress
idINT PK
entity_idINT FK
first_name?NVARCHAR(255)
name?NVARCHAR(255)
street?NVARCHAR(255)
streetnumber?NVARCHAR(50)
zip?NVARCHAR(50)
city?NVARCHAR(255)
county?NVARCHAR(255)
addresstypeNVARCHAR(50)
sortINT
>]; - entity [label=<
entity
idINT PK
customergroup_idINT FK
first_name?NVARCHAR(255)
nameNVARCHAR(255)
number?NVARCHAR(100)
created_atDATETIME2
updated_atDATETIME2
>]; + entity [label=<
entity
idINT PK
customergroup_idINT FK
first_name?NVARCHAR(255)
nameNVARCHAR(255)
number?NVARCHAR(100)
created_atDATETIME2
updated_atDATETIME2
>]; companyuser [label=<
companyuser
idINT PK
company_idINT FK
google_auth_idINT FK
>]; entitycontact [label=<
entitycontact
idINT PK
entity_idINT FK
contacttypeNVARCHAR(50)
contactNVARCHAR(255)
sortINT
>]; - company [label=<
company
idINT PK
taxrate?INT FK
reducedtaxrate?INT FK
taxfreerate?INT FK
poscustomergroup_idINT FK
root_category_id?INT FK
default_language_id?INT FK
nameNVARCHAR(255)
>]; syncpos [label=<
syncpos
idINT PK
company_idINT FK
paircodeNVARCHAR(50)
token?NVARCHAR(255)
>]; entityhistory [label=<
entityhistory
idINT PK
entity_idINT FK
companyuser_id?INT FK
dateDATETIME2
textNVARCHAR(MAX)
>]; companysetting [label=<
companysetting
idINT PK
company_idINT FK
keyNVARCHAR(255)
valueNVARCHAR(MAX)
frontendBIT
>]; @@ -28,41 +28,42 @@ digraph schema { fontcolor="#e2e8f0"; margin=14; language [label=<
language
idINT PK
iso639NVARCHAR(10)
>]; unittext [label=<
unittext
idINT PK
language_idINT FK
unit_idINT FK
symbolNVARCHAR(50)
nameNVARCHAR(255)
>]; - articletext [label=<
articletext
idINT PK
article_idINT FK
language_idINT FK
nameNVARCHAR(255)
slugNVARCHAR(255)
seo?NVARCHAR(MAX)
desc?NVARCHAR(MAX)
>]; categorytext [label=<
categorytext
idINT PK
category_idINT FK
language_idINT FK
nameNVARCHAR(255)
slugNVARCHAR(255)
seo?NVARCHAR(MAX)
desc?NVARCHAR(MAX)
>]; + articletext [label=<
articletext
idINT PK
article_idINT FK
language_idINT FK
nameNVARCHAR(255)
slugNVARCHAR(255)
seo?NVARCHAR(MAX)
desc?NVARCHAR(MAX)
>]; } subgraph cluster_catalog { label="Catalog"; labeljust="l"; fontsize=15; style="rounded,filled"; fillcolor="#202a3a"; color="#3f4c5f"; fontcolor="#e2e8f0"; margin=14; - brand [label=<
brand
idINT PK
company_idINT FK
nameNVARCHAR(255)
gsprpinfo?NVARCHAR(MAX)
>]; - articlehistory [label=<
articlehistory
idINT PK
companyuser_id?INT FK
article_idINT FK
dateDATETIME2
textNVARCHAR(MAX)
>]; - article [label=<
article
idINT PK
brand_id?INT FK
fatherarticle_id?INT FK
articlelist_id?INT FK
optionslist_id?INT FK
unit_idINT FK
sku?NVARCHAR(100)
unitamountFLOAT
weightFLOAT
is_fatherBIT
fathersortINT
created_atDATETIME2
updated_atDATETIME2
>]; - articleean [label=<
articleean
idINT PK
article_idINT FK
eanNVARCHAR(50)
>]; category [label=<
category
idINT PK
parent_id?INT FK
sortINT
>]; + categoryarticle [label=<
categoryarticle
idINT PK
category_idINT FK
article_idINT FK
>]; + brand [label=<
brand
idINT PK
company_idINT FK
nameNVARCHAR(255)
gsprpinfo?NVARCHAR(MAX)
>]; + articlehistory [label=<
articlehistory
idINT PK
article_idINT FK
companyuser_id?INT FK
dateDATETIME2
textNVARCHAR(MAX)
>]; + article [label=<
article
idINT PK
brand_id?INT FK
fatherarticle_id?INT FK
articlelist_id?INT FK
optionslist_id?INT FK
unit_idINT FK
sku?NVARCHAR(100)
unitamountFLOAT
weightFLOAT
is_fatherBIT
fathersortINT
created_atDATETIME2
updated_atDATETIME2
>]; + articleean [label=<
articleean
idINT PK
article_idINT FK
eanNVARCHAR(50)
>]; articlelist [label=<
articlelist
idINT PK
>]; - categoryarticle [label=<
categoryarticle
idINT PK
article_idINT FK
category_idINT FK
>]; articlelistarticle [label=<
articlelistarticle
idINT PK
article_idINT FK
articlelist_idINT FK
countINT
sortINT
>]; - optionslistarticle [label=<
optionslistarticle
idINT PK
article_idINT FK
optionslist_idINT FK
countINT
sortINT
mandatedBIT
>]; + articlesupplier [label=<
articlesupplier
idINT PK
price_id?INT FK
article_idINT FK
entity_idINT FK
SupplierSKU?NVARCHAR(100)
SupplierStock?FLOAT
puFLOAT
>]; optionslist [label=<
optionslist
idINT PK
>]; + optionslistarticle [label=<
optionslistarticle
idINT PK
article_idINT FK
optionslist_idINT FK
countINT
sortINT
mandatedBIT
>]; } subgraph cluster_media { label="Media"; labeljust="l"; fontsize=15; style="rounded,filled"; fillcolor="#202a3a"; color="#3f4c5f"; fontcolor="#e2e8f0"; margin=14; pic [label=<
pic
idINT PK
wINT
hINT
sizeBIGINT
hashNVARCHAR(64)
thumbwINT
thumbhINT
thumbhashNVARCHAR(64)
sourceNVARCHAR(MAX)
fullbinaryVARBINARY(MAX)
thumbbinaryVARBINARY(MAX)
>]; + categorypic [label=<
categorypic
idINT PK
pic_idINT FK
category_idINT FK
sortINT
>]; articlepic [label=<
articlepic
idINT PK
pic_idINT FK
article_idINT FK
sortINT
>]; brandpic [label=<
brandpic
idINT PK
pic_idINT FK
brand_idINT FK
sortINT
>]; - categorypic [label=<
categorypic
idINT PK
pic_idINT FK
category_idINT FK
sortINT
>]; } subgraph cluster_commerce { label="Pricing & Documents"; labeljust="l"; fontsize=15; style="rounded,filled"; fillcolor="#202a3a"; color="#3f4c5f"; fontcolor="#e2e8f0"; margin=14; ledger [label=<
ledger
idINT PK
manifestarticle_idINT FK
transaction_typeNVARCHAR(100)
amountFLOAT
transaction_textNVARCHAR(MAX)
hash_versionINT
prev_hash?NVARCHAR(128)
>]; - manifestarticle [label=<
manifestarticle
idINT PK
manifest_idINT FK
tax_idINT FK
article_id?INT FK
amountFLOAT
netFLOAT
comment?NVARCHAR(MAX)
rebateFLOAT
>]; - manifest [label=<
manifest
idINT PK
entity_id?INT FK
company_idINT FK
numberNVARCHAR(100)
typeNVARCHAR(100)
external_number?NVARCHAR(100)
dateDATETIME2
>]; - price [label=<
price
idINT PK
tax_idINT FK
customergroup_idINT FK
article_idINT FK
minmultiplierDECIMAL(18,4)
netFLOAT
>]; + manifestarticle [label=<
manifestarticle
idINT PK
manifest_idINT FK
tax_idINT FK
article_id?INT FK
customsku?NVARCHAR(100)
customname?NVARCHAR(255)
amountFLOAT
netFLOAT
comment?NVARCHAR(MAX)
rebateFLOAT
>]; + manifest [label=<
manifest
idINT PK
company_idINT FK
entity_id?INT FK
numberNVARCHAR(100)
typeNVARCHAR(100)
external_number?NVARCHAR(100)
dateDATETIME2
>]; + price [label=<
price
idINT PK
tax_idINT FK
article_idINT FK
customergroup_idINT FK
minmultiplierDECIMAL(18,4)
netFLOAT
>]; tax [label=<
tax
idINT PK
rateDECIMAL(18,4)
>]; } subgraph cluster_units { @@ -71,68 +72,76 @@ digraph schema { fontcolor="#e2e8f0"; margin=14; unit [label=<
unit
idINT PK
company_idINT FK
siNVARCHAR(50)
basesiNVARCHAR(50)
basesifactorFLOAT
>]; } - posterminal [label=<
posterminal
idINT PK
company_idINT FK
nameNVARCHAR(255)
token?NVARCHAR(255)
pairingpinNVARCHAR(50)
>]; - posterminalcategory [label=<
posterminalcategory
idINT PK
category_idINT FK
posterminal_idINT FK
>]; + subgraph cluster_pos { + label="POS"; labeljust="l"; fontsize=15; + style="rounded,filled"; fillcolor="#202a3a"; color="#3f4c5f"; + fontcolor="#e2e8f0"; margin=14; + posterminal [label=<
posterminal
idINT PK
company_idINT FK
nameNVARCHAR(255)
token?NVARCHAR(255)
pairingpinNVARCHAR(50)
>]; + posterminalcategory [label=<
posterminalcategory
idINT PK
category_idINT FK
posterminal_idINT FK
>]; + } __legend [label=<
Legend
PKprimary key
FKforeign key
?nullable column
—FOREIGN KEY constraint
- -naming convention only, no constraint
>]; - google_auth:selected_company:w -> company:in7:n [color="#ff6b6b", fontcolor="#ff6b6b", tooltip="FK_google_auth_selected_company", weight=3]; + google_auth:selected_company:e -> company:in9:n [color="#ff6b6b", fontcolor="#ff6b6b", tooltip="FK_google_auth_selected_company", weight=3]; category:parent_id:e -> category:id:e [color="#6bdf9a", fontcolor="#6bdf9a", tooltip="FK_category_parent", constraint=false]; - company:root_category_id:e -> category:in0:n [color="#5aa7ff", fontcolor="#5aa7ff", tooltip="FK_company_root_category", constraint=false, weight=1]; - company:default_language_id:e -> language:in0:n [color="#f5c15c", fontcolor="#f5c15c", tooltip="FK_company_default_language", constraint=false, weight=1]; - company:taxrate:w -> tax:in3:n [color="#c79bff", fontcolor="#c79bff", tooltip="FK_company_taxrate", constraint=false, weight=1]; - company:reducedtaxrate:w -> tax:in2:n [color="#ff9f5a", fontcolor="#ff9f5a", tooltip="FK_company_reducedtaxrate", weight=3]; - company:taxfreerate:w -> tax:in4:n [color="#5cd6d6", fontcolor="#5cd6d6", tooltip="FK_company_taxfreerate", constraint=false, weight=1]; - customergroup:company_id:w -> company:in1:n [color="#ff7fc2", fontcolor="#ff7fc2", tooltip="FK_customergroup_company", weight=3]; - company:poscustomergroup_id:w -> customergroup:in2:n [color="#8f95ff", fontcolor="#8f95ff", tooltip="FK_company_poscustomergroup", constraint=false, weight=1]; + company:root_category_id:w -> category:in5:n [color="#5aa7ff", fontcolor="#5aa7ff", tooltip="FK_company_root_category", constraint=false, weight=1]; + company:default_language_id:sw -> language:in3:n [color="#f5c15c", fontcolor="#f5c15c", tooltip="FK_company_default_language", constraint=false, weight=1]; + company:taxrate:e -> tax:in4:n [color="#c79bff", fontcolor="#c79bff", tooltip="FK_company_taxrate", constraint=false, weight=1]; + company:reducedtaxrate:e -> tax:in2:n [color="#ff9f5a", fontcolor="#ff9f5a", tooltip="FK_company_reducedtaxrate", weight=3]; + company:taxfreerate:e -> tax:in3:n [color="#5cd6d6", fontcolor="#5cd6d6", tooltip="FK_company_taxfreerate", constraint=false, weight=1]; + customergroup:company_id:se -> company:in4:n [color="#ff7fc2", fontcolor="#ff7fc2", tooltip="FK_customergroup_company", weight=3]; + company:poscustomergroup_id:sw -> customergroup:in2:n [color="#8f95ff", fontcolor="#8f95ff", tooltip="FK_company_poscustomergroup", constraint=false, weight=1]; entity:customergroup_id:nw -> customergroup:in1:n [color="#58d18a", fontcolor="#58d18a", tooltip="FK_entity_customergroup", weight=3]; - entityaddress:entity_id:sw -> entity:in1:n [color="#ff8f66", fontcolor="#ff8f66", tooltip="FK_entityaddress_entity", weight=3]; - entitycontact:entity_id:se -> entity:in2:n [color="#4fc4e8", fontcolor="#4fc4e8", tooltip="FK_entitycontact_entity", weight=3]; - entityhistory:entity_id:sw -> entity:in3:n [color="#a98cff", fontcolor="#a98cff", tooltip="FK_entityhistory_entity", weight=3]; - entityhistory:companyuser_id:se -> companyuser:in0:n [color="#ff6f6f", fontcolor="#ff6f6f", tooltip="FK_entityhistory_companyuser", constraint=false, weight=1]; - unit:company_id:e -> company:in8:n [color="#4ecb8f", fontcolor="#4ecb8f", tooltip="FK_unit_company", weight=3]; + entityaddress:entity_id:sw -> entity:in2:n [color="#ff8f66", fontcolor="#ff8f66", tooltip="FK_entityaddress_entity", weight=3]; + entitycontact:entity_id:se -> entity:in3:n [color="#4fc4e8", fontcolor="#4fc4e8", tooltip="FK_entitycontact_entity", weight=3]; + entityhistory:entity_id:sw -> entity:in4:n [color="#a98cff", fontcolor="#a98cff", tooltip="FK_entityhistory_entity", weight=3]; + entityhistory:companyuser_id:se -> companyuser:in1:n [color="#ff6f6f", fontcolor="#ff6f6f", tooltip="FK_entityhistory_companyuser", constraint=false, weight=1]; + unit:company_id:e -> company:in2:n [color="#4ecb8f", fontcolor="#4ecb8f", tooltip="FK_unit_company", weight=3]; unittext:unit_id:se -> unit:in1:n [color="#5aa7e6", fontcolor="#5aa7e6", tooltip="FK_unittext_unit", weight=3]; - unittext:language_id:nw -> language:in1:n [color="#ff7aa8", fontcolor="#ff7aa8", tooltip="FK_unittext_language", constraint=false, weight=1]; - brand:company_id:w -> company:in6:n [color="#e0b25a", fontcolor="#e0b25a", tooltip="FK_brand_company", weight=3]; + unittext:language_id:nw -> language:in0:n [color="#ff7aa8", fontcolor="#ff7aa8", tooltip="FK_unittext_language", constraint=false, weight=1]; + brand:company_id:e -> company:in1:n [color="#e0b25a", fontcolor="#e0b25a", tooltip="FK_brand_company", weight=3]; article:unit_id:e -> unit:in0:n [color="#56c8c8", fontcolor="#56c8c8", tooltip="FK_article_unit", constraint=false, weight=1]; article:brand_id:sw -> brand:in1:n [color="#ff5f9e", fontcolor="#ff5f9e", tooltip="FK_article_brand", constraint=false, weight=1]; - article:articlelist_id:nw -> articlelist:in1:n [color="#ffb25a", fontcolor="#ffb25a", tooltip="FK_article_articlelist", weight=3]; + article:articlelist_id:sw -> articlelist:in0:n [color="#ffb25a", fontcolor="#ffb25a", tooltip="FK_article_articlelist", weight=3]; article:optionslist_id:e -> optionslist:in0:n [color="#56d68a", fontcolor="#56d68a", tooltip="FK_article_optionslist", constraint=false, weight=1]; article:fatherarticle_id:e -> article:id:e [color="#66b3ff", fontcolor="#66b3ff", tooltip="FK_article_fatherarticle", constraint=false]; - articlehistory:article_id:sw -> article:in3:n [color="#c79bff", fontcolor="#c79bff", tooltip="FK_articlehistory_article", weight=3]; - articlehistory:companyuser_id:w -> companyuser:in1:n [color="#ff6b6b", fontcolor="#ff6b6b", tooltip="FK_articlehistory_companyuser", constraint=false, weight=1]; + articlehistory:article_id:ne -> article:in4:n [color="#c79bff", fontcolor="#c79bff", tooltip="FK_articlehistory_article", weight=3]; + articlehistory:companyuser_id:e -> companyuser:in0:n [color="#ff6b6b", fontcolor="#ff6b6b", tooltip="FK_articlehistory_companyuser", constraint=false, weight=1]; articleean:article_id:sw -> article:in6:n [color="#6bdf9a", fontcolor="#6bdf9a", tooltip="FK_articleean_article", weight=3]; articlepic:article_id:e -> article:in2:n [color="#5aa7ff", fontcolor="#5aa7ff", tooltip="FK_articlepic_article", weight=3]; - articlepic:pic_id:e -> pic:in1:n [color="#f5c15c", fontcolor="#f5c15c", tooltip="FK_articlepic_pic", constraint=false, weight=1]; + articlepic:pic_id:w -> pic:in0:n [color="#f5c15c", fontcolor="#f5c15c", tooltip="FK_articlepic_pic", constraint=false, weight=1]; brandpic:brand_id:se -> brand:in0:n [color="#c79bff", fontcolor="#c79bff", tooltip="FK_brandpic_brand", weight=3]; brandpic:pic_id:nw -> pic:in2:n [color="#ff9f5a", fontcolor="#ff9f5a", tooltip="FK_brandpic_pic", constraint=false, weight=1]; - articlelistarticle:articlelist_id:se -> articlelist:in0:n [color="#5cd6d6", fontcolor="#5cd6d6", tooltip="FK_articlelistarticle_articlelist", weight=3]; - articlelistarticle:article_id:w -> article:in5:n [color="#ff7fc2", fontcolor="#ff7fc2", tooltip="FK_articlelistarticle_article", constraint=false, weight=1]; + articlelistarticle:articlelist_id:sw -> articlelist:in1:n [color="#5cd6d6", fontcolor="#5cd6d6", tooltip="FK_articlelistarticle_articlelist", weight=3]; + articlelistarticle:article_id:e -> article:in7:n [color="#ff7fc2", fontcolor="#ff7fc2", tooltip="FK_articlelistarticle_article", constraint=false, weight=1]; optionslistarticle:optionslist_id:se -> optionslist:in1:n [color="#8f95ff", fontcolor="#8f95ff", tooltip="FK_optionslistarticle_optionslist", weight=3]; - optionslistarticle:article_id:w -> article:in8:n [color="#58d18a", fontcolor="#58d18a", tooltip="FK_optionslistarticle_article", constraint=false, weight=1]; - categoryarticle:category_id:se -> category:in3:n [color="#ff8f66", fontcolor="#ff8f66", tooltip="FK_categoryarticle_category", weight=3]; - categoryarticle:article_id:sw -> article:in7:n [color="#4fc4e8", fontcolor="#4fc4e8", tooltip="FK_categoryarticle_article", constraint=false, weight=1]; - categorypic:category_id:w -> category:in1:n [color="#a98cff", fontcolor="#a98cff", tooltip="FK_categorypic_category", weight=3]; - categorypic:pic_id:w -> pic:in0:n [color="#ff6f6f", fontcolor="#ff6f6f", tooltip="FK_categorypic_pic", constraint=false, weight=1]; - categorytext:category_id:w -> category:in4:n [color="#4ecb8f", fontcolor="#4ecb8f", tooltip="FK_categorytext_category", weight=3]; - categorytext:language_id:e -> language:in3:n [color="#5aa7e6", fontcolor="#5aa7e6", tooltip="FK_categorytext_language", constraint=false, weight=1]; - articletext:article_id:e -> article:in9:n [color="#ff7aa8", fontcolor="#ff7aa8", tooltip="FK_articletext_article", weight=3]; - articletext:language_id:w -> language:in2:n [color="#e0b25a", fontcolor="#e0b25a", tooltip="FK_articletext_language", constraint=false, weight=1]; + optionslistarticle:article_id:w -> article:in9:n [color="#58d18a", fontcolor="#58d18a", tooltip="FK_optionslistarticle_article", constraint=false, weight=1]; + categoryarticle:category_id:sw -> category:in2:n [color="#ff8f66", fontcolor="#ff8f66", tooltip="FK_categoryarticle_category", weight=3]; + categoryarticle:article_id:se -> article:in3:n [color="#4fc4e8", fontcolor="#4fc4e8", tooltip="FK_categoryarticle_article", constraint=false, weight=1]; + categorypic:category_id:se -> category:in0:n [color="#a98cff", fontcolor="#a98cff", tooltip="FK_categorypic_category", weight=3]; + categorypic:pic_id:e -> pic:in1:n [color="#ff6f6f", fontcolor="#ff6f6f", tooltip="FK_categorypic_pic", constraint=false, weight=1]; + categorytext:category_id:e -> category:in3:n [color="#4ecb8f", fontcolor="#4ecb8f", tooltip="FK_categorytext_category", weight=3]; + categorytext:language_id:w -> language:in2:n [color="#5aa7e6", fontcolor="#5aa7e6", tooltip="FK_categorytext_language", constraint=false, weight=1]; + articletext:article_id:e -> article:in10:n [color="#ff7aa8", fontcolor="#ff7aa8", tooltip="FK_articletext_article", weight=3]; + articletext:language_id:w -> language:in1:n [color="#e0b25a", fontcolor="#e0b25a", tooltip="FK_articletext_language", constraint=false, weight=1]; price:tax_id:sw -> tax:in1:n [color="#56c8c8", fontcolor="#56c8c8", tooltip="FK_price_tax", constraint=false, weight=1]; - price:article_id:w -> article:in1:n [color="#ff5f9e", fontcolor="#ff5f9e", tooltip="FK_price_article", weight=3]; - price:customergroup_id:se -> customergroup:in0:n [color="#ffb25a", fontcolor="#ffb25a", tooltip="FK_price_customergroup", constraint=false, weight=1]; - manifest:company_id:e -> company:in0:n [color="#56d68a", fontcolor="#56d68a", tooltip="FK_manifest_company", weight=3]; - manifest:entity_id:e -> entity:in0:n [color="#66b3ff", fontcolor="#66b3ff", tooltip="FK_manifest_entity", constraint=false, weight=1]; - manifestarticle:manifest_id:sw -> manifest:in0:n [color="#c79bff", fontcolor="#c79bff", tooltip="FK_manifestarticle_manifest", weight=3]; - manifestarticle:article_id:e -> article:in0:n [color="#ff6b6b", fontcolor="#ff6b6b", tooltip="FK_manifestarticle_article", constraint=false, weight=1]; - manifestarticle:tax_id:sw -> tax:in0:n [color="#6bdf9a", fontcolor="#6bdf9a", tooltip="FK_manifestarticle_tax", constraint=false, weight=1]; - ledger:manifestarticle_id:ne -> manifestarticle:in0:n [color="#5aa7ff", fontcolor="#5aa7ff", tooltip="FK_ledger_manifestarticle", weight=3]; - numbers:company_id:e -> company:in2:n [color="#f5c15c", fontcolor="#f5c15c", tooltip="FK_numbers_company", weight=3]; - companyuser:google_auth_id:e -> google_auth:in0:n [color="#c79bff", fontcolor="#c79bff", tooltip="FK_companyuser_google_auth", constraint=false, weight=1]; - companyuser:company_id:sw -> company:in3:n [color="#ff9f5a", fontcolor="#ff9f5a", tooltip="FK_companyuser_company", weight=3]; - syncpos:company_id:se -> company:in4:n [color="#5cd6d6", fontcolor="#5cd6d6", tooltip="FK_syncpos_company", weight=3]; - companysetting:company_id:sw -> company:in5:n [color="#ff7fc2", fontcolor="#ff7fc2", tooltip="FK_companysetting_company", weight=3]; - posterminal:company_id:w -> company:in9:n [color="#8f95ff", fontcolor="#8f95ff", tooltip="FK_posterminal_company", weight=3]; - posterminalcategory:posterminal_id:sw -> posterminal:in0:n [color="#58d18a", fontcolor="#58d18a", tooltip="FK_posterminalcategory_posterminal", weight=3]; - posterminalcategory:category_id:w -> category:in5:n [color="#ff8f66", fontcolor="#ff8f66", tooltip="FK_posterminalcategory_category", constraint=false, weight=1]; + price:article_id:e -> article:in1:n [color="#ff5f9e", fontcolor="#ff5f9e", tooltip="FK_price_article", weight=3]; + price:customergroup_id:e -> customergroup:in0:n [color="#ffb25a", fontcolor="#ffb25a", tooltip="FK_price_customergroup", constraint=false, weight=1]; + articlesupplier:article_id:sw -> article:in8:n [color="#56d68a", fontcolor="#56d68a", tooltip="FK_articlesupplier_article", weight=3]; + articlesupplier:entity_id:e -> entity:in1:n [color="#66b3ff", fontcolor="#66b3ff", tooltip="FK_articlesupplier_entity", constraint=false, weight=1]; + articlesupplier:price_id:e -> price:in0:n [color="#c79bff", fontcolor="#c79bff", tooltip="FK_articlesupplier_price", constraint=false, weight=1]; + manifest:company_id:w -> company:in0:n [color="#ff6b6b", fontcolor="#ff6b6b", tooltip="FK_manifest_company", weight=3]; + manifest:entity_id:e -> entity:in0:n [color="#6bdf9a", fontcolor="#6bdf9a", tooltip="FK_manifest_entity", constraint=false, weight=1]; + manifestarticle:manifest_id:nw -> manifest:in0:n [color="#5aa7ff", fontcolor="#5aa7ff", tooltip="FK_manifestarticle_manifest", weight=3]; + manifestarticle:article_id:e -> article:in0:n [color="#f5c15c", fontcolor="#f5c15c", tooltip="FK_manifestarticle_article", constraint=false, weight=1]; + manifestarticle:tax_id:sw -> tax:in0:n [color="#c79bff", fontcolor="#c79bff", tooltip="FK_manifestarticle_tax", constraint=false, weight=1]; + ledger:manifestarticle_id:ne -> manifestarticle:in0:n [color="#ff9f5a", fontcolor="#ff9f5a", tooltip="FK_ledger_manifestarticle", weight=3]; + numbers:company_id:sw -> company:in5:n [color="#5cd6d6", fontcolor="#5cd6d6", tooltip="FK_numbers_company", weight=3]; + companyuser:google_auth_id:e -> google_auth:in0:n [color="#ff7fc2", fontcolor="#ff7fc2", tooltip="FK_companyuser_google_auth", constraint=false, weight=1]; + companyuser:company_id:w -> company:in6:n [color="#8f95ff", fontcolor="#8f95ff", tooltip="FK_companyuser_company", weight=3]; + syncpos:company_id:e -> company:in7:n [color="#58d18a", fontcolor="#58d18a", tooltip="FK_syncpos_company", weight=3]; + companysetting:company_id:w -> company:in8:n [color="#ff8f66", fontcolor="#ff8f66", tooltip="FK_companysetting_company", weight=3]; + posterminal:company_id:se -> company:in3:n [color="#4fc4e8", fontcolor="#4fc4e8", tooltip="FK_posterminal_company", weight=3]; + posterminalcategory:posterminal_id:sw -> posterminal:in0:n [color="#a98cff", fontcolor="#a98cff", tooltip="FK_posterminalcategory_posterminal", weight=3]; + posterminalcategory:category_id:w -> category:in4:n [color="#ff6f6f", fontcolor="#ff6f6f", tooltip="FK_posterminalcategory_category", constraint=false, weight=1]; } \ No newline at end of file diff --git a/schema-graph.svg b/schema-graph.svg index f617d9f..2673be6 100644 --- a/schema-graph.svg +++ b/schema-graph.svg @@ -4,30 +4,30 @@ - - + + schema - + cluster_identity - -Identity & Company + +Identity & Company cluster_i18n - -Localization + +Localization cluster_catalog - -Catalog + +Catalog cluster_media - -Media + +Media cluster_commerce @@ -36,473 +36,205 @@ cluster_units - -Units + +Units + + +cluster_pos + +POS + + + +meta + + +meta + + +key +NVARCHAR(255) + + +value +NVARCHAR(MAX) - + customergroup - - - - - -customergroup - - -id -INT -PK - - -company_id -? -INT -FK - - -name -NVARCHAR(255) - - -sort -INT + + + + + +customergroup + + +id +INT +PK + + +company_id +? +INT +FK + + +name +NVARCHAR(255) + + +sort +INT - + company - - - - - - - - - - - - -company - - -id -INT -PK - - -taxrate -? -INT -FK - - -reducedtaxrate -? -INT -FK - - -taxfreerate -? -INT -FK - - -poscustomergroup_id -INT -FK - - -root_category_id -? -INT -FK - - -default_language_id -? -INT -FK - - -name -NVARCHAR(255) + + + + + + + + + + + + +company + + +id +INT +PK + + +taxrate +? +INT +FK + + +reducedtaxrate +? +INT +FK + + +taxfreerate +? +INT +FK + + +root_category_id +? +INT +FK + + +default_language_id +? +INT +FK + + +poscustomergroup_id +INT +FK + + +name +NVARCHAR(255) -customergroup:w->company:n +customergroup:se->company:n - - - - - - - -meta - - -meta - - -key -NVARCHAR(255) - - -value -NVARCHAR(MAX) - - - -numbers - - -numbers - - -id -INT -PK - - -company_id -INT -FK - - -type -NVARCHAR(100) - - -prefix -NVARCHAR(50) - - -padding -INT - - -next -INT - - - -numbers:e->company:n - - - - - - - - -entityaddress - - -entityaddress - - -id -INT -PK - - -entity_id -INT -FK - - -first_name? -NVARCHAR(255) - - -name? -NVARCHAR(255) - - -street? -NVARCHAR(255) - - -streetnumber? -NVARCHAR(50) - - -zip? -NVARCHAR(50) - - -city? -NVARCHAR(255) - - -county? -NVARCHAR(255) - - -addresstype -NVARCHAR(50) - - -sort -INT - - - -entity - - - - - - -entity - - -id -INT -PK - - -customergroup_id -INT -FK - - -first_name? -NVARCHAR(255) - - -name -NVARCHAR(255) - - -number? -NVARCHAR(100) - - -created_at -DATETIME2 - - -updated_at -DATETIME2 - - - -entityaddress:sw->entity:n - - - - - - - - -entity:nw->customergroup:n - - - - - - - - -companyuser - - - - -companyuser - - -id -INT -PK - - -company_id -INT -FK - - -google_auth_id -INT -FK - - - -companyuser:sw->company:n - - - - - - - - -google_auth - - - -google_auth - - -id -INT -PK - - -selected_company -? -INT -FK - - -email -NVARCHAR(320) - - -picture? -NVARCHAR(2048) - - -picturebinary? -VARBINARY(MAX) - - -sub? -NVARCHAR(255) - - -created_at -DATETIME2 - - - -companyuser:e->google_auth:n - - - - - - - - -entitycontact - - -entitycontact - - -id -INT -PK - - -entity_id -INT -FK - - -contacttype -NVARCHAR(50) - - -contact -NVARCHAR(255) - - -sort -INT - - - -entitycontact:se->entity:n - - - + + -company:w->customergroup:n +company:sw->customergroup:n - - + + language - - - - - - -language - - -id -INT -PK - - -iso639 -NVARCHAR(10) + + + + + + +language + + +id +INT +PK + + +iso639 +NVARCHAR(10) -company:e->language:n +company:sw->language:n - - + + - + category - - - - - - - - -category - - -id -INT -PK - - -parent_id -? -INT -FK - - -sort -INT + + + + + + + + +category + + +id +INT +PK + + +parent_id +? +INT +FK + + +sort +INT -company:e->category:n +company:w->category:n - - + + - + tax @@ -524,102 +256,376 @@ -company:w->tax:n +company:e->tax:n - - + + -company:w->tax:n +company:e->tax:n - - + + -company:w->tax:n +company:e->tax:n - - + + + + + + + +numbers + + +numbers + + +id +INT +PK + + +company_id +INT +FK + + +type +NVARCHAR(100) + + +prefix +NVARCHAR(50) + + +padding +INT + + +next +INT + + + +numbers:sw->company:n + + + + + + + + +entityaddress + + +entityaddress + + +id +INT +PK + + +entity_id +INT +FK + + +first_name? +NVARCHAR(255) + + +name? +NVARCHAR(255) + + +street? +NVARCHAR(255) + + +streetnumber? +NVARCHAR(50) + + +zip? +NVARCHAR(50) + + +city? +NVARCHAR(255) + + +county? +NVARCHAR(255) + + +addresstype +NVARCHAR(50) + + +sort +INT + + + +entity + + + + + + + +entity + + +id +INT +PK + + +customergroup_id +INT +FK + + +first_name? +NVARCHAR(255) + + +name +NVARCHAR(255) + + +number? +NVARCHAR(100) + + +created_at +DATETIME2 + + +updated_at +DATETIME2 + + + +entityaddress:sw->entity:n + + + + + + + + +entity:nw->customergroup:n + + + + + + + + +companyuser + + + + +companyuser + + +id +INT +PK + + +company_id +INT +FK + + +google_auth_id +INT +FK + + + +companyuser:w->company:n + + + + + + + + +google_auth + + + +google_auth + + +id +INT +PK + + +selected_company +? +INT +FK + + +email +NVARCHAR(320) + + +picture? +NVARCHAR(2048) + + +picturebinary? +VARBINARY(MAX) + + +sub? +NVARCHAR(255) + + +created_at +DATETIME2 + + + +companyuser:e->google_auth:n + + + + + + + + +entitycontact + + +entitycontact + + +id +INT +PK + + +entity_id +INT +FK + + +contacttype +NVARCHAR(50) + + +contact +NVARCHAR(255) + + +sort +INT + + + +entitycontact:se->entity:n + + + syncpos - - -syncpos - - -id -INT -PK - - -company_id -INT -FK - - -paircode -NVARCHAR(50) - - -token? -NVARCHAR(255) + + +syncpos + + +id +INT +PK + + +company_id +INT +FK + + +paircode +NVARCHAR(50) + + +token? +NVARCHAR(255) - -syncpos:se->company:n - - - + +syncpos:e->company:n + + + entityhistory - - -entityhistory - - -id -INT -PK - - -entity_id -INT -FK - - -companyuser_id -? -INT -FK - - -date -DATETIME2 - - -text -NVARCHAR(MAX) + + +entityhistory + + +id +INT +PK + + +entity_id +INT +FK + + +companyuser_id +? +INT +FK + + +date +DATETIME2 + + +text +NVARCHAR(MAX) entityhistory:sw->entity:n - - + + @@ -627,513 +633,326 @@ entityhistory:se->companyuser:n - - + + companysetting - - -companysetting - - -id -INT -PK - - -company_id -INT -FK - - -key -NVARCHAR(255) - - -value -NVARCHAR(MAX) - - -frontend -BIT + + +companysetting + + +id +INT +PK + + +company_id +INT +FK + + +key +NVARCHAR(255) + + +value +NVARCHAR(MAX) + + +frontend +BIT - -companysetting:sw->company:n - - - + +companysetting:w->company:n + + + -google_auth:w->company:n +google_auth:e->company:n - - + + unittext - - -unittext - - -id -INT -PK - - -language_id -INT -FK - - -unit_id -INT -FK - - -symbol -NVARCHAR(50) - - -name -NVARCHAR(255) + + +unittext + + +id +INT +PK + + +language_id +INT +FK + + +unit_id +INT +FK + + +symbol +NVARCHAR(50) + + +name +NVARCHAR(255) unittext:nw->language:n - - + + - + unit - - - - -unit - - -id -INT -PK - - -company_id -INT -FK - - -si -NVARCHAR(50) - - -basesi -NVARCHAR(50) - - -basesifactor -FLOAT + + + + +unit + + +id +INT +PK + + +company_id +INT +FK + + +si +NVARCHAR(50) + + +basesi +NVARCHAR(50) + + +basesifactor +FLOAT unittext:se->unit:n - - - - - - - -articletext - - -articletext - - -id -INT -PK - - -article_id -INT -FK - - -language_id -INT -FK - - -name -NVARCHAR(255) - - -slug -NVARCHAR(255) - - -seo? -NVARCHAR(MAX) - - -desc? -NVARCHAR(MAX) - - - -articletext:w->language:n - - - - - - - - -article - - - - - - - - - - - - -article - - -id -INT -PK - - -brand_id -? -INT -FK - - -fatherarticle_id -? -INT -FK - - -articlelist_id -? -INT -FK - - -optionslist_id -? -INT -FK - - -unit_id -INT -FK - - -sku? -NVARCHAR(100) - - -unitamount -FLOAT - - -weight -FLOAT - - -is_father -BIT - - -fathersort -INT - - -created_at -DATETIME2 - - -updated_at -DATETIME2 - - - -articletext:e->article:n - - - + + - + categorytext - - -categorytext - - -id -INT -PK - - -category_id -INT -FK - - -language_id -INT -FK - - -name -NVARCHAR(255) - - -slug -NVARCHAR(255) - - -seo? -NVARCHAR(MAX) - - -desc? -NVARCHAR(MAX) + + +categorytext + + +id +INT +PK + + +category_id +INT +FK + + +language_id +INT +FK + + +name +NVARCHAR(255) + + +slug +NVARCHAR(255) + + +seo? +NVARCHAR(MAX) + + +desc? +NVARCHAR(MAX) -categorytext:e->language:n +categorytext:w->language:n - - + + -categorytext:w->category:n +categorytext:e->category:n - - + + - - -brand - - - - -brand - - -id -INT -PK - - -company_id -INT -FK - - -name -NVARCHAR(255) - - -gsprpinfo? -NVARCHAR(MAX) + + +articletext + + +articletext + + +id +INT +PK + + +article_id +INT +FK + + +language_id +INT +FK + + +name +NVARCHAR(255) + + +slug +NVARCHAR(255) + + +seo? +NVARCHAR(MAX) + + +desc? +NVARCHAR(MAX) - - -brand:w->company:n - - - + + +articletext:w->language:n + + + - - -articlehistory - - -articlehistory - - -id -INT -PK - - -companyuser_id -? -INT -FK - - -article_id -INT -FK - - -date -DATETIME2 - - -text -NVARCHAR(MAX) + + +article + + + + + + + + + + + + + +article + + +id +INT +PK + + +brand_id +? +INT +FK + + +fatherarticle_id +? +INT +FK + + +articlelist_id +? +INT +FK + + +optionslist_id +? +INT +FK + + +unit_id +INT +FK + + +sku? +NVARCHAR(100) + + +unitamount +FLOAT + + +weight +FLOAT + + +is_father +BIT + + +fathersort +INT + + +created_at +DATETIME2 + + +updated_at +DATETIME2 - - -articlehistory:w->companyuser:n - - - - - - - - -articlehistory:sw->article:n - - - - - - - - -article:sw->brand:n - - - - - - - - -article:e->article:e - - - - - - - - -articlelist - - - - -articlelist - - -id -INT -PK - - - -article:nw->articlelist:n - - - - - - - - -optionslist - - - - -optionslist - - -id -INT -PK - - - -article:e->optionslist:n - - - - - - - - -article:e->unit:n - - - - - - - - -articleean - - -articleean - - -id -INT -PK - - -article_id -INT -FK - - -ean -NVARCHAR(50) - - - -articleean:sw->article:n - - - + + +articletext:e->article:n + + + @@ -1141,139 +960,430 @@ category:e->category:e - - + + - + categoryarticle - - -categoryarticle - - -id -INT -PK - - -article_id -INT -FK - - -category_id -INT -FK - - - -categoryarticle:sw->article:n - - - - - + + +categoryarticle + + +id +INT +PK + + +category_id +INT +FK + + +article_id +INT +FK -categoryarticle:se->category:n +categoryarticle:sw->category:n - - + + + + + + + +categoryarticle:se->article:n + + + + + + + + +brand + + + + +brand + + +id +INT +PK + + +company_id +INT +FK + + +name +NVARCHAR(255) + + +gsprpinfo? +NVARCHAR(MAX) + + + +brand:e->company:n + + + + + + + + +articlehistory + + +articlehistory + + +id +INT +PK + + +article_id +INT +FK + + +companyuser_id +? +INT +FK + + +date +DATETIME2 + + +text +NVARCHAR(MAX) + + + +articlehistory:e->companyuser:n + + + + + + + + +articlehistory:ne->article:n + + + + + + + + +article:sw->brand:n + + + + + + + + +article:e->article:e + + + + + + + + +articlelist + + + + +articlelist + + +id +INT +PK + + + +article:sw->articlelist:n + + + + + + + + +optionslist + + + + +optionslist + + +id +INT +PK + + + +article:e->optionslist:n + + + + + + + + +article:e->unit:n + + + + + + + + +articleean + + +articleean + + +id +INT +PK + + +article_id +INT +FK + + +ean +NVARCHAR(50) + + + +articleean:sw->article:n + + + articlelistarticle - - -articlelistarticle - - -id -INT -PK - - -article_id -INT -FK - - -articlelist_id -INT -FK - - -count -INT - - -sort -INT + + +articlelistarticle + + +id +INT +PK + + +article_id +INT +FK + + +articlelist_id +INT +FK + + +count +INT + + +sort +INT -articlelistarticle:w->article:n +articlelistarticle:e->article:n - - + + -articlelistarticle:se->articlelist:n +articlelistarticle:sw->articlelist:n - - + + + + + + + +articlesupplier + + +articlesupplier + + +id +INT +PK + + +price_id +? +INT +FK + + +article_id +INT +FK + + +entity_id +INT +FK + + +SupplierSKU? +NVARCHAR(100) + + +SupplierStock? +FLOAT + + +pu +FLOAT + + + +articlesupplier:e->entity:n + + + + + + + + +articlesupplier:sw->article:n + + + + + + + + +price + + + +price + + +id +INT +PK + + +tax_id +INT +FK + + +article_id +INT +FK + + +customergroup_id +INT +FK + + +minmultiplier +DECIMAL(18,4) + + +net +FLOAT + + + +articlesupplier:e->price:n + + + - + optionslistarticle - - -optionslistarticle - - -id -INT -PK - - -article_id -INT -FK - - -optionslist_id -INT -FK - - -count -INT - - -sort -INT - - -mandated -BIT + + +optionslistarticle + + +id +INT +PK + + +article_id +INT +FK + + +optionslist_id +INT +FK + + +count +INT + + +sort +INT + + +mandated +BIT optionslistarticle:w->article:n - - + + @@ -1281,142 +1391,186 @@ optionslistarticle:se->optionslist:n - - + + - + pic - - - - - -pic - - -id -INT -PK - - -w -INT - - -h -INT - - -size -BIGINT - - -hash -NVARCHAR(64) - - -thumbw -INT - - -thumbh -INT - - -thumbhash -NVARCHAR(64) - - -source -NVARCHAR(MAX) - - -fullbinary -VARBINARY(MAX) - - -thumbbinary -VARBINARY(MAX) + + + + + +pic + + +id +INT +PK + + +w +INT + + +h +INT + + +size +BIGINT + + +hash +NVARCHAR(64) + + +thumbw +INT + + +thumbh +INT + + +thumbhash +NVARCHAR(64) + + +source +NVARCHAR(MAX) + + +fullbinary +VARBINARY(MAX) + + +thumbbinary +VARBINARY(MAX) + + + +categorypic + + +categorypic + + +id +INT +PK + + +pic_id +INT +FK + + +category_id +INT +FK + + +sort +INT + + + +categorypic:se->category:n + + + + + + + + +categorypic:e->pic:n + + + + + - + articlepic - - -articlepic - - -id -INT -PK - - -pic_id -INT -FK - - -article_id -INT -FK - - -sort -INT + + +articlepic + + +id +INT +PK + + +pic_id +INT +FK + + +article_id +INT +FK + + +sort +INT articlepic:e->article:n - - + + -articlepic:e->pic:n +articlepic:w->pic:n - - + + - + brandpic - - -brandpic - - -id -INT -PK - - -pic_id -INT -FK - - -brand_id -INT -FK - - -sort -INT + + +brandpic + + +id +INT +PK + + +pic_id +INT +FK + + +brand_id +INT +FK + + +sort +INT brandpic:se->brand:n - - + + @@ -1424,57 +1578,13 @@ brandpic:nw->pic:n - - - - - - - -categorypic - - -categorypic - - -id -INT -PK - - -pic_id -INT -FK - - -category_id -INT -FK - - -sort -INT - - - -categorypic:w->category:n - - - - - - - - -categorypic:w->pic:n - - - + + - + ledger @@ -1511,194 +1621,167 @@ NVARCHAR(128) - + manifestarticle - - - -manifestarticle - - -id -INT -PK - - -manifest_id -INT -FK - - -tax_id -INT -FK - - -article_id -? -INT -FK - - -amount -FLOAT - - -net -FLOAT - - -comment? -NVARCHAR(MAX) - - -rebate -FLOAT + + + +manifestarticle + + +id +INT +PK + + +manifest_id +INT +FK + + +tax_id +INT +FK + + +article_id +? +INT +FK + + +customsku? +NVARCHAR(100) + + +customname? +NVARCHAR(255) + + +amount +FLOAT + + +net +FLOAT + + +comment? +NVARCHAR(MAX) + + +rebate +FLOAT - + ledger:ne->manifestarticle:n - - - + + + - + manifestarticle:e->article:n - - - + + + - + manifest - - - -manifest - - -id -INT -PK - - -entity_id -? -INT -FK - - -company_id -INT -FK - - -number -NVARCHAR(100) - - -type -NVARCHAR(100) - - -external_number? -NVARCHAR(100) - - -date -DATETIME2 + + + +manifest + + +id +INT +PK + + +company_id +INT +FK + + +entity_id +? +INT +FK + + +number +NVARCHAR(100) + + +type +NVARCHAR(100) + + +external_number? +NVARCHAR(100) + + +date +DATETIME2 - -manifestarticle:sw->manifest:n - - - + +manifestarticle:nw->manifest:n + + + - + manifestarticle:sw->tax:n - - - - - - - - -manifest:e->entity:n - - - + + + - -manifest:e->company:n - - - + +manifest:w->company:n + + + - - -price - - -price - - -id -INT -PK - - -tax_id -INT -FK - - -customergroup_id -INT -FK - - -article_id -INT -FK - - -minmultiplier -DECIMAL(18,4) - - -net -FLOAT + + +manifest:e->entity:n + + + + + -price:se->customergroup:n +price:e->customergroup:n - - + + -price:w->article:n +price:e->article:n - - + + @@ -1706,8 +1789,8 @@ price:sw->tax:n - - + + @@ -1715,117 +1798,117 @@ unit:e->company:n - - + + - + posterminal - - - -posterminal - - -id -INT -PK - - -company_id -INT -FK - - -name -NVARCHAR(255) - - -token? -NVARCHAR(255) - - -pairingpin -NVARCHAR(50) + + + +posterminal + + +id +INT +PK + + +company_id +INT +FK + + +name +NVARCHAR(255) + + +token? +NVARCHAR(255) + + +pairingpin +NVARCHAR(50) - -posterminal:w->company:n - - - + +posterminal:se->company:n + + + - + posterminalcategory - - -posterminalcategory - - -id -INT -PK - - -category_id -INT -FK - - -posterminal_id -INT -FK + + +posterminalcategory + + +id +INT +PK + + +category_id +INT +FK + + +posterminal_id +INT +FK - + posterminalcategory:w->category:n - - - + + + - + posterminalcategory:sw->posterminal:n - - - + + + - + __legend - - - -Legend - -PK - -primary key - -FK - -foreign key - -? - -nullable column - -— - -FOREIGN KEY constraint - -- - - -naming convention only, no constraint + + + +Legend + +PK + +primary key + +FK + +foreign key + +? + +nullable column + +— + +FOREIGN KEY constraint + +- - + +naming convention only, no constraint diff --git a/src/schema.js b/src/schema.js index 9f4dd40..a891217 100644 --- a/src/schema.js +++ b/src/schema.js @@ -528,6 +528,27 @@ BEGIN END `; +export const INIT_ARTICLE_SUPPLIER = ` +IF NOT EXISTS (SELECT * FROM sys.tables WHERE name = 'articlesupplier') +BEGIN + CREATE TABLE articlesupplier ( + id INT IDENTITY(1,1) PRIMARY KEY, + article_id INT NOT NULL, + entity_id INT NOT NULL, + price_id INT NULL, + SupplierSKU NVARCHAR(100) NULL, + SupplierStock FLOAT NULL, + pu FLOAT NOT NULL CONSTRAINT DF_articlesupplier_pu DEFAULT 1, + CONSTRAINT FK_articlesupplier_article FOREIGN KEY (article_id) REFERENCES article(id) ON DELETE CASCADE, + CONSTRAINT FK_articlesupplier_entity FOREIGN KEY (entity_id) REFERENCES entity(id) ON DELETE CASCADE, + CONSTRAINT FK_articlesupplier_price FOREIGN KEY (price_id) REFERENCES price(id) ON DELETE NO ACTION + ); + + CREATE INDEX IX_articlesupplier_article_entity + ON articlesupplier (article_id, entity_id); +END +`; + export const INIT_MANIFEST = ` IF NOT EXISTS (SELECT * FROM sys.tables WHERE name = 'manifest') BEGIN @@ -552,6 +573,8 @@ BEGIN id INT IDENTITY(1,1) PRIMARY KEY, manifest_id INT NOT NULL, article_id INT NULL, + customsku NVARCHAR(100) NULL, + customname NVARCHAR(255) NULL, amount FLOAT NOT NULL, net FLOAT NOT NULL, comment NVARCHAR(MAX) NULL, @@ -559,9 +582,41 @@ BEGIN tax_id INT NOT NULL, CONSTRAINT FK_manifestarticle_manifest FOREIGN KEY (manifest_id) REFERENCES manifest(id) ON DELETE CASCADE, CONSTRAINT FK_manifestarticle_article FOREIGN KEY (article_id) REFERENCES article(id) ON DELETE SET NULL, - CONSTRAINT FK_manifestarticle_tax FOREIGN KEY (tax_id) REFERENCES tax(id) ON DELETE CASCADE + CONSTRAINT FK_manifestarticle_tax FOREIGN KEY (tax_id) REFERENCES tax(id) ON DELETE CASCADE, + CONSTRAINT CK_manifestarticle_article_or_custom CHECK ( + (article_id IS NOT NULL AND customsku IS NULL AND customname IS NULL) + OR (article_id IS NULL) + ) ); END + +IF NOT EXISTS ( + SELECT 1 FROM sys.columns + WHERE object_id = OBJECT_ID(N'dbo.manifestarticle') AND name = N'customsku' +) +BEGIN + EXEC('ALTER TABLE dbo.manifestarticle ADD customsku NVARCHAR(100) NULL'); +END + +IF NOT EXISTS ( + SELECT 1 FROM sys.columns + WHERE object_id = OBJECT_ID(N'dbo.manifestarticle') AND name = N'customname' +) +BEGIN + EXEC('ALTER TABLE dbo.manifestarticle ADD customname NVARCHAR(255) NULL'); +END + +IF NOT EXISTS ( + SELECT 1 FROM sys.check_constraints + WHERE name = N'CK_manifestarticle_article_or_custom' +) +BEGIN + EXEC('ALTER TABLE dbo.manifestarticle ADD CONSTRAINT CK_manifestarticle_article_or_custom + CHECK ( + (article_id IS NOT NULL AND customsku IS NULL AND customname IS NULL) + OR (article_id IS NULL) + )'); +END `; export const INIT_LEDGER = ` @@ -773,6 +828,7 @@ export const SCHEMA_SCRIPTS = [ INIT_CATEGORYTEXT, INIT_ARTICLENAME, INIT_PRICE, + INIT_ARTICLE_SUPPLIER, INIT_MANIFEST, INIT_MANIFESTARTICLE, INIT_LEDGER, diff --git a/src/sql/delivery.js b/src/sql/delivery.js index c5095eb..9c6bd85 100644 --- a/src/sql/delivery.js +++ b/src/sql/delivery.js @@ -33,6 +33,8 @@ SELECT ma.id, ma.article_id AS articleId, a.sku, + ma.customsku, + ma.customname, ma.amount, ma.net, ma.rebate, @@ -79,6 +81,8 @@ export const CREATE_LINE = ` INSERT INTO manifestarticle ( manifest_id, article_id, + customsku, + customname, amount, net, comment, @@ -89,6 +93,8 @@ OUTPUT INSERTED.id VALUES ( @manifestId, @articleId, + @customsku, + @customname, @amount, @net, @comment, diff --git a/src/sql/manifestDoc.js b/src/sql/manifestDoc.js index fb6523c..9e4051b 100644 --- a/src/sql/manifestDoc.js +++ b/src/sql/manifestDoc.js @@ -39,6 +39,8 @@ SELECT ma.id, ma.article_id AS articleId, a.sku, + ma.customsku, + ma.customname, ma.amount, ma.net, ma.rebate, @@ -85,6 +87,8 @@ export const CREATE_LINE = ` INSERT INTO manifestarticle ( manifest_id, article_id, + customsku, + customname, amount, net, comment, @@ -95,6 +99,8 @@ OUTPUT INSERTED.id VALUES ( @manifestId, @articleId, + @customsku, + @customname, @amount, @net, @comment, diff --git a/src/sql/purge.js b/src/sql/purge.js index 54445f3..e236af5 100644 --- a/src/sql/purge.js +++ b/src/sql/purge.js @@ -7,6 +7,7 @@ export const TABLES = [ 'posterminal', 'numbers', 'ledger', + 'articlesupplier', 'manifestarticle', 'manifest', 'price', diff --git a/src/store/manifestLines.js b/src/store/manifestLines.js index b65bf36..e862641 100644 --- a/src/store/manifestLines.js +++ b/src/store/manifestLines.js @@ -5,7 +5,7 @@ import request from './request.js'; * Normalizes and validates an array of manifest line inputs. * * @param {Array} lines - The raw line inputs. - * @returns {Array<{sku: string, amount: number, net: number, rebate: number, tax: number, comment: string|null}>} The normalized lines. + * @returns {Array<{sku: string|null, customsku: string|null, customname: string|null, amount: number, net: number, rebate: number, tax: number, comment: string|null}>} The normalized lines. * @throws {Error} If the lines are invalid. */ export function normalizeLines(lines) { @@ -15,7 +15,16 @@ export function normalizeLines(lines) { return lines.map((entry) => { const sku = entry?.sku?.trim?.() ? entry.sku.trim() : null; - if (!sku) { + let customsku = entry?.customsku?.trim?.() ? entry.customsku.trim() : null; + let customname = + entry?.customname?.trim?.() ? entry.customname.trim() : null; + + // If an article SKU is present, the custom fields must be null. + if (sku) { + customsku = null; + customname = null; + } else if (!customsku && !customname) { + // Must have either a catalog SKU or at least one custom field. throw new Error('Invalid lines'); } @@ -38,7 +47,7 @@ export function normalizeLines(lines) { throw new Error('Invalid lines'); } - return { sku, amount, net, rebate, tax, comment }; + return { sku, customsku, customname, amount, net, rebate, tax, comment }; }); } @@ -72,6 +81,8 @@ export function shapeLine(row) { id: row.id, articleId: row.articleId ?? null, sku: row.sku ?? null, + customsku: row.customsku ?? null, + customname: row.customname ?? null, amount: row.amount, net: row.net, rebate: row.rebate, @@ -112,17 +123,16 @@ export async function insertLines( { manifestId, rootCategoryId, lines, ensureTax }, ) { for (const line of lines) { - const articleId = await resolveArticleId( - pool, - tx, - rootCategoryId, - line.sku, - ); + const articleId = line.sku + ? await resolveArticleId(pool, tx, rootCategoryId, line.sku) + : null; const taxId = await ensureTax(line.tax, tx); await request(pool, tx) .input('manifestId', manifestId) .input('articleId', articleId) + .input('customsku', line.sku ? null : line.customsku ?? null) + .input('customname', line.sku ? null : line.customname ?? null) .input('amount', line.amount) .input('net', line.net) .input('comment', line.comment)