@hasna/microservices 0.0.4 → 0.0.5

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 (38) hide show
  1. package/microservices/microservice-ads/src/cli/index.ts +198 -0
  2. package/microservices/microservice-ads/src/db/campaigns.ts +304 -0
  3. package/microservices/microservice-ads/src/mcp/index.ts +160 -0
  4. package/microservices/microservice-contracts/src/cli/index.ts +410 -23
  5. package/microservices/microservice-contracts/src/db/contracts.ts +430 -1
  6. package/microservices/microservice-contracts/src/db/migrations.ts +83 -0
  7. package/microservices/microservice-contracts/src/mcp/index.ts +312 -3
  8. package/microservices/microservice-domains/src/cli/index.ts +253 -0
  9. package/microservices/microservice-domains/src/db/domains.ts +613 -0
  10. package/microservices/microservice-domains/src/index.ts +21 -0
  11. package/microservices/microservice-domains/src/mcp/index.ts +168 -0
  12. package/microservices/microservice-hiring/src/cli/index.ts +318 -8
  13. package/microservices/microservice-hiring/src/db/hiring.ts +503 -0
  14. package/microservices/microservice-hiring/src/db/migrations.ts +21 -0
  15. package/microservices/microservice-hiring/src/index.ts +29 -0
  16. package/microservices/microservice-hiring/src/lib/scoring.ts +206 -0
  17. package/microservices/microservice-hiring/src/mcp/index.ts +245 -0
  18. package/microservices/microservice-payments/src/cli/index.ts +255 -3
  19. package/microservices/microservice-payments/src/db/migrations.ts +18 -0
  20. package/microservices/microservice-payments/src/db/payments.ts +552 -0
  21. package/microservices/microservice-payments/src/mcp/index.ts +223 -0
  22. package/microservices/microservice-payroll/src/cli/index.ts +269 -0
  23. package/microservices/microservice-payroll/src/db/migrations.ts +26 -0
  24. package/microservices/microservice-payroll/src/db/payroll.ts +636 -0
  25. package/microservices/microservice-payroll/src/mcp/index.ts +246 -0
  26. package/microservices/microservice-shipping/src/cli/index.ts +211 -3
  27. package/microservices/microservice-shipping/src/db/migrations.ts +8 -0
  28. package/microservices/microservice-shipping/src/db/shipping.ts +453 -3
  29. package/microservices/microservice-shipping/src/mcp/index.ts +149 -1
  30. package/microservices/microservice-social/src/cli/index.ts +244 -2
  31. package/microservices/microservice-social/src/db/migrations.ts +33 -0
  32. package/microservices/microservice-social/src/db/social.ts +378 -4
  33. package/microservices/microservice-social/src/mcp/index.ts +221 -1
  34. package/microservices/microservice-subscriptions/src/cli/index.ts +315 -0
  35. package/microservices/microservice-subscriptions/src/db/migrations.ts +68 -0
  36. package/microservices/microservice-subscriptions/src/db/subscriptions.ts +567 -3
  37. package/microservices/microservice-subscriptions/src/mcp/index.ts +267 -1
  38. package/package.json +1 -1
@@ -14,6 +14,17 @@ import {
14
14
  reconcileWithInvoice,
15
15
  getPaymentStats,
16
16
  getBalanceByProvider,
17
+ autoReconcile,
18
+ retryPayment,
19
+ listRetries,
20
+ getRetryStats,
21
+ convertCurrency,
22
+ feeAnalysis,
23
+ declineReport,
24
+ addDisputeEvidence,
25
+ splitPayment,
26
+ revenueForecast,
27
+ findReconciliationGaps,
17
28
  type PaymentType,
18
29
  type PaymentStatus,
19
30
  type PaymentProvider,
@@ -154,6 +165,127 @@ paymentCmd
154
165
  }
155
166
  });
156
167
 
168
+ // --- Payment Retry ---
169
+
170
+ paymentCmd
171
+ .command("retry")
172
+ .description("Retry a failed payment")
173
+ .argument("<id>", "Payment ID to retry")
174
+ .option("--json", "Output as JSON", false)
175
+ .action((id, opts) => {
176
+ const attempt = retryPayment(id);
177
+ if (!attempt) {
178
+ console.error(`Cannot retry payment '${id}' — not found or not failed.`);
179
+ process.exit(1);
180
+ }
181
+
182
+ if (opts.json) {
183
+ console.log(JSON.stringify(attempt, null, 2));
184
+ } else {
185
+ console.log(`Retry attempt #${attempt.attempt} for payment ${id}: ${attempt.status}`);
186
+ }
187
+ });
188
+
189
+ paymentCmd
190
+ .command("retries")
191
+ .description("List retry attempts for a payment")
192
+ .argument("<paymentId>", "Payment ID")
193
+ .option("--json", "Output as JSON", false)
194
+ .action((paymentId, opts) => {
195
+ const retries = listRetries(paymentId);
196
+
197
+ if (opts.json) {
198
+ console.log(JSON.stringify(retries, null, 2));
199
+ } else {
200
+ if (retries.length === 0) {
201
+ console.log("No retry attempts found.");
202
+ return;
203
+ }
204
+ for (const r of retries) {
205
+ console.log(` Attempt #${r.attempt} [${r.status}] ${r.attempted_at || ""} ${r.error || ""}`);
206
+ }
207
+ console.log(`\n${retries.length} retry attempt(s)`);
208
+ }
209
+ });
210
+
211
+ // --- Payment Convert ---
212
+
213
+ paymentCmd
214
+ .command("convert")
215
+ .description("Convert currency amount")
216
+ .requiredOption("--from <currency>", "Source currency")
217
+ .requiredOption("--to <currency>", "Target currency")
218
+ .requiredOption("--amount <amount>", "Amount to convert")
219
+ .option("--json", "Output as JSON", false)
220
+ .action((opts) => {
221
+ const result = convertCurrency(parseFloat(opts.amount), opts.from, opts.to);
222
+ if (!result) {
223
+ console.error(`Unsupported currency pair: ${opts.from} -> ${opts.to}`);
224
+ process.exit(1);
225
+ }
226
+
227
+ if (opts.json) {
228
+ console.log(JSON.stringify(result, null, 2));
229
+ } else {
230
+ console.log(`${result.original_amount} ${result.from} = ${result.converted_amount} ${result.to} (rate: ${result.rate})`);
231
+ }
232
+ });
233
+
234
+ // --- Decline Report ---
235
+
236
+ paymentCmd
237
+ .command("decline-report")
238
+ .description("Show decline analytics for failed payments")
239
+ .option("--provider <provider>", "Filter by provider")
240
+ .option("--json", "Output as JSON", false)
241
+ .action((opts) => {
242
+ const report = declineReport(opts.provider as PaymentProvider | undefined);
243
+
244
+ if (opts.json) {
245
+ console.log(JSON.stringify(report, null, 2));
246
+ } else {
247
+ if (report.entries.length === 0) {
248
+ console.log("No declined payments found.");
249
+ return;
250
+ }
251
+ console.log("Decline Report:");
252
+ for (const e of report.entries) {
253
+ console.log(` [${e.provider}] "${e.description || "No description"}" — ${e.count} decline(s), $${e.total_amount.toFixed(2)}`);
254
+ }
255
+ console.log(`\nTotal: ${report.total_declined} decline(s), $${report.total_amount.toFixed(2)}`);
256
+ }
257
+ });
258
+
259
+ // --- Payment Split ---
260
+
261
+ paymentCmd
262
+ .command("split")
263
+ .description("Split a payment into marketplace commission splits")
264
+ .argument("<id>", "Payment ID")
265
+ .requiredOption("--splits <json>", "Split percentages as JSON (e.g. '{\"vendor1\":70,\"vendor2\":30}')")
266
+ .option("--json", "Output as JSON", false)
267
+ .action((id, opts) => {
268
+ let splits: Record<string, number>;
269
+ try {
270
+ splits = JSON.parse(opts.splits);
271
+ } catch {
272
+ console.error("Invalid JSON for --splits");
273
+ process.exit(1);
274
+ }
275
+
276
+ const payment = splitPayment(id, splits);
277
+ if (!payment) {
278
+ console.error(`Payment '${id}' not found.`);
279
+ process.exit(1);
280
+ }
281
+
282
+ if (opts.json) {
283
+ console.log(JSON.stringify(payment, null, 2));
284
+ } else {
285
+ console.log(`Payment ${id} split recorded: ${JSON.stringify(splits)}`);
286
+ }
287
+ });
288
+
157
289
  // --- Disputes ---
158
290
 
159
291
  const disputeCmd = program
@@ -203,6 +335,27 @@ disputeCmd
203
335
  }
204
336
  });
205
337
 
338
+ disputeCmd
339
+ .command("add-evidence")
340
+ .description("Add evidence to a dispute")
341
+ .argument("<id>", "Dispute ID")
342
+ .requiredOption("--description <desc>", "Evidence description")
343
+ .option("--file-ref <path>", "File reference path")
344
+ .option("--json", "Output as JSON", false)
345
+ .action((id, opts) => {
346
+ const dispute = addDisputeEvidence(id, opts.description, opts.fileRef);
347
+ if (!dispute) {
348
+ console.error(`Dispute '${id}' not found.`);
349
+ process.exit(1);
350
+ }
351
+
352
+ if (opts.json) {
353
+ console.log(JSON.stringify(dispute, null, 2));
354
+ } else {
355
+ console.log(`Evidence added to dispute ${id}: "${opts.description}"`);
356
+ }
357
+ });
358
+
206
359
  // --- Payouts ---
207
360
 
208
361
  const payoutCmd = program
@@ -277,15 +430,18 @@ program
277
430
 
278
431
  // --- Revenue ---
279
432
 
280
- program
433
+ const revenueCmd = program
281
434
  .command("revenue")
435
+ .description("Revenue reports and forecasting");
436
+
437
+ revenueCmd
438
+ .command("report")
282
439
  .description("Revenue report for a period")
283
440
  .requiredOption("--period <YYYY-MM>", "Month period (e.g. 2024-01)")
284
441
  .option("--json", "Output as JSON", false)
285
442
  .action((opts) => {
286
443
  const [year, month] = opts.period.split("-").map(Number);
287
444
  const startDate = `${year}-${String(month).padStart(2, "0")}-01`;
288
- // Last day of month
289
445
  const lastDay = new Date(year, month, 0).getDate();
290
446
  const endDate = `${year}-${String(month).padStart(2, "0")}-${String(lastDay).padStart(2, "0")} 23:59:59`;
291
447
 
@@ -302,10 +458,38 @@ program
302
458
  }
303
459
  });
304
460
 
461
+ revenueCmd
462
+ .command("forecast")
463
+ .description("Project revenue for upcoming months based on recent trends")
464
+ .requiredOption("--months <n>", "Number of months to forecast")
465
+ .option("--json", "Output as JSON", false)
466
+ .action((opts) => {
467
+ const result = revenueForecast(parseInt(opts.months));
468
+
469
+ if (opts.json) {
470
+ console.log(JSON.stringify(result, null, 2));
471
+ } else {
472
+ console.log(`Revenue Forecast (${result.months_projected} months, trend: ${result.trend})`);
473
+ console.log(" Historical:");
474
+ for (const h of result.historical) {
475
+ console.log(` ${h.month}: $${h.revenue.toFixed(2)}`);
476
+ }
477
+ console.log(" Forecast:");
478
+ for (const f of result.forecast) {
479
+ console.log(` ${f.month}: $${f.projected_revenue.toFixed(2)} (projected)`);
480
+ }
481
+ console.log(` Average Monthly Revenue: $${result.average_monthly_revenue.toFixed(2)}`);
482
+ }
483
+ });
484
+
305
485
  // --- Reconcile ---
306
486
 
307
- program
487
+ const reconcileCmd = program
308
488
  .command("reconcile")
489
+ .description("Reconciliation commands");
490
+
491
+ reconcileCmd
492
+ .command("link")
309
493
  .description("Link a payment to an invoice")
310
494
  .requiredOption("--payment <id>", "Payment ID")
311
495
  .requiredOption("--invoice <id>", "Invoice ID")
@@ -324,6 +508,74 @@ program
324
508
  }
325
509
  });
326
510
 
511
+ reconcileCmd
512
+ .command("auto")
513
+ .description("Auto-reconcile payments to invoices by amount, email, and date")
514
+ .option("--from <date>", "Start date (YYYY-MM-DD)")
515
+ .option("--to <date>", "End date (YYYY-MM-DD)")
516
+ .option("--json", "Output as JSON", false)
517
+ .action((opts) => {
518
+ const result = autoReconcile(opts.from, opts.to);
519
+
520
+ if (opts.json) {
521
+ console.log(JSON.stringify(result, null, 2));
522
+ } else {
523
+ console.log("Auto-Reconciliation Results:");
524
+ console.log(` Matched: ${result.matched.length}`);
525
+ for (const m of result.matched) {
526
+ console.log(` Payment ${m.payment_id} -> Invoice ${m.invoice_id} (${m.confidence})`);
527
+ }
528
+ console.log(` Unmatched Payments: ${result.unmatched_payments.length}`);
529
+ console.log(` Unmatched Invoices: ${result.unmatched_invoices.length}`);
530
+ }
531
+ });
532
+
533
+ reconcileCmd
534
+ .command("check-gaps")
535
+ .description("Find reconciliation gaps — payments without invoices and vice versa")
536
+ .requiredOption("--from <date>", "Start date (YYYY-MM-DD)")
537
+ .requiredOption("--to <date>", "End date (YYYY-MM-DD)")
538
+ .option("--json", "Output as JSON", false)
539
+ .action((opts) => {
540
+ const gaps = findReconciliationGaps(opts.from, opts.to);
541
+
542
+ if (opts.json) {
543
+ console.log(JSON.stringify(gaps, null, 2));
544
+ } else {
545
+ console.log("Reconciliation Gaps:");
546
+ console.log(` Payments without invoice: ${gaps.payments_without_invoice.length}`);
547
+ for (const p of gaps.payments_without_invoice) {
548
+ console.log(` $${p.amount} ${p.currency} — ${p.customer_email || "no email"} (${p.id})`);
549
+ }
550
+ console.log(` Invoice IDs without succeeded payment: ${gaps.invoice_ids_without_payment.length}`);
551
+ for (const inv of gaps.invoice_ids_without_payment) {
552
+ console.log(` ${inv}`);
553
+ }
554
+ console.log(` Total gaps: ${gaps.gap_count}`);
555
+ }
556
+ });
557
+
558
+ // --- Fees ---
559
+
560
+ program
561
+ .command("fees")
562
+ .description("Fee analysis by provider for a given month")
563
+ .requiredOption("--month <YYYY-MM>", "Month period (e.g. 2026-03)")
564
+ .option("--json", "Output as JSON", false)
565
+ .action((opts) => {
566
+ const result = feeAnalysis(opts.month);
567
+
568
+ if (opts.json) {
569
+ console.log(JSON.stringify(result, null, 2));
570
+ } else {
571
+ console.log(`Fee Analysis: ${result.month}`);
572
+ for (const p of result.providers) {
573
+ console.log(` ${p.provider}: gross $${p.gross.toFixed(2)}, fees $${p.fees.toFixed(2)}, net $${p.net.toFixed(2)} (${p.transaction_count} txns)`);
574
+ }
575
+ console.log(` Total: gross $${result.total_gross.toFixed(2)}, fees $${result.total_fees.toFixed(2)}, net $${result.total_net.toFixed(2)}`);
576
+ }
577
+ });
578
+
327
579
  // --- Stats ---
328
580
 
329
581
  program
@@ -60,4 +60,22 @@ export const MIGRATIONS: MigrationEntry[] = [
60
60
  CREATE INDEX IF NOT EXISTS idx_payouts_status ON payouts(status);
61
61
  `,
62
62
  },
63
+ {
64
+ id: 2,
65
+ name: "retry_attempts",
66
+ sql: `
67
+ CREATE TABLE IF NOT EXISTS retry_attempts (
68
+ id TEXT PRIMARY KEY,
69
+ payment_id TEXT NOT NULL REFERENCES payments(id) ON DELETE CASCADE,
70
+ attempt INTEGER NOT NULL DEFAULT 1,
71
+ status TEXT NOT NULL DEFAULT 'pending' CHECK (status IN ('pending', 'retrying', 'succeeded', 'failed')),
72
+ attempted_at TEXT,
73
+ error TEXT,
74
+ created_at TEXT NOT NULL DEFAULT (datetime('now'))
75
+ );
76
+
77
+ CREATE INDEX IF NOT EXISTS idx_retry_attempts_payment_id ON retry_attempts(payment_id);
78
+ CREATE INDEX IF NOT EXISTS idx_retry_attempts_status ON retry_attempts(status);
79
+ `,
80
+ },
63
81
  ];