@fayz-ai/plugin-orders 0.14.0 → 0.14.1
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/dist/index.js +84 -2
- package/dist/index.js.map +1 -1
- package/dist/migrations/index.d.ts +2 -1
- package/dist/migrations/index.d.ts.map +1 -1
- package/package.json +2 -2
|
@@ -1,4 +1,5 @@
|
|
|
1
|
-
export declare const MIGRATION_001_AN_ORDER_ARRIVES_FROM_A_MARKETPLACE = "-- ============================================================================\n-- 001 \u2014 an order arrives from a marketplace\n--\n-- The V2 sink of ADR 0003: the ONE door through which the integration service\n-- hands a canonical delivery order to this product, and the two beside it to\n-- apply a later status and to read the current one. No rule lives in the\n-- service; what an order IS here is decided here, in one transaction.\n--\n-- There is no person behind these calls \u2014 the actor is the service, acting\n-- for the tenant it names, which is why `p_tenant` is explicit and no\n-- `app.has_permission` is asked (the same reasoning as service_authorize_for).\n-- The functions are therefore closed to every signed-in user and opened only\n-- to the service's own role, `fayz_integration`, when that role exists on the\n-- cluster. Creating the role is an operator step; the grant below is guarded.\n--\n-- The order shape is @fayz-ai/contracts' CanonicalOrder v1: money in\n-- centavos, items with options, totals that close. Everything that this\n-- product has no column for travels in orders.metadata.external, so nothing\n-- the marketplace said is lost.\n-- ============================================================================\n\nCREATE OR REPLACE FUNCTION public.orders_ingest_external(\n p_tenant uuid,\n p_unit uuid,\n p_order jsonb,\n p_status text\n) RETURNS uuid\nLANGUAGE plpgsql SECURITY DEFINER\nSET search_path TO ''\nAS $$\nDECLARE\n v_provider text := p_order ->> 'provider';\n v_external text := p_order ->> 'externalId';\n v_ref text;\n v_kind text;\n v_channel text;\n v_order uuid;\n v_item jsonb;\n v_n integer := 0;\n v_qty numeric;\n v_unit numeric;\n v_total numeric;\n v_sub numeric;\n v_disc numeric;\n v_ship numeric;\nBEGIN\n IF p_tenant IS NULL THEN\n RAISE EXCEPTION 'orders_ingest_external: tenant is required' USING ERRCODE = '22023';\n END IF;\n IF v_provider IS NULL OR v_external IS NULL THEN\n RAISE EXCEPTION 'orders_ingest_external: order needs provider and externalId' USING ERRCODE = '22023';\n END IF;\n IF p_unit IS NOT NULL AND NOT EXISTS (\n SELECT 1 FROM
|
|
1
|
+
export declare const MIGRATION_001_AN_ORDER_ARRIVES_FROM_A_MARKETPLACE = "-- ============================================================================\n-- 001 \u2014 an order arrives from a marketplace\n--\n-- The V2 sink of ADR 0003: the ONE door through which the integration service\n-- hands a canonical delivery order to this product, and the two beside it to\n-- apply a later status and to read the current one. No rule lives in the\n-- service; what an order IS here is decided here, in one transaction.\n--\n-- There is no person behind these calls \u2014 the actor is the service, acting\n-- for the tenant it names, which is why `p_tenant` is explicit and no\n-- `app.has_permission` is asked (the same reasoning as service_authorize_for).\n-- The functions are therefore closed to every signed-in user and opened only\n-- to the service's own role, `fayz_integration`, when that role exists on the\n-- cluster. Creating the role is an operator step; the grant below is guarded.\n--\n-- The order shape is @fayz-ai/contracts' CanonicalOrder v1: money in\n-- centavos, items with options, totals that close. Everything that this\n-- product has no column for travels in orders.metadata.external, so nothing\n-- the marketplace said is lost.\n-- ============================================================================\n\nCREATE OR REPLACE FUNCTION public.orders_ingest_external(\n p_tenant uuid,\n p_unit uuid,\n p_order jsonb,\n p_status text\n) RETURNS uuid\nLANGUAGE plpgsql SECURITY DEFINER\nSET search_path TO ''\nAS $$\nDECLARE\n v_provider text := p_order ->> 'provider';\n v_external text := p_order ->> 'externalId';\n v_ref text;\n v_kind text;\n v_channel text;\n v_order uuid;\n v_item jsonb;\n v_n integer := 0;\n v_qty numeric;\n v_unit numeric;\n v_total numeric;\n v_sub numeric;\n v_disc numeric;\n v_ship numeric;\nBEGIN\n IF p_tenant IS NULL THEN\n RAISE EXCEPTION 'orders_ingest_external: tenant is required' USING ERRCODE = '22023';\n END IF;\n IF v_provider IS NULL OR v_external IS NULL THEN\n RAISE EXCEPTION 'orders_ingest_external: order needs provider and externalId' USING ERRCODE = '22023';\n END IF;\n IF p_unit IS NOT NULL AND NOT EXISTS (\n SELECT 1 FROM app.units u WHERE u.id = p_unit AND u.tenant_id = p_tenant) THEN\n RAISE EXCEPTION 'orders_ingest_external: unit % is not of tenant %', p_unit, p_tenant USING ERRCODE = '22023';\n END IF;\n\n -- Once per (provider, externalId) within the tenant: a replay returns the\n -- order it already made. The key lives in metadata, as order_create's does.\n v_ref := v_provider || ':' || v_external;\n SELECT o.id INTO v_order FROM public.orders o\n WHERE o.tenant_id = p_tenant AND o.metadata ->> 'externalRef' = v_ref\n LIMIT 1;\n IF v_order IS NOT NULL THEN\n RETURN v_order;\n END IF;\n\n -- The contract's kinds are this product's kinds; the channel is where the\n -- order came in, in this product's vocabulary.\n v_kind := CASE p_order ->> 'kind'\n WHEN 'delivery' THEN 'delivery'\n WHEN 'takeout' THEN 'takeout'\n WHEN 'dine_in' THEN 'dine_in'\n ELSE 'delivery' END;\n v_channel := CASE v_kind WHEN 'delivery' THEN 'delivery' WHEN 'dine_in' THEN 'dine_in' ELSE 'balcao' END;\n\n IF p_status IS NULL OR NOT EXISTS (\n SELECT 1 FROM public.order_kinds k WHERE k.kind = v_kind AND p_status = ANY (k.statuses)) THEN\n RAISE EXCEPTION 'orders_ingest_external: % is not a status of kind %', p_status, v_kind USING ERRCODE = '22023';\n END IF;\n\n v_sub := round(coalesce((p_order -> 'totals' ->> 'subtotal')::numeric, 0) / 100, 2);\n v_disc := round(coalesce((p_order -> 'totals' ->> 'discount')::numeric, 0) / 100, 2);\n v_ship := round(coalesce((p_order -> 'totals' ->> 'deliveryFee')::numeric, 0) / 100, 2);\n\n INSERT INTO public.orders (tenant_id, unit_id, kind, channel, status, reference_number,\n subtotal, discount, shipping, total, currency, notes, metadata)\n VALUES (p_tenant, p_unit, v_kind, v_channel, p_status,\n nullif(p_order ->> 'displayId', ''),\n v_sub, v_disc, v_ship,\n round(coalesce((p_order -> 'totals' ->> 'total')::numeric, 0) / 100, 2),\n coalesce(p_order -> 'totals' ->> 'currency', 'BRL'),\n nullif(p_order ->> 'notes', ''),\n jsonb_build_object(\n 'externalRef', v_ref,\n 'idempotencyKey', v_ref,\n 'external', jsonb_build_object(\n 'provider', v_provider,\n 'externalId', v_external,\n 'displayId', p_order -> 'displayId',\n 'merchantRef', p_order -> 'merchantRef',\n 'placedAt', p_order -> 'placedAt',\n 'timing', p_order -> 'timing',\n 'customer', p_order -> 'customer',\n 'payment', p_order -> 'payment',\n 'fulfillment', p_order -> 'fulfillment',\n 'totals', p_order -> 'totals',\n 'lastStatusAt', NULL)))\n RETURNING id INTO v_order;\n\n FOR v_item IN SELECT * FROM jsonb_array_elements(coalesce(p_order -> 'items', '[]'::jsonb)) LOOP\n v_n := v_n + 1;\n v_qty := coalesce((v_item ->> 'quantity')::numeric, 1);\n v_unit := round(coalesce((v_item ->> 'unitPrice')::numeric, 0) / 100, 2);\n v_total := round(coalesce((v_item ->> 'total')::numeric, 0) / 100, 2);\n INSERT INTO public.items (tenant_id, unit_id, order_id, name, description, quantity,\n unit_price, discount, total, sort_order, metadata)\n VALUES (p_tenant, p_unit, v_order,\n coalesce(nullif(btrim(coalesce(v_item ->> 'name', '')), ''), 'Item'),\n nullif(v_item ->> 'notes', ''),\n v_qty, v_unit, 0, v_total, v_n - 1,\n jsonb_build_object(\n 'externalId', v_item -> 'externalId',\n 'externalCode', v_item -> 'externalCode',\n 'unit', v_item -> 'unit',\n 'options', coalesce(v_item -> 'options', '[]'::jsonb)));\n END LOOP;\n\n RETURN v_order;\nEND $$;\n\nCOMMENT ON FUNCTION public.orders_ingest_external(uuid, uuid, jsonb, text) IS\n 'The integration service hands a canonical marketplace order to this product, once per (provider, externalId). Actor: the service, for the named tenant.';\n\n-- A later status for an order the service ingested. Monotonic on the\n-- service side already; here the only rules are \"this order is yours\" and\n-- \"that status exists for its kind\".\nCREATE OR REPLACE FUNCTION public.orders_apply_external_status(\n p_tenant uuid,\n p_order uuid,\n p_status text,\n p_occurred_at timestamptz\n) RETURNS void\nLANGUAGE plpgsql SECURITY DEFINER\nSET search_path TO ''\nAS $$\nDECLARE\n v_kind text;\nBEGIN\n SELECT o.kind INTO v_kind FROM public.orders o\n WHERE o.id = p_order AND o.tenant_id = p_tenant AND o.metadata ? 'externalRef';\n IF v_kind IS NULL THEN\n RAISE EXCEPTION 'orders_apply_external_status: order % is not an external order of tenant %', p_order, p_tenant\n USING ERRCODE = '22023';\n END IF;\n IF NOT EXISTS (SELECT 1 FROM public.order_kinds k WHERE k.kind = v_kind AND p_status = ANY (k.statuses)) THEN\n RAISE EXCEPTION 'orders_apply_external_status: % is not a status of kind %', p_status, v_kind USING ERRCODE = '22023';\n END IF;\n\n UPDATE public.orders\n SET status = p_status,\n completed_at = CASE WHEN p_status IN ('completed', 'delivered') THEN coalesce(p_occurred_at, now()) ELSE completed_at END,\n metadata = jsonb_set(metadata, '{external,lastStatusAt}', to_jsonb(coalesce(p_occurred_at, now())), true),\n updated_at = now()\n WHERE id = p_order;\nEND $$;\n\nCOMMENT ON FUNCTION public.orders_apply_external_status(uuid, uuid, text, timestamptz) IS\n 'A later status from the marketplace for an order the service ingested. Actor: the service, for the named tenant.';\n\nCREATE OR REPLACE FUNCTION public.orders_external_status(p_tenant uuid, p_order uuid)\nRETURNS jsonb\nLANGUAGE sql STABLE SECURITY DEFINER\nSET search_path TO ''\nAS $$\n SELECT jsonb_build_object('status', o.status, 'updatedAt', o.updated_at)\n FROM public.orders o\n WHERE o.id = p_order AND o.tenant_id = p_tenant AND o.metadata ? 'externalRef';\n$$;\n\nCOMMENT ON FUNCTION public.orders_external_status(uuid, uuid) IS\n 'What this product thinks an external order is now. Actor: the service, for the named tenant.';\n\n-- Closed to people, open to the service. The pools grant EXECUTE to\n-- `authenticated` on every new public function by default; a definer function\n-- that takes the tenant as a parameter must not be reachable by a session.\nREVOKE ALL ON FUNCTION public.orders_ingest_external(uuid, uuid, jsonb, text) FROM PUBLIC, anon, authenticated;\nREVOKE ALL ON FUNCTION public.orders_apply_external_status(uuid, uuid, text, timestamptz) FROM PUBLIC, anon, authenticated;\nREVOKE ALL ON FUNCTION public.orders_external_status(uuid, uuid) FROM PUBLIC, anon, authenticated;\nGRANT EXECUTE ON FUNCTION public.orders_ingest_external(uuid, uuid, jsonb, text) TO service_role;\nGRANT EXECUTE ON FUNCTION public.orders_apply_external_status(uuid, uuid, text, timestamptz) TO service_role;\nGRANT EXECUTE ON FUNCTION public.orders_external_status(uuid, uuid) TO service_role;\n\nDO $$\nBEGIN\n IF EXISTS (SELECT 1 FROM pg_roles WHERE rolname = 'fayz_integration') THEN\n GRANT USAGE ON SCHEMA public TO fayz_integration;\n GRANT EXECUTE ON FUNCTION public.orders_ingest_external(uuid, uuid, jsonb, text) TO fayz_integration;\n GRANT EXECUTE ON FUNCTION public.orders_apply_external_status(uuid, uuid, text, timestamptz) TO fayz_integration;\n GRANT EXECUTE ON FUNCTION public.orders_external_status(uuid, uuid) TO fayz_integration;\n END IF;\nEND $$;\n";
|
|
2
|
+
export declare const MIGRATION_002_DELIVERED_IN_EACH_KINDS_WORDS = "-- ============================================================================\n-- 002 \u2014 \"delivered\" in each kind's words\n--\n-- The contract says `delivered` for the moment the food reached the customer,\n-- whatever the kind. This product says it three ways: a delivery is\n-- `delivered`, a takeout is `picked_up`, a dine-in is `served`. The two doors\n-- of 001 took the contract's word and refused it for a takeout; they now\n-- translate before validating, so the sink stays thirty lines and knows\n-- nothing about kinds.\n-- ============================================================================\n\nCREATE OR REPLACE FUNCTION public.orders_status_for_kind(p_kind text, p_status text)\nRETURNS text\nLANGUAGE sql IMMUTABLE\nSET search_path TO ''\nAS $$\n SELECT CASE\n WHEN p_status = 'delivered' AND p_kind = 'takeout' THEN 'picked_up'\n WHEN p_status = 'delivered' AND p_kind = 'dine_in' THEN 'served'\n ELSE p_status\n END;\n$$;\n\nCOMMENT ON FUNCTION public.orders_status_for_kind(text, text) IS\n 'The canonical delivered in this kind''s words: delivered, picked_up or served.';\n\nCREATE OR REPLACE FUNCTION public.orders_apply_external_status(\n p_tenant uuid,\n p_order uuid,\n p_status text,\n p_occurred_at timestamptz\n) RETURNS void\nLANGUAGE plpgsql SECURITY DEFINER\nSET search_path TO ''\nAS $$\nDECLARE\n v_kind text;\n v_status text;\nBEGIN\n SELECT o.kind INTO v_kind FROM public.orders o\n WHERE o.id = p_order AND o.tenant_id = p_tenant AND o.metadata ? 'externalRef';\n IF v_kind IS NULL THEN\n RAISE EXCEPTION 'orders_apply_external_status: order % is not an external order of tenant %', p_order, p_tenant\n USING ERRCODE = '22023';\n END IF;\n v_status := public.orders_status_for_kind(v_kind, p_status);\n IF NOT EXISTS (SELECT 1 FROM public.order_kinds k WHERE k.kind = v_kind AND v_status = ANY (k.statuses)) THEN\n RAISE EXCEPTION 'orders_apply_external_status: % is not a status of kind %', v_status, v_kind USING ERRCODE = '22023';\n END IF;\n\n UPDATE public.orders\n SET status = v_status,\n completed_at = CASE WHEN v_status IN ('completed', 'delivered', 'picked_up', 'served')\n THEN coalesce(p_occurred_at, now()) ELSE completed_at END,\n metadata = jsonb_set(metadata, '{external,lastStatusAt}', to_jsonb(coalesce(p_occurred_at, now())), true),\n updated_at = now()\n WHERE id = p_order;\nEND $$;\n\n-- The status door reads the kind's word back as the contract's.\nCREATE OR REPLACE FUNCTION public.orders_external_status(p_tenant uuid, p_order uuid)\nRETURNS jsonb\nLANGUAGE sql STABLE SECURITY DEFINER\nSET search_path TO ''\nAS $$\n SELECT jsonb_build_object(\n 'status', CASE o.status WHEN 'picked_up' THEN 'delivered' WHEN 'served' THEN 'delivered' ELSE o.status END,\n 'updatedAt', o.updated_at)\n FROM public.orders o\n WHERE o.id = p_order AND o.tenant_id = p_tenant AND o.metadata ? 'externalRef';\n$$;\n\nREVOKE ALL ON FUNCTION public.orders_status_for_kind(text, text) FROM PUBLIC;\nGRANT EXECUTE ON FUNCTION public.orders_status_for_kind(text, text) TO authenticated, service_role;\nDO $$\nBEGIN\n IF EXISTS (SELECT 1 FROM pg_roles WHERE rolname = 'fayz_integration') THEN\n GRANT EXECUTE ON FUNCTION public.orders_status_for_kind(text, text) TO fayz_integration;\n END IF;\nEND $$;\n";
|
|
2
3
|
export declare const MIGRATIONS: Array<{
|
|
3
4
|
id: string;
|
|
4
5
|
sql: string;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/migrations/index.ts"],"names":[],"mappings":"AAIA,eAAO,MAAM,iDAAiD
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/migrations/index.ts"],"names":[],"mappings":"AAIA,eAAO,MAAM,iDAAiD,wnTA0M7D,CAAA;AAED,eAAO,MAAM,2CAA2C,w2GAgFvD,CAAA;AAED,eAAO,MAAM,UAAU,EAAE,KAAK,CAAC;IAAE,EAAE,EAAE,MAAM,CAAC;IAAC,GAAG,EAAE,MAAM,CAAA;CAAE,CAGzD,CAAA"}
|
package/package.json
CHANGED
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
"status": "beta",
|
|
5
5
|
"dependencies": []
|
|
6
6
|
},
|
|
7
|
-
"version": "0.14.
|
|
7
|
+
"version": "0.14.1",
|
|
8
8
|
"description": "Fayz SDK — plugin-orders plugin",
|
|
9
9
|
"type": "module",
|
|
10
10
|
"sideEffects": false,
|
|
@@ -32,7 +32,7 @@
|
|
|
32
32
|
"sonner": "^2.0.7"
|
|
33
33
|
},
|
|
34
34
|
"dependencies": {
|
|
35
|
-
"@fayz-ai/admin": "^0.44.
|
|
35
|
+
"@fayz-ai/admin": "^0.44.1",
|
|
36
36
|
"@fayz-ai/contracts": "^0.1.0",
|
|
37
37
|
"@fayz-ai/core": "^0.44.0",
|
|
38
38
|
"@fayz-ai/plugin-financial": "^0.23.0",
|