diff --git a/db/dump/fixtures.before.sql b/db/dump/fixtures.before.sql index e093c05cb..2dcedcc92 100644 --- a/db/dump/fixtures.before.sql +++ b/db/dump/fixtures.before.sql @@ -2629,9 +2629,9 @@ REPLACE INTO `vn`.`invoiceIn`(`id`, `serialNumber`,`serial`, `supplierFk`, `issu (9, 1009, 'R', 2, DATE_ADD(util.VN_CURDATE(), INTERVAL -1 MONTH), DATE_ADD(util.VN_CURDATE(), INTERVAL -1 MONTH), 1242, 0, 442, 1,util.VN_CURDATE()), (10, 1010, 'R', 2, DATE_ADD(util.VN_CURDATE(), INTERVAL -1 MONTH), DATE_ADD(util.VN_CURDATE(), INTERVAL -1 MONTH), 1243, 0, 442, 1,util.VN_CURDATE()); -INSERT INTO `vn`.`invoiceInConfig` (`id`, `retentionRate`, `retentionName`, `sageFarmerWithholdingFk`, `daysAgo`) +INSERT INTO `vn`.`invoiceInConfig` (`id`, `retentionRate`, `retentionName`, `sageFarmerWithholdingFk`, `daysAgo`, `balanceStartingDate`) VALUES - (1, -2, '2% retention', 2, 45); + (1, -2, '2% retention', 2, 45, '2000-01-01'); INSERT INTO `vn`.`invoiceInDueDay`(`invoiceInFk`, `dueDated`, `bankFk`, `amount`) VALUES @@ -4012,10 +4012,10 @@ INSERT INTO vn.routeAction (id, name, price, isMainlineDelivered) VALUES(1, 'Pin INSERT INTO vn.routeComplement (id, dated, workerFk, price, routeActionFk) VALUES(1, util.VN_CURDATE(), 9, 50.00, 1); -INSERT INTO srt.buffer (id, x, y, `size`, `length`, stateFk, typeFk, isActive, code, stratus, hasWorkerWaiting, reserve, routeFk, dayMinute, lastUnloaded, hasStrapper, typeDefaultFk, motors, editorFk) +INSERT INTO srt.buffer (id, x, y, `size`, `length`, stateFk, typeFk, isActive, code, stratus, hasWorkerWaiting, reserve, routeFk, dayMinute, lastUnloaded, hasStrapper, typeDefaultFk, motors, editorFk) VALUES (0, 0, 0, 0, NULL, 3, 1, 0, 'ENT', 0, 0, NULL, NULL, NULL, NULL, 0, 1, 1, NULL), (1, 0, 9900, 0, NULL, 1, 0, 0, 'NOK', 0, 0, NULL, NULL, NULL, NULL, 0, 1, 1, NULL), - (2, 0, 0, 450, 13000, 1, 0, 1, '01A', 1, 1, NULL, NULL, NULL, NULL, 0, 1, 1, NULL), + (2, 0, 0, 450, 13000, 1, 0, 1, '01A', 1, 1, NULL, NULL, NULL, NULL, 0, 1, 1, NULL), (3, 1400, 0, 450, 13000, 1, 0, 1, '01B', 1, 0, NULL, NULL, NULL, NULL, 0, 1, 1, NULL), (4, 0, 500, 500, 13000, 1, 4, 1, '02A', 2, 1, NULL, NULL, NULL, NULL, 1, 4, 13, NULL), (5, 1400, 500, 500, 13000, 1, 4, 1, '02B', 2, 1, NULL, NULL, NULL, NULL, 1, 4, 13, NULL), @@ -4026,9 +4026,13 @@ INSERT INTO srt.buffer (id, x, y, `size`, `length`, stateFk, typeFk, isActive, c (10, 0, 2000, 500, 13000, 1, 1, 1, '05A', 5, 0, NULL, NULL, NULL, NULL, 0, 1, 1, NULL); -INSERT IGNORE INTO vn.saySimpleCountry (countryFk, channel) +INSERT IGNORE INTO vn.saySimpleCountry (countryFk, channel) VALUES (19, '1169'), (8, '1183'); -INSERT IGNORE INTO vn.saySimpleConfig (url, defaultChannel) - VALUES ('saysimle-url-mock', 1320); \ No newline at end of file +INSERT IGNORE INTO vn.saySimpleConfig (url, defaultChannel) + VALUES ('saysimle-url-mock', 1320); + + + INSERT INTO vn.payment (received,supplierFk,amount,currencyFk,divisa,bankFk,payMethodFk,bankingFees,concept,companyFk,created,isConciliated,dueDated,workerFk) VALUES + ('2024-09-15',1,1000.00,1,NULL,1,1,0.0,'n/pago',442,'2024-11-20 13:06:02.000',1,'2024-09-15',9); diff --git a/db/routines/vn/procedures/supplier_statementWithEntries.sql b/db/routines/vn/procedures/supplier_statementWithEntries.sql index c0014f8e5..b678588fe 100644 --- a/db/routines/vn/procedures/supplier_statementWithEntries.sql +++ b/db/routines/vn/procedures/supplier_statementWithEntries.sql @@ -154,7 +154,7 @@ BEGIN JOIN travel tr ON tr.id = e.travelFk JOIN currency c ON c.id = e.currencyFk WHERE e.supplierFk = vSupplierFk - AND tr.landed >= CURDATE() + AND tr.landed >= util.VN_CURDATE() AND e.invoiceInFk IS NULL AND vHasEntries ORDER BY (dated IS NULL AND NOT isBooked), diff --git a/modules/supplier/back/methods/supplier/createReceipt.js b/modules/supplier/back/methods/supplier/createReceipt.js new file mode 100644 index 000000000..c051a6751 --- /dev/null +++ b/modules/supplier/back/methods/supplier/createReceipt.js @@ -0,0 +1,173 @@ +const UserError = require('vn-loopback/util/user-error'); +// Insert payment + +// divisa = solo en caso que la moneda sea distinta a €, +module.exports = function(Self) { + Self.remoteMethodCtx('createReceipt', { + description: 'Creates receipt and its compensation if necessary', + accessType: 'READ', + accepts: [{ + arg: 'supplierFk', + type: 'number', + description: 'The supplier id', + http: {source: 'path'} + }, + { + arg: 'received', + type: 'Date', + required: true + }, + { + arg: 'dueDate', + type: 'Date', + required: true + }, + { + arg: 'companyFk', + type: 'number', + required: true + }, + { + arg: 'currencyFk', + type: 'number', + required: true + }, + { + arg: 'bankFk', + type: 'number', + required: true + }, + { + arg: 'payMethodFk', + type: 'number', + required: true + }, + { + arg: 'amount', + type: 'number', + required: true + }, + { + arg: 'concept', + type: 'string', + required: true + }, + { + arg: 'divisa', + type: 'number' + }, + { + arg: 'compensationAccount', + type: 'any' + }], + returns: { + root: true, + type: 'Object' + }, + http: { + verb: 'post', + path: '/:supplierFk/createReceipt' + }, + accessScopes: ['DEFAULT', 'read:multimedia'] + }); + + Self.createReceipt = async(ctx, options) => { + const models = Self.app.models; + const args = ctx.args; + const date = Date.vnNew(); + date.setHours(0, 0, 0, 0); + + let tx; + const myOptions = {userId: ctx.req.accessToken.userId}; + + if (typeof options == 'object') + Object.assign(myOptions, options); + + if (!myOptions.transaction) { + tx = await Self.beginTransaction({}); + myOptions.transaction = tx; + } + + try { + delete args.ctx; // Remove unwanted properties + + const originalSupplier = await models.Supplier.findById(args.supplierFk, null, myOptions); + const bank = await models.Accounting.findById(args.bankFk, null, myOptions); + const accountingType = await models.AccountingType.findById(bank.accountingTypeFk, null, myOptions); + + if (accountingType.code == 'compensation') { + if (!args.compensationAccount) + throw new UserError('Compensation account is empty'); + + // Check compensation account exists + await models.Supplier.getClientOrSupplierReference(args.compensationAccount, myOptions); + + await Self.rawSql( + `CALL vn.ledger_doCompensation(?, ?, ?, ?, ?, ?, ?)`, + [ + date, + args.compensationAccount, + args.bankFk, + accountingType.receiptDescription + originalSupplier.accountingAccount, + args.amountPaid, + args.companyFk, + originalSupplier.accountingAccount + ], + myOptions + ); + } else if (accountingType.isAutoConciliated == true) { + const description = + `${originalSupplier.id} : ${originalSupplier.socialName} - ${accountingType.receiptDescription}`; + const [, [xdiarioNew]] = await Self.rawSql( + `CALL xdiario_new(?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, @xdiarioNew); + SELECT @xdiarioNew ledger;`, + [ + null, + date, + bank.account, + originalSupplier.accountingAccount, + description, + args.amountPaid, + 0, + 0, + '', + '', + null, + null, + false, + args.companyFk + ], + myOptions + ); + + await Self.rawSql( + `CALL xdiario_new(?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, @xdiarioNew);`, + [ + xdiarioNew.ledger, + date, + originalSupplier.accountingAccount, + bank.account, + description, + 0, + args.amountPaid, + 0, + '', + '', + null, + null, + false, + args.companyFk + ], + myOptions + ); + } + const newReceipt = await models.Receipt.create(args, myOptions); + if (tx) await tx.commit(); + + return newReceipt; + } catch (e) { + if (tx) await tx.rollback(); + throw e; + } + }; +}; diff --git a/modules/supplier/back/methods/supplier/receipts.js b/modules/supplier/back/methods/supplier/receipts.js new file mode 100644 index 000000000..474cd256f --- /dev/null +++ b/modules/supplier/back/methods/supplier/receipts.js @@ -0,0 +1,73 @@ + +const ParameterizedSQL = require('loopback-connector').ParameterizedSQL; +module.exports = Self => { + Self.remoteMethodCtx('receipts', { + description: 'Find all clients matched by the filter', + accessType: 'READ', + accepts: [ + { + arg: 'supplierId', + type: 'number', + description: 'The supplier id', + }, + { + arg: 'companyId', + type: 'number', + description: 'The company id', + }, + { + arg: 'currencyFk', + type: 'number', + description: 'The currency', + default: 1, + }, + { + arg: 'orderBy', + type: 'string', + description: 'The client fiscal id', + enum: ['issued', ' bookEntried', ' booked', ' dueDate'], + }, + { + arg: 'isConciliated', + default: false, + type: 'boolean', + }, + ], + returns: { + type: ['object'], + root: true + }, + http: { + path: `/receipts`, + verb: 'GET' + } + }); + + Self.receipts = async(ctx, filter, options) => { + const conn = Self.dataSource.connector; + const myOptions = {userId: ctx.req.accessToken.userId}; + const args = ctx.args; + + if (typeof options == 'object') + Object.assign(myOptions, options); + + let stmts = []; + stmts.push(new ParameterizedSQL('CALL vn.supplier_statementWithEntries(?,?,?,?,?,?)', [ + args.supplierId, + args.currencyFk ?? 1, + args.companyId, + args.orderBy ?? 'issued', + args.isConciliated ?? false, + false + ])); + stmts.push(` + SELECT * + FROM tmp.supplierStatement`); + stmts.push(`DROP TEMPORARY TABLE tmp.supplierStatement`); + const sql = ParameterizedSQL.join(stmts, ';'); + const results = await conn.executeStmt(sql); + const resultsIndex = stmts.length - 2; + const result = results[resultsIndex]; + return result; + }; +}; diff --git a/modules/supplier/back/models/supplier.js b/modules/supplier/back/models/supplier.js index 7e6908d57..20313801e 100644 --- a/modules/supplier/back/models/supplier.js +++ b/modules/supplier/back/models/supplier.js @@ -8,6 +8,8 @@ module.exports = Self => { require('../methods/supplier/updateFiscalData')(Self); require('../methods/supplier/consumption')(Self); require('../methods/supplier/freeAgencies')(Self); + require('../methods/supplier/createReceipt')(Self); + require('../methods/supplier/receipts')(Self); require('../methods/supplier/campaignMetricsPdf')(Self); require('../methods/supplier/campaignMetricsEmail')(Self); require('../methods/supplier/newSupplier')(Self);