@unifiedcommerce/plugin-marketplace 0.0.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.
Files changed (122) hide show
  1. package/README.md +479 -0
  2. package/dist/analytics-models.d.ts +13 -0
  3. package/dist/analytics-models.d.ts.map +1 -0
  4. package/dist/analytics-models.js +69 -0
  5. package/dist/hooks.d.ts +4 -0
  6. package/dist/hooks.d.ts.map +1 -0
  7. package/dist/hooks.js +187 -0
  8. package/dist/index.d.ts +4 -0
  9. package/dist/index.d.ts.map +1 -0
  10. package/dist/index.js +105 -0
  11. package/dist/mcp-tools.d.ts +21 -0
  12. package/dist/mcp-tools.d.ts.map +1 -0
  13. package/dist/mcp-tools.js +183 -0
  14. package/dist/routes/b2b.d.ts +9 -0
  15. package/dist/routes/b2b.d.ts.map +1 -0
  16. package/dist/routes/b2b.js +156 -0
  17. package/dist/routes/commission.d.ts +6 -0
  18. package/dist/routes/commission.d.ts.map +1 -0
  19. package/dist/routes/commission.js +85 -0
  20. package/dist/routes/disputes-returns-reviews.d.ts +10 -0
  21. package/dist/routes/disputes-returns-reviews.d.ts.map +1 -0
  22. package/dist/routes/disputes-returns-reviews.js +179 -0
  23. package/dist/routes/payouts.d.ts +6 -0
  24. package/dist/routes/payouts.d.ts.map +1 -0
  25. package/dist/routes/payouts.js +40 -0
  26. package/dist/routes/sub-orders.d.ts +6 -0
  27. package/dist/routes/sub-orders.d.ts.map +1 -0
  28. package/dist/routes/sub-orders.js +44 -0
  29. package/dist/routes/util.d.ts +23 -0
  30. package/dist/routes/util.d.ts.map +1 -0
  31. package/dist/routes/util.js +41 -0
  32. package/dist/routes/vendor-portal.d.ts +14 -0
  33. package/dist/routes/vendor-portal.d.ts.map +1 -0
  34. package/dist/routes/vendor-portal.js +255 -0
  35. package/dist/routes/vendors.d.ts +11 -0
  36. package/dist/routes/vendors.d.ts.map +1 -0
  37. package/dist/routes/vendors.js +185 -0
  38. package/dist/schema.d.ts +3255 -0
  39. package/dist/schema.d.ts.map +1 -0
  40. package/dist/schema.js +225 -0
  41. package/dist/schemas/b2b.d.ts +1009 -0
  42. package/dist/schemas/b2b.d.ts.map +1 -0
  43. package/dist/schemas/b2b.js +208 -0
  44. package/dist/schemas/commission.d.ts +532 -0
  45. package/dist/schemas/commission.d.ts.map +1 -0
  46. package/dist/schemas/commission.js +113 -0
  47. package/dist/schemas/disputes-returns-reviews.d.ts +1405 -0
  48. package/dist/schemas/disputes-returns-reviews.d.ts.map +1 -0
  49. package/dist/schemas/disputes-returns-reviews.js +270 -0
  50. package/dist/schemas/payouts.d.ts +375 -0
  51. package/dist/schemas/payouts.d.ts.map +1 -0
  52. package/dist/schemas/payouts.js +78 -0
  53. package/dist/schemas/sub-orders.d.ts +303 -0
  54. package/dist/schemas/sub-orders.d.ts.map +1 -0
  55. package/dist/schemas/sub-orders.js +67 -0
  56. package/dist/schemas/vendor-portal.d.ts +1785 -0
  57. package/dist/schemas/vendor-portal.d.ts.map +1 -0
  58. package/dist/schemas/vendor-portal.js +294 -0
  59. package/dist/schemas/vendors.d.ts +1348 -0
  60. package/dist/schemas/vendors.d.ts.map +1 -0
  61. package/dist/schemas/vendors.js +245 -0
  62. package/dist/services/commission.d.ts +81 -0
  63. package/dist/services/commission.d.ts.map +1 -0
  64. package/dist/services/commission.js +98 -0
  65. package/dist/services/contract-price.d.ts +64 -0
  66. package/dist/services/contract-price.d.ts.map +1 -0
  67. package/dist/services/contract-price.js +57 -0
  68. package/dist/services/dispute.d.ts +156 -0
  69. package/dist/services/dispute.d.ts.map +1 -0
  70. package/dist/services/dispute.js +77 -0
  71. package/dist/services/payout.d.ts +126 -0
  72. package/dist/services/payout.d.ts.map +1 -0
  73. package/dist/services/payout.js +130 -0
  74. package/dist/services/return.d.ts +181 -0
  75. package/dist/services/return.d.ts.map +1 -0
  76. package/dist/services/return.js +80 -0
  77. package/dist/services/review.d.ts +70 -0
  78. package/dist/services/review.d.ts.map +1 -0
  79. package/dist/services/review.js +60 -0
  80. package/dist/services/rfq.d.ts +122 -0
  81. package/dist/services/rfq.d.ts.map +1 -0
  82. package/dist/services/rfq.js +60 -0
  83. package/dist/services/sub-order.d.ts +336 -0
  84. package/dist/services/sub-order.d.ts.map +1 -0
  85. package/dist/services/sub-order.js +121 -0
  86. package/dist/services/vendor.d.ts +528 -0
  87. package/dist/services/vendor.d.ts.map +1 -0
  88. package/dist/services/vendor.js +119 -0
  89. package/dist/types.d.ts +67 -0
  90. package/dist/types.d.ts.map +1 -0
  91. package/dist/types.js +13 -0
  92. package/package.json +43 -0
  93. package/src/analytics-models.ts +75 -0
  94. package/src/hooks.ts +215 -0
  95. package/src/index.ts +124 -0
  96. package/src/mcp-tools.ts +210 -0
  97. package/src/routes/b2b.ts +179 -0
  98. package/src/routes/commission.ts +95 -0
  99. package/src/routes/disputes-returns-reviews.ts +209 -0
  100. package/src/routes/payouts.ts +49 -0
  101. package/src/routes/sub-orders.ts +54 -0
  102. package/src/routes/util.ts +42 -0
  103. package/src/routes/vendor-portal.ts +277 -0
  104. package/src/routes/vendors.ts +201 -0
  105. package/src/schema.ts +260 -0
  106. package/src/schemas/b2b.ts +238 -0
  107. package/src/schemas/commission.ts +129 -0
  108. package/src/schemas/disputes-returns-reviews.ts +311 -0
  109. package/src/schemas/payouts.ts +90 -0
  110. package/src/schemas/sub-orders.ts +77 -0
  111. package/src/schemas/vendor-portal.ts +344 -0
  112. package/src/schemas/vendors.ts +281 -0
  113. package/src/services/commission.ts +120 -0
  114. package/src/services/contract-price.ts +80 -0
  115. package/src/services/dispute.ts +92 -0
  116. package/src/services/payout.ts +154 -0
  117. package/src/services/return.ts +92 -0
  118. package/src/services/review.ts +76 -0
  119. package/src/services/rfq.ts +82 -0
  120. package/src/services/sub-order.ts +136 -0
  121. package/src/services/vendor.ts +151 -0
  122. package/src/types.ts +164 -0
package/dist/hooks.js ADDED
@@ -0,0 +1,187 @@
1
+ import { eq } from "drizzle-orm";
2
+ import { vendors, vendorEntities, vendorSubOrders } from "./schema";
3
+ import { CommissionService } from "./services/commission";
4
+ import { PayoutService } from "./services/payout";
5
+ function getDbFromHookArgs(args) {
6
+ const a = args;
7
+ return a.context.services.database.db;
8
+ }
9
+ export function buildHooks(options) {
10
+ return [
11
+ // ─── catalog.beforeCreate ──────────────────────────────────────────────
12
+ {
13
+ key: "catalog.beforeCreate",
14
+ async handler(args) {
15
+ const { data } = args;
16
+ const metadata = data?.metadata;
17
+ const vendorId = metadata?.vendorId;
18
+ if (!vendorId)
19
+ return data;
20
+ const db = getDbFromHookArgs(args);
21
+ const [vendor] = await db.select().from(vendors).where(eq(vendors.id, String(vendorId)));
22
+ if (!vendor)
23
+ throw new Error("Vendor not found.");
24
+ if (vendor.status === "suspended")
25
+ throw new Error("Vendor is suspended and cannot create listings.");
26
+ if (vendor.status !== "approved")
27
+ throw new Error("Vendor must be approved before creating marketplace listings.");
28
+ // Check approved categories if set
29
+ const approvedCats = vendor.approvedCategories;
30
+ if (approvedCats && Array.isArray(approvedCats)) {
31
+ const entityType = data.type;
32
+ // approvedCategories holds category slugs; entityType may match
33
+ // This is a basic check; real implementations would check category assignment
34
+ }
35
+ return data;
36
+ },
37
+ },
38
+ // ─── catalog.afterCreate ───────────────────────────────────────────────
39
+ {
40
+ key: "catalog.afterCreate",
41
+ async handler(args) {
42
+ const { result } = args;
43
+ const vendorId = result?.metadata?.vendorId;
44
+ if (!vendorId)
45
+ return;
46
+ const db = getDbFromHookArgs(args);
47
+ await db.insert(vendorEntities).values({
48
+ vendorId: String(vendorId),
49
+ entityId: result.id,
50
+ });
51
+ },
52
+ },
53
+ // ─── catalog.beforeList ────────────────────────────────────────────────
54
+ {
55
+ key: "catalog.beforeList",
56
+ async handler(args) {
57
+ const { data, context: hookContext } = args;
58
+ hookContext.context.marketplaceVendorScope = hookContext.actor?.vendorId ?? null;
59
+ return data;
60
+ },
61
+ },
62
+ // ─── catalog.afterList ─────────────────────────────────────────────────
63
+ {
64
+ key: "catalog.afterList",
65
+ async handler(args) {
66
+ const { result, context: hookContext } = args;
67
+ const vendorScope = hookContext.context.marketplaceVendorScope;
68
+ if (!vendorScope || !Array.isArray(result?.items))
69
+ return;
70
+ const db = getDbFromHookArgs(args);
71
+ const links = await db.select().from(vendorEntities).where(eq(vendorEntities.vendorId, vendorScope));
72
+ const linkedEntityIds = new Set(links.map((l) => l.entityId));
73
+ result.items = result.items.filter((item) => linkedEntityIds.has(item.id));
74
+ },
75
+ },
76
+ // ─── catalog.afterRead ─────────────────────────────────────────────────
77
+ {
78
+ key: "catalog.afterRead",
79
+ async handler(args) {
80
+ const { result } = args;
81
+ const db = getDbFromHookArgs(args);
82
+ const [link] = await db.select().from(vendorEntities).where(eq(vendorEntities.entityId, result.id));
83
+ if (!link)
84
+ return;
85
+ const [vendor] = await db.select().from(vendors).where(eq(vendors.id, link.vendorId));
86
+ if (!vendor)
87
+ return;
88
+ result.marketplace = {
89
+ vendor: {
90
+ id: vendor.id,
91
+ name: vendor.name,
92
+ slug: vendor.slug,
93
+ status: vendor.status,
94
+ tier: vendor.tier,
95
+ logoUrl: vendor.logoUrl,
96
+ },
97
+ };
98
+ },
99
+ },
100
+ // ─── orders.afterCreate ────────────────────────────────────────────────
101
+ {
102
+ key: "orders.afterCreate",
103
+ async handler(args) {
104
+ const { result, context: hookContext } = args;
105
+ const db = getDbFromHookArgs(args);
106
+ const commissionService = new CommissionService(db, options);
107
+ const payoutService = new PayoutService(db, options);
108
+ const grouped = new Map();
109
+ for (const lineItem of result.lineItems ?? []) {
110
+ const [link] = await db.select().from(vendorEntities).where(eq(vendorEntities.entityId, lineItem.entityId));
111
+ if (!link)
112
+ continue;
113
+ const [vendor] = await db.select().from(vendors).where(eq(vendors.id, link.vendorId));
114
+ if (!vendor)
115
+ continue;
116
+ const existing = grouped.get(vendor.id) ?? [];
117
+ existing.push({
118
+ entityId: lineItem.entityId,
119
+ quantity: lineItem.quantity,
120
+ totalPrice: lineItem.totalPrice,
121
+ });
122
+ grouped.set(vendor.id, existing);
123
+ }
124
+ for (const [vendorId, lineItems] of grouped.entries()) {
125
+ const subtotal = lineItems.reduce((sum, item) => sum + item.totalPrice, 0);
126
+ // Resolve commission via rules engine
127
+ const commissionRateBps = await commissionService.resolveRate(vendorId);
128
+ const commissionAmount = Math.round((subtotal * commissionRateBps) / 10000);
129
+ const payoutAmount = subtotal - commissionAmount;
130
+ const [subOrder] = await db.insert(vendorSubOrders).values({
131
+ orderId: result.id,
132
+ vendorId,
133
+ status: "pending",
134
+ subtotal,
135
+ commissionAmount,
136
+ payoutAmount,
137
+ notified: true,
138
+ lineItems,
139
+ metadata: {},
140
+ }).returning();
141
+ if (!subOrder)
142
+ continue;
143
+ // Credit vendor balance with full subtotal
144
+ await payoutService.addLedgerEntry({
145
+ vendorId,
146
+ type: "sale",
147
+ amountCents: subtotal,
148
+ referenceType: "sub_order",
149
+ referenceId: subOrder.id,
150
+ description: `Sale from order ${result.id.slice(0, 8)}`,
151
+ });
152
+ // Record commission as separate entry
153
+ await payoutService.addLedgerEntry({
154
+ vendorId,
155
+ type: "commission",
156
+ amountCents: -commissionAmount,
157
+ referenceType: "sub_order",
158
+ referenceId: subOrder.id,
159
+ description: `Commission (${commissionRateBps}bps) on order ${result.id.slice(0, 8)}`,
160
+ });
161
+ hookContext.logger.info("marketplace_sub_order_created", {
162
+ orderId: result.id,
163
+ subOrderId: subOrder.id,
164
+ vendorId,
165
+ commissionRateBps,
166
+ });
167
+ }
168
+ },
169
+ },
170
+ // ─── orders.beforeStatusChange ─────────────────────────────────────────
171
+ {
172
+ key: "orders.beforeStatusChange",
173
+ async handler(args) {
174
+ const { data } = args;
175
+ if (data.newStatus !== "fulfilled")
176
+ return data;
177
+ const db = getDbFromHookArgs(args);
178
+ const related = await db.select().from(vendorSubOrders).where(eq(vendorSubOrders.orderId, data.orderId));
179
+ const allDelivered = related.every((subOrder) => subOrder.status === "delivered");
180
+ if (!allDelivered) {
181
+ throw new Error("Cannot fulfill parent order until all marketplace sub-orders are delivered.");
182
+ }
183
+ return data;
184
+ },
185
+ },
186
+ ];
187
+ }
@@ -0,0 +1,4 @@
1
+ import type { MarketplacePluginOptions } from "./types";
2
+ export type { MarketplacePluginOptions } from "./types";
3
+ export declare function marketplacePlugin(options?: MarketplacePluginOptions): import("@unifiedcommerce/core").CommercePlugin;
4
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AA0BA,OAAO,KAAK,EAAE,wBAAwB,EAAM,MAAM,SAAS,CAAC;AAE5D,YAAY,EAAE,wBAAwB,EAAE,MAAM,SAAS,CAAC;AA4CxD,wBAAgB,iBAAiB,CAAC,OAAO,GAAE,wBAA6B,kDAmDvE"}
package/dist/index.js ADDED
@@ -0,0 +1,105 @@
1
+ import { defineCommercePlugin } from "@unifiedcommerce/core";
2
+ import { MARKETPLACE_ANALYTICS_MODELS } from "./analytics-models";
3
+ import { vendors, vendorEntities, vendorSubOrders, vendorPayouts, vendorDocuments, commissionRules, vendorBalances, disputes, vendorReviews, returnRequests, rfqs, rfqResponses, contractPrices, } from "./schema";
4
+ import { VendorService } from "./services/vendor";
5
+ import { SubOrderService } from "./services/sub-order";
6
+ import { CommissionService } from "./services/commission";
7
+ import { PayoutService } from "./services/payout";
8
+ import { DisputeService } from "./services/dispute";
9
+ import { ReturnService } from "./services/return";
10
+ import { ReviewService } from "./services/review";
11
+ import { RFQService } from "./services/rfq";
12
+ import { ContractPriceService } from "./services/contract-price";
13
+ import { buildHooks } from "./hooks";
14
+ import { buildMCPTools } from "./mcp-tools";
15
+ import { buildVendorRoutes } from "./routes/vendors";
16
+ import { buildVendorPortalRoutes } from "./routes/vendor-portal";
17
+ import { buildSubOrderRoutes } from "./routes/sub-orders";
18
+ import { buildCommissionRoutes } from "./routes/commission";
19
+ import { buildPayoutRoutes } from "./routes/payouts";
20
+ import { buildDisputesReturnsReviewsRoutes } from "./routes/disputes-returns-reviews";
21
+ import { buildB2BRoutes } from "./routes/b2b";
22
+ function createServices(db, options, kernelServices) {
23
+ const vendor = new VendorService(db);
24
+ const commission = new CommissionService(db, options);
25
+ const payout = new PayoutService(db, options);
26
+ const dispute = new DisputeService(db, options);
27
+ const returnSvc = new ReturnService(db);
28
+ const review = new ReviewService(db, options);
29
+ const rfq = options.b2b?.rfq ? new RFQService(db) : undefined;
30
+ const contractPrice = options.b2b?.contractPricing ? new ContractPriceService(db) : undefined;
31
+ // Cancel callback: release inventory on parent order + reverse balance
32
+ const subOrder = new SubOrderService(db, async (sub) => {
33
+ // Release inventory for cancelled vendor's line items
34
+ const inventory = kernelServices?.inventory;
35
+ if (inventory?.release && sub.lineItems) {
36
+ for (const item of sub.lineItems) {
37
+ await inventory.release({
38
+ entityId: item.entityId,
39
+ quantity: item.quantity,
40
+ orderId: sub.orderId,
41
+ performedBy: "marketplace",
42
+ });
43
+ }
44
+ }
45
+ // Reverse balance: debit the sale credit
46
+ if (sub.payoutAmount > 0) {
47
+ await payout.addLedgerEntry({
48
+ vendorId: sub.vendorId,
49
+ type: "refund_deduction",
50
+ amountCents: -sub.payoutAmount,
51
+ referenceType: "sub_order",
52
+ referenceId: sub.id,
53
+ description: `Cancelled sub-order ${sub.id.slice(0, 8)}`,
54
+ });
55
+ }
56
+ });
57
+ return { vendor, subOrder, commission, payout, dispute, return: returnSvc, review, rfq, contractPrice };
58
+ }
59
+ export function marketplacePlugin(options = {}) {
60
+ return defineCommercePlugin({
61
+ id: "marketplace",
62
+ version: "2.0.0",
63
+ schema: () => ({
64
+ vendors,
65
+ vendorEntities,
66
+ vendorSubOrders,
67
+ vendorPayouts,
68
+ vendorDocuments,
69
+ commissionRules,
70
+ vendorBalances,
71
+ disputes,
72
+ vendorReviews,
73
+ returnRequests,
74
+ rfqs,
75
+ rfqResponses,
76
+ contractPrices,
77
+ }),
78
+ hooks: () => buildHooks(options),
79
+ routes: (ctx) => {
80
+ const db = ctx.database.db;
81
+ const services = db ? createServices(db, options, ctx.services) : null;
82
+ if (!services)
83
+ return [];
84
+ return [
85
+ ...buildVendorRoutes(services, options),
86
+ ...buildVendorPortalRoutes(services),
87
+ ...buildSubOrderRoutes(services),
88
+ ...buildCommissionRoutes(services),
89
+ ...buildPayoutRoutes(services),
90
+ ...buildDisputesReturnsReviewsRoutes(services),
91
+ ...(options.b2b?.rfq || options.b2b?.contractPricing
92
+ ? buildB2BRoutes(services, options)
93
+ : []),
94
+ ];
95
+ },
96
+ mcpTools: (ctx) => {
97
+ const db = ctx.database.db;
98
+ if (!db)
99
+ return [];
100
+ const services = createServices(db, options, ctx.services);
101
+ return buildMCPTools(services, options);
102
+ },
103
+ analyticsModels: () => MARKETPLACE_ANALYTICS_MODELS,
104
+ });
105
+ }
@@ -0,0 +1,21 @@
1
+ import type { MCPTool } from "@unifiedcommerce/core";
2
+ import type { VendorService } from "./services/vendor";
3
+ import type { SubOrderService } from "./services/sub-order";
4
+ import type { CommissionService } from "./services/commission";
5
+ import type { PayoutService } from "./services/payout";
6
+ import type { DisputeService } from "./services/dispute";
7
+ import type { ReviewService } from "./services/review";
8
+ import type { RFQService } from "./services/rfq";
9
+ import type { MarketplacePluginOptions } from "./types";
10
+ interface MCPServices {
11
+ vendor: VendorService;
12
+ subOrder: SubOrderService;
13
+ commission: CommissionService;
14
+ payout: PayoutService;
15
+ dispute: DisputeService;
16
+ review: ReviewService;
17
+ rfq?: RFQService | undefined;
18
+ }
19
+ export declare function buildMCPTools(services: MCPServices, _options: MarketplacePluginOptions): MCPTool[];
20
+ export {};
21
+ //# sourceMappingURL=mcp-tools.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"mcp-tools.d.ts","sourceRoot":"","sources":["../src/mcp-tools.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,uBAAuB,CAAC;AACrD,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,mBAAmB,CAAC;AACvD,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,sBAAsB,CAAC;AAC5D,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,uBAAuB,CAAC;AAC/D,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,mBAAmB,CAAC;AACvD,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,oBAAoB,CAAC;AACzD,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,mBAAmB,CAAC;AACvD,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,gBAAgB,CAAC;AACjD,OAAO,KAAK,EAAE,wBAAwB,EAAE,MAAM,SAAS,CAAC;AAExD,UAAU,WAAW;IACnB,MAAM,EAAE,aAAa,CAAC;IACtB,QAAQ,EAAE,eAAe,CAAC;IAC1B,UAAU,EAAE,iBAAiB,CAAC;IAC9B,MAAM,EAAE,aAAa,CAAC;IACtB,OAAO,EAAE,cAAc,CAAC;IACxB,MAAM,EAAE,aAAa,CAAC;IACtB,GAAG,CAAC,EAAE,UAAU,GAAG,SAAS,CAAC;CAC9B;AAMD,wBAAgB,aAAa,CAAC,QAAQ,EAAE,WAAW,EAAE,QAAQ,EAAE,wBAAwB,GAAG,OAAO,EAAE,CAyLlG"}
@@ -0,0 +1,183 @@
1
+ function p(params) {
2
+ return (params ?? {});
3
+ }
4
+ export function buildMCPTools(services, _options) {
5
+ const tools = [
6
+ {
7
+ name: "marketplace_vendor_list",
8
+ description: "List marketplace vendors with optional status/tier filters.",
9
+ inputSchema: {
10
+ type: "object",
11
+ properties: {
12
+ status: { type: "string" },
13
+ tier: { type: "string" },
14
+ search: { type: "string" },
15
+ },
16
+ },
17
+ async handler(params) {
18
+ const rows = await services.vendor.list(p(params));
19
+ return { content: [{ type: "text", text: JSON.stringify(rows, null, 2) }] };
20
+ },
21
+ },
22
+ {
23
+ name: "marketplace_vendor_performance",
24
+ description: "Get vendor performance metrics including rating, tier, and score.",
25
+ inputSchema: {
26
+ type: "object",
27
+ required: ["vendorId"],
28
+ properties: { vendorId: { type: "string" } },
29
+ },
30
+ async handler(params) {
31
+ const { vendorId } = p(params);
32
+ const vendor = await services.vendor.getById(vendorId);
33
+ if (!vendor)
34
+ return { error: { code: "NOT_FOUND", message: "Vendor not found." } };
35
+ const rating = await services.review.getAggregateRating(vendorId);
36
+ return {
37
+ content: [{
38
+ type: "text",
39
+ text: JSON.stringify({
40
+ vendorId: vendor.id,
41
+ name: vendor.name,
42
+ tier: vendor.tier,
43
+ performanceScore: vendor.performanceScore,
44
+ rating,
45
+ }, null, 2),
46
+ }],
47
+ };
48
+ },
49
+ },
50
+ {
51
+ name: "marketplace_vendor_balance",
52
+ description: "Get vendor current balance and recent ledger entries.",
53
+ inputSchema: {
54
+ type: "object",
55
+ required: ["vendorId"],
56
+ properties: { vendorId: { type: "string" } },
57
+ },
58
+ async handler(params) {
59
+ const { vendorId } = p(params);
60
+ const balance = await services.payout.getBalance(vendorId);
61
+ const ledger = await services.payout.getLedger(vendorId, 20);
62
+ return {
63
+ content: [{
64
+ type: "text",
65
+ text: JSON.stringify({ vendorId, balance, recentEntries: ledger }, null, 2),
66
+ }],
67
+ };
68
+ },
69
+ },
70
+ {
71
+ name: "marketplace_suborder_update",
72
+ description: "Transition a sub-order status (confirm, ship, deliver, cancel).",
73
+ inputSchema: {
74
+ type: "object",
75
+ required: ["subOrderId", "action"],
76
+ properties: {
77
+ subOrderId: { type: "string" },
78
+ action: { type: "string", enum: ["confirm", "ship", "deliver", "cancel"] },
79
+ trackingNumber: { type: "string" },
80
+ carrier: { type: "string" },
81
+ reason: { type: "string" },
82
+ },
83
+ },
84
+ async handler(params) {
85
+ const args = p(params);
86
+ try {
87
+ let result;
88
+ switch (args.action) {
89
+ case "confirm":
90
+ result = await services.subOrder.confirm(args.subOrderId);
91
+ break;
92
+ case "ship":
93
+ result = await services.subOrder.ship(args.subOrderId, {
94
+ trackingNumber: args.trackingNumber ?? "",
95
+ carrier: args.carrier ?? "",
96
+ });
97
+ break;
98
+ case "deliver":
99
+ result = await services.subOrder.deliver(args.subOrderId);
100
+ break;
101
+ case "cancel":
102
+ result = await services.subOrder.cancel(args.subOrderId, args.reason);
103
+ break;
104
+ default:
105
+ return { error: { code: "INVALID_ACTION", message: `Unknown action: ${args.action}` } };
106
+ }
107
+ return { content: [{ type: "text", text: JSON.stringify(result, null, 2) }] };
108
+ }
109
+ catch (err) {
110
+ return { error: { code: "TRANSITION_ERROR", message: err instanceof Error ? err.message : "Unknown error" } };
111
+ }
112
+ },
113
+ },
114
+ {
115
+ name: "marketplace_dispute_summary",
116
+ description: "List open disputes with deadlines.",
117
+ inputSchema: { type: "object" },
118
+ async handler() {
119
+ const open = await services.dispute.list({ status: "open" });
120
+ const pending = await services.dispute.list({ status: "vendor_response_pending" });
121
+ const escalated = await services.dispute.list({ status: "escalated" });
122
+ return {
123
+ content: [{
124
+ type: "text",
125
+ text: JSON.stringify({ open, vendorResponsePending: pending, escalated }, null, 2),
126
+ }],
127
+ };
128
+ },
129
+ },
130
+ {
131
+ name: "marketplace_payout_run",
132
+ description: "Trigger a payout cycle for all eligible vendors.",
133
+ inputSchema: { type: "object" },
134
+ async handler() {
135
+ const results = await services.payout.runPayoutCycle();
136
+ return {
137
+ content: [{
138
+ type: "text",
139
+ text: JSON.stringify({ payoutsProcessed: results.length, details: results }, null, 2),
140
+ }],
141
+ };
142
+ },
143
+ },
144
+ {
145
+ name: "marketplace_commission_preview",
146
+ description: "Preview effective commission rate for a vendor+category+amount.",
147
+ inputSchema: {
148
+ type: "object",
149
+ required: ["vendorId"],
150
+ properties: {
151
+ vendorId: { type: "string" },
152
+ categorySlug: { type: "string" },
153
+ volumeCents: { type: "number" },
154
+ },
155
+ },
156
+ async handler(params) {
157
+ const { vendorId, categorySlug, volumeCents } = p(params);
158
+ const preview = await services.commission.previewRate(vendorId, categorySlug, volumeCents);
159
+ return { content: [{ type: "text", text: JSON.stringify(preview, null, 2) }] };
160
+ },
161
+ },
162
+ ];
163
+ // B2B: RFQ tool
164
+ if (services.rfq) {
165
+ const rfqService = services.rfq;
166
+ tools.push({
167
+ name: "marketplace_rfq_list",
168
+ description: "List open RFQs with optional category filter.",
169
+ inputSchema: {
170
+ type: "object",
171
+ properties: {
172
+ status: { type: "string" },
173
+ categorySlug: { type: "string" },
174
+ },
175
+ },
176
+ async handler(params) {
177
+ const rows = await rfqService.list(p(params));
178
+ return { content: [{ type: "text", text: JSON.stringify(rows, null, 2) }] };
179
+ },
180
+ });
181
+ }
182
+ return tools;
183
+ }
@@ -0,0 +1,9 @@
1
+ import type { PluginRouteRegistration } from "@unifiedcommerce/core";
2
+ import type { RFQService } from "../services/rfq";
3
+ import type { ContractPriceService } from "../services/contract-price";
4
+ import type { MarketplacePluginOptions } from "../types";
5
+ export declare function buildB2BRoutes(services: {
6
+ rfq?: RFQService | undefined;
7
+ contractPrice?: ContractPriceService | undefined;
8
+ }, options: MarketplacePluginOptions): PluginRouteRegistration[];
9
+ //# sourceMappingURL=b2b.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"b2b.d.ts","sourceRoot":"","sources":["../../src/routes/b2b.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,uBAAuB,EAAE,MAAM,uBAAuB,CAAC;AAErE,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,iBAAiB,CAAC;AAClD,OAAO,KAAK,EAAE,oBAAoB,EAAE,MAAM,4BAA4B,CAAC;AACvE,OAAO,KAAK,EAAE,wBAAwB,EAAE,MAAM,UAAU,CAAC;AAUzD,wBAAgB,cAAc,CAAC,QAAQ,EAAE;IACvC,GAAG,CAAC,EAAE,UAAU,GAAG,SAAS,CAAC;IAC7B,aAAa,CAAC,EAAE,oBAAoB,GAAG,SAAS,CAAC;CAClD,EAAE,OAAO,EAAE,wBAAwB,GAAG,uBAAuB,EAAE,CAgK/D"}