@endora-commerce/mod-addresses 0.100.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -0
- package/README.md +48 -0
- package/dist/backend/entities/address.entity.d.ts +23 -0
- package/dist/backend/entities/address.entity.d.ts.map +1 -0
- package/dist/backend/entities/address.entity.js +93 -0
- package/dist/backend/entities/address.entity.js.map +1 -0
- package/dist/backend/index.d.ts +58 -0
- package/dist/backend/index.d.ts.map +1 -0
- package/dist/backend/index.js +55 -0
- package/dist/backend/index.js.map +1 -0
- package/dist/backend/services/address-country-reference.d.ts +12 -0
- package/dist/backend/services/address-country-reference.d.ts.map +1 -0
- package/dist/backend/services/address-country-reference.js +24 -0
- package/dist/backend/services/address-country-reference.js.map +1 -0
- package/dist/backend/services/address-ports.d.ts +37 -0
- package/dist/backend/services/address-ports.d.ts.map +1 -0
- package/dist/backend/services/address-ports.js +88 -0
- package/dist/backend/services/address-ports.js.map +1 -0
- package/dist/backend/services/address-service.d.ts +42 -0
- package/dist/backend/services/address-service.d.ts.map +1 -0
- package/dist/backend/services/address-service.js +120 -0
- package/dist/backend/services/address-service.js.map +1 -0
- package/dist/manifest.d.ts +169 -0
- package/dist/manifest.d.ts.map +1 -0
- package/dist/manifest.js +90 -0
- package/dist/manifest.js.map +1 -0
- package/docs/addresses.md +31 -0
- package/i18n/en.json +3 -0
- package/i18n/pl.json +3 -0
- package/package.json +60 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Endora sp. z o.o. and the Endora Commerce contributors
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
ADDED
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
<!-- Generated by `pnpm --filter backend run manifests:generate`. Delete this line to take the file over; a README without it is never regenerated. -->
|
|
2
|
+
|
|
3
|
+
# @endora-commerce/mod-addresses
|
|
4
|
+
|
|
5
|
+
Reusable address entities used by organizations, customers, and order fulfilment.
|
|
6
|
+
|
|
7
|
+
## What this is
|
|
8
|
+
|
|
9
|
+
An **Endora Commerce module package**. Its module id is `addresses` — the identity of record in the platform's registries, in its migrations and in its setting, permission and translation codes.
|
|
10
|
+
|
|
11
|
+
A module is not imported by application code. The platform discovers the extension packages an instance has installed, reads the manifest on the root subpath and composes the module from it, so installing this package and starting the instance is the whole integration.
|
|
12
|
+
|
|
13
|
+
## Entry points
|
|
14
|
+
|
|
15
|
+
| Import | Contents |
|
|
16
|
+
| --- | --- |
|
|
17
|
+
| `@endora-commerce/mod-addresses` | the module manifest — its id, version, dependencies, settings and activation |
|
|
18
|
+
| `@endora-commerce/mod-addresses/backend` | the composition root the platform calls, with the entities, services, routes and workers it registers |
|
|
19
|
+
|
|
20
|
+
## Depends on
|
|
21
|
+
|
|
22
|
+
Everything below is a **peer** dependency, so the application resolves exactly one copy of each — two copies of a host or of React are a runtime failure rather than a type error. An entry marked *optional* is needed only by the layers that use it.
|
|
23
|
+
|
|
24
|
+
**Endora packages**
|
|
25
|
+
|
|
26
|
+
- `@endora-commerce/contracts`
|
|
27
|
+
- `@endora-commerce/platform`
|
|
28
|
+
|
|
29
|
+
**Third-party**
|
|
30
|
+
|
|
31
|
+
- `@mikro-orm/core` ^6
|
|
32
|
+
- `@mikro-orm/postgresql` ^6
|
|
33
|
+
|
|
34
|
+
## What the tarball carries
|
|
35
|
+
|
|
36
|
+
- `dist/` — the compiled JavaScript and its type declarations
|
|
37
|
+
- `i18n/` — the translation bundles this package ships (`en`, `pl`), loaded by the platform
|
|
38
|
+
- `docs/` — the package’s own pages in the Endora Commerce documentation site
|
|
39
|
+
|
|
40
|
+
## Install
|
|
41
|
+
|
|
42
|
+
```bash
|
|
43
|
+
pnpm add @endora-commerce/mod-addresses
|
|
44
|
+
```
|
|
45
|
+
|
|
46
|
+
## Licence
|
|
47
|
+
|
|
48
|
+
MIT — the text is in `LICENSE`, beside this file.
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import { OptionalProps } from '@mikro-orm/core';
|
|
2
|
+
/**
|
|
3
|
+
* Address — a delivery or billing address belonging to an Organization.
|
|
4
|
+
* The partial unique index "one default per (organization, kind)" is enforced
|
|
5
|
+
* in the migration, not at the ORM layer.
|
|
6
|
+
*/
|
|
7
|
+
export declare class Address {
|
|
8
|
+
[OptionalProps]?: 'id' | 'createdAt' | 'updatedAt' | 'isDefault' | 'phone' | 'deletedAt';
|
|
9
|
+
id: string;
|
|
10
|
+
organizationId: string;
|
|
11
|
+
kind: 'delivery' | 'billing';
|
|
12
|
+
recipientName: string;
|
|
13
|
+
street: string;
|
|
14
|
+
city: string;
|
|
15
|
+
postalCode: string;
|
|
16
|
+
country: string;
|
|
17
|
+
phone?: string | null;
|
|
18
|
+
isDefault: boolean;
|
|
19
|
+
createdAt: Date;
|
|
20
|
+
updatedAt: Date;
|
|
21
|
+
deletedAt?: Date | null;
|
|
22
|
+
}
|
|
23
|
+
//# sourceMappingURL=address.entity.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"address.entity.d.ts","sourceRoot":"","sources":["../../../src/backend/entities/address.entity.ts"],"names":[],"mappings":"AAAA,OAAO,EAAiB,aAAa,EAAwB,MAAM,iBAAiB,CAAC;AAIrF;;;;GAIG;AACH,qBAEa,OAAO;IAClB,CAAC,aAAa,CAAC,CAAC,EACZ,IAAI,GACJ,WAAW,GACX,WAAW,GACX,WAAW,GACX,OAAO,GACP,WAAW,CAAC;IAGhB,EAAE,EAAE,MAAM,CAAgB;IAI1B,cAAc,EAAG,MAAM,CAAC;IAIxB,IAAI,EAAG,UAAU,GAAG,SAAS,CAAC;IAG9B,aAAa,EAAG,MAAM,CAAC;IAGvB,MAAM,EAAG,MAAM,CAAC;IAGhB,IAAI,EAAG,MAAM,CAAC;IAGd,UAAU,EAAG,MAAM,CAAC;IAGpB,OAAO,EAAG,MAAM,CAAC;IAGjB,KAAK,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IAGtB,SAAS,EAAE,OAAO,CAAS;IAG3B,SAAS,EAAE,IAAI,CAAc;IAG7B,SAAS,EAAE,IAAI,CAAc;IAG7B,SAAS,CAAC,EAAE,IAAI,GAAG,IAAI,CAAC;CACzB"}
|
|
@@ -0,0 +1,93 @@
|
|
|
1
|
+
var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
|
|
2
|
+
var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
|
|
3
|
+
if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
|
|
4
|
+
else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
|
|
5
|
+
return c > 3 && r && Object.defineProperty(target, key, r), r;
|
|
6
|
+
};
|
|
7
|
+
var __metadata = (this && this.__metadata) || function (k, v) {
|
|
8
|
+
if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
|
|
9
|
+
};
|
|
10
|
+
import { Entity, Index, OptionalProps, PrimaryKey, Property } from '@mikro-orm/core';
|
|
11
|
+
import { OrgScoped } from '@endora-commerce/platform/tenancy';
|
|
12
|
+
import { randomUUID } from 'crypto';
|
|
13
|
+
/**
|
|
14
|
+
* Address — a delivery or billing address belonging to an Organization.
|
|
15
|
+
* The partial unique index "one default per (organization, kind)" is enforced
|
|
16
|
+
* in the migration, not at the ORM layer.
|
|
17
|
+
*/
|
|
18
|
+
let Address = class Address {
|
|
19
|
+
[OptionalProps];
|
|
20
|
+
id = randomUUID();
|
|
21
|
+
organizationId;
|
|
22
|
+
kind;
|
|
23
|
+
recipientName;
|
|
24
|
+
street;
|
|
25
|
+
city;
|
|
26
|
+
postalCode;
|
|
27
|
+
country;
|
|
28
|
+
phone;
|
|
29
|
+
isDefault = false;
|
|
30
|
+
createdAt = new Date();
|
|
31
|
+
updatedAt = new Date();
|
|
32
|
+
deletedAt;
|
|
33
|
+
};
|
|
34
|
+
__decorate([
|
|
35
|
+
PrimaryKey({ type: 'uuid' }),
|
|
36
|
+
__metadata("design:type", String)
|
|
37
|
+
], Address.prototype, "id", void 0);
|
|
38
|
+
__decorate([
|
|
39
|
+
Property({ type: 'uuid' }),
|
|
40
|
+
Index(),
|
|
41
|
+
__metadata("design:type", String)
|
|
42
|
+
], Address.prototype, "organizationId", void 0);
|
|
43
|
+
__decorate([
|
|
44
|
+
Property({ type: 'string', length: 16 }),
|
|
45
|
+
Index(),
|
|
46
|
+
__metadata("design:type", String)
|
|
47
|
+
], Address.prototype, "kind", void 0);
|
|
48
|
+
__decorate([
|
|
49
|
+
Property({ type: 'string', length: 160 }),
|
|
50
|
+
__metadata("design:type", String)
|
|
51
|
+
], Address.prototype, "recipientName", void 0);
|
|
52
|
+
__decorate([
|
|
53
|
+
Property({ type: 'string', length: 255 }),
|
|
54
|
+
__metadata("design:type", String)
|
|
55
|
+
], Address.prototype, "street", void 0);
|
|
56
|
+
__decorate([
|
|
57
|
+
Property({ type: 'string', length: 120 }),
|
|
58
|
+
__metadata("design:type", String)
|
|
59
|
+
], Address.prototype, "city", void 0);
|
|
60
|
+
__decorate([
|
|
61
|
+
Property({ type: 'string', length: 20 }),
|
|
62
|
+
__metadata("design:type", String)
|
|
63
|
+
], Address.prototype, "postalCode", void 0);
|
|
64
|
+
__decorate([
|
|
65
|
+
Property({ type: 'string', length: 2 }),
|
|
66
|
+
__metadata("design:type", String)
|
|
67
|
+
], Address.prototype, "country", void 0);
|
|
68
|
+
__decorate([
|
|
69
|
+
Property({ type: 'string', length: 32, nullable: true }),
|
|
70
|
+
__metadata("design:type", Object)
|
|
71
|
+
], Address.prototype, "phone", void 0);
|
|
72
|
+
__decorate([
|
|
73
|
+
Property({ type: 'boolean' }),
|
|
74
|
+
__metadata("design:type", Boolean)
|
|
75
|
+
], Address.prototype, "isDefault", void 0);
|
|
76
|
+
__decorate([
|
|
77
|
+
Property({ type: 'datetime', onCreate: () => new Date() }),
|
|
78
|
+
__metadata("design:type", Date)
|
|
79
|
+
], Address.prototype, "createdAt", void 0);
|
|
80
|
+
__decorate([
|
|
81
|
+
Property({ type: 'datetime', onUpdate: () => new Date() }),
|
|
82
|
+
__metadata("design:type", Date)
|
|
83
|
+
], Address.prototype, "updatedAt", void 0);
|
|
84
|
+
__decorate([
|
|
85
|
+
Property({ type: 'datetime', nullable: true }),
|
|
86
|
+
__metadata("design:type", Object)
|
|
87
|
+
], Address.prototype, "deletedAt", void 0);
|
|
88
|
+
Address = __decorate([
|
|
89
|
+
OrgScoped(),
|
|
90
|
+
Entity({ tableName: 'addresses' })
|
|
91
|
+
], Address);
|
|
92
|
+
export { Address };
|
|
93
|
+
//# sourceMappingURL=address.entity.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"address.entity.js","sourceRoot":"","sources":["../../../src/backend/entities/address.entity.ts"],"names":[],"mappings":";;;;;;;;;AAAA,OAAO,EAAE,MAAM,EAAE,KAAK,EAAE,aAAa,EAAE,UAAU,EAAE,QAAQ,EAAE,MAAM,iBAAiB,CAAC;AACrF,OAAO,EAAE,SAAS,EAAE,MAAM,mCAAmC,CAAC;AAC9D,OAAO,EAAE,UAAU,EAAE,MAAM,QAAQ,CAAC;AAEpC;;;;GAIG;AAGI,IAAM,OAAO,GAAb,MAAM,OAAO;IAClB,CAAC,aAAa,CAAC,CAMC;IAGhB,EAAE,GAAW,UAAU,EAAE,CAAC;IAI1B,cAAc,CAAU;IAIxB,IAAI,CAA0B;IAG9B,aAAa,CAAU;IAGvB,MAAM,CAAU;IAGhB,IAAI,CAAU;IAGd,UAAU,CAAU;IAGpB,OAAO,CAAU;IAGjB,KAAK,CAAiB;IAGtB,SAAS,GAAY,KAAK,CAAC;IAG3B,SAAS,GAAS,IAAI,IAAI,EAAE,CAAC;IAG7B,SAAS,GAAS,IAAI,IAAI,EAAE,CAAC;IAG7B,SAAS,CAAe;CACzB,CAAA;AAvCC;IADC,UAAU,CAAC,EAAE,IAAI,EAAE,MAAM,EAAE,CAAC;;mCACH;AAI1B;IAFC,QAAQ,CAAC,EAAE,IAAI,EAAE,MAAM,EAAE,CAAC;IAC1B,KAAK,EAAE;;+CACgB;AAIxB;IAFC,QAAQ,CAAC,EAAE,IAAI,EAAE,QAAQ,EAAE,MAAM,EAAE,EAAE,EAAE,CAAC;IACxC,KAAK,EAAE;;qCACsB;AAG9B;IADC,QAAQ,CAAC,EAAE,IAAI,EAAE,QAAQ,EAAE,MAAM,EAAE,GAAG,EAAE,CAAC;;8CACnB;AAGvB;IADC,QAAQ,CAAC,EAAE,IAAI,EAAE,QAAQ,EAAE,MAAM,EAAE,GAAG,EAAE,CAAC;;uCAC1B;AAGhB;IADC,QAAQ,CAAC,EAAE,IAAI,EAAE,QAAQ,EAAE,MAAM,EAAE,GAAG,EAAE,CAAC;;qCAC5B;AAGd;IADC,QAAQ,CAAC,EAAE,IAAI,EAAE,QAAQ,EAAE,MAAM,EAAE,EAAE,EAAE,CAAC;;2CACrB;AAGpB;IADC,QAAQ,CAAC,EAAE,IAAI,EAAE,QAAQ,EAAE,MAAM,EAAE,CAAC,EAAE,CAAC;;wCACvB;AAGjB;IADC,QAAQ,CAAC,EAAE,IAAI,EAAE,QAAQ,EAAE,MAAM,EAAE,EAAE,EAAE,QAAQ,EAAE,IAAI,EAAE,CAAC;;sCACnC;AAGtB;IADC,QAAQ,CAAC,EAAE,IAAI,EAAE,SAAS,EAAE,CAAC;;0CACH;AAG3B;IADC,QAAQ,CAAC,EAAE,IAAI,EAAE,UAAU,EAAE,QAAQ,EAAE,GAAG,EAAE,CAAC,IAAI,IAAI,EAAE,EAAE,CAAC;8BAChD,IAAI;0CAAc;AAG7B;IADC,QAAQ,CAAC,EAAE,IAAI,EAAE,UAAU,EAAE,QAAQ,EAAE,GAAG,EAAE,CAAC,IAAI,IAAI,EAAE,EAAE,CAAC;8BAChD,IAAI;0CAAc;AAG7B;IADC,QAAQ,CAAC,EAAE,IAAI,EAAE,UAAU,EAAE,QAAQ,EAAE,IAAI,EAAE,CAAC;;0CACvB;AAhDb,OAAO;IAFnB,SAAS,EAAE;IACX,MAAM,CAAC,EAAE,SAAS,EAAE,WAAW,EAAE,CAAC;GACtB,OAAO,CAiDnB"}
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
import type { EntityManager } from '@mikro-orm/postgresql';
|
|
2
|
+
import type { DictionaryValidator } from '@endora-commerce/contracts';
|
|
3
|
+
import type { AuditPort } from '@endora-commerce/platform/kernel';
|
|
4
|
+
import type { ModuleContext } from '@endora-commerce/platform/kernel';
|
|
5
|
+
import { AddressService } from './services/address-service.js';
|
|
6
|
+
import { Address } from './entities/address.entity.js';
|
|
7
|
+
/**
|
|
8
|
+
* `addresses` — one service, where there were three (feature 072, wave 1,
|
|
9
|
+
* T090).
|
|
10
|
+
*
|
|
11
|
+
* The module owns no routes: `organizations`, `customers`, `orders` and
|
|
12
|
+
* `quick_order` each serve their own address surface over the same table. Three
|
|
13
|
+
* of them built their own `AddressService`, and the constructor takes its
|
|
14
|
+
* dictionary validator and its audit writer as **optional** arguments — so the
|
|
15
|
+
* three instances were not required to agree, and one of them did not:
|
|
16
|
+
*
|
|
17
|
+
* orders/plugin.ts: options.addressService ?? new AddressService(options.emFactory)
|
|
18
|
+
*
|
|
19
|
+
* Both roots pass `addressService`, so that branch is dead today. It is a
|
|
20
|
+
* *loaded* dead branch, though, not an inert one: anything constructing
|
|
21
|
+
* `commerceModule` without the option gets an address service that validates no
|
|
22
|
+
* country or region code and writes no audit entry, on the checkout path.
|
|
23
|
+
* `currencies` shipped this exact shape in wave 1 and there it was live, which
|
|
24
|
+
* is the argument for removing the shape rather than the instance.
|
|
25
|
+
*
|
|
26
|
+
* A **port**, because four other modules resolve it: an address write reaching
|
|
27
|
+
* a module the operator switched off should get an explicit 503 rather than a
|
|
28
|
+
* service that half-answers.
|
|
29
|
+
*
|
|
30
|
+
* `dictionaryValidator` goes through `lazyPort`, and the reason changed under
|
|
31
|
+
* it. It used to be read once in the factory body — `const validator =
|
|
32
|
+
* ctx.cradle<…>().dictionaryValidator` — which looks deferred and is not: the
|
|
33
|
+
* factory body runs when the registration is first constructed. That was
|
|
34
|
+
* harmless while `dictionaries` registered a plain value, and stopped being
|
|
35
|
+
* harmless the moment that module converted and made it a port, because a
|
|
36
|
+
* singleton may not hold a transient gate. `lazyPort` defers it per call, which
|
|
37
|
+
* is what the comment always claimed was happening.
|
|
38
|
+
*/
|
|
39
|
+
export interface AddressesCradle {
|
|
40
|
+
readonly emFactory: () => EntityManager;
|
|
41
|
+
readonly auditLogService: AuditPort;
|
|
42
|
+
readonly dictionaryValidator: DictionaryValidator | undefined;
|
|
43
|
+
readonly addressService: AddressService;
|
|
44
|
+
}
|
|
45
|
+
export declare function registerModule(ctx: ModuleContext): void;
|
|
46
|
+
/**
|
|
47
|
+
* The module's persisted entity classes, on the `./backend` subpath, as one
|
|
48
|
+
* array and **no named class export** (D-168).
|
|
49
|
+
*
|
|
50
|
+
* This is the shape the platform reads when the package is *installed*: the
|
|
51
|
+
* boot-time loader (`src/packages/package-runtime.ts`, `exported['entities']`)
|
|
52
|
+
* and the static declaration reader (`scripts/lib/package-declarations.ts`),
|
|
53
|
+
* which is the third source of `check:module-boundary`'s `table→owner` map and
|
|
54
|
+
* the package pass of `check-entity-tenant-classification`. A missing array is
|
|
55
|
+
* answered with `[]` — zero entities registered, no error anywhere.
|
|
56
|
+
*/
|
|
57
|
+
export declare const entities: (typeof Address)[];
|
|
58
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/backend/index.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,uBAAuB,CAAC;AAC3D,OAAO,KAAK,EAAE,mBAAmB,EAEhC,MAAM,4BAA4B,CAAC;AACpC,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,kCAAkC,CAAC;AAElE,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,kCAAkC,CAAC;AAEtE,OAAO,EAAE,cAAc,EAAE,MAAM,+BAA+B,CAAC;AAG/D,OAAO,EAAE,OAAO,EAAE,MAAM,8BAA8B,CAAC;AAEvD;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA+BG;AAEH,MAAM,WAAW,eAAe;IAC9B,QAAQ,CAAC,SAAS,EAAE,MAAM,aAAa,CAAC;IACxC,QAAQ,CAAC,eAAe,EAAE,SAAS,CAAC;IACpC,QAAQ,CAAC,mBAAmB,EAAE,mBAAmB,GAAG,SAAS,CAAC;IAC9D,QAAQ,CAAC,cAAc,EAAE,cAAc,CAAC;CACzC;AAED,wBAAgB,cAAc,CAAC,GAAG,EAAE,aAAa,GAAG,IAAI,CA2DvD;AAED;;;;;;;;;;GAUG;AACH,eAAO,MAAM,QAAQ,oBAEpB,CAAC"}
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
import { lazyPort } from '@endora-commerce/platform/kernel';
|
|
2
|
+
import { AddressService } from './services/address-service.js';
|
|
3
|
+
import { AddressReadService, createAddressServicePort } from './services/address-ports.js';
|
|
4
|
+
import { registerAddressCountryReferences } from './services/address-country-reference.js';
|
|
5
|
+
import { Address } from './entities/address.entity.js';
|
|
6
|
+
export function registerModule(ctx) {
|
|
7
|
+
// ---------------------------------------------------------------------------
|
|
8
|
+
// Feature 075, Phase P — the published surface.
|
|
9
|
+
//
|
|
10
|
+
// `addressService` below hands out the class; four of the ten inbound sites
|
|
11
|
+
// do not want a service at all, they read the `Address` entity to snapshot
|
|
12
|
+
// one onto an order, serialise one, or resolve a buyer's default. These two
|
|
13
|
+
// ports are what both halves rewire to, and every read on the first is
|
|
14
|
+
// scoped by organisation — three of the four direct readers apply that check
|
|
15
|
+
// in a second statement today, in a tenancy-critical table.
|
|
16
|
+
// ---------------------------------------------------------------------------
|
|
17
|
+
ctx.di.providePort('addressReadPort', ctx
|
|
18
|
+
.asFunction(({ emFactory }) => new AddressReadService(emFactory))
|
|
19
|
+
.singleton());
|
|
20
|
+
ctx.di.providePort('addressServicePort', ctx
|
|
21
|
+
.asFunction(() => createAddressServicePort(() => ctx.cradle().addressService))
|
|
22
|
+
.singleton());
|
|
23
|
+
ctx.di.providePort('addressService', ctx
|
|
24
|
+
.asFunction(({ emFactory, auditLogService }) => new AddressService(emFactory, lazyPort(ctx, 'dictionaryValidator'), auditLogService))
|
|
25
|
+
.singleton());
|
|
26
|
+
/**
|
|
27
|
+
* This module's rows carry a country code, so it answers "who still points at
|
|
28
|
+
* this country?" about its own tables (feature 077, D-87), where the owner used
|
|
29
|
+
* to count them with SQL naming this module's tables.
|
|
30
|
+
*
|
|
31
|
+
* A **contribution** hook: it pushes an inert descriptor into `countryReferenceRegistry`,
|
|
32
|
+
* an ungated registry, and carries no presence probe (D-62/D-68). Probing
|
|
33
|
+
* would be wrong in the dangerous direction — a switched-off module still owns
|
|
34
|
+
* the rows, so its country must still refuse the delete, which is the
|
|
35
|
+
* enumeration policy the registry states.
|
|
36
|
+
*/
|
|
37
|
+
ctx.onBoot(() => {
|
|
38
|
+
registerAddressCountryReferences(lazyPort(ctx, 'countryReferenceRegistry'), ctx.cradle().emFactory);
|
|
39
|
+
});
|
|
40
|
+
}
|
|
41
|
+
/**
|
|
42
|
+
* The module's persisted entity classes, on the `./backend` subpath, as one
|
|
43
|
+
* array and **no named class export** (D-168).
|
|
44
|
+
*
|
|
45
|
+
* This is the shape the platform reads when the package is *installed*: the
|
|
46
|
+
* boot-time loader (`src/packages/package-runtime.ts`, `exported['entities']`)
|
|
47
|
+
* and the static declaration reader (`scripts/lib/package-declarations.ts`),
|
|
48
|
+
* which is the third source of `check:module-boundary`'s `table→owner` map and
|
|
49
|
+
* the package pass of `check-entity-tenant-classification`. A missing array is
|
|
50
|
+
* answered with `[]` — zero entities registered, no error anywhere.
|
|
51
|
+
*/
|
|
52
|
+
export const entities = [
|
|
53
|
+
Address,
|
|
54
|
+
];
|
|
55
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/backend/index.ts"],"names":[],"mappings":"AAOA,OAAO,EAAE,QAAQ,EAAE,MAAM,kCAAkC,CAAC;AAC5D,OAAO,EAAE,cAAc,EAAE,MAAM,+BAA+B,CAAC;AAC/D,OAAO,EAAE,kBAAkB,EAAE,wBAAwB,EAAE,MAAM,6BAA6B,CAAC;AAC3F,OAAO,EAAE,gCAAgC,EAAE,MAAM,yCAAyC,CAAC;AAC3F,OAAO,EAAE,OAAO,EAAE,MAAM,8BAA8B,CAAC;AA0CvD,MAAM,UAAU,cAAc,CAAC,GAAkB;IAC/C,8EAA8E;IAC9E,gDAAgD;IAChD,EAAE;IACF,4EAA4E;IAC5E,2EAA2E;IAC3E,4EAA4E;IAC5E,uEAAuE;IACvE,6EAA6E;IAC7E,4DAA4D;IAC5D,8EAA8E;IAE9E,GAAG,CAAC,EAAE,CAAC,WAAW,CAChB,iBAAiB,EACjB,GAAG;SACA,UAAU,CAAC,CAAC,EAAE,SAAS,EAAmB,EAAE,EAAE,CAAC,IAAI,kBAAkB,CAAC,SAAS,CAAC,CAAC;SACjF,SAAS,EAAE,CACf,CAAC;IAEF,GAAG,CAAC,EAAE,CAAC,WAAW,CAChB,oBAAoB,EACpB,GAAG;SACA,UAAU,CAAC,GAAG,EAAE,CACf,wBAAwB,CAAC,GAAG,EAAE,CAAC,GAAG,CAAC,MAAM,EAAmB,CAAC,cAAc,CAAC,CAC7E;SACA,SAAS,EAAE,CACf,CAAC;IAEF,GAAG,CAAC,EAAE,CAAC,WAAW,CAChB,gBAAgB,EAChB,GAAG;SACA,UAAU,CACT,CAAC,EAAE,SAAS,EAAE,eAAe,EAAmB,EAAE,EAAE,CAClD,IAAI,cAAc,CAChB,SAAS,EACT,QAAQ,CAAsB,GAAG,EAAE,qBAAqB,CAAC,EACzD,eAAe,CAChB,CACJ;SACA,SAAS,EAAE,CACf,CAAC;IAEF;;;;;;;;;;OAUG;IACH,GAAG,CAAC,MAAM,CAAC,GAAG,EAAE;QACd,gCAAgC,CAC9B,QAAQ,CAAkC,GAAG,EAAE,0BAA0B,CAAC,EAC1E,GAAG,CAAC,MAAM,EAAmB,CAAC,SAAS,CACxC,CAAC;IACJ,CAAC,CAAC,CAAC;AACL,CAAC;AAED;;;;;;;;;;GAUG;AACH,MAAM,CAAC,MAAM,QAAQ,GAAG;IACtB,OAAO;CACR,CAAC"}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import type { DictionaryReferenceRegistryPort } from '@endora-commerce/contracts';
|
|
2
|
+
import type { EntityManager } from '@mikro-orm/postgresql';
|
|
3
|
+
/**
|
|
4
|
+
* Who in `addresses` still points at a country (feature 077, D-87).
|
|
5
|
+
*
|
|
6
|
+
* `dictionaries` used to count this table itself and join it a second time
|
|
7
|
+
* for the orphan report, naming it in strings no import-level boundary check
|
|
8
|
+
* can see. An address with a country the platform does not have cannot be
|
|
9
|
+
* validated or rendered, so the reference blocks the delete.
|
|
10
|
+
*/
|
|
11
|
+
export declare function registerAddressCountryReferences(registry: DictionaryReferenceRegistryPort, emFactory: () => EntityManager): void;
|
|
12
|
+
//# sourceMappingURL=address-country-reference.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"address-country-reference.d.ts","sourceRoot":"","sources":["../../../src/backend/services/address-country-reference.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,+BAA+B,EAAE,MAAM,4BAA4B,CAAC;AAClF,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,uBAAuB,CAAC;AAE3D;;;;;;;GAOG;AACH,wBAAgB,gCAAgC,CAC9C,QAAQ,EAAE,+BAA+B,EACzC,SAAS,EAAE,MAAM,aAAa,GAC7B,IAAI,CAoBN"}
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Who in `addresses` still points at a country (feature 077, D-87).
|
|
3
|
+
*
|
|
4
|
+
* `dictionaries` used to count this table itself and join it a second time
|
|
5
|
+
* for the orphan report, naming it in strings no import-level boundary check
|
|
6
|
+
* can see. An address with a country the platform does not have cannot be
|
|
7
|
+
* validated or rendered, so the reference blocks the delete.
|
|
8
|
+
*/
|
|
9
|
+
export function registerAddressCountryReferences(registry, emFactory) {
|
|
10
|
+
registry.register({
|
|
11
|
+
ownerModuleId: 'addresses',
|
|
12
|
+
consumer: 'addresses',
|
|
13
|
+
tableName: 'addresses',
|
|
14
|
+
columnName: 'country',
|
|
15
|
+
blocking: true,
|
|
16
|
+
countReferences: async (code) => {
|
|
17
|
+
const rows = (await emFactory().execute(`select count(*)::int as n from "addresses" where "country" = ?`, [code]));
|
|
18
|
+
return rows[0]?.n ?? 0;
|
|
19
|
+
},
|
|
20
|
+
usedCodes: async () => (await emFactory().execute(`select "country" as code, count(*)::int as count
|
|
21
|
+
from "addresses" where "country" is not null group by "country"`)),
|
|
22
|
+
});
|
|
23
|
+
}
|
|
24
|
+
//# sourceMappingURL=address-country-reference.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"address-country-reference.js","sourceRoot":"","sources":["../../../src/backend/services/address-country-reference.ts"],"names":[],"mappings":"AAGA;;;;;;;GAOG;AACH,MAAM,UAAU,gCAAgC,CAC9C,QAAyC,EACzC,SAA8B;IAE9B,QAAQ,CAAC,QAAQ,CAAC;QAChB,aAAa,EAAE,WAAW;QAC1B,QAAQ,EAAE,WAAW;QACrB,SAAS,EAAE,WAAW;QACtB,UAAU,EAAE,SAAS;QACrB,QAAQ,EAAE,IAAI;QACd,eAAe,EAAE,KAAK,EAAE,IAAI,EAAE,EAAE;YAC9B,MAAM,IAAI,GAAG,CAAC,MAAM,SAAS,EAAE,CAAC,OAAO,CACrC,gEAAgE,EAChE,CAAC,IAAI,CAAC,CACP,CAAyB,CAAC;YAC3B,OAAO,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC,IAAI,CAAC,CAAC;QACzB,CAAC;QACD,SAAS,EAAE,KAAK,IAAI,EAAE,CACpB,CAAC,MAAM,SAAS,EAAE,CAAC,OAAO,CACxB;2EACmE,CACpE,CAA2C;KAC/C,CAAC,CAAC;AACL,CAAC"}
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
import type { EntityManager } from '@mikro-orm/postgresql';
|
|
2
|
+
import type { AddressKindValue, AddressReadPort, AddressRecord, AddressServicePort } from '@endora-commerce/contracts';
|
|
3
|
+
import { Address } from '../entities/address.entity.js';
|
|
4
|
+
import type { AddressService } from './address-service.js';
|
|
5
|
+
/**
|
|
6
|
+
* The published face of `addresses` (feature 075, Phase P).
|
|
7
|
+
*
|
|
8
|
+
* Ten inbound sites, six-four: `orders` and `organizations` type themselves
|
|
9
|
+
* against `AddressService`, and four read the `Address` entity directly —
|
|
10
|
+
* `orders` snapshotting one onto a placed order, `customers` serialising one,
|
|
11
|
+
* `quick_order` resolving a buyer's default.
|
|
12
|
+
*
|
|
13
|
+
* Every read here is scoped by organisation. That is not defensiveness: the
|
|
14
|
+
* four direct readers each already know whose address they are asking for, and
|
|
15
|
+
* three of them apply the check in a second statement — which is a check that
|
|
16
|
+
* can be forgotten, and in a tenancy-critical table.
|
|
17
|
+
*/
|
|
18
|
+
export declare class AddressReadService implements AddressReadPort {
|
|
19
|
+
private readonly emFactory;
|
|
20
|
+
constructor(emFactory: () => EntityManager);
|
|
21
|
+
findById(organizationId: string, addressId: string, options?: {
|
|
22
|
+
liveOnly?: boolean;
|
|
23
|
+
}): Promise<AddressRecord | null>;
|
|
24
|
+
findByIds(organizationId: string, addressIds: readonly string[], options?: {
|
|
25
|
+
liveOnly?: boolean;
|
|
26
|
+
}): Promise<AddressRecord[]>;
|
|
27
|
+
listForOrganization(organizationId: string, kind?: AddressKindValue): Promise<AddressRecord[]>;
|
|
28
|
+
findDefault(organizationId: string, kind: AddressKindValue): Promise<AddressRecord | null>;
|
|
29
|
+
}
|
|
30
|
+
/**
|
|
31
|
+
* The write side, adapting `AddressService`. The service arrives as a getter
|
|
32
|
+
* so the adapter resolves it per call rather than capturing this module's own
|
|
33
|
+
* gated port into a singleton.
|
|
34
|
+
*/
|
|
35
|
+
export declare function createAddressServicePort(getService: () => AddressService): AddressServicePort;
|
|
36
|
+
export declare function toAddressRecord(address: Address): AddressRecord;
|
|
37
|
+
//# sourceMappingURL=address-ports.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"address-ports.d.ts","sourceRoot":"","sources":["../../../src/backend/services/address-ports.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,uBAAuB,CAAC;AAC3D,OAAO,KAAK,EACV,gBAAgB,EAChB,eAAe,EACf,aAAa,EACb,kBAAkB,EACnB,MAAM,4BAA4B,CAAC;AACpC,OAAO,EAAE,OAAO,EAAE,MAAM,+BAA+B,CAAC;AACxD,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,sBAAsB,CAAC;AAE3D;;;;;;;;;;;;GAYG;AACH,qBAAa,kBAAmB,YAAW,eAAe;IAC5C,OAAO,CAAC,QAAQ,CAAC,SAAS;gBAAT,SAAS,EAAE,MAAM,aAAa;IAErD,QAAQ,CACZ,cAAc,EAAE,MAAM,EACtB,SAAS,EAAE,MAAM,EACjB,OAAO,CAAC,EAAE;QAAE,QAAQ,CAAC,EAAE,OAAO,CAAA;KAAE,GAC/B,OAAO,CAAC,aAAa,GAAG,IAAI,CAAC;IAS1B,SAAS,CACb,cAAc,EAAE,MAAM,EACtB,UAAU,EAAE,SAAS,MAAM,EAAE,EAC7B,OAAO,CAAC,EAAE;QAAE,QAAQ,CAAC,EAAE,OAAO,CAAA;KAAE,GAC/B,OAAO,CAAC,aAAa,EAAE,CAAC;IAUrB,mBAAmB,CACvB,cAAc,EAAE,MAAM,EACtB,IAAI,CAAC,EAAE,gBAAgB,GACtB,OAAO,CAAC,aAAa,EAAE,CAAC;IASrB,WAAW,CACf,cAAc,EAAE,MAAM,EACtB,IAAI,EAAE,gBAAgB,GACrB,OAAO,CAAC,aAAa,GAAG,IAAI,CAAC;CASjC;AAED;;;;GAIG;AACH,wBAAgB,wBAAwB,CAAC,UAAU,EAAE,MAAM,cAAc,GAAG,kBAAkB,CAc7F;AAED,wBAAgB,eAAe,CAAC,OAAO,EAAE,OAAO,GAAG,aAAa,CAgB/D"}
|
|
@@ -0,0 +1,88 @@
|
|
|
1
|
+
import { Address } from '../entities/address.entity.js';
|
|
2
|
+
/**
|
|
3
|
+
* The published face of `addresses` (feature 075, Phase P).
|
|
4
|
+
*
|
|
5
|
+
* Ten inbound sites, six-four: `orders` and `organizations` type themselves
|
|
6
|
+
* against `AddressService`, and four read the `Address` entity directly —
|
|
7
|
+
* `orders` snapshotting one onto a placed order, `customers` serialising one,
|
|
8
|
+
* `quick_order` resolving a buyer's default.
|
|
9
|
+
*
|
|
10
|
+
* Every read here is scoped by organisation. That is not defensiveness: the
|
|
11
|
+
* four direct readers each already know whose address they are asking for, and
|
|
12
|
+
* three of them apply the check in a second statement — which is a check that
|
|
13
|
+
* can be forgotten, and in a tenancy-critical table.
|
|
14
|
+
*/
|
|
15
|
+
export class AddressReadService {
|
|
16
|
+
emFactory;
|
|
17
|
+
constructor(emFactory) {
|
|
18
|
+
this.emFactory = emFactory;
|
|
19
|
+
}
|
|
20
|
+
async findById(organizationId, addressId, options) {
|
|
21
|
+
const address = await this.emFactory().findOne(Address, {
|
|
22
|
+
id: addressId,
|
|
23
|
+
organizationId,
|
|
24
|
+
...(options?.liveOnly ? { deletedAt: null } : {}),
|
|
25
|
+
});
|
|
26
|
+
return address ? toAddressRecord(address) : null;
|
|
27
|
+
}
|
|
28
|
+
async findByIds(organizationId, addressIds, options) {
|
|
29
|
+
if (addressIds.length === 0)
|
|
30
|
+
return [];
|
|
31
|
+
const addresses = await this.emFactory().find(Address, {
|
|
32
|
+
id: { $in: [...addressIds] },
|
|
33
|
+
organizationId,
|
|
34
|
+
...(options?.liveOnly ? { deletedAt: null } : {}),
|
|
35
|
+
});
|
|
36
|
+
return addresses.map(toAddressRecord);
|
|
37
|
+
}
|
|
38
|
+
async listForOrganization(organizationId, kind) {
|
|
39
|
+
const addresses = await this.emFactory().find(Address, { organizationId, deletedAt: null, ...(kind === undefined ? {} : { kind }) }, { orderBy: { isDefault: 'desc', createdAt: 'asc' } });
|
|
40
|
+
return addresses.map(toAddressRecord);
|
|
41
|
+
}
|
|
42
|
+
async findDefault(organizationId, kind) {
|
|
43
|
+
const address = await this.emFactory().findOne(Address, {
|
|
44
|
+
organizationId,
|
|
45
|
+
kind,
|
|
46
|
+
isDefault: true,
|
|
47
|
+
deletedAt: null,
|
|
48
|
+
});
|
|
49
|
+
return address ? toAddressRecord(address) : null;
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
/**
|
|
53
|
+
* The write side, adapting `AddressService`. The service arrives as a getter
|
|
54
|
+
* so the adapter resolves it per call rather than capturing this module's own
|
|
55
|
+
* gated port into a singleton.
|
|
56
|
+
*/
|
|
57
|
+
export function createAddressServicePort(getService) {
|
|
58
|
+
return {
|
|
59
|
+
async list(organizationId, kind) {
|
|
60
|
+
return (await getService().list(organizationId, kind)).map(toAddressRecord);
|
|
61
|
+
},
|
|
62
|
+
async createAddress(organizationId, input) {
|
|
63
|
+
return toAddressRecord(await getService().createAddress(organizationId, input));
|
|
64
|
+
},
|
|
65
|
+
async updateAddress(organizationId, addressId, patch) {
|
|
66
|
+
return toAddressRecord(await getService().updateAddress(organizationId, addressId, patch));
|
|
67
|
+
},
|
|
68
|
+
deleteAddress: (organizationId, addressId) => getService().deleteAddress(organizationId, addressId),
|
|
69
|
+
};
|
|
70
|
+
}
|
|
71
|
+
export function toAddressRecord(address) {
|
|
72
|
+
return {
|
|
73
|
+
id: address.id,
|
|
74
|
+
organizationId: address.organizationId,
|
|
75
|
+
kind: address.kind,
|
|
76
|
+
recipientName: address.recipientName,
|
|
77
|
+
street: address.street,
|
|
78
|
+
city: address.city,
|
|
79
|
+
postalCode: address.postalCode,
|
|
80
|
+
country: address.country,
|
|
81
|
+
phone: address.phone ?? null,
|
|
82
|
+
isDefault: address.isDefault,
|
|
83
|
+
createdAt: address.createdAt,
|
|
84
|
+
updatedAt: address.updatedAt,
|
|
85
|
+
deletedAt: address.deletedAt ?? null,
|
|
86
|
+
};
|
|
87
|
+
}
|
|
88
|
+
//# sourceMappingURL=address-ports.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"address-ports.js","sourceRoot":"","sources":["../../../src/backend/services/address-ports.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
import type { EntityManager } from '@mikro-orm/postgresql';
|
|
2
|
+
import { type DictionaryValidator } from '@endora-commerce/contracts';
|
|
3
|
+
import type { AuditPort } from '@endora-commerce/platform/kernel';
|
|
4
|
+
import { Address } from '../entities/address.entity.js';
|
|
5
|
+
/**
|
|
6
|
+
* AddressService (T120).
|
|
7
|
+
*
|
|
8
|
+
* `setDefault` is atomic — it clears any prior `is_default=true` address of the
|
|
9
|
+
* same (organization, kind), then sets the new default, inside one transaction.
|
|
10
|
+
* The partial unique index on addresses prevents two defaults from coexisting
|
|
11
|
+
* even under concurrent callers.
|
|
12
|
+
*/
|
|
13
|
+
export declare class AddressService {
|
|
14
|
+
#private;
|
|
15
|
+
private readonly emFactory;
|
|
16
|
+
private readonly dictionaryValidator?;
|
|
17
|
+
private readonly auditLog?;
|
|
18
|
+
constructor(emFactory: () => EntityManager, dictionaryValidator?: DictionaryValidator | undefined, auditLog?: AuditPort | undefined);
|
|
19
|
+
list(organizationId: string, kind?: 'delivery' | 'billing'): Promise<Address[]>;
|
|
20
|
+
createAddress(organizationId: string, input: {
|
|
21
|
+
kind: 'delivery' | 'billing';
|
|
22
|
+
recipientName: string;
|
|
23
|
+
street: string;
|
|
24
|
+
city: string;
|
|
25
|
+
postalCode: string;
|
|
26
|
+
country: string;
|
|
27
|
+
phone?: string;
|
|
28
|
+
isDefault?: boolean;
|
|
29
|
+
}): Promise<Address>;
|
|
30
|
+
updateAddress(organizationId: string, addressId: string, patch: Partial<{
|
|
31
|
+
recipientName: string;
|
|
32
|
+
street: string;
|
|
33
|
+
city: string;
|
|
34
|
+
postalCode: string;
|
|
35
|
+
country: string;
|
|
36
|
+
phone: string;
|
|
37
|
+
isDefault: boolean;
|
|
38
|
+
}>): Promise<Address>;
|
|
39
|
+
deleteAddress(organizationId: string, addressId: string): Promise<void>;
|
|
40
|
+
private validateCountry;
|
|
41
|
+
}
|
|
42
|
+
//# sourceMappingURL=address-service.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"address-service.d.ts","sourceRoot":"","sources":["../../../src/backend/services/address-service.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,uBAAuB,CAAC;AAC3D,OAAO,EAIL,KAAK,mBAAmB,EACzB,MAAM,4BAA4B,CAAC;AAGpC,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,kCAAkC,CAAC;AAClE,OAAO,EAAE,OAAO,EAAE,MAAM,+BAA+B,CAAC;AAExD;;;;;;;GAOG;AACH,qBAAa,cAAc;;IAEvB,OAAO,CAAC,QAAQ,CAAC,SAAS;IAC1B,OAAO,CAAC,QAAQ,CAAC,mBAAmB,CAAC;IACrC,OAAO,CAAC,QAAQ,CAAC,QAAQ,CAAC;gBAFT,SAAS,EAAE,MAAM,aAAa,EAC9B,mBAAmB,CAAC,EAAE,mBAAmB,YAAA,EACzC,QAAQ,CAAC,EAAE,SAAS,YAAA;IASjC,IAAI,CAAC,cAAc,EAAE,MAAM,EAAE,IAAI,CAAC,EAAE,UAAU,GAAG,SAAS,GAAG,OAAO,CAAC,OAAO,EAAE,CAAC;IAO/E,aAAa,CACjB,cAAc,EAAE,MAAM,EACtB,KAAK,EAAE;QACL,IAAI,EAAE,UAAU,GAAG,SAAS,CAAC;QAC7B,aAAa,EAAE,MAAM,CAAC;QACtB,MAAM,EAAE,MAAM,CAAC;QACf,IAAI,EAAE,MAAM,CAAC;QACb,UAAU,EAAE,MAAM,CAAC;QACnB,OAAO,EAAE,MAAM,CAAC;QAChB,KAAK,CAAC,EAAE,MAAM,CAAC;QACf,SAAS,CAAC,EAAE,OAAO,CAAC;KACrB,GACA,OAAO,CAAC,OAAO,CAAC;IA8Bb,aAAa,CACjB,cAAc,EAAE,MAAM,EACtB,SAAS,EAAE,MAAM,EACjB,KAAK,EAAE,OAAO,CAAC;QACb,aAAa,EAAE,MAAM,CAAC;QACtB,MAAM,EAAE,MAAM,CAAC;QACf,IAAI,EAAE,MAAM,CAAC;QACb,UAAU,EAAE,MAAM,CAAC;QACnB,OAAO,EAAE,MAAM,CAAC;QAChB,KAAK,EAAE,MAAM,CAAC;QACd,SAAS,EAAE,OAAO,CAAC;KACpB,CAAC,GACD,OAAO,CAAC,OAAO,CAAC;IAiCb,aAAa,CAAC,cAAc,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;YAc/D,eAAe;CAqB9B"}
|
|
@@ -0,0 +1,120 @@
|
|
|
1
|
+
import { DictionaryReferenceError, ERROR_CODES, dispatchValidatorMode, } from '@endora-commerce/contracts';
|
|
2
|
+
import { HttpError } from '@endora-commerce/platform/http';
|
|
3
|
+
import { recordAuditFromContext } from '@endora-commerce/platform/commands';
|
|
4
|
+
import { Address } from '../entities/address.entity.js';
|
|
5
|
+
/**
|
|
6
|
+
* AddressService (T120).
|
|
7
|
+
*
|
|
8
|
+
* `setDefault` is atomic — it clears any prior `is_default=true` address of the
|
|
9
|
+
* same (organization, kind), then sets the new default, inside one transaction.
|
|
10
|
+
* The partial unique index on addresses prevents two defaults from coexisting
|
|
11
|
+
* even under concurrent callers.
|
|
12
|
+
*/
|
|
13
|
+
export class AddressService {
|
|
14
|
+
emFactory;
|
|
15
|
+
dictionaryValidator;
|
|
16
|
+
auditLog;
|
|
17
|
+
constructor(emFactory, dictionaryValidator, auditLog) {
|
|
18
|
+
this.emFactory = emFactory;
|
|
19
|
+
this.dictionaryValidator = dictionaryValidator;
|
|
20
|
+
this.auditLog = auditLog;
|
|
21
|
+
}
|
|
22
|
+
#audit(em, action, objectId, stateBefore, stateAfter) {
|
|
23
|
+
if (this.auditLog) {
|
|
24
|
+
recordAuditFromContext(this.auditLog, em, { action, objectType: 'address', objectId, stateBefore, stateAfter });
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
async list(organizationId, kind) {
|
|
28
|
+
const em = this.emFactory();
|
|
29
|
+
const where = { organizationId, deletedAt: null };
|
|
30
|
+
if (kind)
|
|
31
|
+
where['kind'] = kind;
|
|
32
|
+
return em.find(Address, where, { orderBy: { isDefault: 'desc', createdAt: 'asc' } });
|
|
33
|
+
}
|
|
34
|
+
async createAddress(organizationId, input) {
|
|
35
|
+
await this.validateCountry(input.country, 'create-or-change');
|
|
36
|
+
const em = this.emFactory();
|
|
37
|
+
return em.transactional(async (txEm) => {
|
|
38
|
+
if (input.isDefault) {
|
|
39
|
+
// Demote any existing default of the same kind.
|
|
40
|
+
await txEm.nativeUpdate(Address, { organizationId, kind: input.kind, isDefault: true, deletedAt: null }, { isDefault: false });
|
|
41
|
+
}
|
|
42
|
+
const address = txEm.create(Address, {
|
|
43
|
+
organizationId,
|
|
44
|
+
kind: input.kind,
|
|
45
|
+
recipientName: input.recipientName,
|
|
46
|
+
street: input.street,
|
|
47
|
+
city: input.city,
|
|
48
|
+
postalCode: input.postalCode,
|
|
49
|
+
country: input.country.toUpperCase(),
|
|
50
|
+
...(input.phone ? { phone: input.phone } : {}),
|
|
51
|
+
isDefault: input.isDefault ?? false,
|
|
52
|
+
});
|
|
53
|
+
txEm.persist(address);
|
|
54
|
+
this.#audit(txEm, 'address.create', address.id, null, { organizationId, kind: address.kind });
|
|
55
|
+
await txEm.flush();
|
|
56
|
+
return address;
|
|
57
|
+
});
|
|
58
|
+
}
|
|
59
|
+
async updateAddress(organizationId, addressId, patch) {
|
|
60
|
+
const em = this.emFactory();
|
|
61
|
+
return em.transactional(async (txEm) => {
|
|
62
|
+
const address = await txEm.findOne(Address, { id: addressId, organizationId, deletedAt: null });
|
|
63
|
+
if (!address) {
|
|
64
|
+
throw new HttpError(404, ERROR_CODES.NOT_FOUND, 'Address not found.');
|
|
65
|
+
}
|
|
66
|
+
if (patch.isDefault === true && !address.isDefault) {
|
|
67
|
+
await txEm.nativeUpdate(Address, { organizationId, kind: address.kind, isDefault: true, deletedAt: null }, { isDefault: false });
|
|
68
|
+
}
|
|
69
|
+
if (patch.country !== undefined) {
|
|
70
|
+
await this.validateCountry(patch.country, dispatchValidatorMode(address.country, patch.country));
|
|
71
|
+
}
|
|
72
|
+
if (patch.recipientName !== undefined)
|
|
73
|
+
address.recipientName = patch.recipientName;
|
|
74
|
+
if (patch.street !== undefined)
|
|
75
|
+
address.street = patch.street;
|
|
76
|
+
if (patch.city !== undefined)
|
|
77
|
+
address.city = patch.city;
|
|
78
|
+
if (patch.postalCode !== undefined)
|
|
79
|
+
address.postalCode = patch.postalCode;
|
|
80
|
+
if (patch.country !== undefined)
|
|
81
|
+
address.country = patch.country.toUpperCase();
|
|
82
|
+
if (patch.phone !== undefined)
|
|
83
|
+
address.phone = patch.phone;
|
|
84
|
+
if (patch.isDefault !== undefined)
|
|
85
|
+
address.isDefault = patch.isDefault;
|
|
86
|
+
this.#audit(txEm, 'address.update', address.id, null, { organizationId, kind: address.kind });
|
|
87
|
+
await txEm.flush();
|
|
88
|
+
return address;
|
|
89
|
+
});
|
|
90
|
+
}
|
|
91
|
+
async deleteAddress(organizationId, addressId) {
|
|
92
|
+
const em = this.emFactory();
|
|
93
|
+
const address = await em.findOne(Address, { id: addressId, organizationId, deletedAt: null });
|
|
94
|
+
if (!address) {
|
|
95
|
+
throw new HttpError(404, ERROR_CODES.NOT_FOUND, 'Address not found.');
|
|
96
|
+
}
|
|
97
|
+
// ADDRESS_IN_USE check lives with the orders module (US2 T142) — the Order
|
|
98
|
+
// entity there references addresses via snapshot, so a true "in-use" block
|
|
99
|
+
// must query the Orders table. For now we soft-delete.
|
|
100
|
+
address.deletedAt = new Date();
|
|
101
|
+
this.#audit(em, 'address.delete', address.id, { organizationId, kind: address.kind }, null);
|
|
102
|
+
await em.flush();
|
|
103
|
+
}
|
|
104
|
+
async validateCountry(country, mode) {
|
|
105
|
+
if (!this.dictionaryValidator)
|
|
106
|
+
return;
|
|
107
|
+
try {
|
|
108
|
+
await this.dictionaryValidator.validateCountryCode(country, mode);
|
|
109
|
+
}
|
|
110
|
+
catch (err) {
|
|
111
|
+
if (err instanceof DictionaryReferenceError) {
|
|
112
|
+
throw new HttpError(409, err.code, err.code === 'DICTIONARY_ENTRY_INACTIVE'
|
|
113
|
+
? `Country code ${err.entryCode} is no longer available for new entries.`
|
|
114
|
+
: `Country code ${err.entryCode} is not recognised.`, [{ path: 'country', issue: err.code }]);
|
|
115
|
+
}
|
|
116
|
+
throw err;
|
|
117
|
+
}
|
|
118
|
+
}
|
|
119
|
+
}
|
|
120
|
+
//# sourceMappingURL=address-service.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"address-service.js","sourceRoot":"","sources":["../../../src/backend/services/address-service.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,169 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Addresses module — manifest backfill (Module Lifecycle, feature 018).
|
|
3
|
+
*
|
|
4
|
+
* Predates the lifecycle system; this manifest is the static record
|
|
5
|
+
* required so the module participates in the registry. No install /
|
|
6
|
+
* uninstall hook today — the module's schema is owned by earlier
|
|
7
|
+
* platform-wide migrations.
|
|
8
|
+
*/
|
|
9
|
+
export declare const manifest: {
|
|
10
|
+
id: string;
|
|
11
|
+
name: string;
|
|
12
|
+
version: string;
|
|
13
|
+
dependencies: string[];
|
|
14
|
+
description?: string | undefined;
|
|
15
|
+
acknowledgedDependencies?: {
|
|
16
|
+
moduleId: string;
|
|
17
|
+
port: string;
|
|
18
|
+
reason: string;
|
|
19
|
+
}[] | undefined;
|
|
20
|
+
nonBindingDependencies?: {
|
|
21
|
+
moduleId: string;
|
|
22
|
+
name: string;
|
|
23
|
+
kind: "contributes-to" | "degrades-without" | "refuses-without";
|
|
24
|
+
reason: string;
|
|
25
|
+
whenAbsent?: string | undefined;
|
|
26
|
+
}[] | undefined;
|
|
27
|
+
activation?: {
|
|
28
|
+
settingCode: string;
|
|
29
|
+
default: boolean;
|
|
30
|
+
} | {
|
|
31
|
+
nonDeactivatable: true;
|
|
32
|
+
reason: string;
|
|
33
|
+
} | undefined;
|
|
34
|
+
settings?: {
|
|
35
|
+
moduleCode: string;
|
|
36
|
+
groups: {
|
|
37
|
+
code: string;
|
|
38
|
+
name: string;
|
|
39
|
+
salesChannelCodes?: string[] | undefined;
|
|
40
|
+
isSystemProtected?: boolean | undefined;
|
|
41
|
+
}[];
|
|
42
|
+
settings: {
|
|
43
|
+
code: string;
|
|
44
|
+
name: string;
|
|
45
|
+
valueType: "string" | "number" | "boolean" | "json" | "string_list" | "secret" | "credential_ref";
|
|
46
|
+
defaultValue: unknown;
|
|
47
|
+
description?: string | undefined;
|
|
48
|
+
groupCode?: string | undefined;
|
|
49
|
+
previousDefaultValues?: unknown[] | undefined;
|
|
50
|
+
salesChannelCodes?: string[] | undefined;
|
|
51
|
+
enumOptions?: string[] | undefined;
|
|
52
|
+
configurationType?: string | undefined;
|
|
53
|
+
hidden?: boolean | undefined;
|
|
54
|
+
}[];
|
|
55
|
+
} | undefined;
|
|
56
|
+
i18n?: {
|
|
57
|
+
bundlesDir: string;
|
|
58
|
+
} | undefined;
|
|
59
|
+
docs?: false | {
|
|
60
|
+
dir: string;
|
|
61
|
+
} | undefined;
|
|
62
|
+
demo?: false | {
|
|
63
|
+
summary: string;
|
|
64
|
+
seed: (context: import("@endora-commerce/contracts").ModuleDemoContext<never>) => Promise<import("@endora-commerce/contracts").DemoSeedResult>;
|
|
65
|
+
reset: (context: import("@endora-commerce/contracts").ModuleDemoContext<never>) => Promise<import("@endora-commerce/contracts").DemoResetResult>;
|
|
66
|
+
after?: readonly string[] | undefined;
|
|
67
|
+
package?: string | undefined;
|
|
68
|
+
} | undefined;
|
|
69
|
+
actions?: {
|
|
70
|
+
id: string;
|
|
71
|
+
labelKey: string;
|
|
72
|
+
icon: "Plus" | "Sparkles" | "Settings" | "Search" | "Boxes" | "Layers" | "Menu" | "PlusCircle" | "PlusSquare" | "FilePlus" | "FolderPlus" | "Upload" | "FileUp" | "CloudUpload" | "Download" | "FileDown" | "FileText" | "BookOpen" | "Rss" | "Package" | "Tag" | "ShoppingCart" | "Receipt" | "CreditCard" | "Users" | "UserPlus" | "Inbox" | "ListChecks" | "ClipboardList" | "Image" | "Video" | "LayoutDashboard" | "PanelLeft" | "KeyRound" | "ShieldCheck" | "Edit" | "Archive" | "Box" | "Truck" | "CircleDollarSign" | "Activity" | "LineChart" | "Smartphone" | "Webhook" | "Scale" | "PlugZap" | "PercentDiamond" | "Newspaper" | "Languages" | "Eraser" | "Warehouse" | "TrendingDown" | "Bell" | "PackageOpen" | "Building2" | "Store" | "ClipboardCheck";
|
|
73
|
+
targetRoute: string;
|
|
74
|
+
keywords: string[];
|
|
75
|
+
weight: number;
|
|
76
|
+
descriptionKey?: string | undefined;
|
|
77
|
+
requiredPermission?: string | undefined;
|
|
78
|
+
}[] | undefined;
|
|
79
|
+
permissions?: {
|
|
80
|
+
code: string;
|
|
81
|
+
label: string;
|
|
82
|
+
module?: string | undefined;
|
|
83
|
+
description?: string | undefined;
|
|
84
|
+
requires?: string[] | undefined;
|
|
85
|
+
}[] | undefined;
|
|
86
|
+
transactionalEmails?: {
|
|
87
|
+
code: string;
|
|
88
|
+
name: string;
|
|
89
|
+
variables: {
|
|
90
|
+
key: string;
|
|
91
|
+
label: string;
|
|
92
|
+
sampleValue?: string | undefined;
|
|
93
|
+
description?: string | undefined;
|
|
94
|
+
}[];
|
|
95
|
+
description?: string | undefined;
|
|
96
|
+
group?: string | undefined;
|
|
97
|
+
}[] | undefined;
|
|
98
|
+
capabilities?: string[] | undefined;
|
|
99
|
+
exclusiveCapabilities?: {
|
|
100
|
+
key: string;
|
|
101
|
+
errorCode: string;
|
|
102
|
+
}[] | undefined;
|
|
103
|
+
errorCodes?: {
|
|
104
|
+
code: string;
|
|
105
|
+
tokens?: string[] | undefined;
|
|
106
|
+
}[] | undefined;
|
|
107
|
+
blocks?: {
|
|
108
|
+
name: string;
|
|
109
|
+
labelKey: string;
|
|
110
|
+
category: string;
|
|
111
|
+
contexts: ("invoice" | "email" | "cms" | "newsletter")[];
|
|
112
|
+
fields: Record<string, {
|
|
113
|
+
type: "number" | "object" | "array" | "text" | "textarea" | "select" | "radio" | "external" | "uuid" | "richtext";
|
|
114
|
+
label?: string | undefined;
|
|
115
|
+
required?: boolean | undefined;
|
|
116
|
+
options?: {
|
|
117
|
+
label: string;
|
|
118
|
+
value: string | number;
|
|
119
|
+
}[] | undefined;
|
|
120
|
+
refKind?: string | undefined;
|
|
121
|
+
}>;
|
|
122
|
+
descriptionKey?: string | undefined;
|
|
123
|
+
defaultProps?: Record<string, unknown> | undefined;
|
|
124
|
+
responsiveFields?: string[] | undefined;
|
|
125
|
+
previewIcon?: string | undefined;
|
|
126
|
+
weight?: number | undefined;
|
|
127
|
+
}[] | undefined;
|
|
128
|
+
blockCategories?: {
|
|
129
|
+
key: string;
|
|
130
|
+
titleKey: string;
|
|
131
|
+
contexts: ("invoice" | "email" | "cms" | "newsletter")[];
|
|
132
|
+
weight?: number | undefined;
|
|
133
|
+
visible?: boolean | undefined;
|
|
134
|
+
}[] | undefined;
|
|
135
|
+
env?: {
|
|
136
|
+
name: string;
|
|
137
|
+
describes: {
|
|
138
|
+
en: string;
|
|
139
|
+
pl: string;
|
|
140
|
+
};
|
|
141
|
+
requirement: {
|
|
142
|
+
kind: "required";
|
|
143
|
+
} | {
|
|
144
|
+
kind: "requiredWhen";
|
|
145
|
+
input: string;
|
|
146
|
+
equals: string;
|
|
147
|
+
} | {
|
|
148
|
+
kind: "optional";
|
|
149
|
+
without: {
|
|
150
|
+
en: string;
|
|
151
|
+
pl: string;
|
|
152
|
+
};
|
|
153
|
+
};
|
|
154
|
+
secret: boolean;
|
|
155
|
+
generable: boolean;
|
|
156
|
+
owner: {
|
|
157
|
+
kind: "platform";
|
|
158
|
+
} | {
|
|
159
|
+
kind: "application";
|
|
160
|
+
application: "admin" | "backend" | "storefront";
|
|
161
|
+
} | {
|
|
162
|
+
kind: "module";
|
|
163
|
+
moduleId: string;
|
|
164
|
+
};
|
|
165
|
+
consumers: ("admin" | "backend" | "storefront")[];
|
|
166
|
+
addressOf: "admin" | "backend" | "storefront" | null;
|
|
167
|
+
}[] | undefined;
|
|
168
|
+
};
|
|
169
|
+
//# sourceMappingURL=manifest.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"manifest.d.ts","sourceRoot":"","sources":["../src/manifest.ts"],"names":[],"mappings":"AAEA;;;;;;;GAOG;AACH,eAAO,MAAM,QAAQ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CAiFnB,CAAC"}
|
package/dist/manifest.js
ADDED
|
@@ -0,0 +1,90 @@
|
|
|
1
|
+
import { defineModuleManifest } from '@endora-commerce/contracts';
|
|
2
|
+
/**
|
|
3
|
+
* Addresses module — manifest backfill (Module Lifecycle, feature 018).
|
|
4
|
+
*
|
|
5
|
+
* Predates the lifecycle system; this manifest is the static record
|
|
6
|
+
* required so the module participates in the registry. No install /
|
|
7
|
+
* uninstall hook today — the module's schema is owned by earlier
|
|
8
|
+
* platform-wide migrations.
|
|
9
|
+
*/
|
|
10
|
+
export const manifest = defineModuleManifest({
|
|
11
|
+
id: 'addresses',
|
|
12
|
+
name: 'Addresses',
|
|
13
|
+
description: 'Reusable address entities used by organizations, customers, and order fulfilment.',
|
|
14
|
+
version: '1.0.0',
|
|
15
|
+
// `addresses.organization_id` foreign-keys `organizations`. The edge was
|
|
16
|
+
// found by the FK-drift validator (feature 065) and is genuinely declarable:
|
|
17
|
+
// quick_order → addresses → organizations → settings stays acyclic.
|
|
18
|
+
//
|
|
19
|
+
// `dictionaries` added with the conversion (feature 072, T090): the one
|
|
20
|
+
// `AddressService` resolves `dictionaryValidator` to check country and region
|
|
21
|
+
// codes, so the edge is real and was previously invisible to the lifecycle.
|
|
22
|
+
dependencies: ['dictionaries', 'organizations'],
|
|
23
|
+
// Feature 074 (Constitution XVII), test C2 — functional base. The flag was
|
|
24
|
+
// previously carried by a port edge: `organizations` resolves this module's
|
|
25
|
+
// `addressService`, and that reached this manifest through the other one's
|
|
26
|
+
// declaration. Ruling 2 withdraws that authority — a module a criterion
|
|
27
|
+
// module happens to resolve is not thereby core — so the ground here is now
|
|
28
|
+
// this module's own. Every B2B document is addressed: an order ships
|
|
29
|
+
// somewhere, an invoice is billed somewhere, a shipment has a destination.
|
|
30
|
+
// A platform that cannot record where is not a smaller commerce platform,
|
|
31
|
+
// it is one that cannot complete a transaction.
|
|
32
|
+
activation: {
|
|
33
|
+
nonDeactivatable: true,
|
|
34
|
+
reason: 'Every B2B document — order, invoice, shipment — is addressed; there is no transaction ' +
|
|
35
|
+
'without one.',
|
|
36
|
+
},
|
|
37
|
+
/**
|
|
38
|
+
* This module's first i18n bundle — D-129's remaining sweep, MR 5.
|
|
39
|
+
*
|
|
40
|
+
* It exists because a **declaring** module that contributes no bundle file is
|
|
41
|
+
* `check:error-translations` exit 2 for the whole tree rather than a finding
|
|
42
|
+
* (`specs/090-module-owned-error-codes/contracts/error-translation-population.md`
|
|
43
|
+
* §2.1, §2.3; `d129-sweep.md` §3.4). The bundle lives at the **package
|
|
44
|
+
* root**, not under `dist`: `manifest-locations.ts` resolves a packaged
|
|
45
|
+
* module's `manifestPath` to its `package.json`, so `dirname` is the package
|
|
46
|
+
* directory. The files are a **flat** `{"a.b.c": "text"}` map, because a
|
|
47
|
+
* nested object fails `TranslationBundleEntriesSchema` and the boot
|
|
48
|
+
* reconciler logs and skips it — silently.
|
|
49
|
+
*/
|
|
50
|
+
i18n: { bundlesDir: 'i18n' },
|
|
51
|
+
docs: { dir: 'docs' },
|
|
52
|
+
/**
|
|
53
|
+
* The two `ADDRESS_*` codes — D-129's remaining sweep, Tier B
|
|
54
|
+
* (`specs/090-module-owned-error-codes/d129-sweep.md` §5.2, Appendix A;
|
|
55
|
+
* MR 5).
|
|
56
|
+
*
|
|
57
|
+
* Both were declared by `_i18n` until this merge request, not because anybody
|
|
58
|
+
* judged them the platform's but because the deleted prefix chain had no rule
|
|
59
|
+
* for them and its last line was `return 'core'`. **D-121 T1 puts them
|
|
60
|
+
* here**: the noun in both is an address, which is this module's entity, and
|
|
61
|
+
* both refusals are about that row — who owns it, and whether anything still
|
|
62
|
+
* points at it.
|
|
63
|
+
*
|
|
64
|
+
* `ADDRESS_NOT_OWNED` is the sweep's cleanest disagreement between the noun
|
|
65
|
+
* and the raise-site count: `orders` is the only module that raises it, and it
|
|
66
|
+
* does so having asked this module's read port for the address and got
|
|
67
|
+
* nothing back. That is the caller reporting the absence of the owner's row —
|
|
68
|
+
* D-95.2's `INVOICE_NOT_READY` shape — and T1 does not ask who throws.
|
|
69
|
+
* `ADDRESS_IN_USE` is raised by nothing at all, which T1 also does not ask
|
|
70
|
+
* about (T10).
|
|
71
|
+
*
|
|
72
|
+
* **One of the two arrives with a sentence.** Each carried a placeholder in
|
|
73
|
+
* `_i18n`'s bundle — `"Address Not Owned."` / `"Błąd: address not owned."` —
|
|
74
|
+
* which D-186 §2 deletes rather than carries, because in this module's own
|
|
75
|
+
* bundle it would read as this module's answer. §5.4 keeps writing the prose
|
|
76
|
+
* available and is not a re-opening of that ruling, so the decision was taken
|
|
77
|
+
* per code: `ADDRESS_NOT_OWNED` is written, because a buyer meets it at
|
|
78
|
+
* checkout and the refusal has one remedy to offer; `ADDRESS_IN_USE` is
|
|
79
|
+
* deleted and joins `UNTRANSLATED_ERROR_CODES` under this module, because
|
|
80
|
+
* with no raise site there is no refusal to describe and a sentence could
|
|
81
|
+
* only be invented from the code's own name.
|
|
82
|
+
*
|
|
83
|
+
* **`tokens` is derived from the raise sites, not from the bundle**
|
|
84
|
+
* (runbook §5), and there are none: the one raise is a bare
|
|
85
|
+
* `HttpError(403, code, message)` with no `details` argument, measured by
|
|
86
|
+
* balanced-paren extraction of the call's own arguments.
|
|
87
|
+
*/
|
|
88
|
+
errorCodes: [{ code: 'ADDRESS_IN_USE' }, { code: 'ADDRESS_NOT_OWNED' }],
|
|
89
|
+
});
|
|
90
|
+
//# sourceMappingURL=manifest.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"manifest.js","sourceRoot":"","sources":["../src/manifest.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,oBAAoB,EAAE,MAAM,4BAA4B,CAAC;AAElE;;;;;;;GAOG;AACH,MAAM,CAAC,MAAM,QAAQ,GAAG,oBAAoB,CAAC;IAC3C,EAAE,EAAE,WAAW;IACf,IAAI,EAAE,WAAW;IACjB,WAAW,EACT,mFAAmF;IACrF,OAAO,EAAE,OAAO;IAChB,yEAAyE;IACzE,6EAA6E;IAC7E,oEAAoE;IACpE,EAAE;IACF,wEAAwE;IACxE,8EAA8E;IAC9E,4EAA4E;IAC5E,YAAY,EAAE,CAAC,cAAc,EAAE,eAAe,CAAC;IAC/C,2EAA2E;IAC3E,4EAA4E;IAC5E,2EAA2E;IAC3E,wEAAwE;IACxE,4EAA4E;IAC5E,qEAAqE;IACrE,2EAA2E;IAC3E,0EAA0E;IAC1E,gDAAgD;IAChD,UAAU,EAAE;QACV,gBAAgB,EAAE,IAAI;QACtB,MAAM,EACJ,wFAAwF;YACxF,cAAc;KACjB;IACD;;;;;;;;;;;;OAYG;IACH,IAAI,EAAE,EAAE,UAAU,EAAE,MAAM,EAAE;IAC5B,IAAI,EAAE,EAAE,GAAG,EAAE,MAAM,EAAE;IACrB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;OAmCG;IACH,UAAU,EAAE,CAAC,EAAE,IAAI,EAAE,gBAAgB,EAAE,EAAE,EAAE,IAAI,EAAE,mBAAmB,EAAE,CAAC;CACxE,CAAC,CAAC"}
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
---
|
|
2
|
+
title: addresses
|
|
3
|
+
description: Customer postal addresses with default-per-kind invariant
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# `addresses`
|
|
7
|
+
|
|
8
|
+
Customer postal addresses scoped to an Organization. Each address has a
|
|
9
|
+
`kind` (`shipping`, `billing`, `delivery`) and a per-kind default flag.
|
|
10
|
+
|
|
11
|
+
## Public surface
|
|
12
|
+
|
|
13
|
+
| Verb + Path | Purpose |
|
|
14
|
+
| --- | --- |
|
|
15
|
+
| `GET /api/v1/organizations/mine/addresses` | List own Organization's addresses |
|
|
16
|
+
| `POST /api/v1/organizations/mine/addresses` | Create; `isDefault=true` atomically demotes the previous default of the same kind |
|
|
17
|
+
| `PATCH /api/v1/organizations/mine/addresses/:id` | Update |
|
|
18
|
+
| `DELETE /api/v1/organizations/mine/addresses/:id` | Delete (rejected if referenced by an order via `409 ADDRESS_IN_USE`) |
|
|
19
|
+
|
|
20
|
+
## Entities
|
|
21
|
+
|
|
22
|
+
`Address` — partial unique index `(organization_id, kind) WHERE
|
|
23
|
+
is_default = true` enforces "at most one default per kind" at the DB level.
|
|
24
|
+
|
|
25
|
+
## Extension points
|
|
26
|
+
|
|
27
|
+
- **Country-specific validation** — `address-service.ts#createAddress`
|
|
28
|
+
delegates per-country format checks; add validators here when new markets
|
|
29
|
+
go live.
|
|
30
|
+
- **Snapshot on order** — Orders embed an `AddressSnapshot` value object so
|
|
31
|
+
later edits to the source address don't mutate historical orders.
|
package/i18n/en.json
ADDED
package/i18n/pl.json
ADDED
package/package.json
ADDED
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@endora-commerce/mod-addresses",
|
|
3
|
+
"version": "0.100.0",
|
|
4
|
+
"type": "module",
|
|
5
|
+
"sideEffects": false,
|
|
6
|
+
"description": "Reusable address entities used by organizations, customers, and order fulfilment.",
|
|
7
|
+
"license": "MIT",
|
|
8
|
+
"endora": {
|
|
9
|
+
"type": "module",
|
|
10
|
+
"id": "addresses"
|
|
11
|
+
},
|
|
12
|
+
"repository": {
|
|
13
|
+
"type": "git",
|
|
14
|
+
"url": "git+https://github.com/endora-commerce/endora-commerce.git",
|
|
15
|
+
"directory": "packages/modules/addresses"
|
|
16
|
+
},
|
|
17
|
+
"publishConfig": {
|
|
18
|
+
"access": "public"
|
|
19
|
+
},
|
|
20
|
+
"exports": {
|
|
21
|
+
".": {
|
|
22
|
+
"types": "./dist/manifest.d.ts",
|
|
23
|
+
"default": "./dist/manifest.js"
|
|
24
|
+
},
|
|
25
|
+
"./backend": {
|
|
26
|
+
"types": "./dist/backend/index.d.ts",
|
|
27
|
+
"default": "./dist/backend/index.js"
|
|
28
|
+
},
|
|
29
|
+
"./package.json": "./package.json"
|
|
30
|
+
},
|
|
31
|
+
"files": [
|
|
32
|
+
"dist",
|
|
33
|
+
"i18n",
|
|
34
|
+
"docs"
|
|
35
|
+
],
|
|
36
|
+
"engines": {
|
|
37
|
+
"node": ">=22.18.0"
|
|
38
|
+
},
|
|
39
|
+
"peerDependencies": {
|
|
40
|
+
"@mikro-orm/core": "^6",
|
|
41
|
+
"@mikro-orm/postgresql": "^6",
|
|
42
|
+
"@endora-commerce/contracts": "0.100.0",
|
|
43
|
+
"@endora-commerce/platform": "0.100.0"
|
|
44
|
+
},
|
|
45
|
+
"devDependencies": {
|
|
46
|
+
"@mikro-orm/core": "^6.6.13",
|
|
47
|
+
"@mikro-orm/postgresql": "^6.6.13",
|
|
48
|
+
"@types/node": "^22.9.0",
|
|
49
|
+
"typescript": "^5.9.3",
|
|
50
|
+
"vitest": "^4.1.11",
|
|
51
|
+
"@endora-commerce/contracts": "0.100.0",
|
|
52
|
+
"@endora-commerce/platform": "0.100.0"
|
|
53
|
+
},
|
|
54
|
+
"scripts": {
|
|
55
|
+
"build": "tsc -p tsconfig.build.json",
|
|
56
|
+
"typecheck": "tsc -p tsconfig.json",
|
|
57
|
+
"lint": "eslint src",
|
|
58
|
+
"test": "vitest run"
|
|
59
|
+
}
|
|
60
|
+
}
|