Merge branch 'dev' into 6553-workerBusiness
gitea/salix/pipeline/pr-dev This commit looks good Details

This commit is contained in:
Carlos Satorres 2025-01-31 09:16:13 +00:00
commit e1125dc46d
30 changed files with 294 additions and 835 deletions

View File

@ -52,7 +52,7 @@
},
"payMethod": {
"type": "belongsTo",
"model": "PayMethodFk",
"model": "PayMethod",
"foreignKey": "payMethodFk"
},
"company": {
@ -61,4 +61,4 @@
"foreignKey": "companyFk"
}
}
}
}

View File

@ -752,13 +752,15 @@ INSERT INTO `vn`.`zoneConfig` (`id`, `scope`) VALUES (1, '1');
INSERT INTO `vn`.`route`(`id`, `time`, `workerFk`, `created`, `vehicleFk`, `agencyModeFk`, `description`, `m3`, `cost`, `started`, `finished`, `zoneFk`, `dated`)
VALUES
(1, '1899-12-30 12:15:00', 56, util.VN_CURDATE(), 1, 1, 'first route', 1.8, 10, util.VN_CURDATE(), DATE_ADD(util.VN_CURDATE(), INTERVAL + 1 DAY), 1, util.VN_CURDATE()),
(1, '1899-12-30 12:15:00', 133, util.VN_CURDATE(), 1, 1, 'first route', 1.8, 10, util.VN_CURDATE(), DATE_ADD(util.VN_CURDATE(), INTERVAL + 1 DAY), 1, util.VN_CURDATE()),
(2, '1899-12-30 13:20:00', 56, util.VN_CURDATE(), 1, 2, 'second route', 0.2, 20, util.VN_CURDATE(), DATE_ADD(util.VN_CURDATE(), INTERVAL + 1 DAY), 9, util.VN_CURDATE()),
(3, '1899-12-30 14:30:00', 56, util.VN_CURDATE(), 2, 3, 'third route', 0.5, 30, util.VN_CURDATE(), DATE_ADD(util.VN_CURDATE(), INTERVAL + 1 DAY), 10, util.VN_CURDATE()),
(4, '1899-12-30 15:45:00', 56, util.VN_CURDATE(), 3, 4, 'fourth route', 0, 40, util.VN_CURDATE(), DATE_ADD(util.VN_CURDATE(), INTERVAL + 1 DAY), 12, util.VN_CURDATE()),
(5, '1899-12-30 16:00:00', 56, util.VN_CURDATE(), 4, 5, 'fifth route', 0.1, 50, util.VN_CURDATE(), DATE_ADD(util.VN_CURDATE(), INTERVAL + 1 DAY), 13, util.VN_CURDATE()),
(6, NULL, 57, util.VN_CURDATE(), 5, 7, 'sixth route', 1.7, 60, util.VN_CURDATE(), DATE_ADD(util.VN_CURDATE(), INTERVAL + 1 DAY), 3, util.VN_CURDATE()),
(7, NULL, 57, util.VN_CURDATE(), 6, 8, 'seventh route', 0, 70, util.VN_CURDATE(), DATE_ADD(util.VN_CURDATE(), INTERVAL + 1 DAY), 5, util.VN_CURDATE());
(7, NULL, 57, util.VN_CURDATE(), 6, 8, 'seventh route', 0, 70, util.VN_CURDATE(), DATE_ADD(util.VN_CURDATE(), INTERVAL + 1 DAY), 5, util.VN_CURDATE()),
(8, NULL, 133, util.VN_CURDATE(), 1, 1, 'eighth route', 1.8, 10.0, util.VN_CURDATE(), DATE_ADD(util.VN_CURDATE(), INTERVAL + 1 DAY), 1, util.VN_CURDATE()),
(9, NULL, 133, util.VN_CURDATE(), 1, 2, 'ninth route', 0.2, 20.0, util.VN_CURDATE(), DATE_ADD(util.VN_CURDATE(), INTERVAL + 1 DAY), 9, util.VN_CURDATE());
INSERT INTO `vn`.`ticket`(`id`, `priority`, `agencyModeFk`,`warehouseFk`,`routeFk`, `shipped`, `landed`, `clientFk`,`nickname`, `addressFk`, `refFk`, `isDeleted`, `zoneFk`, `zonePrice`, `zoneBonus`, `created`, `weight`, `cmrFk`, `problem`, `risk`)
VALUES
@ -2639,9 +2641,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
@ -4050,6 +4052,9 @@ INSERT IGNORE INTO vn.saySimpleConfig (url, defaultChannel)
INSERT INTO vn.workerIrpf (workerFk,spouseNif, geographicMobilityDate)
VALUES (1106,'26493101E','2019-09-20');
INSERT INTO vn.payment (received, supplierFk, amount, currencyFk, divisa, bankFk, payMethodFk, bankingFees, concept, companyFk, created, isConciliated, dueDated, workerFk) VALUES
(util.VN_CURDATE(), 1, 1000.00, 1, NULL, 1, 1, 0.0, 'n/pago', 442, util.VN_CURDATE(), 1, util.VN_CURDATE(), 9);
INSERT INTO vn.referenceRate (currencyFk, dated, value)
VALUES (2, '2000-12-01', 1.0495),
(2, '2001-01-01', 1.0531),
@ -4061,3 +4066,8 @@ INSERT IGNORE INTO vn.osrmConfig (id,url,tolerance)
INSERT IGNORE INTO vn.inventoryConfig
SET id = 1,
supplierFk = 4;
UPDATE vn.worker
SET isFreelance=1
WHERE firstName='deliveryFreelancer';

View File

@ -3,7 +3,7 @@ CREATE OR REPLACE DEFINER=`root`@`localhost` PROCEDURE `cache`.`available_refres
OUT `vCalc` INT,
`vRefresh` INT,
`vWarehouse` INT,
`vDated` DATE
`vAvailabled` DATETIME
)
proc: BEGIN
DECLARE vStartDate DATE;
@ -12,6 +12,7 @@ proc: BEGIN
DECLARE vInventoryDate DATE;
DECLARE vLifeScope DATE;
DECLARE vWarehouseFkInventory INT;
DECLARE vDated DATE;
DECLARE EXIT HANDLER FOR SQLEXCEPTION
BEGIN
@ -19,13 +20,17 @@ proc: BEGIN
RESIGNAL;
END;
IF vDated < util.VN_CURDATE() THEN
IF vAvailabled < util.VN_CURDATE() THEN
LEAVE proc;
END IF;
SET vDated = DATE(vAvailabled);
SET vAvailabled = vDated + INTERVAL HOUR(vAvailabled) HOUR;
CALL vn.item_getStock(vWarehouse, vDated, NULL);
SET vParams = CONCAT_WS('/', vWarehouse, vDated);
SET vParams = CONCAT_WS('/', vWarehouse, vAvailabled);
CALL cache_calc_start (vCalc, vRefresh, 'available', vParams);
IF !vRefresh THEN
@ -87,11 +92,10 @@ proc: BEGIN
SELECT i.itemFk, i.landed, i.quantity
FROM vn.itemEntryIn i
JOIN itemRange ir ON ir.itemFk = i.itemFk
LEFT JOIN edi.warehouseFloramondo wf ON wf.entryFk = i.entryFk
WHERE i.landed >= vStartDate
AND IFNULL(i.availabled, i.landed) <= vAvailabled
AND (ir.ended IS NULL OR i.landed <= ir.ended)
AND i.warehouseInFk = vWarehouse
AND ISNULL(wf.entryFk)
UNION ALL
SELECT i.itemFk, i.shipped, i.quantity
FROM vn.itemEntryOut i

View File

@ -1,11 +1,11 @@
DELIMITER $$
CREATE OR REPLACE DEFINER=`root`@`localhost` PROCEDURE `sage`.`accountingMovements_add`(
vYear INT,
vYear INT,
vCompanyFk INT
)
BEGIN
/**
* Traslada la info de contabilidad generada en base a vn.XDiario a la tabla sage.movConta
* Traslada la info de contabilidad generada en base a vn.XDiario a la tabla sage.movConta
* para poder ejecutar posteriormente el proceso de importación de datos de SQL Server
* Solo traladará los asientos marcados con el campo vn.XDiario.enlazadoSage = FALSE
* @vYear Año contable del que se quiere trasladar la información
@ -23,6 +23,7 @@ BEGIN
DECLARE vInvoiceTypeInformativeCode VARCHAR(1);
DECLARE vCountryCanariasCode, vCountryCeutaMelillaCode VARCHAR(2);
DECLARE vCompanyCode INT;
DECLARE vHasErrorTax BOOL DEFAULT FALSE;
SELECT SiglaNacion INTO vCountryCanariasCode
FROM Naciones
@ -44,12 +45,12 @@ BEGIN
FROM taxType
WHERE code = 'import4';
SELECT shipmentTransactionTypeFk,
definitiveExportTransactionTypeFk,
SELECT shipmentTransactionTypeFk,
definitiveExportTransactionTypeFk,
pendingServiceTransactionTypeFk,
company_getCode(vCompanyFk)
INTO vTransactionExportTaxFreeFk,
vTransactionExportFk,
vTransactionExportFk,
vDuaTransactionFk,
vCompanyCode
FROM config;
@ -66,6 +67,24 @@ BEGIN
WHERE enlazadoSage = FALSE
AND Asiento <> 1 ;
SELECT EXISTS (
SELECT TRUE
FROM vn.XDiario x
JOIN vn.invoiceIn ii ON ii.id = x.CLAVE
JOIN vn.invoiceInTax it ON it.invoiceInFk = ii.id
LEFT JOIN TiposIva ti ON ti.CodigoIva = it.taxTypeSageFk
LEFT JOIN taxType tt ON tt.id = it.taxTypeSageFk
WHERE x.FECHA BETWEEN vDatedFrom AND vDatedTo
AND NOT x.enlazadoSage
AND x.empresa_id = vCompanyFk
AND it.taxTypeSageFk
AND (ti.CodigoIva IS NULL OR tt.id IS NULL)
) INTO vHasErrorTax;
IF vHasErrorTax tHEN
CALL util.throw ('Error in tables for received invoices tax');
END IF;
CALL invoiceOut_manager(vYear, vCompanyFk);
CALL invoiceIn_manager(vYear, vCompanyFk);
@ -306,8 +325,8 @@ BEGIN
mci.FechaFacturaOriginal = x.FECHA_EX,
mci.SuFacturaNo = x.FACTURAEX,
mci.FechaOperacion = x.FECHA_OP,
mci.ImporteFactura = mci.ImporteFactura +
x.BASEEURO +
mci.ImporteFactura = mci.ImporteFactura +
x.BASEEURO +
CAST((x.IVA / 100) * x.BASEEURO AS DECIMAL(10, 2))
WHERE pm.description = 'HP Iva pendiente'
AND mci.enlazadoSage = FALSE
@ -326,7 +345,7 @@ BEGIN
mci.CodigoIva2 = vTaxImportFk,
mci.IvaDeducible2 = TRUE,
mci.ImporteFactura = mci.ImporteFactura +
x.BASEEURO +
x.BASEEURO +
CAST((x.IVA / 100) * x.BASEEURO AS DECIMAL(10, 2))
WHERE pm.description = 'HP Iva pendiente'
AND mci.enlazadoSage = FALSE
@ -344,8 +363,8 @@ BEGIN
mci.CodigoTransaccion3 = vDuaTransactionFk ,
mci.CodigoIva3 = vTaxImportSuperReducedFk,
mci.IvaDeducible3 = TRUE,
mci.ImporteFactura = mci.ImporteFactura +
x.BASEEURO +
mci.ImporteFactura = mci.ImporteFactura +
x.BASEEURO +
CAST((x.IVA / 100) * x.BASEEURO AS DECIMAL(10, 2))
WHERE pm.description = 'HP Iva pendiente'
AND mci.enlazadoSage = FALSE
@ -379,14 +398,14 @@ BEGIN
OR CodigoTransaccion2 = vTransactionExportFk
OR CodigoTransaccion3 = vTransactionExportFk
OR CodigoTransaccion4 = vTransactionExportFk)
AND SiglaNacion IN (vCountryCanariasCode COLLATE utf8mb3_unicode_ci,
AND SiglaNacion IN (vCountryCanariasCode COLLATE utf8mb3_unicode_ci,
vCountryCeutaMelillaCode COLLATE utf8mb3_unicode_ci);
UPDATE movConta mc
SET CodigoDivisa = 'USD',
FactorCambio = TRUE,
ImporteCambio = ABS( CAST( IF( ImporteDivisa <> 0 AND ImporteCambio = 0,
ImporteAsiento / ImporteDivisa,
ImporteCambio = ABS( CAST( IF( ImporteDivisa <> 0 AND ImporteCambio = 0,
ImporteAsiento / ImporteDivisa,
ImporteCambio) AS DECIMAL( 10, 2)))
WHERE enlazadoSage = FALSE
AND (ImporteCambio <> 0 OR ImporteDivisa <> 0 OR FactorCambio);
@ -403,20 +422,20 @@ BEGIN
WITH client AS(
SELECT DISTINCT c.id
FROM sage.movConta mc
JOIN vn.client c ON c.accountingAccount = mc.CodigoCuenta
WHERE NOT enlazadoSage
),supplier AS(
JOIN vn.client c ON c.accountingAccount = mc.CodigoCuenta
WHERE NOT enlazadoSage
),supplier AS(
SELECT DISTINCT s.id
FROM sage.movConta mc
JOIN vn.supplier s ON s.account = mc.CodigoCuenta
WHERE NOT enlazadoSage
JOIN vn.supplier s ON s.account = mc.CodigoCuenta
WHERE NOT enlazadoSage
),clientSupplierSync AS(
SELECT idClientSupplier, `type`
FROM sage.clientSupplier cs
WHERE isSync
FROM sage.clientSupplier cs
WHERE isSync
)
SELECT idClientSupplier, `type`
FROM sage.clientSupplier cs
FROM sage.clientSupplier cs
WHERE NOT isSync
UNION
SELECT id, 'C'
@ -424,7 +443,7 @@ BEGIN
LEFT JOIN clientSupplierSync cs ON cs.idClientSupplier = c.id
AND cs.Type ='C'
WHERE cs.idClientSupplier IS NULL
UNION
UNION
SELECT id, 'P'
FROM supplier s
LEFT JOIN clientSupplierSync cs ON cs.idClientSupplier = s.id
@ -436,7 +455,7 @@ BEGIN
INSERT IGNORE INTO sage.clientSupplier (companyFk, `type`, idClientSupplier, isSync)
SELECT vCompanyCode, `type`, idClientSupplier, FALSE
FROM tmp.clientSupplier;
DROP TEMPORARY TABLE tmp.clientSupplier;
CALL pgc_add(vCompanyFk);

View File

@ -1,5 +1,5 @@
DELIMITER $$
CREATE OR REPLACE DEFINER=`root`@`localhost` PROCEDURE `vn`.`entry_transfer`(
CREATE OR REPLACE DEFINER=`vn`@`localhost` PROCEDURE `vn`.`entry_transfer`(
vOriginalEntry INT,
OUT vNewEntryFk INT
)

View File

@ -15,8 +15,6 @@ BEGIN
*
* @return tmp.itemList(itemFk, stock, visible, available)
*/
DECLARE vIsLogifloraDay BOOL DEFAULT vn.isLogifloraDay(vDated, vWarehouseFk);
SET vDated = TIMESTAMP(vDated, '00:00:00');
CREATE OR REPLACE TEMPORARY TABLE tmp.itemList
@ -36,14 +34,11 @@ BEGIN
UNION ALL
SELECT iei.itemFk, iei.quantity
FROM itemEntryIn iei
LEFT JOIN edi.warehouseFloramondo wf ON wf.entryFk = iei.entryFk
JOIN item i ON i.id = iei.itemFk
WHERE iei.landed >= util.VN_CURDATE()
AND iei.landed < vDated
AND iei.warehouseInFk = vWarehouseFk
AND (vItemFk IS NULL OR iei.itemFk = vItemFk)
AND (wf.entryFk IS NULL OR vIsLogifloraDay)
AND NOT (iei.landed > util.VN_CURDATE() AND i.isFloramondo)
UNION ALL
SELECT ieo.itemFk, ieo.quantity
FROM itemEntryOut ieo
@ -52,7 +47,6 @@ BEGIN
AND ieo.shipped < vDated
AND ieo.warehouseOutFk = vWarehouseFk
AND (vItemFk IS NULL OR ieo.itemFk = vItemFk)
AND NOT (ieo.shipped > util.VN_CURDATE() AND i.isFloramondo)
) sub
GROUP BY itemFk
HAVING stock;

View File

@ -41,6 +41,7 @@ BEGIN
) currencyBalance
FROM (
SELECT NULL bankFk,
NULL bank,
ii.companyFk,
ii.serial,
ii.id,
@ -74,6 +75,7 @@ BEGIN
GROUP BY iid.id, ii.id
UNION ALL
SELECT p.bankFk,
a.bank,
p.companyFk,
NULL,
p.id,
@ -109,6 +111,7 @@ BEGIN
AND (vIsConciliated = p.isConciliated OR NOT vIsConciliated)
UNION ALL
SELECT NULL,
NULL bankFk,
companyFk,
NULL,
se.id,
@ -136,6 +139,7 @@ BEGIN
AND (vIsConciliated = se.isConciliated OR NOT vIsConciliated)
UNION ALL
SELECT NULL bankFk,
NULL,
e.companyFk,
'E' serial,
e.invoiceNumber id,
@ -154,7 +158,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),

View File

@ -16,5 +16,9 @@ BEGIN
IF NEW.awbFk IS NOT NULL THEN
CALL travel_throwAwb(NEW.id);
END IF;
IF NEW.availabled < NEW.landed THEN
CALL util.throw('The travel availabled cannot be earlier than landed');
END IF;
END$$
DELIMITER ;

View File

@ -40,5 +40,9 @@ BEGIN
IF (NOT(NEW.awbFk <=> OLD.awbFk)) AND NEW.awbFk IS NOT NULL THEN
CALL travel_throwAwb(NEW.id);
END IF;
IF NEW.availabled < NEW.landed THEN
CALL util.throw('The travel availabled cannot be earlier than landed');
END IF;
END$$
DELIMITER ;

View File

@ -7,7 +7,8 @@ AS SELECT `t`.`warehouseInFk` AS `warehouseInFk`,
`b`.`quantity` AS `quantity`,
`t`.`isReceived` AS `isReceived`,
`t`.`isRaid` AS `isVirtualStock`,
`e`.`id` AS `entryFk`
`e`.`id` AS `entryFk`,
`t`.`availabled`
FROM (
(
`vn`.`buy` `b`

View File

@ -0,0 +1,19 @@
INSERT INTO account.`role` (name,description,hasLogin)
VALUES ('deliveryFreelancer','Repartidor autónomo',1);
INSERT INTO salix.ACL (model, property, accessType, permission, principalType, principalId)
VALUES
('Route', 'getTickets', 'READ', 'ALLOW', 'ROLE', 'deliveryFreelancer'),
('AgencyTerm', 'filter', 'READ', 'ALLOW', 'ROLE', 'deliveryFreelancer'),
('Route', 'summary', 'READ', 'ALLOW', 'ROLE', 'deliveryFreelancer'),
('Route', 'getRouteByAgency', 'WRITE', 'ALLOW', 'ROLE', 'deliveryFreelancer'),
('Route','filter','READ','ALLOW','ROLE','deliveryFreelancer'),
('UserConfig','getUserConfig','*','ALLOW','ROLE','deliveryFreelancer'),
('Route', 'getTickets', 'READ', 'ALLOW', 'ROLE', 'deliveryFreelancer'),
('Route','guessPriority','WRITE','ALLOW','ROLE','deliveryFreelancer'),
('Route','getDeliveryPoint','READ','ALLOW','ROLE','deliveryFreelancer'),
('Route', 'findById', 'READ', 'ALLOW', 'ROLE', 'deliveryFreelancer'),
('Route','sendSms','WRITE','ALLOW','ROLE','deliveryFreelancer'),
('Ticket','updateAttributes','WRITE','ALLOW','ROLE','deliveryFreelancer'),
('Client','findById','READ','ALLOW','ROLE','deliveryFreelancer');
;

View File

@ -0,0 +1,3 @@
-- Place your SQL code here
ALTER TABLE vn.travel ADD IF NOT EXISTS availabled DATETIME NULL
COMMENT 'Indicates the moment in time when the goods become available for picking';

View File

@ -0,0 +1,2 @@
INSERT IGNORE INTO salix.ACL (model, property, accessType, permission, principalType, principalId)
VALUES('Entry', 'transfer', 'WRITE', 'ALLOW', 'ROLE', 'coolerBoss');

View File

@ -0,0 +1,2 @@
ALTER TABLE vn.packaging
ADD COLUMN flippingCost decimal(10, 2) NOT NULL DEFAULT 0.00

View File

@ -0,0 +1 @@
ALTER TABLE vn.travel CHANGE appointment appointment__ datetime DEFAULT NULL COMMENT '@deprecated 2025-01-28';

View File

@ -1,65 +0,0 @@
import selectors from '../../helpers/selectors.js';
import getBrowser from '../../helpers/puppeteer';
describe('Client defaulter path', () => {
let browser;
let page;
beforeAll(async() => {
browser = await getBrowser();
page = browser.page;
await page.loginAndModule('insurance', 'client');
await page.accessToSection('client.defaulter');
});
afterAll(async() => {
await browser.close();
});
it('should count the amount of clients in the turns section', async() => {
const result = await page.countElement(selectors.clientDefaulter.anyClient);
expect(result).toEqual(6);
});
it('should check contain expected client', async() => {
const clientName =
await page.waitToGetProperty(selectors.clientDefaulter.firstClientName, 'innerText');
const salesPersonName =
await page.waitToGetProperty(selectors.clientDefaulter.firstSalesPersonName, 'innerText');
expect(clientName).toEqual('Ororo Munroe');
expect(salesPersonName).toEqual('salesperson');
});
it('should first observation not changed', async() => {
const expectedObservation = 'Madness, as you know, is like gravity, all it takes is a little push';
const result = await page.waitToGetProperty(selectors.clientDefaulter.firstObservation, 'value');
expect(result).toContain(expectedObservation);
});
it('should not add empty observation', async() => {
await page.waitToClick(selectors.clientDefaulter.allDefaulterCheckbox);
await page.waitToClick(selectors.clientDefaulter.addObservationButton);
await page.write(selectors.clientDefaulter.observation, '');
await page.waitToClick(selectors.clientDefaulter.saveButton);
const message = await page.waitForSnackbar();
expect(message.text).toContain(`The message can't be empty`);
});
it('should checked all defaulters', async() => {
await page.loginAndModule('insurance', 'client');
await page.accessToSection('client.defaulter');
await page.waitToClick(selectors.clientDefaulter.allDefaulterCheckbox);
});
it('should add observation for all clients', async() => {
await page.waitToClick(selectors.clientDefaulter.addObservationButton);
await page.write(selectors.clientDefaulter.observation, 'My new observation');
await page.waitToClick(selectors.clientDefaulter.saveButton);
});
});

View File

@ -1,200 +1,2 @@
<vn-crud-model
vn-id="model"
url="Defaulters/filter"
filter="::$ctrl.filter"
limit="20"
order="amount DESC"
data="$ctrl.defaulters"
on-data-change="$ctrl.reCheck()"
auto-load="true">
</vn-crud-model>
<vn-portal slot="topbar">
<vn-searchbar
vn-focus
placeholder="Search client"
info="Search client by id or name"
auto-state="false"
model="model">
</vn-searchbar>
</vn-portal>
<vn-card>
<smart-table
model="model"
options="$ctrl.smartTableOptions"
expr-builder="$ctrl.exprBuilder(param, value)">
<slot-actions>
<div>
<div class="totalBox" style="text-align: center;">
<h6 translate>Total</h6>
<vn-label-value
label="Balance due"
value="{{$ctrl.balanceDueTotal | currency: 'EUR': 2}}">
</vn-label-value>
</div>
</div>
<div class="vn-pa-md">
<vn-button
disabled="$ctrl.checked.length == 0"
ng-click="notesDialog.show()"
name="notesDialog"
vn-tooltip="Add observation"
icon="icon-notes">
</vn-button>
</div>
</slot-actions>
<slot-table>
<table>
<thead>
<tr>
<th shrink>
<vn-multi-check
model="model">
</vn-multi-check>
</th>
<th field="clientFk">
<span translate>Client</span>
</th>
<th field="isWorker">
<span translate>Es trabajador</span>
</th>
<th field="salesPersonFk">
<span translate>Comercial</span>
</th>
<th field="countryFk">
<span translate>Country</span>
</th>
<th field="payMethod"
vn-tooltip="Pay Method">
<span translate>P.Method</span>
</th>
<th
field="amount"
vn-tooltip="Balance due">
<span translate>Balance D.</span>
</th>
<th
field="workerFk"
vn-tooltip="Worker who made the last observation">
<span translate>Author</span>
</th>
<th field="observation" expand>
<span translate>Last observation</span>
</th>
<th
vn-tooltip="Last observation date"
field="created">
<span translate>L. O. Date</span>
</th>
<th
vn-tooltip="Credit insurance"
field="creditInsurance"
shrink>
<span translate>Credit I.</span>
</th>
<th field="defaulterSinced">
<span translate>From</span>
</th>
</tr>
</thead>
<tbody>
<tr ng-repeat="defaulter in $ctrl.defaulters">
<td shrink>
<vn-check
ng-model="defaulter.checked"
on-change="$ctrl.saveChecked(defaulter.clientFk)"
vn-click-stop>
</vn-check>
</td>
<td title="{{::defaulter.clientName}}">
<span
vn-click-stop="clientDescriptor.show($event, defaulter.clientFk)"
title ="{{::defaulter.clientName}}"
class="link">
{{::defaulter.clientName}}
</span>
</td>
<td>
<vn-check
ng-model="defaulter.isWorker"
disabled="true">
</vn-check>
</td>
<td>
<span
title="{{::defaulter.salesPersonName}}"
vn-click-stop="workerDescriptor.show($event, defaulter.salesPersonFk)"
class="link">
{{::defaulter.salesPersonName | dashIfEmpty}}
</span>
</td>
<td>
{{::defaulter.country}}
</td>
<td>
{{::defaulter.payMethod}}
</td>
<td>{{::defaulter.amount | currency: 'EUR': 2}}</td>
<td>
<span
title="{{::defaulter.workerName}}"
vn-click-stop="workerDescriptor.show($event, defaulter.workerFk)"
class="link">
{{::defaulter.workerName | dashIfEmpty}}
</span>
</td>
<td expand>
<vn-textarea
vn-three
disabled="true"
ng-model="defaulter.observation">
</vn-textarea>
</td>
<td shrink-date>
<span class="chip {{::$ctrl.chipColor(defaulter.created)}}">
{{::defaulter.created | date: 'dd/MM/yyyy'}}
</span>
</td>
<td shrink>{{::defaulter.creditInsurance | currency: 'EUR': 2}}</td>
<td shrink-date>{{::defaulter.defaulterSinced | date: 'dd/MM/yyyy'}}</td>
</tr>
</tbody>
</table>
</slot-table>
</smart-table>
</vn-card>
<vn-client-descriptor-popover
vn-id="client-descriptor">
</vn-client-descriptor-popover>
<vn-worker-descriptor-popover
vn-id="worker-descriptor">
</vn-worker-descriptor-popover>
<vn-popup vn-id="dialog-summary-client">
<vn-client-summary
client="$ctrl.clientSelected">
</vn-client-summary>
</vn-popup>
<!-- Dialog of add notes button -->
<vn-dialog
vn-id="notesDialog"
on-accept="$ctrl.onResponse()">
<tpl-body>
<section class="SMSDialog">
<h5 class="vn-py-sm">{{$ctrl.$t('Add observation to all selected clients', {total: $ctrl.checked.length})}}</h5>
<vn-horizontal>
<vn-textarea vn-one
vn-id="message"
label="Message"
ng-model="$ctrl.defaulter.observation"
rows="3"
required="true"
rule>
</vn-textarea>
</vn-horizontal>
</section>
</tpl-body>
<tpl-buttons>
<input type="button" response="cancel" translate-attr="{value: 'Cancel'}"/>
<button response="accept" translate>Save</button>
</tpl-buttons>
</vn-dialog>

View File

@ -1,199 +1,13 @@
import ngModule from '../module';
import Section from 'salix/components/section';
import UserError from 'core/lib/user-error';
export default class Controller extends Section {
constructor($element, $) {
super($element, $);
this.defaulter = {};
this.defaulters = [];
this.checkedDefaulers = [];
this.smartTableOptions = {
activeButtons: {
search: true
},
columns: [
{
field: 'clientFk',
autocomplete: {
url: 'Clients',
showField: 'name',
valueField: 'id'
}
}, {
field: 'salesPersonFk',
autocomplete: {
url: 'Workers/activeWithInheritedRole',
where: `{role: 'salesPerson'}`,
searchFunction: '{firstName: $search}',
showField: 'name',
valueField: 'id',
}
}, {
field: 'countryFk',
autocomplete: {
url: 'Countries',
showField: 'country',
valueField: 'id'
}
}, {
field: 'payMethodFk',
autocomplete: {
showField: 'name',
valueField: 'id'
}
},
{
field: 'workerFk',
autocomplete: {
url: 'Workers/activeWithInheritedRole',
searchFunction: '{firstName: $search}',
showField: 'name',
valueField: 'id',
}
},
{
field: 'observation',
searchable: false
},
{
field: 'isWorker',
checkbox: true,
},
{
field: 'created',
datepicker: true
},
{
field: 'defaulterSinced',
datepicker: true
}
]
};
this.getBalanceDueTotal();
}
set defaulters(value) {
if (!value || !value.length) return;
this._defaulters = value;
}
get defaulters() {
return this._defaulters;
}
get checked() {
const clients = this.$.model.data || [];
const checkedLines = [];
for (let defaulter of clients) {
if (defaulter.checked)
checkedLines.push(defaulter);
}
return checkedLines;
}
saveChecked(clientId) {
this.checkedDefaulers = this.checkedDefaulers.includes(clientId) ?
this.checkedDefaulers.filter(id => id !== clientId) : [...this.checkedDefaulers, clientId];
}
reCheck() {
if (!this.$.model.data || !this.checkedDefaulers.length) return;
this.$.model.data.forEach(defaulter => {
defaulter.checked = this.checkedDefaulers.includes(defaulter.clientFk);
});
}
getBalanceDueTotal() {
this.$http.get('Defaulters/filter')
.then(res => {
if (!res.data) return 0;
this.balanceDueTotal = res.data.reduce(
(accumulator, currentValue) => {
return accumulator + (currentValue['amount'] || 0);
}, 0);
});
}
chipColor(date) {
const day = 24 * 60 * 60 * 1000;
const today = Date.vnNew();
today.setHours(0, 0, 0, 0);
const observationShipped = new Date(date);
observationShipped.setHours(0, 0, 0, 0);
const difference = today - observationShipped;
if (difference > (day * 20))
return 'alert';
if (difference > (day * 10))
return 'warning';
}
onResponse() {
if (!this.defaulter.observation)
throw new UserError(`The message can't be empty`);
const params = [];
for (let defaulter of this.checked) {
params.push({
text: this.defaulter.observation,
clientFk: defaulter.clientFk
});
}
this.$http.post(`ClientObservations`, params) .then(() => {
this.vnApp.showSuccess(this.$t('Observation saved!'));
this.sendMail();
this.$state.reload();
});
}
sendMail() {
const params = {
defaulters: this.checked,
observation: this.defaulter.observation,
};
this.$http.post(`Defaulters/observationEmail`, params);
}
exprBuilder(param, value) {
switch (param) {
case 'isWorker':
return {isWorker: value};
case 'creditInsurance':
case 'amount':
case 'clientFk':
case 'workerFk':
case 'countryFk':
case 'payMethod':
case 'salesPersonFk':
return {[`d.${param}`]: value};
case 'created':
return {'d.created': {
between: this.dateRange(value)}
};
case 'defaulterSinced':
return {'d.defaulterSinced': {
between: this.dateRange(value)}
};
}
}
dateRange(value) {
const minHour = new Date(value);
minHour.setHours(0, 0, 0, 0);
const maxHour = new Date(value);
maxHour.setHours(23, 59, 59, 59);
return [minHour, maxHour];
async $onInit() {
this.$state.go('customer.defaulter', {id: this.$params.id});
window.location.href = await this.vnApp.getUrl(`customer/defaulter`);
}
}

View File

@ -1,179 +0,0 @@
import './index';
import crudModel from 'core/mocks/crud-model';
describe('client defaulter', () => {
describe('Component vnClientDefaulter', () => {
let controller;
let $httpBackend;
beforeEach(ngModule('client'));
beforeEach(inject(($componentController, _$httpBackend_) => {
$httpBackend = _$httpBackend_;
const $element = angular.element('<vn-client-defaulter></vn-client-defaulter>');
controller = $componentController('vnClientDefaulter', {$element});
controller.$.model = crudModel;
controller.$.model.data = [
{clientFk: 1101, amount: 125},
{clientFk: 1102, amount: 500},
{clientFk: 1103, amount: 250}
];
}));
describe('checked() getter', () => {
it('should return the checked lines', () => {
const data = controller.$.model.data;
data[1].checked = true;
data[2].checked = true;
const checkedRows = controller.checked;
const firstCheckedRow = checkedRows[0];
const secondCheckedRow = checkedRows[1];
expect(firstCheckedRow.clientFk).toEqual(1102);
expect(secondCheckedRow.clientFk).toEqual(1103);
});
});
describe('chipColor()', () => {
it('should return undefined when the date is the present', () => {
let today = Date.vnNew();
let result = controller.chipColor(today);
expect(result).toEqual(undefined);
});
it('should return warning when the date is 10 days in the past', () => {
let pastDate = Date.vnNew();
pastDate = pastDate.setDate(pastDate.getDate() - 11);
let result = controller.chipColor(pastDate);
expect(result).toEqual('warning');
});
it('should return alert when the date is 20 days in the past', () => {
let pastDate = Date.vnNew();
pastDate = pastDate.setDate(pastDate.getDate() - 21);
let result = controller.chipColor(pastDate);
expect(result).toEqual('alert');
});
});
describe('onResponse()', () => {
it('should return error for empty message', () => {
let error;
try {
controller.onResponse();
} catch (e) {
error = e;
}
expect(error).toBeDefined();
expect(error.message).toBe(`The message can't be empty`);
});
it('should return saved message', () => {
const data = controller.$.model.data;
data[1].checked = true;
controller.defaulter = {observation: 'My new observation'};
const params = [{text: controller.defaulter.observation, clientFk: data[1].clientFk}];
jest.spyOn(controller.vnApp, 'showSuccess');
$httpBackend.expect('GET', `Defaulters/filter`).respond(200);
$httpBackend.expect('POST', `ClientObservations`, params).respond(200, params);
$httpBackend.expect('POST', `Defaulters/observationEmail`).respond(200);
controller.onResponse();
$httpBackend.flush();
expect(controller.vnApp.showSuccess).toHaveBeenCalledWith('Observation saved!');
});
});
describe('exprBuilder()', () => {
it('should search by sales person', () => {
const expr = controller.exprBuilder('salesPersonFk', '5');
expect(expr).toEqual({'d.salesPersonFk': '5'});
});
it('should search by client', () => {
const expr = controller.exprBuilder('clientFk', '5');
expect(expr).toEqual({'d.clientFk': '5'});
});
});
describe('getBalanceDueTotal()', () => {
it('should return balance due total', () => {
const defaulters = controller.$.model.data;
$httpBackend.when('GET', `Defaulters/filter`).respond(defaulters);
controller.getBalanceDueTotal();
$httpBackend.flush();
expect(controller.balanceDueTotal).toEqual(875);
});
});
describe('dateRange()', () => {
it('should return two dates with the hours at the start and end of the given date', () => {
const now = Date.vnNew();
const today = now.getDate();
const dateRange = controller.dateRange(now);
const start = dateRange[0].toString();
const end = dateRange[1].toString();
expect(start).toContain(today);
expect(start).toContain('00:00:00');
expect(end).toContain(today);
expect(end).toContain('23:59:59');
});
});
describe('reCheck()', () => {
it(`should recheck buys`, () => {
controller.$.model.data = [
{checked: false, clientFk: 1},
{checked: false, clientFk: 2},
{checked: false, clientFk: 3},
{checked: false, clientFk: 4},
];
controller.checkedDefaulers = [1, 2];
controller.reCheck();
expect(controller.$.model.data[0].checked).toEqual(true);
expect(controller.$.model.data[1].checked).toEqual(true);
expect(controller.$.model.data[2].checked).toEqual(false);
expect(controller.$.model.data[3].checked).toEqual(false);
});
});
describe('saveChecked()', () => {
it(`should check buy`, () => {
const buyCheck = 3;
controller.checkedDefaulers = [1, 2];
controller.saveChecked(buyCheck);
expect(controller.checkedDefaulers[2]).toEqual(buyCheck);
});
it(`should uncheck buy`, () => {
const buyUncheck = 3;
controller.checkedDefaulers = [1, 2, 3];
controller.saveChecked(buyUncheck);
expect(controller.checkedDefaulers[2]).toEqual(undefined);
});
});
});
});

View File

@ -1,14 +0,0 @@
Add observation: Añadir observación
Add observation to all selected clients: Añadir observación a {{total}} cliente(s) seleccionado(s)
Balance D.: Saldo V.
Credit I.: Crédito A.
Last observation: Última observación
L. O. Date: Fecha Ú. O.
Last observation date: Fecha última observación
Search client: Buscar clientes
Worker who made the last observation: Trabajador que ha realizado la última observación
Email sended!: Email enviado!
Observation saved!: Observación añadida!
P.Method: F.Pago
Pay Method: Forma de Pago
Country: Pais

View File

@ -1,6 +1,7 @@
module.exports = Self => {
Self.remoteMethodCtx('transfer', {
description: 'Transfer merchandise from one entry to the next day',
accessType: 'WRITE',
accepts: [
{
arg: 'id',

View File

@ -18,7 +18,7 @@ describe('AgencyTerm filter()', () => {
const firstAgencyTerm = agencyTerms[0];
expect(firstAgencyTerm.routeFk).toEqual(1);
expect(agencyTerms.length).toEqual(5);
expect(agencyTerms.length).toEqual(7);
await tx.rollback();
} catch (e) {
@ -49,7 +49,7 @@ describe('AgencyTerm filter()', () => {
let result = await app.models.AgencyTerm.filter(ctx);
expect(result.length).toEqual(2);
expect(result.length).toEqual(4);
});
it('should return results matching "from" and "to"', async() => {
@ -72,7 +72,7 @@ describe('AgencyTerm filter()', () => {
const results = await models.AgencyTerm.filter(ctx, options);
expect(results.length).toBe(5);
expect(results.length).toBe(7);
await tx.rollback();
} catch (e) {
@ -90,7 +90,7 @@ describe('AgencyTerm filter()', () => {
let result = await app.models.AgencyTerm.filter(ctx);
expect(result.length).toEqual(1);
expect(result.length).toEqual(2);
expect(result[0].routeFk).toEqual(1);
});
@ -103,7 +103,7 @@ describe('AgencyTerm filter()', () => {
let result = await app.models.AgencyTerm.filter(ctx);
expect(result.length).toEqual(1);
expect(result.length).toEqual(2);
expect(result[0].routeFk).toEqual(2);
});
});

View File

@ -1,109 +0,0 @@
const app = require('vn-loopback/server/server');
const models = require('vn-loopback/server/server').models;
describe('AgencyTerm filter()', () => {
const authUserId = 9;
const today = Date.vnNew();
today.setHours(2, 0, 0, 0);
it('should return all results matching the filter', async() => {
const tx = await models.AgencyTerm.beginTransaction({});
try {
const options = {transaction: tx};
const filter = {};
const ctx = {req: {accessToken: {userId: authUserId}}};
const agencyTerms = await models.AgencyTerm.filter(ctx, filter, options);
const firstAgencyTerm = agencyTerms[0];
expect(firstAgencyTerm.routeFk).toEqual(1);
expect(agencyTerms.length).toEqual(5);
await tx.rollback();
} catch (e) {
await tx.rollback();
throw e;
}
});
it('should return results matching "search" searching by integer', async() => {
let ctx = {
args: {
search: 1,
}
};
let result = await app.models.AgencyTerm.filter(ctx);
expect(result.length).toEqual(1);
expect(result[0].routeFk).toEqual(1);
});
it('should return results matching "search" searching by string', async() => {
let ctx = {
args: {
search: 'Plants SL',
}
};
let result = await app.models.AgencyTerm.filter(ctx);
expect(result.length).toEqual(2);
});
it('should return results matching "from" and "to"', async() => {
const tx = await models.Buy.beginTransaction({});
const options = {transaction: tx};
try {
const from = Date.vnNew();
from.setHours(0, 0, 0, 0);
const to = Date.vnNew();
to.setHours(23, 59, 59, 999);
const ctx = {
args: {
from: from,
to: to
}
};
const results = await models.AgencyTerm.filter(ctx, options);
expect(results.length).toBe(5);
await tx.rollback();
} catch (e) {
await tx.rollback();
throw e;
}
});
it('should return results matching "agencyModeFk"', async() => {
let ctx = {
args: {
agencyModeFk: 1,
}
};
let result = await app.models.AgencyTerm.filter(ctx);
expect(result.length).toEqual(1);
expect(result[0].routeFk).toEqual(1);
});
it('should return results matching "agencyFk"', async() => {
let ctx = {
args: {
agencyFk: 2,
}
};
let result = await app.models.AgencyTerm.filter(ctx);
expect(result.length).toEqual(1);
expect(result[0].routeFk).toEqual(2);
});
});

View File

@ -87,6 +87,8 @@ module.exports = Self => {
Self.filter = async(ctx, filter) => {
let conn = Self.dataSource.connector;
const userId = ctx.req.accessToken.userId;
const models = Self.app.models;
let where = buildFilter(ctx.args, (param, value) => {
switch (param) {
@ -110,6 +112,13 @@ module.exports = Self => {
filter = mergeFilters(filter, {where});
const worker = await models.Worker.findById(userId, {fields: ['isFreelance']});
const getMyRoute = await models.ACL.checkAccessAcl(ctx, 'Route', 'getRouteByAgency', 'WRITE');
if (userId && getMyRoute && worker.isFreelance) {
if (!filter.where) filter.where = {};
filter.where[`workerFk`] = userId;
}
let stmts = [];
let stmt;

View File

@ -8,14 +8,32 @@ describe('Route filter()', () => {
it('should return the routes matching "search"', async() => {
const ctx = {
args: {
search: 1,
search: 5,
},
req: {
accessToken: {
userId: 9
}
}
};
const result = await app.models.Route.filter(ctx);
expect(result.length).toEqual(1);
expect(result[0].id).toEqual(5);
});
it('should return all results matching the filter', async() => {
const ctx = {
req: {
accessToken: {
userId: 133
}
}
};
const result = await app.models.Route.filter(ctx);
expect(result.length).toEqual(1);
expect(result[0].id).toEqual(1);
expect(result.length).toEqual(3);
});
it('should return results matching "from" and "to"', async() => {
@ -32,12 +50,16 @@ describe('Route filter()', () => {
args: {
from: from,
to: to
},
req: {
accessToken: {
userId: 9
}
}
};
const results = await models.Route.filter(ctx, options);
expect(results.length).toBe(7);
expect(results.length).toBe(9);
await tx.rollback();
} catch (e) {
@ -50,6 +72,11 @@ describe('Route filter()', () => {
const ctx = {
args: {
m3: 0.1,
},
req: {
accessToken: {
userId: 9
}
}
};
@ -62,6 +89,11 @@ describe('Route filter()', () => {
const ctx = {
args: {
description: 'third route',
},
req: {
accessToken: {
userId: 9
}
}
};
@ -74,24 +106,33 @@ describe('Route filter()', () => {
const ctx = {
args: {
workerFk: 56,
},
req: {
accessToken: {
userId: 9
}
}
};
const result = await app.models.Route.filter(ctx);
expect(result.length).toEqual(5);
expect(result.length).toEqual(4);
});
it('should return the routes matching "warehouseFk"', async() => {
const ctx = {
args: {
warehouseFk: 1,
},
req: {
accessToken: {
userId: 9
}
}
};
let result = await app.models.Route.filter(ctx);
expect(result.length).toEqual(7);
expect(result.length).toEqual(9);
ctx.args.warehouseFk = 2;
@ -104,9 +145,13 @@ describe('Route filter()', () => {
const ctx = {
args: {
vehicleFk: 2,
},
req: {
accessToken: {
userId: 9
}
}
};
const result = await app.models.Route.filter(ctx);
expect(result.length).toEqual(1);
@ -116,6 +161,11 @@ describe('Route filter()', () => {
const ctx = {
args: {
agencyModeFk: 7,
},
req: {
accessToken: {
userId: 9
}
}
};

View File

@ -22,7 +22,7 @@ describe('route summary()', () => {
});
it(`should return a summary object containing it's worker`, async() => {
const result = await app.models.Route.summary(1);
const result = await app.models.Route.summary(2);
const worker = result.route.worker().user();
expect(worker.name).toEqual('delivery');

View File

@ -0,0 +1,100 @@
const ParameterizedSQL = require('loopback-connector').ParameterizedSQL;
const {buildFilter, mergeFilters} = require('vn-loopback/util/filter');
module.exports = Self => {
Self.remoteMethodCtx('receipts', {
description: 'Find all suppliers matched by the filter',
accessType: 'READ',
accepts: [
{
arg: 'filter',
type: 'object',
description: 'Filter defining where, order, offset, and limit - must be a JSON-encoded string',
http: {source: 'query'}
}, {
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: 'bankFk',
type: 'number',
description: 'The bank',
default: 1,
},
{
arg: 'orderBy',
type: 'string',
description: 'The supplier 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);
const where = buildFilter(ctx.args, (param, value) => {
switch (param) {
case 'bankFk':
return {[param]: value};
}
});
let stmts = [];
stmts.push(new ParameterizedSQL('CALL vn.supplier_statementWithEntries(?,?,?,?,?,?)', [
args.supplierId,
args.currencyFk,
args.companyId,
args.orderBy ?? 'issued',
args.isConciliated ?? false,
false
]));
const stmt = new ParameterizedSQL(`
SELECT *
FROM tmp.supplierStatement`);
filter = mergeFilters(args.filter, {where});
stmt.merge(conn.makeSuffix(filter));
stmts.push(stmt);
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;
};
};

View File

@ -8,6 +8,7 @@ module.exports = Self => {
require('../methods/supplier/updateFiscalData')(Self);
require('../methods/supplier/consumption')(Self);
require('../methods/supplier/freeAgencies')(Self);
require('../methods/supplier/receipts')(Self);
require('../methods/supplier/campaignMetricsPdf')(Self);
require('../methods/supplier/campaignMetricsEmail')(Self);
require('../methods/supplier/newSupplier')(Self);

View File

@ -166,12 +166,11 @@ module.exports = Self => {
LEFT JOIN account.emailUser eu ON eu.userFk = u.id`
);
stmt.merge(conn.makeWhere(filter.where));
stmts.push(stmt);
stmt.merge(conn.makeSuffix(filter));
let itemsIndex = stmts.push(stmt) - 1;
const itemsIndex = stmts.push(stmt) - 1;
const sql = ParameterizedSQL.join(stmts, ';');
const result = await conn.executeStmt(sql, myOptions);
return result[itemsIndex];
let sql = ParameterizedSQL.join(stmts, ';');
let result = await conn.executeStmt(sql);
return itemsIndex === 0 ? result : result[itemsIndex];
};
};

View File

@ -1,5 +1,4 @@
module.exports = Self => {
const validateTin = require('vn-loopback/util/validateTin');
require('../methods/worker/filter')(Self);
require('../methods/worker/mySubordinates')(Self);
require('../methods/worker/isSubordinate')(Self);
@ -23,26 +22,10 @@ module.exports = Self => {
require('../methods/worker/getAvailablePda')(Self);
require('../methods/worker/myTeam')(Self);
Self.validateAsync('fi', tinIsValid, {
message: 'Invalid TIN'
});
Self.canModifyAbsenceInPast = async(ctx, time) => {
const hasPrivs = await Self.app.models.ACL.checkAccessAcl(ctx, 'Worker', 'canModifyAbsenceInPast', 'WRITE');
const today = Date.vnNew();
today.setHours(0, 0, 0, 0);
return hasPrivs || today.getTime() < time;
};
async function tinIsValid(err, done) {
const country = await Self.app.models.Country.findOne({
fields: ['code'],
where: {id: this.originCountryFk}
});
const code = country ? country.code.toLowerCase() : null;
if (!this.fi || !validateTin(this.fi, code))
err();
done();
}
};