diff --git a/README.md b/README.md
index e1c4799..cae33ff 100644
--- a/README.md
+++ b/README.md
@@ -187,6 +187,78 @@ Company-scoped brands. Auth via `resolveCompanyAccess`.
---
+## `customergroup`
+
+Company-scoped customer groups. Auth via `resolveCompanyAccess`.
+
+| Method | Signature | Returns |
+| --- | --- | --- |
+| `list` | `(googleAuthId, companyId)` | `{ id, name }[]` |
+| `create` | `(googleAuthId, companyId, { name })` | `{ id, name }` |
+| `update` | `(googleAuthId, companyId, id, { name })` | `{ id, name }` |
+| `delete` | `(googleAuthId, companyId, id)` | `void` |
+
+Empty name → `Name required`. Deleting the POS group (or any group still referenced with `NO ACTION`) → `Customer group in use`. Cascades entities when the group is deleted. Errors: `Company not found`, `Not authorized`, `Name required`, `Customer group not found`, `Customer group in use`.
+
+---
+
+## `entity`
+
+Entities belong to a `customergroup` of the company. Auth via `resolveCompanyAccess`.
+
+| Method | Signature | Returns |
+| --- | --- | --- |
+| `list` | `(googleAuthId, companyId, { customergroupId? })` | `Entity[]` |
+| `getById` | `(googleAuthId, companyId, id)` | `Entity` |
+| `create` | `(googleAuthId, companyId, { customergroupId, name, firstName? })` | `Entity` |
+| `update` | `(googleAuthId, companyId, id, { name?, firstName? })` | `Entity` |
+| `delete` | `(googleAuthId, companyId, id)` | `void` |
+
+```ts
+type Entity = {
+ id: number,
+ customergroupId: number,
+ firstName: string | null,
+ name: string,
+ createdAt: Date,
+ updatedAt: Date,
+}
+```
+
+`create` / `update` write `entityhistory` (`Erstellt`, or name/firstName change lines). `customergroupId` must belong to the company. Errors: `Company not found`, `Not authorized`, `Name required`, `Customer group not found`, `Entity not found`.
+
+---
+
+## `entityaddress`
+
+Addresses for an entity. Auth via company + entity ownership.
+
+| Method | Signature | Returns |
+| --- | --- | --- |
+| `list` | `(googleAuthId, companyId, entityId)` | `EntityAddress[]` |
+| `create` | `(googleAuthId, companyId, entityId, { addressType, firstName?, name?, street?, streetNumber?, zip?, city?, county? })` | `EntityAddress` |
+| `update` | `(googleAuthId, companyId, entityId, id, patch)` | `EntityAddress` |
+| `delete` | `(googleAuthId, companyId, entityId, id)` | `void` |
+
+`addressType` required. Errors: `Company not found`, `Not authorized`, `Entity not found`, `Address type required`, `Address not found`.
+
+---
+
+## `entitycontact`
+
+Contacts for an entity (`contactType` e.g. `email`, `phone`). Auth via company + entity ownership.
+
+| Method | Signature | Returns |
+| --- | --- | --- |
+| `list` | `(googleAuthId, companyId, entityId)` | `{ id, entityId, contactType, contact }[]` |
+| `create` | `(googleAuthId, companyId, entityId, { contactType, contact })` | row |
+| `update` | `(googleAuthId, companyId, entityId, id, { contactType?, contact? })` | row |
+| `delete` | `(googleAuthId, companyId, entityId, id)` | `void` |
+
+Errors: `Company not found`, `Not authorized`, `Entity not found`, `Contact required`, `Contact not found`.
+
+---
+
## `supplier`
Suppliers are `entity` rows in the company `customergroup` named `Lieferanten` (created implicitly on first supplier). Auth via `resolveCompanyAccess`.
@@ -519,6 +591,23 @@ res.set('Content-Type', thumb.contentType).send(thumb.buffer);
| `GET /api/brand` | `brand.list(googleAuthId, companyId)` |
| `PUT /api/brand` | `brand.upsert(googleAuthId, companyId, { name, gsprpinfo? })` |
| `DELETE /api/brand/:id` | `brand.delete(googleAuthId, companyId, id)` |
+| `GET /api/customergroup` | `customergroup.list(googleAuthId, companyId)` |
+| `POST /api/customergroup` | `customergroup.create(googleAuthId, companyId, { name })` |
+| `PATCH /api/customergroup/:id` | `customergroup.update(googleAuthId, companyId, id, { name })` |
+| `DELETE /api/customergroup/:id` | `customergroup.delete(googleAuthId, companyId, id)` |
+| `GET /api/entity` | `entity.list(googleAuthId, companyId, { customergroupId? })` |
+| `GET /api/entity/:id` | `entity.getById(googleAuthId, companyId, id)` |
+| `POST /api/entity` | `entity.create(googleAuthId, companyId, { customergroupId, name, firstName? })` |
+| `PATCH /api/entity/:id` | `entity.update(googleAuthId, companyId, id, { name?, firstName? })` |
+| `DELETE /api/entity/:id` | `entity.delete(googleAuthId, companyId, id)` |
+| `GET /api/entity/:entityId/address` | `entityaddress.list(googleAuthId, companyId, entityId)` |
+| `POST /api/entity/:entityId/address` | `entityaddress.create(googleAuthId, companyId, entityId, { addressType, … })` |
+| `PATCH /api/entity/:entityId/address/:id` | `entityaddress.update(googleAuthId, companyId, entityId, id, patch)` |
+| `DELETE /api/entity/:entityId/address/:id` | `entityaddress.delete(googleAuthId, companyId, entityId, id)` |
+| `GET /api/entity/:entityId/contact` | `entitycontact.list(googleAuthId, companyId, entityId)` |
+| `POST /api/entity/:entityId/contact` | `entitycontact.create(googleAuthId, companyId, entityId, { contactType, contact })` |
+| `PATCH /api/entity/:entityId/contact/:id` | `entitycontact.update(googleAuthId, companyId, entityId, id, patch)` |
+| `DELETE /api/entity/:entityId/contact/:id` | `entitycontact.delete(googleAuthId, companyId, entityId, id)` |
| `GET /api/supplier` | `supplier.list(googleAuthId, companyId)` |
| `POST /api/supplier` | `supplier.create(googleAuthId, companyId, { name })` |
| `PATCH /api/supplier/:id` | `supplier.update(googleAuthId, companyId, id, { name })` |
diff --git a/generate-schema-graph.js b/generate-schema-graph.js
index ff6b20f..eb43386 100644
--- a/generate-schema-graph.js
+++ b/generate-schema-graph.js
@@ -275,6 +275,8 @@ const CLUSTERS = [
'customergroup',
'entity',
'entityhistory',
+ 'entityaddress',
+ 'entitycontact',
'numbers',
],
},
diff --git a/schema-graph.dot b/schema-graph.dot
index 5797eb8..d8b2770 100644
--- a/schema-graph.dot
+++ b/schema-graph.dot
@@ -10,14 +10,16 @@ digraph schema {
style="rounded,filled"; fillcolor="#ffffff60"; color="#a0aec0";
fontcolor="#1a365d"; margin=14;
customergroup [label=<
>];
+ entityaddress [label=<| entityaddress |
|
|
|
|
|
| streetnumber? | NVARCHAR(50) |
|
|
|
|
|
>];
numbers [label=<>];
+ entity [label=<>];
company [label=< |
|
| default_language_id? | INT FK |
|
|
| poscustomergroup_id | INT FK |
|
|
|
|
|
>];
- entity [label=<>];
syncpos [label=<>];
+ entitycontact [label=<>];
meta [label=<>];
+ companysetting [label=<>];
entityhistory [label=<>];
companyuser [label=<>];
- companysetting [label=<>];
google_auth [label=< |
|
|
|
|
| picturebinary? | VARBINARY(MAX) |
|
|
|
>];
}
subgraph cluster_i18n {
@@ -58,9 +60,9 @@ digraph schema {
style="rounded,filled"; fillcolor="#ffffff60"; color="#a0aec0";
fontcolor="#1a365d"; margin=14;
tax [label=<>];
- manifest [label=<>];
- manifestarticle [label=<>];
price [label=<| price |
|
|
|
|
| minmultiplier | DECIMAL(18,4) |
|
|
>];
+ manifest [label=<>];
+ manifestarticle [label=<>];
}
subgraph cluster_units {
label="Units"; labeljust="l"; fontsize=15;
@@ -81,52 +83,54 @@ digraph schema {
company:reducedtaxrate:e -> tax:in0:n [color="#c05621", fontcolor="#c05621", tooltip="FK_company_reducedtaxrate"];
company:taxfreerate:e -> tax:in1:n [color="#319795", fontcolor="#319795", tooltip="FK_company_taxfreerate"];
customergroup:company_id:se -> company:in3:n [color="#97266d", fontcolor="#97266d", tooltip="FK_customergroup_company"];
- company:poscustomergroup_id:nw -> customergroup:in0:n [color="#4c51bf", fontcolor="#4c51bf", tooltip="FK_company_poscustomergroup"];
- entity:customergroup_id:sw -> customergroup:in1:n [color="#276749", fontcolor="#276749", tooltip="FK_entity_customergroup"];
- entityhistory:entity_id:sw -> entity:in0:n [color="#9c4221", fontcolor="#9c4221", tooltip="FK_entityhistory_entity"];
- entityhistory:companyuser_id:se -> companyuser:in1:n [color="#0987a0", fontcolor="#0987a0", tooltip="FK_entityhistory_companyuser"];
- unit:company_id:e -> company:in0:n [color="#553c9a", fontcolor="#553c9a", tooltip="FK_unit_company"];
- unittext:unit_id:se -> unit:in0:n [color="#742a2a", fontcolor="#742a2a", tooltip="FK_unittext_unit"];
- unittext:language_id:se -> language:in0:n [color="#22543d", fontcolor="#22543d", tooltip="FK_unittext_language"];
- brand:company_id:e -> company:in1:n [color="#1a365d", fontcolor="#1a365d", tooltip="FK_brand_company"];
- article:unit_id:sw -> unit:in1:n [color="#702459", fontcolor="#702459", tooltip="FK_article_unit"];
- article:brand_id:e -> brand:in0:n [color="#744210", fontcolor="#744210", tooltip="FK_article_brand"];
- article:articlelist_id:sw -> articlelist:in1:n [color="#285e61", fontcolor="#285e61", tooltip="FK_article_articlelist"];
- article:optionslist_id:se -> optionslist:in0:n [color="#d53f8c", fontcolor="#d53f8c", tooltip="FK_article_optionslist"];
- article:fatherarticle_id:e -> article:id:e [color="#dd6b20", fontcolor="#dd6b20", tooltip="FK_article_fatherarticle"];
- articlehistory:article_id:w -> article:in5:n [color="#38a169", fontcolor="#38a169", tooltip="FK_articlehistory_article"];
- articlehistory:companyuser_id:e -> companyuser:in0:n [color="#3182ce", fontcolor="#3182ce", tooltip="FK_articlehistory_companyuser"];
- articleean:article_id:sw -> article:in3:n [color="#805ad5", fontcolor="#805ad5", tooltip="FK_articleean_article"];
- articlepic:article_id:w -> article:in7:n [color="#c53030", fontcolor="#c53030", tooltip="FK_articlepic_article"];
- articlepic:pic_id:se -> pic:in1:n [color="#2f855a", fontcolor="#2f855a", tooltip="FK_articlepic_pic"];
- brandpic:brand_id:w -> brand:in1:n [color="#2b6cb0", fontcolor="#2b6cb0", tooltip="FK_brandpic_brand"];
- brandpic:pic_id:se -> pic:in0:n [color="#b7791f", fontcolor="#b7791f", tooltip="FK_brandpic_pic"];
- articlelistarticle:articlelist_id:se -> articlelist:in0:n [color="#6b46c1", fontcolor="#6b46c1", tooltip="FK_articlelistarticle_articlelist"];
- articlelistarticle:article_id:se -> article:in1:n [color="#c05621", fontcolor="#c05621", tooltip="FK_articlelistarticle_article"];
- optionslistarticle:optionslist_id:sw -> optionslist:in1:n [color="#319795", fontcolor="#319795", tooltip="FK_optionslistarticle_optionslist"];
- optionslistarticle:article_id:sw -> article:in4:n [color="#97266d", fontcolor="#97266d", tooltip="FK_optionslistarticle_article"];
- categoryarticle:category_id:sw -> category:in2:n [color="#4c51bf", fontcolor="#4c51bf", tooltip="FK_categoryarticle_category"];
- categoryarticle:article_id:w -> article:in6:n [color="#276749", fontcolor="#276749", tooltip="FK_categoryarticle_article"];
- categorypic:category_id:sw -> category:in4:n [color="#9c4221", fontcolor="#9c4221", tooltip="FK_categorypic_category"];
- categorypic:pic_id:sw -> pic:in2:n [color="#0987a0", fontcolor="#0987a0", tooltip="FK_categorypic_pic"];
- categorytext:category_id:e -> category:in0:n [color="#553c9a", fontcolor="#553c9a", tooltip="FK_categorytext_category"];
- categorytext:language_id:sw -> language:in1:n [color="#742a2a", fontcolor="#742a2a", tooltip="FK_categorytext_language"];
- articletext:article_id:e -> article:in0:n [color="#22543d", fontcolor="#22543d", tooltip="FK_articletext_article"];
- articletext:language_id:sw -> language:in2:n [color="#1a365d", fontcolor="#1a365d", tooltip="FK_articletext_language"];
- price:tax_id:sw -> tax:in4:n [color="#702459", fontcolor="#702459", tooltip="FK_price_tax"];
- price:article_id:w -> article:in9:n [color="#744210", fontcolor="#744210", tooltip="FK_price_article"];
- price:customergroup_id:w -> customergroup:in2:n [color="#285e61", fontcolor="#285e61", tooltip="FK_price_customergroup"];
- manifest:company_id:sw -> company:in9:n [color="#d53f8c", fontcolor="#d53f8c", tooltip="FK_manifest_company"];
- manifest:entity_id:w -> entity:in1:n [color="#dd6b20", fontcolor="#dd6b20", tooltip="FK_manifest_entity"];
- manifestarticle:manifest_id:sw -> manifest:in0:n [color="#38a169", fontcolor="#38a169", tooltip="FK_manifestarticle_manifest"];
- manifestarticle:article_id:w -> article:in8:n [color="#3182ce", fontcolor="#3182ce", tooltip="FK_manifestarticle_article"];
- manifestarticle:tax_id:sw -> tax:in3:n [color="#805ad5", fontcolor="#805ad5", tooltip="FK_manifestarticle_tax"];
- numbers:company_id:se -> company:in4:n [color="#c53030", fontcolor="#c53030", tooltip="FK_numbers_company"];
- companyuser:google_auth_id:se -> google_auth:in0:n [color="#2f855a", fontcolor="#2f855a", tooltip="FK_companyuser_google_auth"];
- companyuser:company_id:sw -> company:in6:n [color="#2b6cb0", fontcolor="#2b6cb0", tooltip="FK_companyuser_company"];
- syncpos:company_id:sw -> company:in5:n [color="#b7791f", fontcolor="#b7791f", tooltip="FK_syncpos_company"];
- companysetting:company_id:sw -> company:in7:n [color="#6b46c1", fontcolor="#6b46c1", tooltip="FK_companysetting_company"];
- posterminal:company_id:e -> company:in2:n [color="#c05621", fontcolor="#c05621", tooltip="FK_posterminal_company"];
- posterminalcategory:posterminal_id:se -> posterminal:in0:n [color="#319795", fontcolor="#319795", tooltip="FK_posterminalcategory_posterminal"];
- posterminalcategory:category_id:sw -> category:in3:n [color="#97266d", fontcolor="#97266d", tooltip="FK_posterminalcategory_category"];
+ company:poscustomergroup_id:nw -> customergroup:in1:n [color="#4c51bf", fontcolor="#4c51bf", tooltip="FK_company_poscustomergroup"];
+ entity:customergroup_id:sw -> customergroup:in0:n [color="#276749", fontcolor="#276749", tooltip="FK_entity_customergroup"];
+ entityaddress:entity_id:se -> entity:in0:n [color="#9c4221", fontcolor="#9c4221", tooltip="FK_entityaddress_entity"];
+ entitycontact:entity_id:sw -> entity:in1:n [color="#0987a0", fontcolor="#0987a0", tooltip="FK_entitycontact_entity"];
+ entityhistory:entity_id:sw -> entity:in2:n [color="#553c9a", fontcolor="#553c9a", tooltip="FK_entityhistory_entity"];
+ entityhistory:companyuser_id:se -> companyuser:in1:n [color="#742a2a", fontcolor="#742a2a", tooltip="FK_entityhistory_companyuser"];
+ unit:company_id:e -> company:in0:n [color="#22543d", fontcolor="#22543d", tooltip="FK_unit_company"];
+ unittext:unit_id:se -> unit:in0:n [color="#1a365d", fontcolor="#1a365d", tooltip="FK_unittext_unit"];
+ unittext:language_id:se -> language:in0:n [color="#702459", fontcolor="#702459", tooltip="FK_unittext_language"];
+ brand:company_id:e -> company:in1:n [color="#744210", fontcolor="#744210", tooltip="FK_brand_company"];
+ article:unit_id:sw -> unit:in1:n [color="#285e61", fontcolor="#285e61", tooltip="FK_article_unit"];
+ article:brand_id:e -> brand:in0:n [color="#d53f8c", fontcolor="#d53f8c", tooltip="FK_article_brand"];
+ article:articlelist_id:sw -> articlelist:in1:n [color="#dd6b20", fontcolor="#dd6b20", tooltip="FK_article_articlelist"];
+ article:optionslist_id:se -> optionslist:in0:n [color="#38a169", fontcolor="#38a169", tooltip="FK_article_optionslist"];
+ article:fatherarticle_id:e -> article:id:e [color="#3182ce", fontcolor="#3182ce", tooltip="FK_article_fatherarticle"];
+ articlehistory:article_id:w -> article:in5:n [color="#805ad5", fontcolor="#805ad5", tooltip="FK_articlehistory_article"];
+ articlehistory:companyuser_id:e -> companyuser:in0:n [color="#c53030", fontcolor="#c53030", tooltip="FK_articlehistory_companyuser"];
+ articleean:article_id:sw -> article:in3:n [color="#2f855a", fontcolor="#2f855a", tooltip="FK_articleean_article"];
+ articlepic:article_id:w -> article:in7:n [color="#2b6cb0", fontcolor="#2b6cb0", tooltip="FK_articlepic_article"];
+ articlepic:pic_id:se -> pic:in1:n [color="#b7791f", fontcolor="#b7791f", tooltip="FK_articlepic_pic"];
+ brandpic:brand_id:w -> brand:in1:n [color="#6b46c1", fontcolor="#6b46c1", tooltip="FK_brandpic_brand"];
+ brandpic:pic_id:se -> pic:in0:n [color="#c05621", fontcolor="#c05621", tooltip="FK_brandpic_pic"];
+ articlelistarticle:articlelist_id:se -> articlelist:in0:n [color="#319795", fontcolor="#319795", tooltip="FK_articlelistarticle_articlelist"];
+ articlelistarticle:article_id:se -> article:in1:n [color="#97266d", fontcolor="#97266d", tooltip="FK_articlelistarticle_article"];
+ optionslistarticle:optionslist_id:sw -> optionslist:in1:n [color="#4c51bf", fontcolor="#4c51bf", tooltip="FK_optionslistarticle_optionslist"];
+ optionslistarticle:article_id:sw -> article:in4:n [color="#276749", fontcolor="#276749", tooltip="FK_optionslistarticle_article"];
+ categoryarticle:category_id:sw -> category:in2:n [color="#9c4221", fontcolor="#9c4221", tooltip="FK_categoryarticle_category"];
+ categoryarticle:article_id:w -> article:in6:n [color="#0987a0", fontcolor="#0987a0", tooltip="FK_categoryarticle_article"];
+ categorypic:category_id:sw -> category:in4:n [color="#553c9a", fontcolor="#553c9a", tooltip="FK_categorypic_category"];
+ categorypic:pic_id:sw -> pic:in2:n [color="#742a2a", fontcolor="#742a2a", tooltip="FK_categorypic_pic"];
+ categorytext:category_id:e -> category:in0:n [color="#22543d", fontcolor="#22543d", tooltip="FK_categorytext_category"];
+ categorytext:language_id:sw -> language:in1:n [color="#1a365d", fontcolor="#1a365d", tooltip="FK_categorytext_language"];
+ articletext:article_id:e -> article:in0:n [color="#702459", fontcolor="#702459", tooltip="FK_articletext_article"];
+ articletext:language_id:sw -> language:in2:n [color="#744210", fontcolor="#744210", tooltip="FK_articletext_language"];
+ price:tax_id:sw -> tax:in3:n [color="#285e61", fontcolor="#285e61", tooltip="FK_price_tax"];
+ price:article_id:w -> article:in8:n [color="#d53f8c", fontcolor="#d53f8c", tooltip="FK_price_article"];
+ price:customergroup_id:w -> customergroup:in2:n [color="#dd6b20", fontcolor="#dd6b20", tooltip="FK_price_customergroup"];
+ manifest:company_id:sw -> company:in9:n [color="#38a169", fontcolor="#38a169", tooltip="FK_manifest_company"];
+ manifest:entity_id:w -> entity:in3:n [color="#3182ce", fontcolor="#3182ce", tooltip="FK_manifest_entity"];
+ manifestarticle:manifest_id:sw -> manifest:in0:n [color="#805ad5", fontcolor="#805ad5", tooltip="FK_manifestarticle_manifest"];
+ manifestarticle:article_id:w -> article:in9:n [color="#c53030", fontcolor="#c53030", tooltip="FK_manifestarticle_article"];
+ manifestarticle:tax_id:sw -> tax:in4:n [color="#2f855a", fontcolor="#2f855a", tooltip="FK_manifestarticle_tax"];
+ numbers:company_id:se -> company:in4:n [color="#2b6cb0", fontcolor="#2b6cb0", tooltip="FK_numbers_company"];
+ companyuser:google_auth_id:se -> google_auth:in0:n [color="#b7791f", fontcolor="#b7791f", tooltip="FK_companyuser_google_auth"];
+ companyuser:company_id:sw -> company:in7:n [color="#6b46c1", fontcolor="#6b46c1", tooltip="FK_companyuser_company"];
+ syncpos:company_id:sw -> company:in5:n [color="#c05621", fontcolor="#c05621", tooltip="FK_syncpos_company"];
+ companysetting:company_id:sw -> company:in6:n [color="#319795", fontcolor="#319795", tooltip="FK_companysetting_company"];
+ posterminal:company_id:e -> company:in2:n [color="#97266d", fontcolor="#97266d", tooltip="FK_posterminal_company"];
+ posterminalcategory:posterminal_id:se -> posterminal:in0:n [color="#4c51bf", fontcolor="#4c51bf", tooltip="FK_posterminalcategory_posterminal"];
+ posterminalcategory:category_id:sw -> category:in3:n [color="#276749", fontcolor="#276749", tooltip="FK_posterminalcategory_category"];
}
\ No newline at end of file
diff --git a/schema-graph.svg b/schema-graph.svg
index 2360bcd..f7e0d52 100644
--- a/schema-graph.svg
+++ b/schema-graph.svg
@@ -4,175 +4,279 @@
-