@happyvertical/smrt-sales 0.57.9 → 0.57.11

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.
Files changed (29) hide show
  1. package/README.md +65 -0
  2. package/agents/commissions.md +2 -1
  3. package/dist/agreements.js +1 -1
  4. package/dist/chunks/{__smrt-register__-Czzs3Vof.js → __smrt-register__-uEzmP44h.js} +2 -2
  5. package/dist/chunks/__smrt-register__-uEzmP44h.js.map +1 -0
  6. package/dist/chunks/{commissions-Cip6QZ9Q.js → commissions-D5x24d4o.js} +443 -67
  7. package/dist/chunks/commissions-D5x24d4o.js.map +1 -0
  8. package/dist/chunks/{referrals-CYgrGkEo.js → referrals-BRdTkEFt.js} +2 -2
  9. package/dist/chunks/{referrals-CYgrGkEo.js.map → referrals-BRdTkEFt.js.map} +1 -1
  10. package/dist/commissions/models/Earner.d.ts +41 -1
  11. package/dist/commissions/models/Earner.d.ts.map +1 -1
  12. package/dist/commissions/models/index.d.ts +1 -1
  13. package/dist/commissions/models/index.d.ts.map +1 -1
  14. package/dist/commissions/services/CommissionPayoutService.d.ts.map +1 -1
  15. package/dist/commissions/services/WaterfallCalculationService.d.ts +33 -1
  16. package/dist/commissions/services/WaterfallCalculationService.d.ts.map +1 -1
  17. package/dist/commissions/services/index.d.ts +1 -1
  18. package/dist/commissions/services/index.d.ts.map +1 -1
  19. package/dist/commissions/types.d.ts +169 -1
  20. package/dist/commissions/types.d.ts.map +1 -1
  21. package/dist/commissions.js +3 -3
  22. package/dist/crm.js +1 -1
  23. package/dist/index.js +4 -4
  24. package/dist/manifest.json +93 -5
  25. package/dist/referrals.js +2 -2
  26. package/dist/smrt-knowledge.json +132 -6
  27. package/package.json +8 -8
  28. package/dist/chunks/__smrt-register__-Czzs3Vof.js.map +0 -1
  29. package/dist/chunks/commissions-Cip6QZ9Q.js.map +0 -1
@@ -2,7 +2,7 @@
2
2
  "version": "1.0.0",
3
3
  "timestamp": 0,
4
4
  "packageName": "@happyvertical/smrt-sales",
5
- "packageVersion": "0.57.9",
5
+ "packageVersion": "0.57.11",
6
6
  "objects": {
7
7
  "@happyvertical/smrt-sales:AgreementCollection": {
8
8
  "name": "agreementcollection",
@@ -4670,7 +4670,7 @@
4670
4670
  "collectionExportName": "EarnerCollectionCollection",
4671
4671
  "schema": {
4672
4672
  "tableName": "earners",
4673
- "ddl": "CREATE TABLE IF NOT EXISTS \"earners\" (\n \"id\" UUID PRIMARY KEY NOT NULL,\n \"slug\" TEXT NOT NULL,\n \"context\" TEXT NOT NULL DEFAULT '',\n \"created_at\" TIMESTAMP NOT NULL DEFAULT current_timestamp,\n \"updated_at\" TIMESTAMP NOT NULL DEFAULT current_timestamp,\n \"tenant_id\" UUID,\n \"profile_id\" UUID,\n \"display_name\" TEXT DEFAULT '',\n \"status\" TEXT DEFAULT 'pending',\n \"payout_method\" TEXT DEFAULT 'bank_transfer',\n \"payout_threshold_cents\" INTEGER DEFAULT 5000,\n \"payout_schedule_key\" TEXT DEFAULT 'manual',\n \"currency\" TEXT DEFAULT 'USD',\n \"metadata\" TEXT DEFAULT '{}'\n);",
4673
+ "ddl": "CREATE TABLE IF NOT EXISTS \"earners\" (\n \"id\" UUID PRIMARY KEY NOT NULL,\n \"slug\" TEXT NOT NULL,\n \"context\" TEXT NOT NULL DEFAULT '',\n \"created_at\" TIMESTAMP NOT NULL DEFAULT current_timestamp,\n \"updated_at\" TIMESTAMP NOT NULL DEFAULT current_timestamp,\n \"tenant_id\" UUID,\n \"kind\" TEXT DEFAULT 'person',\n \"profile_id\" UUID,\n \"display_name\" TEXT DEFAULT '',\n \"status\" TEXT DEFAULT 'pending',\n \"payout_method\" TEXT DEFAULT 'bank_transfer',\n \"payout_destination\" TEXT DEFAULT '{}',\n \"payout_threshold_cents\" INTEGER DEFAULT 5000,\n \"payout_schedule_key\" TEXT DEFAULT 'manual',\n \"currency\" TEXT DEFAULT 'USD',\n \"metadata\" TEXT DEFAULT '{}'\n);",
4674
4674
  "columns": {
4675
4675
  "id": {
4676
4676
  "type": "UUID",
@@ -4703,6 +4703,12 @@
4703
4703
  "notNull": false,
4704
4704
  "unique": false
4705
4705
  },
4706
+ "kind": {
4707
+ "type": "TEXT",
4708
+ "notNull": false,
4709
+ "unique": false,
4710
+ "default": "person"
4711
+ },
4706
4712
  "profile_id": {
4707
4713
  "type": "UUID",
4708
4714
  "referenceKind": "crossPackageRef",
@@ -4727,6 +4733,12 @@
4727
4733
  "unique": false,
4728
4734
  "default": "bank_transfer"
4729
4735
  },
4736
+ "payout_destination": {
4737
+ "type": "TEXT",
4738
+ "notNull": false,
4739
+ "unique": false,
4740
+ "default": "{}"
4741
+ },
4730
4742
  "payout_threshold_cents": {
4731
4743
  "type": "INTEGER",
4732
4744
  "notNull": false,
@@ -4777,7 +4789,7 @@
4777
4789
  ]
4778
4790
  }
4779
4791
  ],
4780
- "version": "b1c567fb"
4792
+ "version": "f9c840f9"
4781
4793
  },
4782
4794
  "importPath": "@happyvertical/smrt-sales/commissions"
4783
4795
  },
@@ -7248,6 +7260,11 @@
7248
7260
  }
7249
7261
  }
7250
7262
  },
7263
+ "kind": {
7264
+ "type": "text",
7265
+ "required": false,
7266
+ "default": "person"
7267
+ },
7251
7268
  "profileId": {
7252
7269
  "type": "crossPackageRef",
7253
7270
  "required": false,
@@ -7268,6 +7285,11 @@
7268
7285
  "required": false,
7269
7286
  "default": "bank_transfer"
7270
7287
  },
7288
+ "payoutDestination": {
7289
+ "type": "text",
7290
+ "required": false,
7291
+ "default": "{}"
7292
+ },
7271
7293
  "payoutThresholdCents": {
7272
7294
  "type": "integer",
7273
7295
  "required": false,
@@ -7335,6 +7357,60 @@
7335
7357
  "returnType": "void",
7336
7358
  "isStatic": false,
7337
7359
  "isPublic": true
7360
+ },
7361
+ "isOrganisation": {
7362
+ "name": "isOrganisation",
7363
+ "async": false,
7364
+ "parameters": [],
7365
+ "returnType": "boolean",
7366
+ "isStatic": false,
7367
+ "isPublic": true
7368
+ },
7369
+ "isTreasury": {
7370
+ "name": "isTreasury",
7371
+ "async": false,
7372
+ "parameters": [],
7373
+ "returnType": "boolean",
7374
+ "isStatic": false,
7375
+ "isPublic": true
7376
+ },
7377
+ "getPayoutDestination": {
7378
+ "name": "getPayoutDestination",
7379
+ "async": false,
7380
+ "parameters": [],
7381
+ "returnType": "PayoutDestination | null",
7382
+ "isStatic": false,
7383
+ "isPublic": true
7384
+ },
7385
+ "setPayoutDestination": {
7386
+ "name": "setPayoutDestination",
7387
+ "async": false,
7388
+ "parameters": [
7389
+ {
7390
+ "name": "destination",
7391
+ "type": "PayoutDestination | null",
7392
+ "optional": false,
7393
+ "unionBranches": [
7394
+ {
7395
+ "type": "PayoutDestination"
7396
+ },
7397
+ {
7398
+ "type": "null"
7399
+ }
7400
+ ]
7401
+ }
7402
+ ],
7403
+ "returnType": "void",
7404
+ "isStatic": false,
7405
+ "isPublic": true
7406
+ },
7407
+ "save": {
7408
+ "name": "save",
7409
+ "async": true,
7410
+ "parameters": [],
7411
+ "returnType": "Promise<this>",
7412
+ "isStatic": false,
7413
+ "isPublic": true
7338
7414
  }
7339
7415
  },
7340
7416
  "decoratorConfig": {
@@ -7370,7 +7446,7 @@
7370
7446
  "collectionExportName": "EarnerCollection",
7371
7447
  "schema": {
7372
7448
  "tableName": "earners",
7373
- "ddl": "CREATE TABLE IF NOT EXISTS \"earners\" (\n \"id\" UUID PRIMARY KEY NOT NULL,\n \"slug\" TEXT NOT NULL,\n \"context\" TEXT NOT NULL DEFAULT '',\n \"created_at\" TIMESTAMP NOT NULL DEFAULT current_timestamp,\n \"updated_at\" TIMESTAMP NOT NULL DEFAULT current_timestamp,\n \"tenant_id\" UUID,\n \"profile_id\" UUID,\n \"display_name\" TEXT DEFAULT '',\n \"status\" TEXT DEFAULT 'pending',\n \"payout_method\" TEXT DEFAULT 'bank_transfer',\n \"payout_threshold_cents\" INTEGER DEFAULT 5000,\n \"payout_schedule_key\" TEXT DEFAULT 'manual',\n \"currency\" TEXT DEFAULT 'USD',\n \"metadata\" TEXT DEFAULT '{}'\n);",
7449
+ "ddl": "CREATE TABLE IF NOT EXISTS \"earners\" (\n \"id\" UUID PRIMARY KEY NOT NULL,\n \"slug\" TEXT NOT NULL,\n \"context\" TEXT NOT NULL DEFAULT '',\n \"created_at\" TIMESTAMP NOT NULL DEFAULT current_timestamp,\n \"updated_at\" TIMESTAMP NOT NULL DEFAULT current_timestamp,\n \"tenant_id\" UUID,\n \"kind\" TEXT DEFAULT 'person',\n \"profile_id\" UUID,\n \"display_name\" TEXT DEFAULT '',\n \"status\" TEXT DEFAULT 'pending',\n \"payout_method\" TEXT DEFAULT 'bank_transfer',\n \"payout_destination\" TEXT DEFAULT '{}',\n \"payout_threshold_cents\" INTEGER DEFAULT 5000,\n \"payout_schedule_key\" TEXT DEFAULT 'manual',\n \"currency\" TEXT DEFAULT 'USD',\n \"metadata\" TEXT DEFAULT '{}'\n);",
7374
7450
  "columns": {
7375
7451
  "id": {
7376
7452
  "type": "UUID",
@@ -7403,6 +7479,12 @@
7403
7479
  "notNull": false,
7404
7480
  "unique": false
7405
7481
  },
7482
+ "kind": {
7483
+ "type": "TEXT",
7484
+ "notNull": false,
7485
+ "unique": false,
7486
+ "default": "person"
7487
+ },
7406
7488
  "profile_id": {
7407
7489
  "type": "UUID",
7408
7490
  "referenceKind": "crossPackageRef",
@@ -7427,6 +7509,12 @@
7427
7509
  "unique": false,
7428
7510
  "default": "bank_transfer"
7429
7511
  },
7512
+ "payout_destination": {
7513
+ "type": "TEXT",
7514
+ "notNull": false,
7515
+ "unique": false,
7516
+ "default": "{}"
7517
+ },
7430
7518
  "payout_threshold_cents": {
7431
7519
  "type": "INTEGER",
7432
7520
  "notNull": false,
@@ -7477,7 +7565,7 @@
7477
7565
  ]
7478
7566
  }
7479
7567
  ],
7480
- "version": "b1c567fb"
7568
+ "version": "f9c840f9"
7481
7569
  },
7482
7570
  "importPath": "@happyvertical/smrt-sales/commissions"
7483
7571
  },
package/dist/referrals.js CHANGED
@@ -1,3 +1,3 @@
1
- import "./chunks/__smrt-register__-Czzs3Vof.js";
2
- import { A as ReferralClickOperation, B as ATTRIBUTION_RESOLUTION_MODES, C as ReferralTouchCollection, D as ReferralCollection, E as assertHttpTargetUrl, F as validateAttributionPolicyTerms, G as REFERRAL_STATUSES, H as REFERRAL_AGREEMENT_STATUSES, I as ATTRIBUTION_CONFLICT_BEHAVIORS, J as AttributionExceptionCollection, K as REFERRAL_TOUCH_KINDS, L as ATTRIBUTION_CREDIT_MODES, M as ReferralAgreement, N as AttributionPolicyCollection, O as Referral, P as AttributionPolicy, R as ATTRIBUTION_EXCEPTION_STATUSES, S as generateReferralCode, T as ReferralLink, U as REFERRAL_LINK_STATUSES, V as REFERRAL_AGREEMENT_APPROVAL_MODES, W as REFERRAL_PROGRAM_STATUSES, Y as AttributionException, _ as REFERRAL_CODE_ALPHABET, a as ReferralAgreementExecutionService, b as ReferralClickValidationError, c as ReferrerCollection, d as ReferralTermSnapshot, f as ReferralProgramCollection, g as MAX_REFERRAL_CLICK_IDEMPOTENCY_KEY_BYTES, h as MAX_CODE_GENERATION_ATTEMPTS, i as REFERRAL_AGREEMENT_SOURCE_KIND, j as ReferralAgreementCollection, k as ReferralClickOperationCollection, l as Referrer, m as DEFAULT_REFERRAL_CLICK_EVIDENCE_MAX_BYTES, n as REFERRAL_TERMS_SNAPSHOT_KIND, o as AttributionService, p as ReferralProgram, q as REFERRER_STATUSES, r as ReferralCommissionService, s as QualifiedReferralOverrideError, t as ReferralQualificationService, u as ReferralTermSnapshotCollection, v as REFERRAL_CODE_LENGTH, w as ReferralTouch, x as ReferralLinkCollection, y as ReferralClickReplayConflictError, z as ATTRIBUTION_POLICY_STATUSES } from "./chunks/referrals-CYgrGkEo.js";
1
+ import "./chunks/__smrt-register__-uEzmP44h.js";
2
+ import { A as ReferralClickOperation, B as ATTRIBUTION_RESOLUTION_MODES, C as ReferralTouchCollection, D as ReferralCollection, E as assertHttpTargetUrl, F as validateAttributionPolicyTerms, G as REFERRAL_STATUSES, H as REFERRAL_AGREEMENT_STATUSES, I as ATTRIBUTION_CONFLICT_BEHAVIORS, J as AttributionExceptionCollection, K as REFERRAL_TOUCH_KINDS, L as ATTRIBUTION_CREDIT_MODES, M as ReferralAgreement, N as AttributionPolicyCollection, O as Referral, P as AttributionPolicy, R as ATTRIBUTION_EXCEPTION_STATUSES, S as generateReferralCode, T as ReferralLink, U as REFERRAL_LINK_STATUSES, V as REFERRAL_AGREEMENT_APPROVAL_MODES, W as REFERRAL_PROGRAM_STATUSES, Y as AttributionException, _ as REFERRAL_CODE_ALPHABET, a as ReferralAgreementExecutionService, b as ReferralClickValidationError, c as ReferrerCollection, d as ReferralTermSnapshot, f as ReferralProgramCollection, g as MAX_REFERRAL_CLICK_IDEMPOTENCY_KEY_BYTES, h as MAX_CODE_GENERATION_ATTEMPTS, i as REFERRAL_AGREEMENT_SOURCE_KIND, j as ReferralAgreementCollection, k as ReferralClickOperationCollection, l as Referrer, m as DEFAULT_REFERRAL_CLICK_EVIDENCE_MAX_BYTES, n as REFERRAL_TERMS_SNAPSHOT_KIND, o as AttributionService, p as ReferralProgram, q as REFERRER_STATUSES, r as ReferralCommissionService, s as QualifiedReferralOverrideError, t as ReferralQualificationService, u as ReferralTermSnapshotCollection, v as REFERRAL_CODE_LENGTH, w as ReferralTouch, x as ReferralLinkCollection, y as ReferralClickReplayConflictError, z as ATTRIBUTION_POLICY_STATUSES } from "./chunks/referrals-BRdTkEFt.js";
3
3
  export { ATTRIBUTION_CONFLICT_BEHAVIORS, ATTRIBUTION_CREDIT_MODES, ATTRIBUTION_EXCEPTION_STATUSES, ATTRIBUTION_POLICY_STATUSES, ATTRIBUTION_RESOLUTION_MODES, AttributionException, AttributionExceptionCollection, AttributionPolicy, AttributionPolicyCollection, AttributionService, DEFAULT_REFERRAL_CLICK_EVIDENCE_MAX_BYTES, MAX_CODE_GENERATION_ATTEMPTS, MAX_REFERRAL_CLICK_IDEMPOTENCY_KEY_BYTES, QualifiedReferralOverrideError, REFERRAL_AGREEMENT_APPROVAL_MODES, REFERRAL_AGREEMENT_SOURCE_KIND, REFERRAL_AGREEMENT_STATUSES, REFERRAL_CODE_ALPHABET, REFERRAL_CODE_LENGTH, REFERRAL_LINK_STATUSES, REFERRAL_PROGRAM_STATUSES, REFERRAL_STATUSES, REFERRAL_TERMS_SNAPSHOT_KIND, REFERRAL_TOUCH_KINDS, REFERRER_STATUSES, Referral, ReferralAgreement, ReferralAgreementCollection, ReferralAgreementExecutionService, ReferralClickOperation, ReferralClickOperationCollection, ReferralClickReplayConflictError, ReferralClickValidationError, ReferralCollection, ReferralCommissionService, ReferralLink, ReferralLinkCollection, ReferralProgram, ReferralProgramCollection, ReferralQualificationService, ReferralTermSnapshot, ReferralTermSnapshotCollection, ReferralTouch, ReferralTouchCollection, Referrer, ReferrerCollection, assertHttpTargetUrl, generateReferralCode, validateAttributionPolicyTerms };
@@ -3,15 +3,15 @@
3
3
  "sensitiveFieldsExcluded": true,
4
4
  "generatedAt": "1970-01-01T00:00:00.000Z",
5
5
  "packageName": "@happyvertical/smrt-sales",
6
- "packageVersion": "0.57.9",
6
+ "packageVersion": "0.57.11",
7
7
  "sourceManifestPath": "dist/manifest.json",
8
8
  "agentDocPath": "AGENTS.md",
9
9
  "sourceHashes": {
10
- "manifest": "28af5ca67cbfcffb5166fb3350b790f158cac6b035c37ed56b51da3746a38428",
11
- "packageJson": "1771e55b4acb9b0af79b8d3df41f56a7d9f49e015e7bff72f9a07039ba45e785",
10
+ "manifest": "ed4d64dff91b735232ab51a66907f074e338d1b10b8a94fd0ec5a2f705f08ecf",
11
+ "packageJson": "bee8a154154e9918a3d2bad81b47c8a6c4722d0b775a50fd127bc3a5ce983d10",
12
12
  "agents": "3100099d7cef116237e4deb4f83e32d836f3cc26e0f68a7dd55f14de942eda11",
13
13
  "moduleDoc:agents/agreements.md": "eb6ff147a0f60ee627d3337e8e9394d06fda955b4a4fff228537a9bd2803f886",
14
- "moduleDoc:agents/commissions.md": "b649ef658d41f9c27287281f6d3c9ee06dec2aede89da070a3ebfc7b4c289798",
14
+ "moduleDoc:agents/commissions.md": "ba06d133f2cbee14ea9a5f18b632f155e3484c4cd721ed5ac993436397198cf8",
15
15
  "moduleDoc:agents/crm.md": "b2c01c33e6ac559ba32073a089760d885095969d2ad749c30428c76e6aa2cc71",
16
16
  "moduleDoc:agents/referrals.md": "ef48c88e63155e3d08b9003f4643bc66ebb1942f1fb713252dc042ddcaad38e5",
17
17
  "moduleDoc:agents/svelte.md": "819f690b91cf93980f6f79e89da8d9629ca6e5c565494e3fd2ca3be7587ad6de"
@@ -4415,6 +4415,13 @@
4415
4415
  "required": false,
4416
4416
  "columnType": "UUID"
4417
4417
  },
4418
+ {
4419
+ "name": "kind",
4420
+ "type": "text",
4421
+ "required": false,
4422
+ "columnType": "TEXT",
4423
+ "default": "person"
4424
+ },
4418
4425
  {
4419
4426
  "name": "profileId",
4420
4427
  "type": "crossPackageRef",
@@ -4443,6 +4450,13 @@
4443
4450
  "columnType": "TEXT",
4444
4451
  "default": "bank_transfer"
4445
4452
  },
4453
+ {
4454
+ "name": "payoutDestination",
4455
+ "type": "text",
4456
+ "required": false,
4457
+ "columnType": "TEXT",
4458
+ "default": "{}"
4459
+ },
4446
4460
  {
4447
4461
  "name": "payoutThresholdCents",
4448
4462
  "type": "integer",
@@ -4483,20 +4497,33 @@
4483
4497
  ],
4484
4498
  "methods": [
4485
4499
  "getMetadata",
4500
+ "getPayoutDestination",
4486
4501
  "isActive",
4502
+ "isOrganisation",
4487
4503
  "isPending",
4488
4504
  "isSuspended",
4489
- "setMetadata"
4505
+ "isTreasury",
4506
+ "save",
4507
+ "setMetadata",
4508
+ "setPayoutDestination"
4490
4509
  ],
4491
4510
  "methodSignatures": [
4492
4511
  {
4493
4512
  "name": "getMetadata",
4494
4513
  "returns": "Record<string, unknown>"
4495
4514
  },
4515
+ {
4516
+ "name": "getPayoutDestination",
4517
+ "returns": "PayoutDestination | null"
4518
+ },
4496
4519
  {
4497
4520
  "name": "isActive",
4498
4521
  "returns": "boolean"
4499
4522
  },
4523
+ {
4524
+ "name": "isOrganisation",
4525
+ "returns": "boolean"
4526
+ },
4500
4527
  {
4501
4528
  "name": "isPending",
4502
4529
  "returns": "boolean"
@@ -4505,12 +4532,28 @@
4505
4532
  "name": "isSuspended",
4506
4533
  "returns": "boolean"
4507
4534
  },
4535
+ {
4536
+ "name": "isTreasury",
4537
+ "returns": "boolean"
4538
+ },
4539
+ {
4540
+ "name": "save",
4541
+ "async": true,
4542
+ "returns": "Promise<this>"
4543
+ },
4508
4544
  {
4509
4545
  "name": "setMetadata",
4510
4546
  "params": [
4511
4547
  "data: Record<string, unknown>"
4512
4548
  ],
4513
4549
  "returns": "void"
4550
+ },
4551
+ {
4552
+ "name": "setPayoutDestination",
4553
+ "params": [
4554
+ "destination: PayoutDestination | null"
4555
+ ],
4556
+ "returns": "void"
4514
4557
  }
4515
4558
  ],
4516
4559
  "tenant": {
@@ -4616,6 +4659,30 @@
4616
4659
  "operation": "setMetadata",
4617
4660
  "objectName": "@happyvertical/smrt-sales:Earner"
4618
4661
  },
4662
+ {
4663
+ "kind": "cli",
4664
+ "name": "earner_isOrganisation",
4665
+ "operation": "isOrganisation",
4666
+ "objectName": "@happyvertical/smrt-sales:Earner"
4667
+ },
4668
+ {
4669
+ "kind": "cli",
4670
+ "name": "earner_isTreasury",
4671
+ "operation": "isTreasury",
4672
+ "objectName": "@happyvertical/smrt-sales:Earner"
4673
+ },
4674
+ {
4675
+ "kind": "cli",
4676
+ "name": "earner_getPayoutDestination",
4677
+ "operation": "getPayoutDestination",
4678
+ "objectName": "@happyvertical/smrt-sales:Earner"
4679
+ },
4680
+ {
4681
+ "kind": "cli",
4682
+ "name": "earner_setPayoutDestination",
4683
+ "operation": "setPayoutDestination",
4684
+ "objectName": "@happyvertical/smrt-sales:Earner"
4685
+ },
4619
4686
  {
4620
4687
  "kind": "mcp",
4621
4688
  "name": "earner_list",
@@ -4643,6 +4710,13 @@
4643
4710
  "reason": "not listed in api.include",
4644
4711
  "objectName": "@happyvertical/smrt-sales:Earner"
4645
4712
  },
4713
+ {
4714
+ "kind": "api",
4715
+ "operation": "getPayoutDestination",
4716
+ "code": "not-included",
4717
+ "reason": "not listed in api.include",
4718
+ "objectName": "@happyvertical/smrt-sales:Earner"
4719
+ },
4646
4720
  {
4647
4721
  "kind": "api",
4648
4722
  "operation": "isActive",
@@ -4650,6 +4724,13 @@
4650
4724
  "reason": "not listed in api.include",
4651
4725
  "objectName": "@happyvertical/smrt-sales:Earner"
4652
4726
  },
4727
+ {
4728
+ "kind": "api",
4729
+ "operation": "isOrganisation",
4730
+ "code": "not-included",
4731
+ "reason": "not listed in api.include",
4732
+ "objectName": "@happyvertical/smrt-sales:Earner"
4733
+ },
4653
4734
  {
4654
4735
  "kind": "api",
4655
4736
  "operation": "isPending",
@@ -4664,12 +4745,33 @@
4664
4745
  "reason": "not listed in api.include",
4665
4746
  "objectName": "@happyvertical/smrt-sales:Earner"
4666
4747
  },
4748
+ {
4749
+ "kind": "api",
4750
+ "operation": "isTreasury",
4751
+ "code": "not-included",
4752
+ "reason": "not listed in api.include",
4753
+ "objectName": "@happyvertical/smrt-sales:Earner"
4754
+ },
4755
+ {
4756
+ "kind": "api",
4757
+ "operation": "save",
4758
+ "code": "lifecycle-method",
4759
+ "reason": "`save` is a framework lifecycle method, not a distinct operation",
4760
+ "objectName": "@happyvertical/smrt-sales:Earner"
4761
+ },
4667
4762
  {
4668
4763
  "kind": "api",
4669
4764
  "operation": "setMetadata",
4670
4765
  "code": "not-included",
4671
4766
  "reason": "not listed in api.include",
4672
4767
  "objectName": "@happyvertical/smrt-sales:Earner"
4768
+ },
4769
+ {
4770
+ "kind": "api",
4771
+ "operation": "setPayoutDestination",
4772
+ "code": "not-included",
4773
+ "reason": "not listed in api.include",
4774
+ "objectName": "@happyvertical/smrt-sales:Earner"
4673
4775
  }
4674
4776
  ],
4675
4777
  "relationshipFeatures": [
@@ -11820,6 +11922,30 @@
11820
11922
  "operation": "setMetadata",
11821
11923
  "objectName": "@happyvertical/smrt-sales:Earner"
11822
11924
  },
11925
+ {
11926
+ "kind": "cli",
11927
+ "name": "earner_isOrganisation",
11928
+ "operation": "isOrganisation",
11929
+ "objectName": "@happyvertical/smrt-sales:Earner"
11930
+ },
11931
+ {
11932
+ "kind": "cli",
11933
+ "name": "earner_isTreasury",
11934
+ "operation": "isTreasury",
11935
+ "objectName": "@happyvertical/smrt-sales:Earner"
11936
+ },
11937
+ {
11938
+ "kind": "cli",
11939
+ "name": "earner_getPayoutDestination",
11940
+ "operation": "getPayoutDestination",
11941
+ "objectName": "@happyvertical/smrt-sales:Earner"
11942
+ },
11943
+ {
11944
+ "kind": "cli",
11945
+ "name": "earner_setPayoutDestination",
11946
+ "operation": "setPayoutDestination",
11947
+ "objectName": "@happyvertical/smrt-sales:Earner"
11948
+ },
11823
11949
  {
11824
11950
  "kind": "mcp",
11825
11951
  "name": "earner_list",
@@ -13486,7 +13612,7 @@
13486
13612
  {
13487
13613
  "path": "agents/commissions.md",
13488
13614
  "module": "commissions",
13489
- "content": "# smrt-sales/commissions — neutral financial core\n\nPer-module semantics for `@happyvertical/smrt-sales/commissions`. Package orientation, the\ncross-module invariants (currency, tenancy, cross-package refs, roles vs.\nmoney), and the Gotchas that apply before editing anything live in\n[../AGENTS.md](../AGENTS.md) — read that first.\n\n- **Earner**: `profileId`, `status` (`pending|active|suspended`), `payoutMethod` (`bank_transfer|check|paypal|credit|other`), `payoutThresholdCents`, `payoutScheduleKey` (open string: `manual`, `monthly`, …), `currency`, `metadata`.\n- **EarnerSourceAttribution** (#1986): indexed external attribution mapping `(sourceKind, sourceId) → earnerId` with `status` (`active|inactive`) — the queryable replacement for stashing associations in `Earner.metadata`. Natural key `(tenant_id, source_kind, source_id)` (one mapping per key per tenant; a re-`create` upserts/re-points; the adapters' null-aware upsert dedups NULL-tenant keys too, but duplicate global rows minted outside the model layer remain representable). The attribution kind space is consumer-defined and need not match earning-event source kinds. `EarnerAttributionService` is the public API: `registerAttribution` (idempotent; reports `created`/`previousEarnerId`; also the documented metadata-migration backfill primitive — loop earners, register each association, verify, drop the metadata key) and `resolveActiveEarnerBySource`/`resolveActiveEarnersBySources` (single/batched; 2 queries bounded by the REQUESTED ids, never a scan of active earners; fail-closed typed reasons `no_mapping|mapping_inactive|ambiguous_mapping|earner_not_found|earner_not_active` — more than one ACTIVE row for a key refuses rather than guessing). In tenant context mappings resolve within that tenant only; without context, lookups span all rows and cross-tenant duplicates surface as `ambiguous_mapping`.\n- **CommissionPlan**: versioned calculation terms. Natural key `(tenant_id, plan_key, version)` (per-tenant keys; the adapters' null-aware upsert dedups NULL-tenant keys through the model layer, though duplicate global rows minted via raw SQL remain representable); `status` (`draft|active|superseded|retired`); `effectiveFrom`. Components are a JSON-string array (`getComponents()`): each component has `key`, `trigger` (earning-event kind or `*`), `basis` (`fixed|gross|net|margin|custom`), `rate` or `fixedAmountCents`, `recurrence` (`one_time` or `recurring` with optional `maxOccurrences`/`windowMonths`). **Immutable once active** — amendments create a new row with `version + 1`; prior versions are never rewritten. `latestActiveByKey(key, at?)` resolves the highest active version **already in effect at `at`** — a future-dated amendment can be activated ahead of time without governing earlier qualifications (same rule on `AttributionPolicyCollection`).\n- **EarningEvent**: immutable commercial-event evidence (`conversion`, `agreement_execution`, `invoice_payment`, `collected_revenue`, `recognized_margin`, or any extensible kind). Carries `sourceKind`/`sourceId` (generic earning source), `grossAmountCents`/`netAmountCents`/`marginCents`, `currency`, `occurredAt`, and a `dedupeKey` natural key for idempotent ingestion. No update/delete surface, plus a save-time immutability guard: hydrated edits, blind id overwrites, and fresh creates onto an existing dedupe key all throw (the upsert would rotate the row id and orphan referencing Commissions) — idempotent ingestion goes through `getOrCreateByDedupeKey()`.\n- **Commission**: one earning record per earner per plan component per event occurrence. Integer-cents amounts (`baseAmountCents`, `amountCents`), decimal `rate`, `basis`, snapshot references (`planKey`/`planVersion`, generic `termsSnapshotKind`/`termsSnapshotId`), a JSON `calculationTrace` sufficient to reproduce the amount, split support (`splitGroupId`, `shareFraction`), and a `dedupeKey` for idempotent creation. Lifecycle `pending → earned → approved → payable → paid`, enforced by a save-time transition guard with an authoritative prior-status re-read (commerce pattern).\n- **CommissionAdjustment**: append-only corrections (`refund|credit|chargeback|dispute|correction`), signed `amountCents`, required `reason`, immutable once created. `CommissionAdjustmentService.createAdjustment()` is the tenant-required idempotent creation door: callers provide a stable operation UUID; the database creates exactly one row, exact retries return it, and changed tenant/commission/earner/kind/cents/currency/reason/operator/metadata fail with a typed replay conflict. Parent Commission plus denormalized tenant/Earner/currency and operator UUID are validated before insert. The financial row deliberately exposes no operation field. A private, required-tenant `CommissionAdjustmentOperation` table uses the globally unique operation UUID as its primary id, maps it to the adjustment UUID, and is inserted in the same transaction as the adjustment. Claims tolerate either primary-key or tenant/slug/context uniqueness arbitration without aborting the transaction; only the tenant-scoped operation UUID lookup and immutable intent comparison authorize a replay. Existing adjustment schemas and legacy direct creates remain unchanged. Earned/paid Commissions are never rewritten — adjustments append to them.\n- **Payable balance**: computed, not stored — `CommissionBalanceService` sums payable Commissions plus unsettled Adjustments per Earner/currency.\n- **CommissionPayout**: settlement batch per Earner. `pending → approved → processing → completed | failed` (`resetFromFailed()` is the only exit from failed), plus the terminal operator decline `rejected` reachable from pending/approved (#1987 — reject through `transitionPayoutForSource`, which also RELEASES the batch's membership so the rows settle through a future batch; `reject()` on the model mutates status only). Settles rows via the collections' **conditional `claimForPayout`** (model-layer get/save/re-read — tenancy- and dialect-safe; a row owned by another batch is never re-claimed) and stores totals recomputed from the VERIFIED claimed membership; idempotent via `idempotencyKey` natural key (default `${earnerId}:${currency}:${YYYY-MM-DD}`) — a clean replay touches nothing, while a pending payout whose totals disagree with its stamped rows (interrupted claim pass) is repaired on replay (the claim pass re-checks the payout is still pending first). Each batch/repair pass also derives the payout's **single-source stamp** (`sourceKind`/`sourceId`, indexed): set when every claimed commission — and every claimed adjustment through its parent commission — shares exactly one non-empty source; empty for mixed/unknown/empty membership. `completePayout` flips member commissions to `paid` BEFORE the terminal transition, so a mid-loop failure stays retryable. Enforces `totalAmountCents = commissionTotalCents + adjustmentTotalCents` at save; retains `paymentReference`/`providerRef`; optional `invoiceId` cross-package string ref to a commerce Invoice. Generated surface is read-only on ALL doors (api/mcp/cli `list`/`get`) — writes go through `CommissionPayoutService`. `createPayoutBatch` settles the whole earner+currency by default, or a **scoped** subset: pass `sourceKind`+`sourceId` to settle just one earning source (e.g. one ad network), or `commissionIds` for an explicit set (each validated payable+unsettled+earner+currency; requires its own `idempotencyKey`) — adjustments are narrowed to the same scope, and the default idempotency key folds the source in. Scoping is how concurrent per-source batches settle safely: batches scoped to different sources (or disjoint `commissionIds`) gather **disjoint** row sets and never contend. The collection layer has no cross-row transaction, so OVERLAPPING concurrent scopes (a source batch + the earner-wide batch, intersecting id sets, or a batch replay racing a lifecycle transition of the same payout) must be serialized by the caller — `claimForPayout` still won't double-own a row, but a shared commission and its adjustment could split across the two batches.\n- **Source-scoped payout history** (#1985): `CommissionPayoutService.getSourcePayoutHistory({ sourceKind, sourceId, limit?, offset? })` — one page of the payouts belonging to ONE earning source, newest first (`created_at DESC, id DESC`), offset contract (`nextOffset` advances by the scanned count; `null` when exhausted). Candidates come from the indexed stamp, so work is bounded by page size (a sparse source never scans the global history); each page is re-verified against actual membership in three batched queries (no per-payout N+1) — every member commission and every adjustment's parent must carry exactly the requested source and agree on earner/currency/tenant; unprovable rows (mixed-source, missing parents, memberless artifacts, released/rejected batches) are excluded fail-closed and reported in `excluded` with reasons. Adjustment-only payouts prove ownership through parent commissions and list normally. Payouts minted before the stamp existed are invisible until backfilled with `restampPayoutSource(payoutId)` (idempotent, any status).\n- **Source-authorized lifecycle transitions** (#1987): `CommissionPayoutService.transitionPayoutForSource({ payoutId, sourceKind, sourceId, action, expectedStatus?, paymentReference?, reason? })` — atomic alternative to load-verify-transition. Actions `approve | mark_processing | complete | fail | reject`. Everything runs on one transaction database: the payout row is locked (PostgreSQL `SELECT … FOR UPDATE`, replica-safe; single-connection engines serialize transitions per database in-process), membership is re-verified under the lock (every commission and every adjustment through its parent must carry exactly the requested source and matching earner/currency/tenant — else typed fail-closed refusals with no payout/member data), and only authorized calls receive status-derived `already_applied` or `status_conflict` outcomes. Totals are then recomputed under the lock (drift refuses the money-forward actions with `totals_drift`; repair = `createPayoutBatch` replay while pending; `fail`/`reject` proceed despite drift — reject IS the remedy), and the transition plus member writes commit or roll back together (`complete` flips members to paid in the same transaction; `reject` releases membership then declines). Outcomes: `transitioned`, `already_applied` (idempotent echo — terminal completion NEVER rewrites `paymentReference`/`providerRef`/`paidAt`), or `refused` with `{ reason, detail }`. Concurrent duplicate calls for actions that retain membership resolve as one `transitioned` + `already_applied` echoes; `reject` releases its membership evidence, so serialized replays fail `membership_empty` rather than expose rejected state. `expectedStatus` adds optimistic-concurrency strictness after authorization. Memberless batch artifacts cannot pass the source-authorized door (`membership_empty`) — they stay operator-level.\n"
13615
+ "content": "# smrt-sales/commissions — neutral financial core\n\nPer-module semantics for `@happyvertical/smrt-sales/commissions`. Package orientation, the\ncross-module invariants (currency, tenancy, cross-package refs, roles vs.\nmoney), and the Gotchas that apply before editing anything live in\n[../AGENTS.md](../AGENTS.md) — read that first.\n\n- **Earner**: `kind` (`person|organisation|treasury`, default `person`; organisation and treasury earners need no person Profile, so `profileId` stays empty unless the host links an organisation Profile), `profileId`, `status` (`pending|active|suspended`), `payoutMethod` (`bank_transfer|check|paypal|credit|other|stripe_connect|crypto`), `payoutDestination` (typed JSON descriptor via `getPayoutDestination()`/`setPayoutDestination()`: Stripe Connect `connectedAccountId` (`acct_…`) or crypto `network`/`asset`/`address`/`memo?`; unknown fields are refused so secrets cannot ride along; its `method` must equal `payoutMethod`, and an `active` Stripe Connect/crypto earner must have one — enforced at save; `createPayoutBatch` refuses to create a NEW Stripe Connect/crypto batch, including a `payoutMethod` override, unless the earner's destination has that method; an existing idempotency key still replays/repairs its batch after the preference changes), `payoutThresholdCents`, `payoutScheduleKey` (open string: `manual`, `monthly`, …), `currency`, `metadata`. Descriptors only: nothing in this package calls a payment provider. Map an organisation earner to the host's own organisation id through `EarnerSourceAttribution`.\n- **EarnerSourceAttribution** (#1986): indexed external attribution mapping `(sourceKind, sourceId) → earnerId` with `status` (`active|inactive`) — the queryable replacement for stashing associations in `Earner.metadata`. Natural key `(tenant_id, source_kind, source_id)` (one mapping per key per tenant; a re-`create` upserts/re-points; the adapters' null-aware upsert dedups NULL-tenant keys too, but duplicate global rows minted outside the model layer remain representable). The attribution kind space is consumer-defined and need not match earning-event source kinds. `EarnerAttributionService` is the public API: `registerAttribution` (idempotent; reports `created`/`previousEarnerId`; also the documented metadata-migration backfill primitive — loop earners, register each association, verify, drop the metadata key) and `resolveActiveEarnerBySource`/`resolveActiveEarnersBySources` (single/batched; 2 queries bounded by the REQUESTED ids, never a scan of active earners; fail-closed typed reasons `no_mapping|mapping_inactive|ambiguous_mapping|earner_not_found|earner_not_active` — more than one ACTIVE row for a key refuses rather than guessing). In tenant context mappings resolve within that tenant only; without context, lookups span all rows and cross-tenant duplicates surface as `ambiguous_mapping`.\n- **CommissionPlan**: versioned calculation terms. Natural key `(tenant_id, plan_key, version)` (per-tenant keys; the adapters' null-aware upsert dedups NULL-tenant keys through the model layer, though duplicate global rows minted via raw SQL remain representable); `status` (`draft|active|superseded|retired`); `effectiveFrom`. Components are a JSON-string array (`getComponents()`): each component has `key`, `trigger` (earning-event kind or `*`), `basis` (`fixed|gross|net|margin|custom`), `rate` or `fixedAmountCents`, `recurrence` (`one_time` or `recurring` with optional `maxOccurrences`/`windowMonths`). **Immutable once active** — amendments create a new row with `version + 1`; prior versions are never rewritten. `latestActiveByKey(key, at?)` resolves the highest active version **already in effect at `at`** — a future-dated amendment can be activated ahead of time without governing earlier qualifications (same rule on `AttributionPolicyCollection`).\n- **Waterfall pool steps** (#3834): a v2 step with `pool: { metric?, residual?, zeroWeight? }` splits its amount across the members of the pool its `recipient` descriptor resolves to (through the normal recipient registry). Membership comes from an injected `WaterfallPoolMembershipResolver`, weights from an injected `MetricProvider` (omitted `metric` ⇒ equal weights, no provider call). `calculateWaterfall` stays synchronous and never calls providers: `prepareWaterfallPools()` (async) resolves pools at the authoritative event and the result is passed as `pools`; `calculateWaterfallWithPools()` does both. Weights must be non-negative safe integers (hosts scale fractional metrics); members are canonicalized to `(kind, id)` order and duplicates, non-member metric rows, and repeated rows are refused; members the provider omits weigh zero. Allocation is exact BigInt `floor(amount × weight ÷ total)`; the residual goes to the remainder sink (default) or, with `residual: 'largest_remainder'`, one unit at a time by largest fractional remainder, then larger weight, then `kind`, then `id` (UTF-16 code-unit order). An empty pool, or an all-zero pool under the default `zeroWeight: 'remainder_sink'`, sends the whole step to the sink; `zeroWeight: 'equal'` splits equally instead. The trace freezes `frozenInputs.poolsByStep` (pool identity, metric, ISO period, members, weights) so replay never re-reads metrics; frozen and prepared snapshots must match their frozen recipient and the step's declared `metric` (`null` for equal weights), and a prepared snapshot (or `null` unresolved marker) must equal what the step's descriptor resolves to at calculation, so pools prepared for another plan are refused. Replay trusts the evidence it is given — like frozen `recipientByStep`, an internally consistent edit to stored evidence is undetectable without keyed integrity, so hosts store it immutably. Pool-free plans emit no `poolsByStep` key or step `pool` field, so their traces are unchanged. A pool step cannot be the application-fee offset recipient. Each member trace carries `shareFraction` for hosts that persist member allocations as Commissions under one `splitGroupId` (for example `${earningEventId}:${stepKey}`).\n- **EarningEvent**: immutable commercial-event evidence (`conversion`, `agreement_execution`, `invoice_payment`, `collected_revenue`, `recognized_margin`, or any extensible kind). Carries `sourceKind`/`sourceId` (generic earning source), `grossAmountCents`/`netAmountCents`/`marginCents`, `currency`, `occurredAt`, and a `dedupeKey` natural key for idempotent ingestion. No update/delete surface, plus a save-time immutability guard: hydrated edits, blind id overwrites, and fresh creates onto an existing dedupe key all throw (the upsert would rotate the row id and orphan referencing Commissions) — idempotent ingestion goes through `getOrCreateByDedupeKey()`.\n- **Commission**: one earning record per earner per plan component per event occurrence. Integer-cents amounts (`baseAmountCents`, `amountCents`), decimal `rate`, `basis`, snapshot references (`planKey`/`planVersion`, generic `termsSnapshotKind`/`termsSnapshotId`), a JSON `calculationTrace` sufficient to reproduce the amount, split support (`splitGroupId`, `shareFraction`), and a `dedupeKey` for idempotent creation. Lifecycle `pending → earned → approved → payable → paid`, enforced by a save-time transition guard with an authoritative prior-status re-read (commerce pattern).\n- **CommissionAdjustment**: append-only corrections (`refund|credit|chargeback|dispute|correction`), signed `amountCents`, required `reason`, immutable once created. `CommissionAdjustmentService.createAdjustment()` is the tenant-required idempotent creation door: callers provide a stable operation UUID; the database creates exactly one row, exact retries return it, and changed tenant/commission/earner/kind/cents/currency/reason/operator/metadata fail with a typed replay conflict. Parent Commission plus denormalized tenant/Earner/currency and operator UUID are validated before insert. The financial row deliberately exposes no operation field. A private, required-tenant `CommissionAdjustmentOperation` table uses the globally unique operation UUID as its primary id, maps it to the adjustment UUID, and is inserted in the same transaction as the adjustment. Claims tolerate either primary-key or tenant/slug/context uniqueness arbitration without aborting the transaction; only the tenant-scoped operation UUID lookup and immutable intent comparison authorize a replay. Existing adjustment schemas and legacy direct creates remain unchanged. Earned/paid Commissions are never rewritten — adjustments append to them.\n- **Payable balance**: computed, not stored — `CommissionBalanceService` sums payable Commissions plus unsettled Adjustments per Earner/currency.\n- **CommissionPayout**: settlement batch per Earner. `pending → approved → processing → completed | failed` (`resetFromFailed()` is the only exit from failed), plus the terminal operator decline `rejected` reachable from pending/approved (#1987 — reject through `transitionPayoutForSource`, which also RELEASES the batch's membership so the rows settle through a future batch; `reject()` on the model mutates status only). Settles rows via the collections' **conditional `claimForPayout`** (model-layer get/save/re-read — tenancy- and dialect-safe; a row owned by another batch is never re-claimed) and stores totals recomputed from the VERIFIED claimed membership; idempotent via `idempotencyKey` natural key (default `${earnerId}:${currency}:${YYYY-MM-DD}`) — a clean replay touches nothing, while a pending payout whose totals disagree with its stamped rows (interrupted claim pass) is repaired on replay (the claim pass re-checks the payout is still pending first). Each batch/repair pass also derives the payout's **single-source stamp** (`sourceKind`/`sourceId`, indexed): set when every claimed commission — and every claimed adjustment through its parent commission — shares exactly one non-empty source; empty for mixed/unknown/empty membership. `completePayout` flips member commissions to `paid` BEFORE the terminal transition, so a mid-loop failure stays retryable. Enforces `totalAmountCents = commissionTotalCents + adjustmentTotalCents` at save; retains `paymentReference`/`providerRef`; optional `invoiceId` cross-package string ref to a commerce Invoice. Generated surface is read-only on ALL doors (api/mcp/cli `list`/`get`) — writes go through `CommissionPayoutService`. `createPayoutBatch` settles the whole earner+currency by default, or a **scoped** subset: pass `sourceKind`+`sourceId` to settle just one earning source (e.g. one ad network), or `commissionIds` for an explicit set (each validated payable+unsettled+earner+currency; requires its own `idempotencyKey`) — adjustments are narrowed to the same scope, and the default idempotency key folds the source in. Scoping is how concurrent per-source batches settle safely: batches scoped to different sources (or disjoint `commissionIds`) gather **disjoint** row sets and never contend. The collection layer has no cross-row transaction, so OVERLAPPING concurrent scopes (a source batch + the earner-wide batch, intersecting id sets, or a batch replay racing a lifecycle transition of the same payout) must be serialized by the caller — `claimForPayout` still won't double-own a row, but a shared commission and its adjustment could split across the two batches.\n- **Source-scoped payout history** (#1985): `CommissionPayoutService.getSourcePayoutHistory({ sourceKind, sourceId, limit?, offset? })` — one page of the payouts belonging to ONE earning source, newest first (`created_at DESC, id DESC`), offset contract (`nextOffset` advances by the scanned count; `null` when exhausted). Candidates come from the indexed stamp, so work is bounded by page size (a sparse source never scans the global history); each page is re-verified against actual membership in three batched queries (no per-payout N+1) — every member commission and every adjustment's parent must carry exactly the requested source and agree on earner/currency/tenant; unprovable rows (mixed-source, missing parents, memberless artifacts, released/rejected batches) are excluded fail-closed and reported in `excluded` with reasons. Adjustment-only payouts prove ownership through parent commissions and list normally. Payouts minted before the stamp existed are invisible until backfilled with `restampPayoutSource(payoutId)` (idempotent, any status).\n- **Source-authorized lifecycle transitions** (#1987): `CommissionPayoutService.transitionPayoutForSource({ payoutId, sourceKind, sourceId, action, expectedStatus?, paymentReference?, reason? })` — atomic alternative to load-verify-transition. Actions `approve | mark_processing | complete | fail | reject`. Everything runs on one transaction database: the payout row is locked (PostgreSQL `SELECT … FOR UPDATE`, replica-safe; single-connection engines serialize transitions per database in-process), membership is re-verified under the lock (every commission and every adjustment through its parent must carry exactly the requested source and matching earner/currency/tenant — else typed fail-closed refusals with no payout/member data), and only authorized calls receive status-derived `already_applied` or `status_conflict` outcomes. Totals are then recomputed under the lock (drift refuses the money-forward actions with `totals_drift`; repair = `createPayoutBatch` replay while pending; `fail`/`reject` proceed despite drift — reject IS the remedy), and the transition plus member writes commit or roll back together (`complete` flips members to paid in the same transaction; `reject` releases membership then declines). Outcomes: `transitioned`, `already_applied` (idempotent echo — terminal completion NEVER rewrites `paymentReference`/`providerRef`/`paidAt`), or `refused` with `{ reason, detail }`. Concurrent duplicate calls for actions that retain membership resolve as one `transitioned` + `already_applied` echoes; `reject` releases its membership evidence, so serialized replays fail `membership_empty` rather than expose rejected state. `expectedStatus` adds optimistic-concurrency strictness after authorization. Memberless batch artifacts cannot pass the source-authorized door (`membership_empty`) — they stay operator-level.\n"
13490
13616
  },
13491
13617
  {
13492
13618
  "path": "agents/crm.md",
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@happyvertical/smrt-sales",
3
- "version": "0.57.9",
3
+ "version": "0.57.11",
4
4
  "smrtJsdoc": "strict",
5
5
  "description": "Modular sales for the SMRT framework: agreement execution, CRM, referral intake, neutral commissions, and reusable Svelte surfaces",
6
6
  "type": "module",
@@ -42,18 +42,18 @@
42
42
  "./manifest.json": "./dist/manifest.json"
43
43
  },
44
44
  "dependencies": {
45
- "@happyvertical/signatures": "^0.105.2",
46
- "@happyvertical/smrt-assets": "0.57.9",
47
- "@happyvertical/smrt-core": "0.57.9",
48
- "@happyvertical/smrt-tenancy": "0.57.9",
49
- "@happyvertical/smrt-ui": "0.57.9",
50
- "@happyvertical/sql": "^0.105.2"
45
+ "@happyvertical/signatures": "^0.105.5",
46
+ "@happyvertical/smrt-assets": "0.57.11",
47
+ "@happyvertical/smrt-core": "0.57.11",
48
+ "@happyvertical/smrt-tenancy": "0.57.11",
49
+ "@happyvertical/smrt-ui": "0.57.11",
50
+ "@happyvertical/sql": "^0.105.5"
51
51
  },
52
52
  "peerDependencies": {
53
53
  "svelte": "^5.57.0"
54
54
  },
55
55
  "devDependencies": {
56
- "@happyvertical/smrt-vitest": "0.57.9",
56
+ "@happyvertical/smrt-vitest": "0.57.11",
57
57
  "@sveltejs/package": "^2.5.8",
58
58
  "@sveltejs/vite-plugin-svelte": "^7.3.0",
59
59
  "@types/node": "24.13.6",