@aglyn/plugins-commerce 1.0.0-beta.217 → 1.0.0-beta.219
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/README.md +1 -0
- package/package.json +15 -15
- package/src/index.d.ts +1 -0
- package/src/index.js +1 -19
- package/src/index.js.map +1 -1
- package/src/lib/components/cart.js +2 -1
- package/src/lib/components/cart.js.map +1 -1
- package/src/lib/components/console/catalog-organization-card.component.js +44 -18
- package/src/lib/components/console/catalog-organization-card.component.js.map +1 -1
- package/src/lib/components/console/host-orders-card.component.js +2 -1
- package/src/lib/components/console/host-orders-card.component.js.map +1 -1
- package/src/lib/components/console/member-posts-card.component.js +1 -1
- package/src/lib/components/console/member-posts-card.component.js.map +1 -1
- package/src/lib/components/console/order-detail-dialog.component.js +8 -7
- package/src/lib/components/console/order-detail-dialog.component.js.map +1 -1
- package/src/lib/components/console/payments-settings-card.component.js +1 -1
- package/src/lib/components/console/payments-settings-card.component.js.map +1 -1
- package/src/lib/components/console/product-editor-dialog.component.js +114 -63
- package/src/lib/components/console/product-editor-dialog.component.js.map +1 -1
- package/src/lib/components/console/product-zones.d.ts +184 -0
- package/src/lib/components/console/product-zones.js +54 -0
- package/src/lib/components/console/product-zones.js.map +1 -0
- package/src/lib/components/console/products-hub-card.component.js +3 -2
- package/src/lib/components/console/products-hub-card.component.js.map +1 -1
- package/src/lib/components/console/products-hub-zone.component.d.ts +1 -1
- package/src/lib/components/console/products-hub-zone.component.js +2 -1
- package/src/lib/components/console/products-hub-zone.component.js.map +1 -1
- package/src/lib/components/console/store-settings-card.component.js +2 -2
- package/src/lib/components/console/store-settings-card.component.js.map +1 -1
- package/src/lib/components/console/storefront-tax-summary-card.component.js +1 -1
- package/src/lib/components/console/storefront-tax-summary-card.component.js.map +1 -1
- package/src/lib/components/product-detail.js +2 -1
- package/src/lib/components/product-detail.js.map +1 -1
- package/src/lib/components/product-grid.js +1 -1
- package/src/lib/components/product-grid.js.map +1 -1
- package/src/lib/components/storefront-payment-element.js +36 -5
- package/src/lib/components/storefront-payment-element.js.map +1 -1
- package/src/lib/constants/storefront-payments.d.ts +33 -0
- package/src/lib/constants/storefront-payments.js +35 -0
- package/src/lib/constants/storefront-payments.js.map +1 -0
- package/src/lib/declarations.server.d.ts +3 -1
- package/src/lib/declarations.server.js +19 -1
- package/src/lib/declarations.server.js.map +1 -1
- package/src/lib/model/commerce-record-routes.d.ts +27 -0
- package/src/lib/model/commerce-record-routes.js +72 -0
- package/src/lib/model/commerce-record-routes.js.map +1 -0
- package/src/lib/model/commerce-search-sources.d.ts +21 -0
- package/src/lib/model/commerce-search-sources.js +40 -0
- package/src/lib/model/commerce-search-sources.js.map +1 -0
- package/src/lib/model/storefront-tax-summary.d.ts +121 -0
- package/src/lib/model/storefront-tax-summary.js +148 -0
- package/src/lib/model/storefront-tax-summary.js.map +1 -0
- package/src/lib/plan-entitlements.d.ts +35 -0
- package/src/lib/plan-entitlements.js +54 -0
- package/src/lib/plan-entitlements.js.map +1 -0
- package/src/lib/plugin.js +9 -0
- package/src/lib/plugin.js.map +1 -1
- package/src/lib/server/billing-webhook.js +135 -168
- package/src/lib/server/billing-webhook.js.map +1 -1
- package/src/lib/server/cart-checkout.js +2 -2
- package/src/lib/server/cart-checkout.js.map +1 -1
- package/src/lib/server/checkout.js +2 -2
- package/src/lib/server/checkout.js.map +1 -1
- package/src/lib/server/connect.js +5 -5
- package/src/lib/server/connect.js.map +1 -1
- package/src/lib/server/draft-order.js +2 -2
- package/src/lib/server/draft-order.js.map +1 -1
- package/src/lib/server/membership-content.js +1 -1
- package/src/lib/server/membership-content.js.map +1 -1
- package/src/lib/server/native-checkout.d.ts +20 -5
- package/src/lib/server/native-checkout.js +27 -6
- package/src/lib/server/native-checkout.js.map +1 -1
- package/src/lib/server/pos-order.js +2 -2
- package/src/lib/server/pos-order.js.map +1 -1
- package/src/lib/server/product-record-index.d.ts +19 -0
- package/src/lib/server/product-record-index.js +115 -0
- package/src/lib/server/product-record-index.js.map +1 -0
- package/src/lib/server/reserve.js +2 -2
- package/src/lib/server/reserve.js.map +1 -1
- package/src/lib/server/revenue-source.d.ts +130 -0
- package/src/lib/server/revenue-source.js +286 -0
- package/src/lib/server/revenue-source.js.map +1 -0
- package/src/lib/server/tax-profile.d.ts +8 -5
- package/src/lib/server/tax-profile.js +22 -5
- package/src/lib/server/tax-profile.js.map +1 -1
- package/src/lib/server/tax-return-source.d.ts +125 -0
- package/src/lib/server/tax-return-source.js +524 -0
- package/src/lib/server/tax-return-source.js.map +1 -0
- package/src/lib/server/tax-summary.d.ts +92 -0
- package/src/lib/server/tax-summary.js +258 -0
- package/src/lib/server/tax-summary.js.map +1 -0
- package/src/lib/server.d.ts +1 -0
- package/src/lib/server.js +23 -0
- package/src/lib/server.js.map +1 -1
- package/src/lib/tenant-emails.d.ts +33 -0
- package/src/lib/tenant-emails.js +511 -0
- package/src/lib/tenant-emails.js.map +1 -0
- package/src/lib/testing/stand-in-crm-record-routes.d.ts +25 -0
- package/src/lib/testing/stand-in-crm-record-routes.js +46 -0
- package/src/lib/testing/stand-in-crm-record-routes.js.map +1 -0
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @license
|
|
3
|
+
* Copyright 2026 Aglyn LLC
|
|
4
|
+
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
5
|
+
* you may not use this file except in compliance with the License.
|
|
6
|
+
* You may obtain a copy of the License at
|
|
7
|
+
* http://www.apache.org/licenses/LICENSE-2.0
|
|
8
|
+
* Unless required by applicable law or agreed to in writing, software
|
|
9
|
+
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
10
|
+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
11
|
+
* See the License for the specific language governing permissions and
|
|
12
|
+
* limitations under the License.
|
|
13
|
+
*/
|
|
14
|
+
/** The query key the Orders list opens one order's dialog by. */
|
|
15
|
+
export declare const ORDERS_ORDER_PARAM = "order";
|
|
16
|
+
/**
|
|
17
|
+
* The query key the Orders list narrows to one buyer by — the same word the
|
|
18
|
+
* CRM's contacts list opens a person by, so a reader who edits one URL into
|
|
19
|
+
* the other is not surprised.
|
|
20
|
+
*/
|
|
21
|
+
export declare const ORDERS_CUSTOMER_PARAM = "email";
|
|
22
|
+
/**
|
|
23
|
+
* Called from the console registrar. The owner is named rather than read off
|
|
24
|
+
* the loader's marker, so the route registers the same way under a spec that
|
|
25
|
+
* calls the registrar directly.
|
|
26
|
+
*/
|
|
27
|
+
export declare function registerCommerceRecordRoutes(): void;
|
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @license
|
|
3
|
+
* Copyright 2026 Aglyn LLC
|
|
4
|
+
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
5
|
+
* you may not use this file except in compliance with the License.
|
|
6
|
+
* You may obtain a copy of the License at
|
|
7
|
+
* http://www.apache.org/licenses/LICENSE-2.0
|
|
8
|
+
* Unless required by applicable law or agreed to in writing, software
|
|
9
|
+
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
10
|
+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
11
|
+
* See the License for the specific language governing permissions and
|
|
12
|
+
* limitations under the License.
|
|
13
|
+
*/ import { buildRoute, Route } from "@aglyn/aglyn/app-utils/console-routes";
|
|
14
|
+
import { registerPluginRecordRoute } from "@aglyn/aglyn/plugin-manager/plugin-record-routes";
|
|
15
|
+
import { BUNDLE_ID } from "../constants/bundle-common.js";
|
|
16
|
+
/**
|
|
17
|
+
* Where a product and an order are read, published for every other surface
|
|
18
|
+
* (AGL-3080).
|
|
19
|
+
*
|
|
20
|
+
* A surface elsewhere — the AI plugin's job drawer opening a product it
|
|
21
|
+
* drafted, a contact's timeline opening the order it names, a person's page
|
|
22
|
+
* listing their orders — asks the record-route registry for `product` or
|
|
23
|
+
* `order` rather than spelling this plugin's console path, and gets `null`
|
|
24
|
+
* (text instead of a link) where this plugin is not loaded.
|
|
25
|
+
*
|
|
26
|
+
* Both are a site's, so only a site has an address. The catalog has no page
|
|
27
|
+
* per product — a product opens in the hub's editor — so the record answers
|
|
28
|
+
* the catalog a caller would find it in. An order opens as a dialog over the
|
|
29
|
+
* Orders list, which reads {@link ORDERS_ORDER_PARAM} on arrival; the list
|
|
30
|
+
* narrowed to one buyer reads {@link ORDERS_CUSTOMER_PARAM}.
|
|
31
|
+
*/ /** The nav slug the shell resolves the catalog by. */ const PRODUCTS_SLUG = 'products';
|
|
32
|
+
/** The query key the Orders list opens one order's dialog by. */ export const ORDERS_ORDER_PARAM = 'order';
|
|
33
|
+
/**
|
|
34
|
+
* The query key the Orders list narrows to one buyer by — the same word the
|
|
35
|
+
* CRM's contacts list opens a person by, so a reader who edits one URL into
|
|
36
|
+
* the other is not surprised.
|
|
37
|
+
*/ export const ORDERS_CUSTOMER_PARAM = 'email';
|
|
38
|
+
function catalog(context) {
|
|
39
|
+
return context.host ? buildRoute(Route.HOST_PLUGIN, {
|
|
40
|
+
orgSlug: context.orgSlug,
|
|
41
|
+
host: context.host,
|
|
42
|
+
pluginSlug: PRODUCTS_SLUG
|
|
43
|
+
}) : null;
|
|
44
|
+
}
|
|
45
|
+
function orders(context, key, value) {
|
|
46
|
+
const list = catalog(context);
|
|
47
|
+
if (!list) return null;
|
|
48
|
+
return key && value ? `${list}/orders?${new URLSearchParams({
|
|
49
|
+
[key]: value
|
|
50
|
+
}).toString()}` : `${list}/orders`;
|
|
51
|
+
}
|
|
52
|
+
/**
|
|
53
|
+
* Called from the console registrar. The owner is named rather than read off
|
|
54
|
+
* the loader's marker, so the route registers the same way under a spec that
|
|
55
|
+
* calls the registrar directly.
|
|
56
|
+
*/ export function registerCommerceRecordRoutes() {
|
|
57
|
+
registerPluginRecordRoute('product', {
|
|
58
|
+
list: catalog,
|
|
59
|
+
record: catalog
|
|
60
|
+
}, {
|
|
61
|
+
pluginId: BUNDLE_ID
|
|
62
|
+
});
|
|
63
|
+
registerPluginRecordRoute('order', {
|
|
64
|
+
list: (context)=>orders(context),
|
|
65
|
+
record: (context, id)=>orders(context, ORDERS_ORDER_PARAM, id),
|
|
66
|
+
byEmail: (context, email)=>orders(context, ORDERS_CUSTOMER_PARAM, email)
|
|
67
|
+
}, {
|
|
68
|
+
pluginId: BUNDLE_ID
|
|
69
|
+
});
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
//# sourceMappingURL=commerce-record-routes.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../../../../../../../libs/plugins/commerce/src/lib/model/commerce-record-routes.ts"],"sourcesContent":["/**\n * @license\n * Copyright 2026 Aglyn LLC\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n * http://www.apache.org/licenses/LICENSE-2.0\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n\nimport { buildRoute, Route } from '@aglyn/aglyn/app-utils/console-routes'\nimport {\n registerPluginRecordRoute,\n type PluginRecordRouteContext,\n} from '@aglyn/aglyn/plugin-manager/plugin-record-routes'\nimport { BUNDLE_ID } from '../constants/bundle-common'\n\n/**\n * Where a product and an order are read, published for every other surface\n * (AGL-3080).\n *\n * A surface elsewhere — the AI plugin's job drawer opening a product it\n * drafted, a contact's timeline opening the order it names, a person's page\n * listing their orders — asks the record-route registry for `product` or\n * `order` rather than spelling this plugin's console path, and gets `null`\n * (text instead of a link) where this plugin is not loaded.\n *\n * Both are a site's, so only a site has an address. The catalog has no page\n * per product — a product opens in the hub's editor — so the record answers\n * the catalog a caller would find it in. An order opens as a dialog over the\n * Orders list, which reads {@link ORDERS_ORDER_PARAM} on arrival; the list\n * narrowed to one buyer reads {@link ORDERS_CUSTOMER_PARAM}.\n */\n\n/** The nav slug the shell resolves the catalog by. */\nconst PRODUCTS_SLUG = 'products'\n\n/** The query key the Orders list opens one order's dialog by. */\nexport const ORDERS_ORDER_PARAM = 'order'\n\n/**\n * The query key the Orders list narrows to one buyer by — the same word the\n * CRM's contacts list opens a person by, so a reader who edits one URL into\n * the other is not surprised.\n */\nexport const ORDERS_CUSTOMER_PARAM = 'email'\n\nfunction catalog(context: PluginRecordRouteContext): string | null {\n return context.host\n ? buildRoute(Route.HOST_PLUGIN, {\n orgSlug: context.orgSlug,\n host: context.host,\n pluginSlug: PRODUCTS_SLUG,\n })\n : null\n}\n\nfunction orders(context: PluginRecordRouteContext, key?: string, value?: string): string | null {\n const list = catalog(context)\n if (!list) return null\n return key && value\n ? `${list}/orders?${new URLSearchParams({ [key]: value }).toString()}`\n : `${list}/orders`\n}\n\n/**\n * Called from the console registrar. The owner is named rather than read off\n * the loader's marker, so the route registers the same way under a spec that\n * calls the registrar directly.\n */\nexport function registerCommerceRecordRoutes(): void {\n registerPluginRecordRoute(\n 'product',\n { list: catalog, record: catalog },\n { pluginId: BUNDLE_ID },\n )\n registerPluginRecordRoute(\n 'order',\n {\n list: (context) => orders(context),\n record: (context, id) => orders(context, ORDERS_ORDER_PARAM, id),\n byEmail: (context, email) => orders(context, ORDERS_CUSTOMER_PARAM, email),\n },\n { pluginId: BUNDLE_ID },\n )\n}\n"],"names":["buildRoute","Route","registerPluginRecordRoute","BUNDLE_ID","PRODUCTS_SLUG","ORDERS_ORDER_PARAM","ORDERS_CUSTOMER_PARAM","catalog","context","host","HOST_PLUGIN","orgSlug","pluginSlug","orders","key","value","list","URLSearchParams","toString","registerCommerceRecordRoutes","record","pluginId","id","byEmail","email"],"mappings":"AAAA;;;;;;;;;;;;CAYC,GAGD,SAASA,UAAU,EAAEC,KAAK,QAAQ,wCAAuC;AACzE,SACEC,yBAAyB,QAEpB,mDAAkD;AACzD,SAASC,SAAS,QAAQ,gCAA4B;AAEtD;;;;;;;;;;;;;;;CAeC,GAED,oDAAoD,GACpD,MAAMC,gBAAgB;AAEtB,+DAA+D,GAC/D,OAAO,MAAMC,qBAAqB,QAAO;AAEzC;;;;CAIC,GACD,OAAO,MAAMC,wBAAwB,QAAO;AAE5C,SAASC,QAAQC,OAAiC;IAChD,OAAOA,QAAQC,IAAI,GACfT,WAAWC,MAAMS,WAAW,EAAE;QAC5BC,SAASH,QAAQG,OAAO;QACxBF,MAAMD,QAAQC,IAAI;QAClBG,YAAYR;IACd,KACA;AACN;AAEA,SAASS,OAAOL,OAAiC,EAAEM,GAAY,EAAEC,KAAc;IAC7E,MAAMC,OAAOT,QAAQC;IACrB,IAAI,CAACQ,MAAM,OAAO;IAClB,OAAOF,OAAOC,QACV,GAAGC,KAAK,QAAQ,EAAE,IAAIC,gBAAgB;QAAE,CAACH,IAAI,EAAEC;IAAM,GAAGG,QAAQ,IAAI,GACpE,GAAGF,KAAK,OAAO,CAAC;AACtB;AAEA;;;;CAIC,GACD,OAAO,SAASG;IACdjB,0BACE,WACA;QAAEc,MAAMT;QAASa,QAAQb;IAAQ,GACjC;QAAEc,UAAUlB;IAAU;IAExBD,0BACE,SACA;QACEc,MAAM,CAACR,UAAYK,OAAOL;QAC1BY,QAAQ,CAACZ,SAASc,KAAOT,OAAOL,SAASH,oBAAoBiB;QAC7DC,SAAS,CAACf,SAASgB,QAAUX,OAAOL,SAASF,uBAAuBkB;IACtE,GACA;QAAEH,UAAUlB;IAAU;AAE1B"}
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @license
|
|
3
|
+
* Copyright 2026 Aglyn LLC
|
|
4
|
+
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
5
|
+
* you may not use this file except in compliance with the License.
|
|
6
|
+
* You may obtain a copy of the License at
|
|
7
|
+
* http://www.apache.org/licenses/LICENSE-2.0
|
|
8
|
+
* Unless required by applicable law or agreed to in writing, software
|
|
9
|
+
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
10
|
+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
11
|
+
* See the License for the specific language governing permissions and
|
|
12
|
+
* limitations under the License.
|
|
13
|
+
*/
|
|
14
|
+
import type { ConsoleSearchSource } from '@aglyn/aglyn';
|
|
15
|
+
/**
|
|
16
|
+
* What the console's search finds in a store (AGL-3080): its products, by
|
|
17
|
+
* name or slug, opening on the site's Products page. Read only where the plan
|
|
18
|
+
* carries commerce and allows the site products, so a workspace that holds
|
|
19
|
+
* none never spends a read on them.
|
|
20
|
+
*/
|
|
21
|
+
export declare const COMMERCE_SEARCH_SOURCES: readonly ConsoleSearchSource[];
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @license
|
|
3
|
+
* Copyright 2026 Aglyn LLC
|
|
4
|
+
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
5
|
+
* you may not use this file except in compliance with the License.
|
|
6
|
+
* You may obtain a copy of the License at
|
|
7
|
+
* http://www.apache.org/licenses/LICENSE-2.0
|
|
8
|
+
* Unless required by applicable law or agreed to in writing, software
|
|
9
|
+
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
10
|
+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
11
|
+
* See the License for the specific language governing permissions and
|
|
12
|
+
* limitations under the License.
|
|
13
|
+
*/ import { buildRoute, Route } from "@aglyn/aglyn/app-utils/console-routes";
|
|
14
|
+
/**
|
|
15
|
+
* What the console's search finds in a store (AGL-3080): its products, by
|
|
16
|
+
* name or slug, opening on the site's Products page. Read only where the plan
|
|
17
|
+
* carries commerce and allows the site products, so a workspace that holds
|
|
18
|
+
* none never spends a read on them.
|
|
19
|
+
*/ export const COMMERCE_SEARCH_SOURCES = [
|
|
20
|
+
{
|
|
21
|
+
id: 'products',
|
|
22
|
+
group: 'Products',
|
|
23
|
+
noun: 'products',
|
|
24
|
+
scope: 'host',
|
|
25
|
+
collection: 'products',
|
|
26
|
+
nameField: 'name',
|
|
27
|
+
extraFields: [
|
|
28
|
+
'slug'
|
|
29
|
+
],
|
|
30
|
+
entitlementKey: 'productsPerHost',
|
|
31
|
+
featureFlag: 'commerce',
|
|
32
|
+
order: 160,
|
|
33
|
+
href: (_row, { orgSlug, host })=>host ? buildRoute(Route.HOST_PRODUCTS, {
|
|
34
|
+
orgSlug,
|
|
35
|
+
host
|
|
36
|
+
}) : null
|
|
37
|
+
}
|
|
38
|
+
];
|
|
39
|
+
|
|
40
|
+
//# sourceMappingURL=commerce-search-sources.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../../../../../../../libs/plugins/commerce/src/lib/model/commerce-search-sources.ts"],"sourcesContent":["/**\n * @license\n * Copyright 2026 Aglyn LLC\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n * http://www.apache.org/licenses/LICENSE-2.0\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nimport type { ConsoleSearchSource } from '@aglyn/aglyn'\nimport { buildRoute, Route } from '@aglyn/aglyn/app-utils/console-routes'\n\n/**\n * What the console's search finds in a store (AGL-3080): its products, by\n * name or slug, opening on the site's Products page. Read only where the plan\n * carries commerce and allows the site products, so a workspace that holds\n * none never spends a read on them.\n */\nexport const COMMERCE_SEARCH_SOURCES: readonly ConsoleSearchSource[] = [\n {\n id: 'products',\n group: 'Products',\n noun: 'products',\n scope: 'host',\n collection: 'products',\n nameField: 'name',\n extraFields: ['slug'],\n entitlementKey: 'productsPerHost',\n featureFlag: 'commerce',\n order: 160,\n href: (_row, { orgSlug, host }) =>\n host ? buildRoute(Route.HOST_PRODUCTS, { orgSlug, host }) : null,\n },\n]\n"],"names":["buildRoute","Route","COMMERCE_SEARCH_SOURCES","id","group","noun","scope","collection","nameField","extraFields","entitlementKey","featureFlag","order","href","_row","orgSlug","host","HOST_PRODUCTS"],"mappings":"AAAA;;;;;;;;;;;;CAYC,GAGD,SAASA,UAAU,EAAEC,KAAK,QAAQ,wCAAuC;AAEzE;;;;;CAKC,GACD,OAAO,MAAMC,0BAA0D;IACrE;QACEC,IAAI;QACJC,OAAO;QACPC,MAAM;QACNC,OAAO;QACPC,YAAY;QACZC,WAAW;QACXC,aAAa;YAAC;SAAO;QACrBC,gBAAgB;QAChBC,aAAa;QACbC,OAAO;QACPC,MAAM,CAACC,MAAM,EAAEC,OAAO,EAAEC,IAAI,EAAE,GAC5BA,OAAOhB,WAAWC,MAAMgB,aAAa,EAAE;gBAAEF;gBAASC;YAAK,KAAK;IAChE;CACD,CAAA"}
|
|
@@ -0,0 +1,121 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @license
|
|
3
|
+
* Copyright 2026 Aglyn LLC
|
|
4
|
+
*
|
|
5
|
+
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
6
|
+
* you may not use this file except in compliance with the License.
|
|
7
|
+
* You may obtain a copy of the License at
|
|
8
|
+
*
|
|
9
|
+
* http://www.apache.org/licenses/LICENSE-2.0
|
|
10
|
+
*
|
|
11
|
+
* Unless required by applicable law or agreed to in writing, software
|
|
12
|
+
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
13
|
+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
14
|
+
* See the License for the specific language governing permissions and
|
|
15
|
+
* limitations under the License.
|
|
16
|
+
*/
|
|
17
|
+
/**
|
|
18
|
+
* The tax storefront sales collected, by who is liable for it (AGL-1904).
|
|
19
|
+
*
|
|
20
|
+
* A storefront sale is a merchant's sale — but on a `mode: 'stripe'` store
|
|
21
|
+
* the tax charged to the shopper is computed against the PLATFORM's
|
|
22
|
+
* registrations on a Checkout Session created on the platform's own account
|
|
23
|
+
* (measured, not inferred: with the platform unregistered and the destination
|
|
24
|
+
* connected account registered in Texas, Stripe answered `amount_tax: 0` /
|
|
25
|
+
* `not_collecting`; with the platform registered and nothing else changed,
|
|
26
|
+
* 8.25% / `standard_rated`; both sessions reported
|
|
27
|
+
* `automatic_tax.liability: { type: "self" }`). That tax lands in the
|
|
28
|
+
* platform's balance, and the operator's return reads it through this fold.
|
|
29
|
+
*
|
|
30
|
+
* ## THREE BUCKETS, AND THEY MUST NEVER BE SUMMED
|
|
31
|
+
*
|
|
32
|
+
* There is deliberately no grand total on this summary, and adding one would
|
|
33
|
+
* be the bug — the three are answers to three different questions:
|
|
34
|
+
*
|
|
35
|
+
* - **`aglynLiable`** — Stripe Tax computed it against the platform's own
|
|
36
|
+
* registrations (`taxMode: 'stripe-automatic'`,
|
|
37
|
+
* `taxLiability: 'platform'`). This is money the platform is holding.
|
|
38
|
+
* - **`merchantManual`** — the merchant's own configured rate, added as an
|
|
39
|
+
* ordinary line item Stripe is never told is tax. The platform's
|
|
40
|
+
* registrations played no part; it is not the platform's to remit, and
|
|
41
|
+
* counting it would have the operator filing and paying another
|
|
42
|
+
* company's tax.
|
|
43
|
+
* - **`connectedAccountLiable`** — Stripe Tax that named a CONNECTED
|
|
44
|
+
* account as the liable party. None exists today (no storefront path sets
|
|
45
|
+
* `on_behalf_of`), and the bucket is here so that if one ever does, the
|
|
46
|
+
* money moves out of `aglynLiable` visibly rather than silently.
|
|
47
|
+
*
|
|
48
|
+
* `aglynLiable` is a wire name the merchant's own tax report and the staff
|
|
49
|
+
* return have both read since AGL-1904; it means "the platform operator".
|
|
50
|
+
*
|
|
51
|
+
* The classification comes from the stored `taxMode` / `taxLiability`, which
|
|
52
|
+
* `storefront-tax.ts` derives from `automatic_tax.enabled` and never from the
|
|
53
|
+
* presence of tax lines — a manual-mode subscription renewal carries genuine
|
|
54
|
+
* Stripe Tax Rates (AGL-1751) and is otherwise indistinguishable.
|
|
55
|
+
*
|
|
56
|
+
* **This says nothing about marketplace-facilitator status.** It reports which
|
|
57
|
+
* registration computed which tax and where the money is. Which line of a
|
|
58
|
+
* filed return each bucket belongs on is a question for the preparer and for
|
|
59
|
+
* counsel, and this module deliberately declines to answer it — which is
|
|
60
|
+
* exactly why there is no merged total to mistake for one.
|
|
61
|
+
*
|
|
62
|
+
* Rows that cannot be classified or read are counted in `attention`, never
|
|
63
|
+
* dropped and never zeroed.
|
|
64
|
+
*
|
|
65
|
+
* Pure and total: no Firestore, no clock. Read by the staff return's source
|
|
66
|
+
* (`server/tax-return-source.ts`) and by the merchant's own tax report, so
|
|
67
|
+
* both read the same rows through the same classifier.
|
|
68
|
+
*/
|
|
69
|
+
import { type TaxLineInput, type TaxReturnJurisdiction } from '@aglyn/aglyn/app-utils/tax-jurisdiction-figures';
|
|
70
|
+
/** The subset of a `storefrontTaxCollected/{stripeId}` row this reads. */
|
|
71
|
+
export interface StorefrontTaxReturnRowInput {
|
|
72
|
+
id: string;
|
|
73
|
+
hostId?: unknown;
|
|
74
|
+
orgId?: string | null;
|
|
75
|
+
taxMode?: unknown;
|
|
76
|
+
taxLiability?: unknown;
|
|
77
|
+
grossCents?: unknown;
|
|
78
|
+
taxCents?: unknown;
|
|
79
|
+
currency?: unknown;
|
|
80
|
+
customerAddress?: {
|
|
81
|
+
country?: unknown;
|
|
82
|
+
state?: unknown;
|
|
83
|
+
} | null;
|
|
84
|
+
taxLines?: TaxLineInput[] | null;
|
|
85
|
+
paidAt?: unknown;
|
|
86
|
+
}
|
|
87
|
+
export interface StorefrontTaxBucket {
|
|
88
|
+
transactionCount: number;
|
|
89
|
+
/** What shoppers paid, tax included. NOT the platform's revenue. */
|
|
90
|
+
grossCents: number;
|
|
91
|
+
/** Stripe's `taxable_amount` summed; 0 for buckets that state no base. */
|
|
92
|
+
taxableSalesCents: number;
|
|
93
|
+
taxCollectedCents: number;
|
|
94
|
+
/** Keyed `COUNTRY-STATE`, or `unknown`. */
|
|
95
|
+
byJurisdiction: Record<string, TaxReturnJurisdiction>;
|
|
96
|
+
}
|
|
97
|
+
export interface StorefrontTaxSummary {
|
|
98
|
+
periodStart: string;
|
|
99
|
+
periodEnd: string;
|
|
100
|
+
transactionCount: number;
|
|
101
|
+
/** Tax computed against the PLATFORM's registrations. See the module note. */
|
|
102
|
+
aglynLiable: StorefrontTaxBucket;
|
|
103
|
+
/** The merchant's own configured tax. Never the platform's to remit. */
|
|
104
|
+
merchantManual: StorefrontTaxBucket;
|
|
105
|
+
/** Stripe Tax that named a connected account liable. Empty today. */
|
|
106
|
+
connectedAccountLiable: StorefrontTaxBucket;
|
|
107
|
+
attention: {
|
|
108
|
+
/** Tax collected but no line states its base — see the module note. */
|
|
109
|
+
rowsMissingTaxableBase: number;
|
|
110
|
+
rowsMissingAddress: number;
|
|
111
|
+
nonUsdRows: number;
|
|
112
|
+
rowsMissingPaidAt: number;
|
|
113
|
+
/** A `taxMode` this code does not recognize — never silently bucketed. */
|
|
114
|
+
rowsUnclassified: number;
|
|
115
|
+
};
|
|
116
|
+
}
|
|
117
|
+
/** The storefront figures from one period's rows. See the module note. */
|
|
118
|
+
export declare function storefrontTaxSummary(rows: readonly StorefrontTaxReturnRowInput[], period: {
|
|
119
|
+
start: Date;
|
|
120
|
+
end: Date;
|
|
121
|
+
}): StorefrontTaxSummary;
|
|
@@ -0,0 +1,148 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @license
|
|
3
|
+
* Copyright 2026 Aglyn LLC
|
|
4
|
+
*
|
|
5
|
+
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
6
|
+
* you may not use this file except in compliance with the License.
|
|
7
|
+
* You may obtain a copy of the License at
|
|
8
|
+
*
|
|
9
|
+
* http://www.apache.org/licenses/LICENSE-2.0
|
|
10
|
+
*
|
|
11
|
+
* Unless required by applicable law or agreed to in writing, software
|
|
12
|
+
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
13
|
+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
14
|
+
* See the License for the specific language governing permissions and
|
|
15
|
+
* limitations under the License.
|
|
16
|
+
*/ /**
|
|
17
|
+
* The tax storefront sales collected, by who is liable for it (AGL-1904).
|
|
18
|
+
*
|
|
19
|
+
* A storefront sale is a merchant's sale — but on a `mode: 'stripe'` store
|
|
20
|
+
* the tax charged to the shopper is computed against the PLATFORM's
|
|
21
|
+
* registrations on a Checkout Session created on the platform's own account
|
|
22
|
+
* (measured, not inferred: with the platform unregistered and the destination
|
|
23
|
+
* connected account registered in Texas, Stripe answered `amount_tax: 0` /
|
|
24
|
+
* `not_collecting`; with the platform registered and nothing else changed,
|
|
25
|
+
* 8.25% / `standard_rated`; both sessions reported
|
|
26
|
+
* `automatic_tax.liability: { type: "self" }`). That tax lands in the
|
|
27
|
+
* platform's balance, and the operator's return reads it through this fold.
|
|
28
|
+
*
|
|
29
|
+
* ## THREE BUCKETS, AND THEY MUST NEVER BE SUMMED
|
|
30
|
+
*
|
|
31
|
+
* There is deliberately no grand total on this summary, and adding one would
|
|
32
|
+
* be the bug — the three are answers to three different questions:
|
|
33
|
+
*
|
|
34
|
+
* - **`aglynLiable`** — Stripe Tax computed it against the platform's own
|
|
35
|
+
* registrations (`taxMode: 'stripe-automatic'`,
|
|
36
|
+
* `taxLiability: 'platform'`). This is money the platform is holding.
|
|
37
|
+
* - **`merchantManual`** — the merchant's own configured rate, added as an
|
|
38
|
+
* ordinary line item Stripe is never told is tax. The platform's
|
|
39
|
+
* registrations played no part; it is not the platform's to remit, and
|
|
40
|
+
* counting it would have the operator filing and paying another
|
|
41
|
+
* company's tax.
|
|
42
|
+
* - **`connectedAccountLiable`** — Stripe Tax that named a CONNECTED
|
|
43
|
+
* account as the liable party. None exists today (no storefront path sets
|
|
44
|
+
* `on_behalf_of`), and the bucket is here so that if one ever does, the
|
|
45
|
+
* money moves out of `aglynLiable` visibly rather than silently.
|
|
46
|
+
*
|
|
47
|
+
* `aglynLiable` is a wire name the merchant's own tax report and the staff
|
|
48
|
+
* return have both read since AGL-1904; it means "the platform operator".
|
|
49
|
+
*
|
|
50
|
+
* The classification comes from the stored `taxMode` / `taxLiability`, which
|
|
51
|
+
* `storefront-tax.ts` derives from `automatic_tax.enabled` and never from the
|
|
52
|
+
* presence of tax lines — a manual-mode subscription renewal carries genuine
|
|
53
|
+
* Stripe Tax Rates (AGL-1751) and is otherwise indistinguishable.
|
|
54
|
+
*
|
|
55
|
+
* **This says nothing about marketplace-facilitator status.** It reports which
|
|
56
|
+
* registration computed which tax and where the money is. Which line of a
|
|
57
|
+
* filed return each bucket belongs on is a question for the preparer and for
|
|
58
|
+
* counsel, and this module deliberately declines to answer it — which is
|
|
59
|
+
* exactly why there is no merged total to mistake for one.
|
|
60
|
+
*
|
|
61
|
+
* Rows that cannot be classified or read are counted in `attention`, never
|
|
62
|
+
* dropped and never zeroed.
|
|
63
|
+
*
|
|
64
|
+
* Pure and total: no Firestore, no clock. Read by the staff return's source
|
|
65
|
+
* (`server/tax-return-source.ts`) and by the merchant's own tax report, so
|
|
66
|
+
* both read the same rows through the same classifier.
|
|
67
|
+
*/ import { accumulateTaxWorkingPapers, asRowDate, emptyTaxJurisdiction, taxJurisdictionKey } from "@aglyn/aglyn/app-utils/tax-jurisdiction-figures";
|
|
68
|
+
function cents(value) {
|
|
69
|
+
const parsed = Number(value != null ? value : 0);
|
|
70
|
+
return Number.isFinite(parsed) ? Math.round(parsed) : 0;
|
|
71
|
+
}
|
|
72
|
+
function emptyBucket() {
|
|
73
|
+
return {
|
|
74
|
+
transactionCount: 0,
|
|
75
|
+
grossCents: 0,
|
|
76
|
+
taxableSalesCents: 0,
|
|
77
|
+
taxCollectedCents: 0,
|
|
78
|
+
byJurisdiction: {}
|
|
79
|
+
};
|
|
80
|
+
}
|
|
81
|
+
/** The storefront figures from one period's rows. See the module note. */ export function storefrontTaxSummary(rows, period) {
|
|
82
|
+
const summary = {
|
|
83
|
+
periodStart: period.start.toISOString(),
|
|
84
|
+
periodEnd: period.end.toISOString(),
|
|
85
|
+
transactionCount: 0,
|
|
86
|
+
aglynLiable: emptyBucket(),
|
|
87
|
+
merchantManual: emptyBucket(),
|
|
88
|
+
connectedAccountLiable: emptyBucket(),
|
|
89
|
+
attention: {
|
|
90
|
+
rowsMissingTaxableBase: 0,
|
|
91
|
+
rowsMissingAddress: 0,
|
|
92
|
+
nonUsdRows: 0,
|
|
93
|
+
rowsMissingPaidAt: 0,
|
|
94
|
+
rowsUnclassified: 0
|
|
95
|
+
}
|
|
96
|
+
};
|
|
97
|
+
for (const row of rows != null ? rows : []){
|
|
98
|
+
var _row_currency, _row_taxMode, _row_taxLiability, _bucket_byJurisdiction, _jurisdiction, _;
|
|
99
|
+
const gross = cents(row.grossCents);
|
|
100
|
+
const tax = cents(row.taxCents);
|
|
101
|
+
const lines = Array.isArray(row.taxLines) ? row.taxLines : [];
|
|
102
|
+
const statedBases = lines.map((line)=>line == null ? void 0 : line.taxableAmountCents).filter((base)=>base !== null && base !== undefined && Number.isFinite(Number(base)));
|
|
103
|
+
const taxableBase = statedBases.reduce((sum, base)=>sum + Math.round(Number(base)), 0);
|
|
104
|
+
const jurisdiction = taxJurisdictionKey(row.customerAddress);
|
|
105
|
+
if (jurisdiction === 'unknown') summary.attention.rowsMissingAddress += 1;
|
|
106
|
+
if (String((_row_currency = row.currency) != null ? _row_currency : 'usd').toLowerCase() !== 'usd') {
|
|
107
|
+
summary.attention.nonUsdRows += 1;
|
|
108
|
+
}
|
|
109
|
+
if (!asRowDate(row.paidAt)) summary.attention.rowsMissingPaidAt += 1;
|
|
110
|
+
// The bucket is chosen from the STORED classification, and an unfamiliar
|
|
111
|
+
// one falls through to `rowsUnclassified` rather than defaulting into a
|
|
112
|
+
// bucket — a default here would put a merchant's tax on the platform's
|
|
113
|
+
// return, or the platform's tax nowhere, and neither is allowed to happen
|
|
114
|
+
// quietly.
|
|
115
|
+
const mode = String((_row_taxMode = row.taxMode) != null ? _row_taxMode : '');
|
|
116
|
+
const liability = String((_row_taxLiability = row.taxLiability) != null ? _row_taxLiability : '');
|
|
117
|
+
const bucket = mode === 'stripe-automatic' && liability === 'connected-account' ? summary.connectedAccountLiable : mode === 'stripe-automatic' ? summary.aglynLiable : mode === 'manual' ? summary.merchantManual : null;
|
|
118
|
+
if (!bucket) {
|
|
119
|
+
summary.attention.rowsUnclassified += 1;
|
|
120
|
+
continue;
|
|
121
|
+
}
|
|
122
|
+
// A taxed row that cannot state the base the rate was applied to —
|
|
123
|
+
// counted ONLY for Stripe-computed tax. A manual-mode row has no Stripe
|
|
124
|
+
// base by construction (Stripe was never told the amount was tax), so
|
|
125
|
+
// flagging it here would raise a permanent alarm about a figure that can
|
|
126
|
+
// never exist, and an alarm that is always on is an alarm nobody reads.
|
|
127
|
+
if (mode === 'stripe-automatic' && tax > 0 && statedBases.length === 0) {
|
|
128
|
+
summary.attention.rowsMissingTaxableBase += 1;
|
|
129
|
+
}
|
|
130
|
+
summary.transactionCount += 1;
|
|
131
|
+
bucket.transactionCount += 1;
|
|
132
|
+
bucket.grossCents += gross;
|
|
133
|
+
bucket.taxableSalesCents += taxableBase;
|
|
134
|
+
bucket.taxCollectedCents += tax;
|
|
135
|
+
const jurisdictionBucket = (_ = (_bucket_byJurisdiction = bucket.byJurisdiction)[_jurisdiction = jurisdiction]) != null ? _ : _bucket_byJurisdiction[_jurisdiction] = emptyTaxJurisdiction();
|
|
136
|
+
jurisdictionBucket.transactionCount += 1;
|
|
137
|
+
jurisdictionBucket.totalSalesCents += gross - tax;
|
|
138
|
+
jurisdictionBucket.taxableSalesCents += taxableBase;
|
|
139
|
+
jurisdictionBucket.taxCollectedCents += tax;
|
|
140
|
+
// The storefront working papers (AGL-2329). Its lines carry the richer
|
|
141
|
+
// detail — `percentage`, `rateState`, `jurisdiction` — three fields the
|
|
142
|
+
// writer annotates "for the working papers".
|
|
143
|
+
accumulateTaxWorkingPapers(jurisdictionBucket, lines);
|
|
144
|
+
}
|
|
145
|
+
return summary;
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
//# sourceMappingURL=storefront-tax-summary.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../../../../../../../libs/plugins/commerce/src/lib/model/storefront-tax-summary.ts"],"sourcesContent":["/**\n * @license\n * Copyright 2026 Aglyn LLC\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n/**\n * The tax storefront sales collected, by who is liable for it (AGL-1904).\n *\n * A storefront sale is a merchant's sale — but on a `mode: 'stripe'` store\n * the tax charged to the shopper is computed against the PLATFORM's\n * registrations on a Checkout Session created on the platform's own account\n * (measured, not inferred: with the platform unregistered and the destination\n * connected account registered in Texas, Stripe answered `amount_tax: 0` /\n * `not_collecting`; with the platform registered and nothing else changed,\n * 8.25% / `standard_rated`; both sessions reported\n * `automatic_tax.liability: { type: \"self\" }`). That tax lands in the\n * platform's balance, and the operator's return reads it through this fold.\n *\n * ## THREE BUCKETS, AND THEY MUST NEVER BE SUMMED\n *\n * There is deliberately no grand total on this summary, and adding one would\n * be the bug — the three are answers to three different questions:\n *\n * - **`aglynLiable`** — Stripe Tax computed it against the platform's own\n * registrations (`taxMode: 'stripe-automatic'`,\n * `taxLiability: 'platform'`). This is money the platform is holding.\n * - **`merchantManual`** — the merchant's own configured rate, added as an\n * ordinary line item Stripe is never told is tax. The platform's\n * registrations played no part; it is not the platform's to remit, and\n * counting it would have the operator filing and paying another\n * company's tax.\n * - **`connectedAccountLiable`** — Stripe Tax that named a CONNECTED\n * account as the liable party. None exists today (no storefront path sets\n * `on_behalf_of`), and the bucket is here so that if one ever does, the\n * money moves out of `aglynLiable` visibly rather than silently.\n *\n * `aglynLiable` is a wire name the merchant's own tax report and the staff\n * return have both read since AGL-1904; it means \"the platform operator\".\n *\n * The classification comes from the stored `taxMode` / `taxLiability`, which\n * `storefront-tax.ts` derives from `automatic_tax.enabled` and never from the\n * presence of tax lines — a manual-mode subscription renewal carries genuine\n * Stripe Tax Rates (AGL-1751) and is otherwise indistinguishable.\n *\n * **This says nothing about marketplace-facilitator status.** It reports which\n * registration computed which tax and where the money is. Which line of a\n * filed return each bucket belongs on is a question for the preparer and for\n * counsel, and this module deliberately declines to answer it — which is\n * exactly why there is no merged total to mistake for one.\n *\n * Rows that cannot be classified or read are counted in `attention`, never\n * dropped and never zeroed.\n *\n * Pure and total: no Firestore, no clock. Read by the staff return's source\n * (`server/tax-return-source.ts`) and by the merchant's own tax report, so\n * both read the same rows through the same classifier.\n */\n\nimport {\n accumulateTaxWorkingPapers,\n asRowDate,\n emptyTaxJurisdiction,\n taxJurisdictionKey,\n type TaxLineInput,\n type TaxReturnJurisdiction,\n} from '@aglyn/aglyn/app-utils/tax-jurisdiction-figures'\n\n/** The subset of a `storefrontTaxCollected/{stripeId}` row this reads. */\nexport interface StorefrontTaxReturnRowInput {\n id: string\n hostId?: unknown\n orgId?: string | null\n taxMode?: unknown\n taxLiability?: unknown\n grossCents?: unknown\n taxCents?: unknown\n currency?: unknown\n customerAddress?: {\n country?: unknown\n state?: unknown\n } | null\n taxLines?: TaxLineInput[] | null\n paidAt?: unknown\n}\n\nexport interface StorefrontTaxBucket {\n transactionCount: number\n /** What shoppers paid, tax included. NOT the platform's revenue. */\n grossCents: number\n /** Stripe's `taxable_amount` summed; 0 for buckets that state no base. */\n taxableSalesCents: number\n taxCollectedCents: number\n /** Keyed `COUNTRY-STATE`, or `unknown`. */\n byJurisdiction: Record<string, TaxReturnJurisdiction>\n}\n\nexport interface StorefrontTaxSummary {\n periodStart: string\n periodEnd: string\n transactionCount: number\n /** Tax computed against the PLATFORM's registrations. See the module note. */\n aglynLiable: StorefrontTaxBucket\n /** The merchant's own configured tax. Never the platform's to remit. */\n merchantManual: StorefrontTaxBucket\n /** Stripe Tax that named a connected account liable. Empty today. */\n connectedAccountLiable: StorefrontTaxBucket\n attention: {\n /** Tax collected but no line states its base — see the module note. */\n rowsMissingTaxableBase: number\n rowsMissingAddress: number\n nonUsdRows: number\n rowsMissingPaidAt: number\n /** A `taxMode` this code does not recognize — never silently bucketed. */\n rowsUnclassified: number\n }\n}\n\nfunction cents(value: unknown): number {\n const parsed = Number(value ?? 0)\n return Number.isFinite(parsed) ? Math.round(parsed) : 0\n}\n\nfunction emptyBucket(): StorefrontTaxBucket {\n return {\n transactionCount: 0,\n grossCents: 0,\n taxableSalesCents: 0,\n taxCollectedCents: 0,\n byJurisdiction: {},\n }\n}\n\n/** The storefront figures from one period's rows. See the module note. */\nexport function storefrontTaxSummary(\n rows: readonly StorefrontTaxReturnRowInput[],\n period: { start: Date; end: Date },\n): StorefrontTaxSummary {\n const summary: StorefrontTaxSummary = {\n periodStart: period.start.toISOString(),\n periodEnd: period.end.toISOString(),\n transactionCount: 0,\n aglynLiable: emptyBucket(),\n merchantManual: emptyBucket(),\n connectedAccountLiable: emptyBucket(),\n attention: {\n rowsMissingTaxableBase: 0,\n rowsMissingAddress: 0,\n nonUsdRows: 0,\n rowsMissingPaidAt: 0,\n rowsUnclassified: 0,\n },\n }\n\n for (const row of rows ?? []) {\n const gross = cents(row.grossCents)\n const tax = cents(row.taxCents)\n const lines = Array.isArray(row.taxLines) ? row.taxLines : []\n const statedBases = lines\n .map((line) => line?.taxableAmountCents)\n .filter(\n (base): base is number =>\n base !== null && base !== undefined && Number.isFinite(Number(base)),\n )\n const taxableBase = statedBases.reduce(\n (sum, base) => sum + Math.round(Number(base)),\n 0,\n )\n\n const jurisdiction = taxJurisdictionKey(row.customerAddress)\n if (jurisdiction === 'unknown') summary.attention.rowsMissingAddress += 1\n if (String(row.currency ?? 'usd').toLowerCase() !== 'usd') {\n summary.attention.nonUsdRows += 1\n }\n if (!asRowDate(row.paidAt)) summary.attention.rowsMissingPaidAt += 1\n\n // The bucket is chosen from the STORED classification, and an unfamiliar\n // one falls through to `rowsUnclassified` rather than defaulting into a\n // bucket — a default here would put a merchant's tax on the platform's\n // return, or the platform's tax nowhere, and neither is allowed to happen\n // quietly.\n const mode = String(row.taxMode ?? '')\n const liability = String(row.taxLiability ?? '')\n const bucket =\n mode === 'stripe-automatic' && liability === 'connected-account'\n ? summary.connectedAccountLiable\n : mode === 'stripe-automatic'\n ? summary.aglynLiable\n : mode === 'manual'\n ? summary.merchantManual\n : null\n if (!bucket) {\n summary.attention.rowsUnclassified += 1\n continue\n }\n\n // A taxed row that cannot state the base the rate was applied to —\n // counted ONLY for Stripe-computed tax. A manual-mode row has no Stripe\n // base by construction (Stripe was never told the amount was tax), so\n // flagging it here would raise a permanent alarm about a figure that can\n // never exist, and an alarm that is always on is an alarm nobody reads.\n if (mode === 'stripe-automatic' && tax > 0 && statedBases.length === 0) {\n summary.attention.rowsMissingTaxableBase += 1\n }\n\n summary.transactionCount += 1\n bucket.transactionCount += 1\n bucket.grossCents += gross\n bucket.taxableSalesCents += taxableBase\n bucket.taxCollectedCents += tax\n const jurisdictionBucket = (bucket.byJurisdiction[jurisdiction] ??=\n emptyTaxJurisdiction())\n jurisdictionBucket.transactionCount += 1\n jurisdictionBucket.totalSalesCents += gross - tax\n jurisdictionBucket.taxableSalesCents += taxableBase\n jurisdictionBucket.taxCollectedCents += tax\n // The storefront working papers (AGL-2329). Its lines carry the richer\n // detail — `percentage`, `rateState`, `jurisdiction` — three fields the\n // writer annotates \"for the working papers\".\n accumulateTaxWorkingPapers(jurisdictionBucket, lines)\n }\n\n return summary\n}\n"],"names":["accumulateTaxWorkingPapers","asRowDate","emptyTaxJurisdiction","taxJurisdictionKey","cents","value","parsed","Number","isFinite","Math","round","emptyBucket","transactionCount","grossCents","taxableSalesCents","taxCollectedCents","byJurisdiction","storefrontTaxSummary","rows","period","summary","periodStart","start","toISOString","periodEnd","end","aglynLiable","merchantManual","connectedAccountLiable","attention","rowsMissingTaxableBase","rowsMissingAddress","nonUsdRows","rowsMissingPaidAt","rowsUnclassified","row","bucket","jurisdiction","gross","tax","taxCents","lines","Array","isArray","taxLines","statedBases","map","line","taxableAmountCents","filter","base","undefined","taxableBase","reduce","sum","customerAddress","String","currency","toLowerCase","paidAt","mode","taxMode","liability","taxLiability","length","jurisdictionBucket","totalSalesCents"],"mappings":"AAAA;;;;;;;;;;;;;;;CAeC,GAED;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CAmDC,GAED,SACEA,0BAA0B,EAC1BC,SAAS,EACTC,oBAAoB,EACpBC,kBAAkB,QAGb,kDAAiD;AAoDxD,SAASC,MAAMC,KAAc;IAC3B,MAAMC,SAASC,OAAOF,gBAAAA,QAAS;IAC/B,OAAOE,OAAOC,QAAQ,CAACF,UAAUG,KAAKC,KAAK,CAACJ,UAAU;AACxD;AAEA,SAASK;IACP,OAAO;QACLC,kBAAkB;QAClBC,YAAY;QACZC,mBAAmB;QACnBC,mBAAmB;QACnBC,gBAAgB,CAAC;IACnB;AACF;AAEA,wEAAwE,GACxE,OAAO,SAASC,qBACdC,IAA4C,EAC5CC,MAAkC;IAElC,MAAMC,UAAgC;QACpCC,aAAaF,OAAOG,KAAK,CAACC,WAAW;QACrCC,WAAWL,OAAOM,GAAG,CAACF,WAAW;QACjCX,kBAAkB;QAClBc,aAAaf;QACbgB,gBAAgBhB;QAChBiB,wBAAwBjB;QACxBkB,WAAW;YACTC,wBAAwB;YACxBC,oBAAoB;YACpBC,YAAY;YACZC,mBAAmB;YACnBC,kBAAkB;QACpB;IACF;IAEA,KAAK,MAAMC,OAAOjB,eAAAA,OAAQ,EAAE,CAAE;YAiBjBiB,eAUSA,cACKA,mBA4BGC,wBAAsBC,eAAtBD;QAvD5B,MAAME,QAAQlC,MAAM+B,IAAItB,UAAU;QAClC,MAAM0B,MAAMnC,MAAM+B,IAAIK,QAAQ;QAC9B,MAAMC,QAAQC,MAAMC,OAAO,CAACR,IAAIS,QAAQ,IAAIT,IAAIS,QAAQ,GAAG,EAAE;QAC7D,MAAMC,cAAcJ,MACjBK,GAAG,CAAC,CAACC,OAASA,wBAAAA,KAAMC,kBAAkB,EACtCC,MAAM,CACL,CAACC,OACCA,SAAS,QAAQA,SAASC,aAAa5C,OAAOC,QAAQ,CAACD,OAAO2C;QAEpE,MAAME,cAAcP,YAAYQ,MAAM,CACpC,CAACC,KAAKJ,OAASI,MAAM7C,KAAKC,KAAK,CAACH,OAAO2C,QACvC;QAGF,MAAMb,eAAelC,mBAAmBgC,IAAIoB,eAAe;QAC3D,IAAIlB,iBAAiB,WAAWjB,QAAQS,SAAS,CAACE,kBAAkB,IAAI;QACxE,IAAIyB,QAAOrB,gBAAAA,IAAIsB,QAAQ,YAAZtB,gBAAgB,OAAOuB,WAAW,OAAO,OAAO;YACzDtC,QAAQS,SAAS,CAACG,UAAU,IAAI;QAClC;QACA,IAAI,CAAC/B,UAAUkC,IAAIwB,MAAM,GAAGvC,QAAQS,SAAS,CAACI,iBAAiB,IAAI;QAEnE,yEAAyE;QACzE,wEAAwE;QACxE,uEAAuE;QACvE,0EAA0E;QAC1E,WAAW;QACX,MAAM2B,OAAOJ,QAAOrB,eAAAA,IAAI0B,OAAO,YAAX1B,eAAe;QACnC,MAAM2B,YAAYN,QAAOrB,oBAAAA,IAAI4B,YAAY,YAAhB5B,oBAAoB;QAC7C,MAAMC,SACJwB,SAAS,sBAAsBE,cAAc,sBACzC1C,QAAQQ,sBAAsB,GAC9BgC,SAAS,qBACPxC,QAAQM,WAAW,GACnBkC,SAAS,WACPxC,QAAQO,cAAc,GACtB;QACV,IAAI,CAACS,QAAQ;YACXhB,QAAQS,SAAS,CAACK,gBAAgB,IAAI;YACtC;QACF;QAEA,mEAAmE;QACnE,wEAAwE;QACxE,sEAAsE;QACtE,yEAAyE;QACzE,wEAAwE;QACxE,IAAI0B,SAAS,sBAAsBrB,MAAM,KAAKM,YAAYmB,MAAM,KAAK,GAAG;YACtE5C,QAAQS,SAAS,CAACC,sBAAsB,IAAI;QAC9C;QAEAV,QAAQR,gBAAgB,IAAI;QAC5BwB,OAAOxB,gBAAgB,IAAI;QAC3BwB,OAAOvB,UAAU,IAAIyB;QACrBF,OAAOtB,iBAAiB,IAAIsC;QAC5BhB,OAAOrB,iBAAiB,IAAIwB;QAC5B,MAAM0B,sBAAsB7B,IAAAA,CAAAA,yBAAAA,OAAOpB,cAAc,CAAA,CAACqB,gBAAAA,aAAa,YAAnCD,IAAAA,sBAAqB,CAACC,cAAa,GAC7DnC;QACF+D,mBAAmBrD,gBAAgB,IAAI;QACvCqD,mBAAmBC,eAAe,IAAI5B,QAAQC;QAC9C0B,mBAAmBnD,iBAAiB,IAAIsC;QACxCa,mBAAmBlD,iBAAiB,IAAIwB;QACxC,uEAAuE;QACvE,wEAAwE;QACxE,6CAA6C;QAC7CvC,2BAA2BiE,oBAAoBxB;IACjD;IAEA,OAAOrB;AACT"}
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @license
|
|
3
|
+
* Copyright 2026 Aglyn LLC
|
|
4
|
+
*
|
|
5
|
+
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
6
|
+
* you may not use this file except in compliance with the License.
|
|
7
|
+
* You may obtain a copy of the License at
|
|
8
|
+
*
|
|
9
|
+
* http://www.apache.org/licenses/LICENSE-2.0
|
|
10
|
+
*
|
|
11
|
+
* Unless required by applicable law or agreed to in writing, software
|
|
12
|
+
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
13
|
+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
14
|
+
* See the License for the specific language governing permissions and
|
|
15
|
+
* limitations under the License.
|
|
16
|
+
*/
|
|
17
|
+
import type { PluginPlanEntitlementsDeclaration } from '@aglyn/aglyn/plugin-manager/plugin-plan-entitlements';
|
|
18
|
+
/**
|
|
19
|
+
* COMMERCE'S PLAN GATES (AGL-3080): the storefront features that start above
|
|
20
|
+
* the entry tiers. Compiled into core's `PLAN_ENTITLEMENTS` by the manifest
|
|
21
|
+
* generator (`register.planEntitlements`), so the plan comparison and every
|
|
22
|
+
* gate read the answers they always have.
|
|
23
|
+
*/
|
|
24
|
+
declare module '@aglyn/aglyn/plugin-manager/plugin-entitlement-keys' {
|
|
25
|
+
interface PluginEntitlementFeatures {
|
|
26
|
+
/** Recurring storefront subscription products (AGL-303). */
|
|
27
|
+
storefrontSubscriptions?: boolean;
|
|
28
|
+
/** Gift cards & store credit (AGL-322). */
|
|
29
|
+
giftCards?: boolean;
|
|
30
|
+
/** Commerce analytics dashboard (AGL-327). */
|
|
31
|
+
commerceAnalytics?: boolean;
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
/** Commerce's plan gates, for the manifest generator. */
|
|
35
|
+
export declare function commercePlanEntitlements(): PluginPlanEntitlementsDeclaration;
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @license
|
|
3
|
+
* Copyright 2026 Aglyn LLC
|
|
4
|
+
*
|
|
5
|
+
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
6
|
+
* you may not use this file except in compliance with the License.
|
|
7
|
+
* You may obtain a copy of the License at
|
|
8
|
+
*
|
|
9
|
+
* http://www.apache.org/licenses/LICENSE-2.0
|
|
10
|
+
*
|
|
11
|
+
* Unless required by applicable law or agreed to in writing, software
|
|
12
|
+
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
13
|
+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
14
|
+
* See the License for the specific language governing permissions and
|
|
15
|
+
* limitations under the License.
|
|
16
|
+
*/ // Types only: the manifest generator loads this module on its own to compile
|
|
17
|
+
// the plan figures, so nothing here may reach a core module at runtime.
|
|
18
|
+
import { _ as _extends } from "@swc/helpers/_/_extends";
|
|
19
|
+
/** Business and up. */ const FROM_BUSINESS = {
|
|
20
|
+
free: false,
|
|
21
|
+
starter: false,
|
|
22
|
+
pro: false,
|
|
23
|
+
business: true,
|
|
24
|
+
scale: true,
|
|
25
|
+
advanced: true,
|
|
26
|
+
agency: true,
|
|
27
|
+
enterprise: true
|
|
28
|
+
};
|
|
29
|
+
/** Pro and up. */ const FROM_PRO = _extends({}, FROM_BUSINESS, {
|
|
30
|
+
pro: true
|
|
31
|
+
});
|
|
32
|
+
/** Commerce's plan gates, for the manifest generator. */ export function commercePlanEntitlements() {
|
|
33
|
+
return {
|
|
34
|
+
features: [
|
|
35
|
+
{
|
|
36
|
+
key: 'storefrontSubscriptions',
|
|
37
|
+
label: 'Storefront subscriptions',
|
|
38
|
+
byPlan: FROM_BUSINESS
|
|
39
|
+
},
|
|
40
|
+
{
|
|
41
|
+
key: 'giftCards',
|
|
42
|
+
label: 'Gift cards',
|
|
43
|
+
byPlan: FROM_BUSINESS
|
|
44
|
+
},
|
|
45
|
+
{
|
|
46
|
+
key: 'commerceAnalytics',
|
|
47
|
+
label: 'Commerce analytics',
|
|
48
|
+
byPlan: FROM_PRO
|
|
49
|
+
}
|
|
50
|
+
]
|
|
51
|
+
};
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
//# sourceMappingURL=plan-entitlements.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../../../../../../libs/plugins/commerce/src/lib/plan-entitlements.ts"],"sourcesContent":["/**\n * @license\n * Copyright 2026 Aglyn LLC\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n// Types only: the manifest generator loads this module on its own to compile\n// the plan figures, so nothing here may reach a core module at runtime.\nimport type { OrgPlan } from '@aglyn/aglyn/foundation/definitions/org-billing.types'\nimport type { PluginPlanEntitlementsDeclaration } from '@aglyn/aglyn/plugin-manager/plugin-plan-entitlements'\n\n/**\n * COMMERCE'S PLAN GATES (AGL-3080): the storefront features that start above\n * the entry tiers. Compiled into core's `PLAN_ENTITLEMENTS` by the manifest\n * generator (`register.planEntitlements`), so the plan comparison and every\n * gate read the answers they always have.\n */\ndeclare module '@aglyn/aglyn/plugin-manager/plugin-entitlement-keys' {\n interface PluginEntitlementFeatures {\n /** Recurring storefront subscription products (AGL-303). */\n storefrontSubscriptions?: boolean\n /** Gift cards & store credit (AGL-322). */\n giftCards?: boolean\n /** Commerce analytics dashboard (AGL-327). */\n commerceAnalytics?: boolean\n }\n}\n\n/** Business and up. */\nconst FROM_BUSINESS: Readonly<Record<OrgPlan, boolean>> = {\n free: false,\n starter: false,\n pro: false,\n business: true,\n scale: true,\n advanced: true,\n agency: true,\n enterprise: true,\n}\n\n/** Pro and up. */\nconst FROM_PRO: Readonly<Record<OrgPlan, boolean>> = {\n ...FROM_BUSINESS,\n pro: true,\n}\n\n/** Commerce's plan gates, for the manifest generator. */\nexport function commercePlanEntitlements(): PluginPlanEntitlementsDeclaration {\n return {\n features: [\n {\n key: 'storefrontSubscriptions',\n label: 'Storefront subscriptions',\n byPlan: FROM_BUSINESS,\n },\n { key: 'giftCards', label: 'Gift cards', byPlan: FROM_BUSINESS },\n {\n key: 'commerceAnalytics',\n label: 'Commerce analytics',\n byPlan: FROM_PRO,\n },\n ],\n }\n}\n"],"names":["FROM_BUSINESS","free","starter","pro","business","scale","advanced","agency","enterprise","FROM_PRO","commercePlanEntitlements","features","key","label","byPlan"],"mappings":"AAAA;;;;;;;;;;;;;;;CAeC,GAED,6EAA6E;AAC7E,wEAAwE;;AAqBxE,qBAAqB,GACrB,MAAMA,gBAAoD;IACxDC,MAAM;IACNC,SAAS;IACTC,KAAK;IACLC,UAAU;IACVC,OAAO;IACPC,UAAU;IACVC,QAAQ;IACRC,YAAY;AACd;AAEA,gBAAgB,GAChB,MAAMC,WAA+C,aAChDT;IACHG,KAAK;;AAGP,uDAAuD,GACvD,OAAO,SAASO;IACd,OAAO;QACLC,UAAU;YACR;gBACEC,KAAK;gBACLC,OAAO;gBACPC,QAAQd;YACV;YACA;gBAAEY,KAAK;gBAAaC,OAAO;gBAAcC,QAAQd;YAAc;YAC/D;gBACEY,KAAK;gBACLC,OAAO;gBACPC,QAAQL;YACV;SACD;IACH;AACF"}
|
package/src/lib/plugin.js
CHANGED
|
@@ -22,6 +22,9 @@ const NewestSiteUsersCard = lazy(()=>import("./components/console/newest-site-us
|
|
|
22
22
|
import { COMMERCE_CONSOLE_SECTIONS } from "./components/commerce-console-sections.js";
|
|
23
23
|
import { BUNDLE_ID } from "./constants/bundle-common.js";
|
|
24
24
|
import { COMMERCE_PERMISSIONS } from "./model/plugin-permissions.js";
|
|
25
|
+
import { registerCommerceRecordRoutes } from "./model/commerce-record-routes.js";
|
|
26
|
+
import { registerCommerceZones } from "./components/console/product-zones.js";
|
|
27
|
+
import { COMMERCE_SEARCH_SOURCES } from "./model/commerce-search-sources.js";
|
|
25
28
|
import { COMMERCE_CONFIG_SCHEMA } from "./plugin-config.js";
|
|
26
29
|
/** Code-split: the Products console page only loads when opened. */ const CommerceConsolePage = lazy(()=>import("./components/commerce-console-page.js"));
|
|
27
30
|
/**
|
|
@@ -34,6 +37,11 @@ import { COMMERCE_CONFIG_SCHEMA } from "./plugin-config.js";
|
|
|
34
37
|
* tracked as follow-up. The nav item omits a `Component`, so the shell's
|
|
35
38
|
* nav renders the link and the existing named route serves the page.
|
|
36
39
|
*/ export function registerCommerceConsole() {
|
|
40
|
+
// Where a product is read, for surfaces outside this plugin (AGL-3080).
|
|
41
|
+
registerCommerceRecordRoutes();
|
|
42
|
+
// The zones its product editor, products hub and CSV import dialog host,
|
|
43
|
+
// with the props each hands a widget (AGL-2916, AGL-3080).
|
|
44
|
+
registerCommerceZones();
|
|
37
45
|
// Plugin-declared permissions (AGL-435): tier defaults ride every
|
|
38
46
|
// resolved role set; custom roles override key-by-key.
|
|
39
47
|
Aglyn.registerPluginPermissions(COMMERCE_PERMISSIONS);
|
|
@@ -43,6 +51,7 @@ import { COMMERCE_CONFIG_SCHEMA } from "./plugin-config.js";
|
|
|
43
51
|
Aglyn.registerConsoleExtension({
|
|
44
52
|
pluginId: BUNDLE_ID,
|
|
45
53
|
displayName: 'Commerce',
|
|
54
|
+
searchSources: COMMERCE_SEARCH_SOURCES,
|
|
46
55
|
// Dashboard/analytics glance card (AGL-419): rendered through the
|
|
47
56
|
// shell's 'commerceGlance' widget slot.
|
|
48
57
|
widgets: [
|