tango-app-api-payment-subscription 3.5.5 → 3.5.6

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.
@@ -0,0 +1,125 @@
1
+ <!doctype html>
2
+ <html lang="en">
3
+ <head>
4
+ <meta charset="utf-8">
5
+ <style>
6
+ * { box-sizing: border-box; }
7
+ body { font-family: Arial, Helvetica, sans-serif; color: #1f2937; font-size: 12px; margin: 0; }
8
+ .wrap { padding: 6px 4px; }
9
+ .head { display: flex; justify-content: space-between; align-items: flex-start; }
10
+ .brand { display: flex; align-items: center; gap: 10px; }
11
+ .brand img { height: 40px; }
12
+ .doc-title { font-size: 26px; font-weight: 800; color: #101828; letter-spacing: .5px; }
13
+ .doc-sub { color: #667085; font-size: 12px; margin-top: 2px; }
14
+ .pill { display: inline-block; padding: 3px 11px; border-radius: 999px; font-size: 11px; font-weight: 700; }
15
+ .pill-draft { background: #f1f3f6; color: #667085; }
16
+ .pill-sent { background: #e8f3fe; color: #1573c4; }
17
+ .pill-accepted { background: #e7f7ee; color: #138a52; }
18
+ .pill-declined, .pill-expired { background: #fdecec; color: #d64545; }
19
+ .row { display: flex; justify-content: space-between; margin-top: 22px; }
20
+ .label { font-size: 10px; letter-spacing: .05em; text-transform: uppercase; color: #98a2b3; font-weight: 700; }
21
+ .client-name { font-size: 15px; font-weight: 700; color: #101828; margin-top: 4px; }
22
+ .meta td { padding: 2px 0; }
23
+ .meta .m-label { color: #98a2b3; padding-right: 14px; }
24
+ .meta .m-val { font-weight: 700; color: #344054; text-align: right; }
25
+ .total-box { text-align: right; }
26
+ .total-box .t-label { color: #667085; font-size: 12px; font-weight: 600; }
27
+ .total-box .t-amt { font-size: 24px; font-weight: 800; color: #101828; }
28
+ table.items { width: 100%; border-collapse: collapse; margin-top: 26px; }
29
+ table.items thead th { background: #f4f6f9; color: #667085; font-size: 10px; text-transform: uppercase;
30
+ letter-spacing: .04em; text-align: left; padding: 9px 10px; border-bottom: 1px solid #e4e6ea; }
31
+ table.items tbody td { padding: 11px 10px; border-bottom: 1px solid #eceef1; vertical-align: top; }
32
+ .prod-name { font-weight: 700; color: #101828; }
33
+ .prod-desc { color: #98a2b3; font-size: 11px; margin-top: 2px; }
34
+ .ta-r { text-align: right; }
35
+ .ta-c { text-align: center; }
36
+ .totals { width: 46%; margin-left: auto; margin-top: 16px; }
37
+ .totals td { padding: 6px 0; }
38
+ .totals .tl { color: #667085; }
39
+ .totals .tv { text-align: right; font-weight: 700; color: #101828; }
40
+ .totals .grand td { border-top: 1px solid #e4e6ea; padding-top: 10px; font-size: 15px; font-weight: 800; }
41
+ .note { margin-top: 24px; background: #f7f8fa; border: 1px solid #eceef1; border-radius: 8px;
42
+ padding: 10px 14px; color: #475467; }
43
+ .foot { margin-top: 26px; color: #98a2b3; font-size: 11px; border-top: 1px solid #eceef1; padding-top: 10px; }
44
+ </style>
45
+ </head>
46
+ <body>
47
+ <div class="wrap">
48
+ <div class="head">
49
+ <div class="brand">
50
+ <img src="{{logo}}" alt="">
51
+ <div>
52
+ <div style="font-weight:800;font-size:16px;color:#101828">Tango Eye</div>
53
+ <div class="doc-sub">Tango IT Solutions India Pvt Ltd</div>
54
+ </div>
55
+ </div>
56
+ <div style="text-align:right">
57
+ <div class="doc-title">ESTIMATE</div>
58
+ <div class="doc-sub">{{estimate}}</div>
59
+ <div style="margin-top:6px"><span class="pill pill-{{status}}">{{statusLabel}}</span></div>
60
+ </div>
61
+ </div>
62
+
63
+ <div class="row">
64
+ <div style="max-width:55%">
65
+ <div class="label">Estimate For</div>
66
+ <div class="client-name">{{companyName}}</div>
67
+ {{#if companyAddress}}<div style="margin-top:3px">{{companyAddress}}</div>{{/if}}
68
+ {{#if GSTNumber}}<div style="margin-top:6px"><strong>GSTIN {{GSTNumber}}</strong></div>{{/if}}
69
+ {{#if PlaceOfSupply}}<div>Place Of Supply: {{PlaceOfSupply}}</div>{{/if}}
70
+ {{#if groupName}}<div style="margin-top:6px">Billing Group: {{groupName}}</div>{{/if}}
71
+ </div>
72
+ <div class="total-box">
73
+ <div class="t-label">Estimate Total</div>
74
+ <div class="t-amt">{{currencyType}} {{totalAmount}}</div>
75
+ <table class="meta" style="margin-top:12px;margin-left:auto">
76
+ <tr><td class="m-label">Period</td><td class="m-val">{{period}}</td></tr>
77
+ <tr><td class="m-label">Generated</td><td class="m-val">{{createdDate}}</td></tr>
78
+ <tr><td class="m-label">Valid Till</td><td class="m-val">{{validTill}}</td></tr>
79
+ </table>
80
+ </div>
81
+ </div>
82
+
83
+ <table class="items">
84
+ <thead>
85
+ <tr>
86
+ <th style="width:36px">#</th>
87
+ <th>Product &amp; Description</th>
88
+ <th style="width:90px">HSN/SAC</th>
89
+ <th class="ta-c" style="width:80px">Stores</th>
90
+ <th class="ta-r" style="width:110px">Rate</th>
91
+ <th class="ta-r" style="width:120px">Amount</th>
92
+ </tr>
93
+ </thead>
94
+ <tbody>
95
+ {{#each products}}
96
+ <tr>
97
+ <td>{{index}}</td>
98
+ <td>
99
+ <div class="prod-name">{{productName}}</div>
100
+ {{#if description}}<div class="prod-desc">{{description}}</div>{{/if}}
101
+ </td>
102
+ <td>{{hsn}}</td>
103
+ <td class="ta-c"><strong>{{storeCount}}</strong></td>
104
+ <td class="ta-r">{{../currencyType}} {{price}}</td>
105
+ <td class="ta-r">{{../currencyType}} {{amount}}</td>
106
+ </tr>
107
+ {{/each}}
108
+ </tbody>
109
+ </table>
110
+
111
+ <table class="totals">
112
+ <tr><td class="tl">Sub Total</td><td class="tv">{{currencyType}} {{amount}}</td></tr>
113
+ {{#each tax}}
114
+ <tr><td class="tl">{{type}} ({{value}}%)</td><td class="tv">{{../currencyType}} {{taxAmount}}</td></tr>
115
+ {{/each}}
116
+ <tr class="grand"><td class="tl">Total Amount</td><td class="tv">{{currencyType}} {{totalAmount}}</td></tr>
117
+ </table>
118
+
119
+ {{#if notes}}<div class="note">{{notes}}</div>{{/if}}
120
+
121
+ <div class="foot">This is an estimate, not a tax invoice. Valid until {{validTill}}. Prices are subject to the
122
+ terms agreed in the final subscription.</div>
123
+ </div>
124
+ </body>
125
+ </html>
@@ -1730,6 +1730,30 @@
1730
1730
  {{/each}}
1731
1731
 
1732
1732
  </div>
1733
+ <div class="column" style="max-width: 90px;">
1734
+ <div class="table-header-cell" style="padding: 13px 30px 12px 22px; background-color:#D0D5DD;">
1735
+ <div class="table-header">
1736
+ <div class="text6">Qty</div>
1737
+ </div>
1738
+ </div>
1739
+ {{#each annuxureData}}
1740
+ <div class="table-cell" style="height: 70px !important;">
1741
+ <div class="text7">{{units}}</div>
1742
+ </div>
1743
+ {{/each}}
1744
+ </div>
1745
+ <div class="column" style="max-width: 150px;">
1746
+ <div class="table-header-cell" style="padding: 13px 30px 12px 22px; background-color:#D0D5DD;">
1747
+ <div class="table-header">
1748
+ <div class="text6">Unit Price</div>
1749
+ </div>
1750
+ </div>
1751
+ {{#each annuxureData}}
1752
+ <div class="table-cell" style="height: 70px !important;">
1753
+ <div class="text7">{{../currencyType}} {{standardPrice}}<br>{{unitBasis}}</div>
1754
+ </div>
1755
+ {{/each}}
1756
+ </div>
1733
1757
  <div class="column">
1734
1758
  <div class="table-header-cell" style="padding: 13px 60px 12px 22px; background-color:#D0D5DD;">
1735
1759
  <div class="table-header">
@@ -1744,6 +1768,9 @@
1744
1768
  </div>
1745
1769
  </div>
1746
1770
  </div>
1771
+ <div style="display:flex; justify-content:flex-end; margin-top:14px; padding-right:10px;">
1772
+ <div class="text6" style="font-weight:700;">Total: {{currencyType}} {{annuxureTotal}}</div>
1773
+ </div>
1747
1774
  {{/eq}}
1748
1775
  </div>
1749
1776
  </div>
@@ -1,6 +1,6 @@
1
1
 
2
2
  import express from 'express';
3
- import { brandsBillingList, brandInvoiceList, latestDailyPricing, brandBillingGroups, updateDailyPricingWorkingDays, updateDailyPricingStoreField, getClientBillingInfo, bulkDownloadBillingGroups, bulkUpdateBillingGroups } from '../controllers/brandsBilling.controller.js';
3
+ import { brandsBillingList, brandInvoiceList, latestDailyPricing, brandBillingGroups, updateDailyPricingWorkingDays, updateDailyPricingStoreField, getClientBillingInfo, bulkDownloadBillingGroups, bulkUpdateBillingGroups, billingSummary } from '../controllers/brandsBilling.controller.js';
4
4
  import { isAllowedSessionHandler, accessVerification } from 'tango-app-api-middleware';
5
5
 
6
6
  export const brandsBillingRouter = express.Router();
@@ -14,3 +14,4 @@ brandsBillingRouter.put( '/updateDailyPricingStoreField', isAllowedSessionHandle
14
14
  brandsBillingRouter.post( '/getClientBillingInfo', isAllowedSessionHandler, accessVerification( { userType: [ 'tango' ], access: [ { featureName: 'TangoAdmin', name: 'invoiceApproval', permissions: [] } ] } ), getClientBillingInfo );
15
15
  brandsBillingRouter.get( '/bulk-download-billing-groups', isAllowedSessionHandler, accessVerification( { userType: [ 'tango' ], access: [ { featureName: 'TangoAdmin', name: 'invoiceApproval', permissions: [ 'isEdit' ] } ] } ), bulkDownloadBillingGroups );
16
16
  brandsBillingRouter.post( '/bulk-update-billing-groups', isAllowedSessionHandler, accessVerification( { userType: [ 'tango' ], access: [ { featureName: 'TangoAdmin', name: 'invoiceApproval', permissions: [ 'isEdit' ] } ] } ), bulkUpdateBillingGroups );
17
+ brandsBillingRouter.get( '/billingSummary', isAllowedSessionHandler, accessVerification( { userType: [ 'tango' ], access: [ { featureName: 'TangoAdmin', name: 'invoiceApproval', permissions: [] } ] } ), billingSummary );
@@ -2,6 +2,8 @@ import express from 'express';
2
2
  import { createInvoice, invoiceDownload, invoiceDownloadBulk, clientInvoiceList, creditTransactionlist, pendingInvoices, applyDiscount, migrateInvoice, PaymentStatusChange, checkPaymentStatus, getInvoice, invoiceAnnexure, updateInvoice, getClientBasePricing, deleteInvoice, approveInvoiceCsm, approveInvoiceFinance, approveInvoiceApproval, recordPayment } from '../controllers/invoice.controller.js';
3
3
  import { isAllowedSessionHandler, accessVerification, validate } from 'tango-app-api-middleware';
4
4
  import { getInvoiceHeads, updateInvoiceHeads } from '../controllers/applicationDefault.controllers.js';
5
+ import { uploadBankStatement, bankTransactionList, resolveOptions, resolveBankTransaction } from '../controllers/bankTransaction.controller.js';
6
+ import { estimateList, createEstimate, getEstimate, estimateStatusUpdate, deleteEstimate, downloadEstimate } from '../controllers/estimate.controller.js';
5
7
  import { updateInvoiceHeadsSchema } from '../dtos/validation.dtos.js';
6
8
  export const invoiceRouter = express.Router();
7
9
 
@@ -43,3 +45,19 @@ invoiceRouter.post( '/approveInvoiceApproval', isAllowedSessionHandler, superadm
43
45
 
44
46
  invoiceRouter.get( '/getInvoiceHeads', isAllowedSessionHandler, getInvoiceHeads );
45
47
  invoiceRouter.post( '/updateInvoiceHeads', isAllowedSessionHandler, validate( updateInvoiceHeadsSchema ), updateInvoiceHeads );
48
+
49
+ // Bank-statement reconciliation (billing "Transactions" tab). Upload mutates
50
+ // the banktransaction collection so it needs edit rights; list is read-only.
51
+ invoiceRouter.post( '/bankStatement/upload', isAllowedSessionHandler, superadminBypass( { userType: [ 'tango' ], access: [ { featureName: 'TangoAdmin', name: 'invoiceApproval', permissions: [ 'isEdit' ] } ] } ), uploadBankStatement );
52
+ invoiceRouter.post( '/bankStatement/list', isAllowedSessionHandler, superadminBypass( { userType: [ 'tango' ], access: [ { featureName: 'TangoAdmin', name: 'invoiceApproval', permissions: [] } ] } ), bankTransactionList );
53
+ invoiceRouter.get( '/bankStatement/resolveOptions', isAllowedSessionHandler, superadminBypass( { userType: [ 'tango' ], access: [ { featureName: 'TangoAdmin', name: 'invoiceApproval', permissions: [] } ] } ), resolveOptions );
54
+ invoiceRouter.post( '/bankStatement/resolve', isAllowedSessionHandler, superadminBypass( { userType: [ 'tango' ], access: [ { featureName: 'TangoAdmin', name: 'invoiceApproval', permissions: [ 'isEdit' ] } ] } ), resolveBankTransaction );
55
+
56
+ // Estimates (quotations) — per-brand quotation documents with their own
57
+ // lifecycle. List/get are read; create/status/delete need edit rights.
58
+ invoiceRouter.post( '/estimate/list', isAllowedSessionHandler, superadminBypass( { userType: [ 'tango' ], access: [ { featureName: 'TangoAdmin', name: 'invoiceApproval', permissions: [] } ] } ), estimateList );
59
+ invoiceRouter.post( '/estimate/create', isAllowedSessionHandler, superadminBypass( { userType: [ 'tango' ], access: [ { featureName: 'TangoAdmin', name: 'invoiceApproval', permissions: [ 'isEdit' ] } ] } ), createEstimate );
60
+ invoiceRouter.post( '/estimate/download/:estimateId', isAllowedSessionHandler, superadminBypass( { userType: [ 'tango' ], access: [ { featureName: 'TangoAdmin', name: 'invoiceApproval', permissions: [] } ] } ), downloadEstimate );
61
+ invoiceRouter.get( '/estimate/:estimateId', isAllowedSessionHandler, superadminBypass( { userType: [ 'tango' ], access: [ { featureName: 'TangoAdmin', name: 'invoiceApproval', permissions: [] } ] } ), getEstimate );
62
+ invoiceRouter.post( '/estimate/statusUpdate', isAllowedSessionHandler, superadminBypass( { userType: [ 'tango' ], access: [ { featureName: 'TangoAdmin', name: 'invoiceApproval', permissions: [ 'isEdit' ] } ] } ), estimateStatusUpdate );
63
+ invoiceRouter.delete( '/estimate/:estimateId', isAllowedSessionHandler, superadminBypass( { userType: [ 'tango' ], access: [ { featureName: 'TangoAdmin', name: 'invoiceApproval', permissions: [ 'isEdit' ] } ] } ), deleteEstimate );
@@ -0,0 +1,21 @@
1
+ import model from 'tango-api-schema';
2
+
3
+ // Bank-statement transactions (uploaded from the billing Transactions tab).
4
+ // Lives in its own `banktransaction` collection — the legacy `transaction`
5
+ // collection holds payment/wallet entries and must not be mixed with these.
6
+
7
+ export const insertMany = async ( data ) => {
8
+ return await model.bankTransactionModel.insertMany( data );
9
+ };
10
+
11
+ export const aggregate = async ( query = [] ) => {
12
+ return await model.bankTransactionModel.aggregate( query );
13
+ };
14
+
15
+ export const find = async ( query = {}, projection = {} ) => {
16
+ return await model.bankTransactionModel.find( query, projection );
17
+ };
18
+
19
+ export const updateOne = async ( filter, update ) => {
20
+ return await model.bankTransactionModel.updateOne( filter, update );
21
+ };
@@ -0,0 +1,25 @@
1
+ import model from 'tango-api-schema';
2
+
3
+ export const aggregate = async ( query = [] ) => {
4
+ return await model.estimateModel.aggregate( query );
5
+ };
6
+
7
+ export const find = async ( query = {}, projection = {} ) => {
8
+ return await model.estimateModel.find( query, projection );
9
+ };
10
+
11
+ export const findOne = async ( query = {}, projection = {} ) => {
12
+ return await model.estimateModel.findOne( query, projection );
13
+ };
14
+
15
+ export const create = async ( data ) => {
16
+ return await model.estimateModel.create( data );
17
+ };
18
+
19
+ export const updateOne = async ( filter, update ) => {
20
+ return await model.estimateModel.updateOne( filter, update );
21
+ };
22
+
23
+ export const count = async ( query = {} ) => {
24
+ return await model.estimateModel.countDocuments( query );
25
+ };