Merge branch 'dev' into 2873-create-supplier-beneficiary
gitea/salix/pipeline/head This commit looks good Details

This commit is contained in:
Javi Gallego 2021-04-01 13:14:26 +02:00
commit 67f822ebdc
41 changed files with 6585 additions and 4731 deletions

View File

@ -20,17 +20,37 @@ module.exports = Self => {
}
});
Self.removeFile = async(ctx, id) => {
Self.removeFile = async(ctx, id, options) => {
let tx;
let myOptions = {};
if (typeof options == 'object')
Object.assign(myOptions, options);
if (!myOptions.transaction) {
tx = await Self.beginTransaction({});
myOptions.transaction = tx;
}
try {
const models = Self.app.models;
const dms = await models.Dms.findById(id);
const dms = await models.Dms.findById(id, null, myOptions);
const trashDmsType = await models.DmsType.findOne({
where: {code: 'trash'}
});
}, myOptions);
const hasWriteRole = await models.DmsType.hasWriteRole(ctx, dms.dmsTypeFk);
const hasWriteRole = await models.DmsType.hasWriteRole(ctx, dms.dmsTypeFk, myOptions);
if (!hasWriteRole)
throw new UserError(`You don't have enough privileges`);
return dms.updateAttribute('dmsTypeFk', trashDmsType.id);
await dms.updateAttribute('dmsTypeFk', trashDmsType.id, myOptions);
if (tx) await tx.commit();
return dms;
} catch (e) {
if (tx) await tx.rollback();
throw e;
}
};
};

View File

@ -8,25 +8,23 @@
},
"properties": {
"id": {
"type": "Number",
"type": "number",
"id": true,
"description": "The id"
},
"name": {
"type": "String",
"type": "string",
"required": true
},
"collectionFk": {
"type": "String",
"type": "string",
"required": true
},
"updated": {
"type": "Number"
"type": "number"
},
"nRefs": {
"type": "Number",
"required": true,
"default": 1
"type": "number"
}
},
"relations": {

View File

@ -1,4 +0,0 @@
ALTER TABLE `vn`.`department`
ADD code VARCHAR(45) NULL AFTER id;
UPDATE `vn`.`department` t SET t.code = 'IT', t.chatName = 'informatica-cau' WHERE t.id = 31;

View File

@ -1,2 +0,0 @@
ALTER TABLE `vn`.`itemImageQueue`
ADD attempts INT default 0 NULL AFTER error;

View File

@ -1,5 +0,0 @@
INSERT INTO `salix`.`ACL` (`model`, `property`, `accessType`, `permission`, `principalType`, `principalId`)
VALUES
('SupplierAccount', '*', '*', 'ALLOW', 'ROLE', 'administrative'),
('Entry', '*', '*', 'ALLOW', 'ROLE', 'administrative'),
('InvoiceIn', '*', '*', 'ALLOW', 'ROLE', 'administrative');

View File

@ -1,137 +0,0 @@
DROP PROCEDURE `vn`.`item_getBalance`;
DELIMITER $$
$$
CREATE DEFINER=`root`@`%` PROCEDURE `vn`.`item_getBalance`(IN vItemId INT, IN vWarehouse INT)
BEGIN
DECLARE vDateInventory DATETIME;
DECLARE vCurdate DATE DEFAULT CURDATE();
DECLARE vDayEnd DATETIME DEFAULT util.dayEnd(vCurdate);
SELECT inventoried INTO vDateInventory FROM config;
SET @a = 0;
SET @currentLineFk = 0;
SET @shipped = '';
SELECT DATE(@shipped:= shipped) shipped,
alertLevel,
stateName,
origin,
reference,
clientFk,
name,
`in`,
`out`,
@a := @a + IFNULL(`in`,0) - IFNULL(`out`,0) as balance,
@currentLineFk := IF (@shipped < CURDATE()
OR (@shipped = CURDATE() AND (isPicked OR alertLevel >= 2)),
lineFk,@currentLineFk) lastPreparedLineFk,
isTicket,
lineFk,
isPicked,
clientType
FROM
( SELECT tr.landed AS shipped,
b.quantity AS `in`,
NULL AS `out`,
al.alertLevel AS alertLevel,
st.name AS stateName,
s.name AS name,
e.ref AS reference,
e.id AS origin,
s.id AS clientFk,
IF(al.alertLevel = 3, TRUE, FALSE) isPicked,
FALSE AS isTicket,
b.id lineFk,
NULL `order`,
NULL AS clientType
FROM buy b
JOIN entry e ON e.id = b.entryFk
JOIN travel tr ON tr.id = e.travelFk
JOIN supplier s ON s.id = e.supplierFk
JOIN alertLevel al ON al.alertLevel =
CASE
WHEN tr.shipped < CURDATE() THEN 3
WHEN tr.shipped = CURDATE() AND tr.isReceived = TRUE THEN 3
ELSE 0
END
JOIN state st ON st.code = al.code
WHERE tr.landed >= vDateInventory
AND vWarehouse = tr.warehouseInFk
AND b.itemFk = vItemId
AND e.isInventory = FALSE
AND e.isRaid = FALSE
UNION ALL
SELECT tr.shipped,
NULL as `in`,
b.quantity AS `out`,
al.alertLevel AS alertLevel,
st.name AS stateName,
s.name AS name,
e.ref AS reference,
e.id AS origin,
s.id AS clientFk,
IF(al.alertLevel = 3, TRUE, FALSE) isPicked,
FALSE AS isTicket,
b.id,
NULL `order`,
NULL AS clientType
FROM buy b
JOIN entry e ON e.id = b.entryFk
JOIN travel tr ON tr.id = e.travelFk
JOIN warehouse w ON w.id = tr.warehouseOutFk
JOIN supplier s ON s.id = e.supplierFk
JOIN alertLevel al ON al.alertLevel =
CASE
WHEN tr.shipped < CURDATE() THEN 3
WHEN tr.shipped = CURDATE() AND tr.isReceived = TRUE THEN 3
ELSE 0
END
JOIN state st ON st.code = al.code
WHERE tr.shipped >= vDateInventory
AND vWarehouse =tr.warehouseOutFk
AND s.id <> 4
AND b.itemFk = vItemId
AND e.isInventory = FALSE
AND w.isFeedStock = FALSE
AND e.isRaid = FALSE
UNION ALL
SELECT DATE(t.shipped),
NULL as `in`,
s.quantity AS `out`,
al.alertLevel AS alertLevel,
st.name AS stateName,
t.nickname AS name,
t.refFk AS reference,
t.id AS origin,
t.clientFk,
stk.id AS isPicked,
TRUE AS isTicket,
s.id,
st.`order`,
ct.code AS clientType
FROM sale s
JOIN ticket t ON t.id = s.ticketFk
LEFT JOIN ticketState ts ON ts.ticket = t.id
LEFT JOIN state st ON st.code = ts.code
JOIN client c ON c.id = t.clientFk
JOIN clientType ct ON ct.id = c.clientTypeFk
JOIN alertLevel al ON al.alertLevel =
CASE
WHEN t.shipped < curdate() THEN 3
WHEN t.shipped > util.dayEnd(curdate()) THEN 0
ELSE IFNULL(ts.alertLevel, 0)
END
LEFT JOIN state stPrep ON stPrep.`code` = 'PREPARED'
LEFT JOIN saleTracking stk ON stk.saleFk = s.id AND stk.stateFk = stPrep.id
WHERE t.shipped >= vDateInventory
AND s.itemFk = vItemId
AND vWarehouse =t.warehouseFk
ORDER BY shipped, alertLevel DESC, isTicket, `order` DESC, isPicked DESC, `in` DESC, `out` DESC
) AS itemDiary;
END$$
DELIMITER ;

View File

@ -1,2 +0,0 @@
ALTER TABLE `vn`.`supplier` ADD COLUMN `workerFk` INT(11) NULL DEFAULT NULL COMMENT 'Responsible for approving invoices' AFTER `isTrucker`;
ALTER TABLE `vn`.`supplier` ADD CONSTRAINT `supplier_workerFk` FOREIGN KEY (`workerFk`) REFERENCES `vn`.`worker` (`id`) ON UPDATE CASCADE;

View File

@ -1,4 +0,0 @@
INSERT INTO `salix`.`ACL` (`model`, `property`, `accessType`, `permission`, `principalType`, `principalId`)
VALUES
('StarredModule', '*', '*', 'ALLOW', 'ROLE', 'employee'),
('ItemBotanical', '*', 'WRITE', 'ALLOW', 'ROLE', 'logisticBoss');

View File

@ -1,20 +0,0 @@
CREATE TABLE `salix`.`module` (
`code` VARCHAR(45) COLLATE utf8_unicode_ci NOT NULL,
PRIMARY KEY (`code`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci;
INSERT INTO `salix`.`module`(`code`)
VALUES
('Items'),
('Orders'),
('Clients'),
('Entries'),
('Travels'),
('Invoices out'),
('Suppliers'),
('Claims'),
('Routes'),
('Tickets'),
('Workers'),
('Users'),
('Zones');

View File

@ -1,10 +0,0 @@
CREATE TABLE `vn`.`starredModule` (
`id` INT(11) unsigned NOT NULL AUTO_INCREMENT,
`workerFk` INT(10) NOT NULL,
`moduleFk` VARCHAR(45) COLLATE utf8_unicode_ci NOT NULL,
PRIMARY KEY (`id`),
KEY `starred_workerFk` (`workerFk`),
KEY `starred_moduleFk` (`moduleFk`),
CONSTRAINT `starred_workerFk` FOREIGN KEY (`workerFk`) REFERENCES `vn`.`worker` (`id`) ON UPDATE CASCADE,
CONSTRAINT `starred_moduleFk` FOREIGN KEY (`moduleFk`) REFERENCES `salix`.`module` (`code`) ON UPDATE CASCADE
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci;

View File

@ -1,5 +0,0 @@
INSERT INTO `salix`.`ACL` (`model`, `property`, `accessType`, `permission`, `principalType`, `principalId`)
VALUES
('Genus', '*', 'WRITE', 'ALLOW', 'ROLE', 'logisticBoss'),
('Specie', '*', 'WRITE', 'ALLOW', 'ROLE', 'logisticBoss'),
('InvoiceOut', 'createPdf', 'WRITE', 'ALLOW', 'ROLE', 'invoicing');

File diff suppressed because one or more lines are too long

View File

@ -294,6 +294,10 @@ INSERT INTO `vn`.`clientManaCache`(`clientFk`, `mana`, `dated`)
(103, 0, DATE_ADD(CURDATE(), INTERVAL -1 MONTH)),
(104, -30, DATE_ADD(CURDATE(), INTERVAL -1 MONTH));
INSERT INTO `vn`.`clientConfig`(`riskTolerance`)
VALUES
(200);
INSERT INTO `vn`.`address`(`id`, `nickname`, `street`, `city`, `postalCode`, `provinceFk`, `phone`, `mobile`, `isActive`, `clientFk`, `agencyModeFk`, `longitude`, `latitude`, `isEqualizated`, `isDefaultAddress`)
VALUES
(1, 'Bruce Wayne', '1007 Mountain Drive, Gotham', 'Silla', 46460, 1, 1111111111, 222222222, 1, 101, 2, NULL, NULL, 0, 1),
@ -711,14 +715,14 @@ INSERT INTO `vn`.`itemCategory`(`id`, `name`, `display`, `color`, `icon`, `code`
(7, 'Accessories', 1, NULL, 'icon-accessory', 'accessory'),
(8, 'Fruit', 1, NULL, 'icon-fruit', 'fruit');
INSERT INTO `vn`.`itemType`(`id`, `code`, `name`, `categoryFk`, `life`,`workerFk`, `isPackaging`)
INSERT INTO `vn`.`itemType`(`id`, `code`, `name`, `categoryFk`, `warehouseFk`, `life`,`workerFk`, `isPackaging`)
VALUES
(1, 'CRI', 'Crisantemo', 2, 31, 35, 0),
(2, 'ITG', 'Anthurium', 1, 31, 35, 0),
(3, 'WPN', 'Paniculata', 2, 31, 35, 0),
(4, 'PRT', 'Delivery ports', 3, NULL, 35, 1),
(5, 'CON', 'Container', 3, NULL, 35, 1),
(6, 'ALS', 'Alstroemeria', 1, 31, 35, 0);
(1, 'CRI', 'Crisantemo', 2, 1, 31, 35, 0),
(2, 'ITG', 'Anthurium', 1, 1, 31, 35, 0),
(3, 'WPN', 'Paniculata', 2, 1, 31, 35, 0),
(4, 'PRT', 'Delivery ports', 3, 1, NULL, 35, 1),
(5, 'CON', 'Container', 3, 1, NULL, 35, 1),
(6, 'ALS', 'Alstroemeria', 1, 1, 31, 35, 0);
INSERT INTO `vn`.`ink`(`id`, `name`, `picture`, `showOrder`, `hex`)
VALUES
@ -2254,7 +2258,10 @@ INSERT INTO `vn`.`duaInvoiceIn`(`id`, `duaFk`, `invoiceInFk`)
(10, 10, 10);
INSERT INTO `vn`.`ticketRecalc`(`ticketFk`)
SELECT `id` FROM `vn`.`ticket`;
SELECT `id`
FROM `vn`.`ticket` t
LEFT JOIN vn.ticketRecalc tr ON tr.ticketFk = t.id
WHERE tr.ticketFk IS NULL;
CALL `vn`.`ticket_doRecalc`();

File diff suppressed because it is too large Load Diff

View File

@ -86,6 +86,8 @@ IGNORETABLES=(
--ignore-table=vn.warehouseJoined
--ignore-table=vn.workerTeam__
--ignore-table=vn.XDiario__
--ignore-table=sage.movConta
--ignore-table=sage.movContaCopia
)
mysqldump \
--defaults-file=config.production.ini \

View File

@ -2,7 +2,7 @@ const app = require('vn-loopback/server/server');
const ParameterizedSQL = require('loopback-connector').ParameterizedSQL;
describe('item_getBalance()', () => {
it(`should return the item balance`, async() => {
it(`should return the item balance ordered by alert level`, async() => {
let stmts = [];
let params = {
@ -21,12 +21,11 @@ describe('item_getBalance()', () => {
let result = await conn.executeStmt(sql);
let itemBalance = result[0];
expect(itemBalance[0].alertLevel >= itemBalance[1].alertLevel).toBeTruthy();
expect(itemBalance[1].alertLevel >= itemBalance[2].alertLevel).toBeTruthy();
expect(itemBalance[2].alertLevel >= itemBalance[3].alertLevel).toBeTruthy();
expect(itemBalance[3].alertLevel >= itemBalance[4].alertLevel).toBeTruthy();
expect(itemBalance[4].alertLevel >= itemBalance[5].alertLevel).toBeTruthy();
expect(itemBalance[5].alertLevel >= itemBalance[6].alertLevel).toBeTruthy();
expect(itemBalance[0].alertLevel).toBeGreaterThanOrEqual(itemBalance[1].alertLevel);
expect(itemBalance[1].alertLevel).toBeGreaterThanOrEqual(itemBalance[2].alertLevel);
expect(itemBalance[2].alertLevel).toBeGreaterThanOrEqual(itemBalance[3].alertLevel);
expect(itemBalance[3].alertLevel).toBeGreaterThanOrEqual(itemBalance[4].alertLevel);
expect(itemBalance[4].alertLevel).toBeGreaterThanOrEqual(itemBalance[5].alertLevel);
expect(itemBalance[5].alertLevel).toBeGreaterThanOrEqual(itemBalance[6].alertLevel);
});
});

View File

@ -59,6 +59,6 @@ describe('Account create and basic data path', () => {
await page.accessToSection('account.card.roles');
const rolesCount = await page.countElement(selectors.accountRoles.anyResult);
expect(rolesCount).toEqual(3);
expect(rolesCount).toEqual(4);
});
});

View File

@ -81,6 +81,6 @@ describe('Account Role create and basic data path', () => {
await page.accessToSection('account.role.card.inherited');
const rolesCount = await page.countElement(selectors.accountRoleInheritance.anyResult);
expect(rolesCount).toEqual(6);
expect(rolesCount).toEqual(7);
});
});

View File

@ -3,7 +3,7 @@ module.exports = Self => {
description: 'Returns a list of allowed contentTypes',
accessType: 'READ',
returns: {
type: ['Object'],
type: ['object'],
root: true
},
http: {

View File

@ -4,12 +4,12 @@ module.exports = Self => {
accessType: 'WRITE',
accepts: {
arg: 'id',
type: 'Number',
type: 'number',
description: 'The document id',
http: {source: 'path'}
},
returns: {
type: 'Object',
type: 'object',
root: true
},
http: {
@ -18,16 +18,36 @@ module.exports = Self => {
}
});
Self.removeFile = async(ctx, id) => {
Self.removeFile = async(ctx, id, options) => {
let tx;
let myOptions = {};
if (typeof options == 'object')
Object.assign(myOptions, options);
if (!myOptions.transaction) {
tx = await Self.beginTransaction({});
myOptions.transaction = tx;
}
try {
const models = Self.app.models;
const targetClaimDms = await models.ClaimDms.findById(id);
const targetDms = await models.Dms.findById(targetClaimDms.dmsFk);
const trashDmsType = await models.DmsType.findOne({where: {code: 'trash'}});
const targetClaimDms = await models.ClaimDms.findById(id, null, myOptions);
const targetDms = await models.Dms.findById(targetClaimDms.dmsFk, null, myOptions);
const trashDmsType = await models.DmsType.findOne({where: {code: 'trash'}}, myOptions);
await models.Dms.removeFile(ctx, targetClaimDms.dmsFk);
await targetClaimDms.destroy();
await models.Dms.removeFile(ctx, targetClaimDms.dmsFk, myOptions);
await targetClaimDms.destroy(myOptions);
return targetDms.updateAttribute('dmsTypeFk', trashDmsType.id);
await targetDms.updateAttribute('dmsTypeFk', trashDmsType.id, myOptions);
if (tx) await tx.commit();
return targetDms;
} catch (e) {
if (tx) await tx.rollback();
throw e;
}
};
};

View File

@ -17,14 +17,27 @@ module.exports = Self => {
}
});
Self.importTicketSales = async(ctx, params) => {
Self.importTicketSales = async(ctx, params, options) => {
let models = Self.app.models;
let userId = ctx.req.accessToken.userId;
let worker = await models.Worker.findOne({where: {userFk: userId}});
let tx;
let myOptions = {};
if (typeof options == 'object')
Object.assign(myOptions, options);
if (!myOptions.transaction) {
tx = await Self.beginTransaction({});
myOptions.transaction = tx;
}
try {
const worker = await models.Worker.findOne({where: {userFk: userId}}, myOptions);
let ticketSales = await models.Sale.find({
where: {ticketFk: params.ticketFk}
});
}, myOptions);
let claimEnds = [];
ticketSales.forEach(sale => {
@ -35,6 +48,14 @@ module.exports = Self => {
});
});
return await Self.create(claimEnds);
const createdClaimEnds = await Self.create(claimEnds, myOptions);
if (tx) await tx.commit();
return createdClaimEnds;
} catch (e) {
if (tx) await tx.rollback();
throw e;
}
};
};

View File

@ -1,25 +1,26 @@
const app = require('vn-loopback/server/server');
describe('Claim importTicketSales()', () => {
let claimEnds;
afterAll(async done => {
claimEnds.forEach(async line => {
await line.destroy();
});
done();
});
it('should import sales to a claim actions from an specific ticket', async() => {
let ctx = {req: {accessToken: {userId: 5}}};
claimEnds = await app.models.ClaimEnd.importTicketSales(ctx, {
const ctx = {req: {accessToken: {userId: 5}}};
const tx = await app.models.Entry.beginTransaction({});
try {
const options = {transaction: tx};
const claimEnds = await app.models.ClaimEnd.importTicketSales(ctx, {
claimFk: 1,
ticketFk: 1
});
}, options);
expect(claimEnds.length).toEqual(4);
expect(claimEnds[0].saleFk).toEqual(1);
expect(claimEnds[2].saleFk).toEqual(3);
await tx.rollback();
} catch (e) {
await tx.rollback();
throw e;
}
});
});

View File

@ -24,15 +24,22 @@ module.exports = Self => {
}
});
Self.createFromSales = async(ctx, ticketId, sales) => {
Self.createFromSales = async(ctx, ticketId, sales, options) => {
let tx;
let myOptions = {};
if (typeof options == 'object')
Object.assign(myOptions, options);
if (!myOptions.transaction) {
tx = await Self.beginTransaction({});
myOptions.transaction = tx;
}
const models = Self.app.models;
const userId = ctx.req.accessToken.userId;
const tx = await Self.beginTransaction({});
try {
let options = {transaction: tx};
const ticket = await models.Ticket.findById(ticketId, null, options);
const ticket = await models.Ticket.findById(ticketId, null, myOptions);
if (ticket.isDeleted)
throw new UserError(`You can't create a claim for a removed ticket`);
@ -41,7 +48,7 @@ module.exports = Self => {
clientFk: ticket.clientFk,
ticketCreated: ticket.shipped,
workerFk: userId
}, options);
}, myOptions);
const promises = [];
for (const sale of sales) {
@ -49,17 +56,18 @@ module.exports = Self => {
saleFk: sale.id,
claimFk: newClaim.id,
quantity: sale.quantity
}, options);
}, myOptions);
promises.push(newClaimBeginning);
}
await Promise.all(promises);
await tx.commit();
if (tx) await tx.commit();
return newClaim;
} catch (e) {
await tx.rollback();
if (tx) await tx.rollback();
throw e;
}
};

View File

@ -10,58 +10,67 @@ module.exports = Self => {
accepts: [
{
arg: 'filter',
type: 'Object',
type: 'object',
description: 'Filter defining where, order, offset, and limit - must be a JSON-encoded string',
http: {source: 'query'}
}, {
},
{
arg: 'tags',
type: ['Object'],
type: ['object'],
description: 'List of tags to filter with',
http: {source: 'query'}
}, {
},
{
arg: 'search',
type: 'String',
type: 'string',
description: `If it's and integer searchs by id, otherwise it searchs by client name`,
http: {source: 'query'}
}, {
},
{
arg: 'client',
type: 'String',
type: 'string',
description: 'The worker name',
http: {source: 'query'}
}, {
},
{
arg: 'id',
type: 'Integer',
type: 'integer',
description: 'The claim id',
http: {source: 'query'}
}, {
},
{
arg: 'clientFk',
type: 'Integer',
type: 'integer',
description: 'The client id',
http: {source: 'query'}
}, {
},
{
arg: 'claimStateFk',
type: 'Integer',
type: 'integer',
description: 'The claim state id',
http: {source: 'query'}
}, {
},
{
arg: 'salesPersonFk',
type: 'Integer',
type: 'integer',
description: 'The salesPerson id',
http: {source: 'query'}
}, {
},
{
arg: 'attenderFk',
type: 'Integer',
type: 'integer',
description: 'The attender worker id',
http: {source: 'query'}
}, {
},
{
arg: 'created',
type: 'Date',
type: 'date',
description: 'The to date filter',
http: {source: 'query'}
}
],
returns: {
type: ['Object'],
type: ['object'],
root: true
},
http: {

View File

@ -18,32 +18,39 @@ module.exports = Self => {
}
});
Self.regularizeClaim = async(ctx, claimFk) => {
Self.regularizeClaim = async(ctx, claimFk, options) => {
const models = Self.app.models;
const $t = ctx.req.__; // $translate
const resolvedState = 3;
let tx = await Self.beginTransaction({});
try {
let options = {transaction: tx};
let tx;
let myOptions = {};
if (typeof options == 'object')
Object.assign(myOptions, options);
if (!myOptions.transaction) {
tx = await Self.beginTransaction({});
myOptions.transaction = tx;
}
try {
const claimEnds = await models.ClaimEnd.find({
include: {
relation: 'claimDestination',
fields: ['addressFk']
},
where: {claimFk: claimFk}
}, options);
}, myOptions);
for (let i = 0; i < claimEnds.length; i++) {
const claimEnd = claimEnds[i];
for (let claimEnd of claimEnds) {
const destination = claimEnd.claimDestination();
const sale = await getSale(claimEnd.saleFk, options);
const sale = await getSale(claimEnd.saleFk, myOptions);
const addressId = destination && destination.addressFk;
let address;
if (addressId)
address = await models.Address.findById(addressId, null, options);
address = await models.Address.findById(addressId, null, myOptions);
const salesPerson = sale.ticket().client().salesPersonUser();
if (salesPerson) {
@ -67,7 +74,7 @@ module.exports = Self => {
addressFk: addressId,
companyFk: sale.ticket().companyFk,
warehouseFk: sale.ticket().warehouseFk
}, options);
}, myOptions);
if (!ticketFk) {
ticketFk = await createTicket(ctx, {
@ -75,7 +82,7 @@ module.exports = Self => {
warehouseId: sale.ticket().warehouseFk,
companyId: sale.ticket().companyFk,
addressId: addressId
}, options);
}, myOptions);
}
await models.Sale.create({
@ -85,19 +92,19 @@ module.exports = Self => {
quantity: -sale.quantity,
price: sale.price,
discount: 100
}, options);
}, myOptions);
}
let claim = await Self.findById(claimFk, null, options);
let claim = await Self.findById(claimFk, null, myOptions);
claim = await claim.updateAttributes({
claimStateFk: resolvedState
}, options);
}, myOptions);
await tx.commit();
if (tx) await tx.commit();
return claim;
} catch (e) {
await tx.rollback();
if (tx) await tx.rollback();
throw e;
}
};

View File

@ -10,36 +10,45 @@ describe('Claim createFromSales()', () => {
const ctx = {req: {accessToken: {userId: 1}}};
it('should create a new claim', async() => {
let claim = await app.models.Claim.createFromSales(ctx, ticketId, newSale);
const tx = await app.models.Claim.beginTransaction({});
try {
const options = {transaction: tx};
const claim = await app.models.Claim.createFromSales(ctx, ticketId, newSale, options);
expect(claim.ticketFk).toEqual(ticketId);
let claimBeginning = await app.models.ClaimBeginning.findOne({where: {claimFk: claim.id}});
let claimBeginning = await app.models.ClaimBeginning.findOne({where: {claimFk: claim.id}}, options);
expect(claimBeginning.saleFk).toEqual(newSale[0].id);
expect(claimBeginning.quantity).toEqual(newSale[0].quantity);
const createdClaimId = claim.id;
// restores
await app.models.Claim.destroyById(createdClaimId);
await tx.rollback();
} catch (e) {
await tx.rollback();
throw e;
}
});
it('should not be able to create a claim if exists that sale', async() => {
let claim = await app.models.Claim.createFromSales(ctx, ticketId, newSale);
const createdClaimId = claim.id;
const tx = await app.models.Claim.beginTransaction({});
let error;
await app.models.Claim.createFromSales(ctx, ticketId, newSale)
try {
const options = {transaction: tx};
.catch(e => {
await app.models.Claim.createFromSales(ctx, ticketId, newSale, options);
await app.models.Claim.createFromSales(ctx, ticketId, newSale, options);
await tx.rollback();
} catch (e) {
error = e;
});
await tx.rollback();
}
expect(error.toString()).toContain(`A claim with that sale already exists`);
// restores
await app.models.Claim.destroyById(createdClaimId);
});
});

View File

@ -21,81 +21,94 @@ describe('regularizeClaim()', () => {
let claimEnds = [];
let trashTicket;
afterEach(async done => {
try {
let claim = await app.models.Claim.findById(claimFk);
await claim.updateAttributes({
claimStateFk: pendentState,
hasToPickUp: false
});
for (claimEnd of claimEnds)
await claimEnd.destroy();
if (trashTicket)
await app.models.Ticket.destroyById(trashTicket.id);
} catch (error) {
console.error(error);
}
done();
});
it('should send a chat message with value "Trash" and then change claim state to resolved', async() => {
const tx = await app.models.Claim.beginTransaction({});
try {
const options = {transaction: tx};
spyOn(chatModel, 'sendCheckingPresence').and.callThrough();
claimEnds = await app.models.ClaimEnd.importTicketSales(ctx, {
claimFk: claimFk,
ticketFk: 1
});
}, options);
for (claimEnd of claimEnds)
await claimEnd.updateAttributes({claimDestinationFk: trashDestination});
await claimEnd.updateAttributes({claimDestinationFk: trashDestination}, options);
let claimBefore = await app.models.Claim.findById(claimFk);
await app.models.Claim.regularizeClaim(ctx, claimFk);
let claimAfter = await app.models.Claim.findById(claimFk);
let claimBefore = await app.models.Claim.findById(claimFk, null, options);
await app.models.Claim.regularizeClaim(ctx, claimFk, options);
let claimAfter = await app.models.Claim.findById(claimFk, null, options);
trashTicket = await app.models.Ticket.findOne({where: {addressFk: 12}});
trashTicket = await app.models.Ticket.findOne({where: {addressFk: 12}}, options);
expect(trashTicket.addressFk).toEqual(trashAddress);
expect(claimBefore.claimStateFk).toEqual(pendentState);
expect(claimAfter.claimStateFk).toEqual(resolvedState);
expect(chatModel.sendCheckingPresence).toHaveBeenCalledWith(ctx, 18, 'Trash');
expect(chatModel.sendCheckingPresence).toHaveBeenCalledTimes(4);
await tx.rollback();
} catch (e) {
await tx.rollback();
throw e;
}
});
it('should send a chat message with value "Bueno" and then change claim state to resolved', async() => {
const tx = await app.models.Claim.beginTransaction({});
try {
const options = {transaction: tx};
spyOn(chatModel, 'sendCheckingPresence').and.callThrough();
claimEnds = await app.models.ClaimEnd.importTicketSales(ctx, {
claimFk: claimFk,
ticketFk: 1
});
}, options);
for (claimEnd of claimEnds)
await claimEnd.updateAttributes({claimDestinationFk: okDestination});
await claimEnd.updateAttributes({claimDestinationFk: okDestination}, options);
await app.models.Claim.regularizeClaim(ctx, claimFk);
await app.models.Claim.regularizeClaim(ctx, claimFk, options);
expect(chatModel.sendCheckingPresence).toHaveBeenCalledWith(ctx, 18, 'Bueno');
expect(chatModel.sendCheckingPresence).toHaveBeenCalledTimes(4);
await tx.rollback();
} catch (e) {
await tx.rollback();
throw e;
}
});
it('should send a chat message to the salesPerson when claim isPickUp is enabled', async() => {
const tx = await app.models.Claim.beginTransaction({});
try {
const options = {transaction: tx};
spyOn(chatModel, 'sendCheckingPresence').and.callThrough();
claimEnds = await app.models.ClaimEnd.importTicketSales(ctx, {
claimFk: claimFk,
ticketFk: 1
});
}, options);
for (claimEnd of claimEnds)
await claimEnd.updateAttributes({claimDestinationFk: okDestination});
await claimEnd.updateAttributes({claimDestinationFk: okDestination}, options);
await app.models.Claim.regularizeClaim(ctx, claimFk);
await app.models.Claim.regularizeClaim(ctx, claimFk, options);
expect(chatModel.sendCheckingPresence).toHaveBeenCalledWith(ctx, 18, 'Bueno');
expect(chatModel.sendCheckingPresence).toHaveBeenCalledTimes(4);
await tx.rollback();
} catch (e) {
await tx.rollback();
throw e;
}
});
});

View File

@ -1,7 +1,7 @@
const app = require('vn-loopback/server/server');
describe('Update Claim', () => {
let newDate = new Date();
const newDate = new Date();
const originalData = {
ticketFk: 3,
clientFk: 101,
@ -14,7 +14,15 @@ describe('Update Claim', () => {
};
it(`should throw an error as the user doesn't have rights`, async() => {
let newClaim = await app.models.Claim.create(originalData);
const tx = await app.models.Claim.beginTransaction({});
let error;
try {
const options = {transaction: tx};
const newClaim = await app.models.Claim.create(originalData, options);
const forbiddenState = 3;
const salesPersonId = 18;
const ctx = {
@ -28,19 +36,24 @@ describe('Update Claim', () => {
observation: 'valid observation'
}
};
await app.models.Claim.updateClaim(ctx, newClaim.id)
.catch(e => {
await app.models.Claim.updateClaim(ctx, newClaim.id, options);
await tx.rollback();
} catch (e) {
error = e;
});
await tx.rollback();
}
expect(error.message).toEqual(`You don't have enough privileges to change that field`);
// restores
await app.models.Claim.destroyById(newClaim.id);
});
it(`should success to update the claim within privileges `, async() => {
let newClaim = await app.models.Claim.create(originalData);
const tx = await app.models.Claim.beginTransaction({});
try {
const options = {transaction: tx};
const newClaim = await app.models.Claim.create(originalData, options);
const canceledState = 4;
const claimManagerId = 72;
@ -56,18 +69,27 @@ describe('Update Claim', () => {
hasToPickUp: false
}
};
await app.models.Claim.updateClaim(ctx, newClaim.id);
await app.models.Claim.updateClaim(ctx, newClaim.id, options);
let updatedClaim = await app.models.Claim.findById(newClaim.id);
let updatedClaim = await app.models.Claim.findById(newClaim.id, null, options);
expect(updatedClaim.observation).toEqual(ctx.args.observation);
// restores
await app.models.Claim.destroyById(newClaim.id);
await tx.rollback();
} catch (e) {
await tx.rollback();
throw e;
}
});
it('should change some sensible fields as claimManager', async() => {
let newClaim = await app.models.Claim.create(originalData);
const tx = await app.models.Claim.beginTransaction({});
try {
const options = {transaction: tx};
const newClaim = await app.models.Claim.create(originalData, options);
const chatModel = app.models.Chat;
spyOn(chatModel, 'sendCheckingPresence').and.callThrough();
@ -87,16 +109,19 @@ describe('Update Claim', () => {
ctx.req.__ = (value, params) => {
return params.nickname;
};
await app.models.Claim.updateClaim(ctx, newClaim.id);
await app.models.Claim.updateClaim(ctx, newClaim.id, options);
let updatedClaim = await app.models.Claim.findById(newClaim.id);
let updatedClaim = await app.models.Claim.findById(newClaim.id, null, options);
expect(updatedClaim.observation).toEqual(ctx.args.observation);
expect(updatedClaim.claimStateFk).toEqual(ctx.args.claimStateFk);
expect(updatedClaim.workerFk).toEqual(ctx.args.workerFk);
expect(chatModel.sendCheckingPresence).toHaveBeenCalled();
// restores
await app.models.Claim.destroyById(newClaim.id);
await tx.rollback();
} catch (e) {
await tx.rollback();
throw e;
}
});
});

View File

@ -1,9 +1,8 @@
const app = require('vn-loopback/server/server');
describe('Update Claim', () => {
let newDate = new Date();
let newInstance;
let original = {
const newDate = new Date();
const original = {
ticketFk: 3,
clientFk: 101,
ticketCreated: newDate,
@ -14,30 +13,43 @@ describe('Update Claim', () => {
observation: 'observation'
};
beforeAll(async done => {
newInstance = await app.models.Claim.create(original);
done();
});
afterAll(async done => {
await app.models.Claim.destroyById(newInstance.id);
done();
});
it('should update the claim isChargedToMana attribute', async() => {
const tx = await app.models.Claim.beginTransaction({});
try {
const options = {transaction: tx};
const ctx = {args: {isChargedToMana: false}};
const result = await app.models.Claim.updateClaimAction(ctx, newInstance.id);
const newInstance = await app.models.Claim.create(original, options);
const result = await app.models.Claim.updateClaimAction(ctx, newInstance.id, options);
expect(result.id).toEqual(newInstance.id);
expect(result.isChargedToMana).toBeFalsy();
await tx.rollback();
} catch (e) {
await tx.rollback();
throw e;
}
});
it('should update the claim responsibility attribute', async() => {
const tx = await app.models.Claim.beginTransaction({});
try {
const options = {transaction: tx};
const ctx = {args: {responsibility: 2}};
const result = await app.models.Claim.updateClaimAction(ctx, newInstance.id);
const newInstance = await app.models.Claim.create(original, options);
const result = await app.models.Claim.updateClaimAction(ctx, newInstance.id, options);
expect(result.id).toEqual(newInstance.id);
expect(result.responsibility).toEqual(2);
await tx.rollback();
} catch (e) {
await tx.rollback();
throw e;
}
});
});

View File

@ -4,25 +4,26 @@ module.exports = Self => {
description: 'Update a claim with privileges',
accepts: [{
arg: 'ctx',
type: 'Object',
type: 'object',
http: {source: 'context'}
}, {
},
{
arg: 'id',
type: 'Number',
type: 'number',
description: 'Claim id',
http: {source: 'path'}
},
{
arg: 'workerFk',
type: 'Number'
type: 'number'
},
{
arg: 'claimStateFk',
type: 'Number'
type: 'number'
},
{
arg: 'observation',
type: 'String'
type: 'string'
},
{
arg: 'hasToPickUp',
@ -38,11 +39,23 @@ module.exports = Self => {
}
});
Self.updateClaim = async(ctx, id) => {
Self.updateClaim = async(ctx, id, options) => {
const models = Self.app.models;
const userId = ctx.req.accessToken.userId;
const args = ctx.args;
const $t = ctx.req.__; // $translate
let tx;
let myOptions = {};
if (typeof options == 'object')
Object.assign(myOptions, options);
if (!myOptions.transaction) {
tx = await Self.beginTransaction({});
myOptions.transaction = tx;
}
try {
const claim = await models.Claim.findById(id, {
include: {
relation: 'client',
@ -52,21 +65,21 @@ module.exports = Self => {
}
}
}
});
}, myOptions);
let changedHasToPickUp = false;
if (args.hasToPickUp)
changedHasToPickUp = true;
if (args.claimStateFk) {
const canUpdate = await canChangeState(ctx, claim.claimStateFk);
const hasRights = await canChangeState(ctx, args.claimStateFk);
const isClaimManager = await models.Account.hasRole(userId, 'claimManager');
const canUpdate = await canChangeState(ctx, claim.claimStateFk, myOptions);
const hasRights = await canChangeState(ctx, args.claimStateFk, myOptions);
const isClaimManager = await models.Account.hasRole(userId, 'claimManager', myOptions);
if (!canUpdate || !hasRights || changedHasToPickUp && !isClaimManager)
throw new UserError(`You don't have enough privileges to change that field`);
}
delete args.ctx;
const updatedClaim = await claim.updateAttributes(args);
const updatedClaim = await claim.updateAttributes(args, myOptions);
// Get sales person from claim client
const salesPerson = claim.client().salesPersonUser();
if (salesPerson && changedHasToPickUp && updatedClaim.hasToPickUp) {
@ -79,10 +92,16 @@ module.exports = Self => {
await models.Chat.sendCheckingPresence(ctx, salesPerson.id, message);
}
if (tx) await tx.commit();
return updatedClaim;
} catch (e) {
if (tx) await tx.rollback();
throw e;
}
};
async function canChangeState(ctx, id) {
async function canChangeState(ctx, id, options) {
let models = Self.app.models;
let userId = ctx.req.accessToken.userId;
@ -90,9 +109,9 @@ module.exports = Self => {
include: {
relation: 'writeRole'
}
});
}, options);
let stateRole = state.writeRole().name;
let canUpdate = await models.Account.hasRole(userId, stateRole);
let canUpdate = await models.Account.hasRole(userId, stateRole, options);
return canUpdate;
}

View File

@ -28,12 +28,32 @@ module.exports = Self => {
}
});
Self.updateClaimAction = async(ctx, id) => {
Self.updateClaimAction = async(ctx, id, options) => {
let tx;
let myOptions = {};
if (typeof options == 'object')
Object.assign(myOptions, options);
if (!myOptions.transaction) {
tx = await Self.beginTransaction({});
myOptions.transaction = tx;
}
try {
const models = Self.app.models;
const claim = await models.Claim.findById(id);
const claim = await models.Claim.findById(id, null, myOptions);
const args = ctx.args;
delete args.ctx;
return await claim.updateAttributes(args);
const updatedClaim = await claim.updateAttributes(args, myOptions);
if (tx) await tx.commit();
return updatedClaim;
} catch (e) {
if (tx) await tx.rollback();
throw e;
}
};
};

View File

@ -4,40 +4,46 @@ module.exports = Self => {
accessType: 'WRITE',
accepts: [{
arg: 'id',
type: 'Number',
type: 'number',
description: 'The claim id',
http: {source: 'path'}
}, {
},
{
arg: 'warehouseId',
type: 'Number',
type: 'number',
description: 'The warehouse id',
required: true
}, {
},
{
arg: 'companyId',
type: 'Number',
type: 'number',
description: 'The company id',
required: true
}, {
},
{
arg: 'dmsTypeId',
type: 'Number',
type: 'number',
description: 'The dms type id',
required: true
}, {
},
{
arg: 'reference',
type: 'String',
type: 'string',
required: true
}, {
},
{
arg: 'description',
type: 'String',
type: 'string',
required: true
}, {
},
{
arg: 'hasFile',
type: 'Boolean',
type: 'boolean',
description: 'True if has an attached file',
required: true
}],
returns: {
type: 'Object',
type: 'object',
root: true
},
http: {
@ -46,31 +52,38 @@ module.exports = Self => {
}
});
Self.uploadFile = async(ctx, id) => {
Self.uploadFile = async(ctx, id, options) => {
let tx;
let myOptions = {};
if (typeof options == 'object')
Object.assign(myOptions, options);
if (!myOptions.transaction) {
tx = await Self.beginTransaction({});
myOptions.transaction = tx;
}
const models = Self.app.models;
const promises = [];
const tx = await Self.beginTransaction({});
try {
const options = {transaction: tx};
const uploadedFiles = await models.Dms.uploadFile(ctx, options);
const uploadedFiles = await models.Dms.uploadFile(ctx, myOptions);
uploadedFiles.forEach(dms => {
const newClaimDms = models.ClaimDms.create({
claimFk: id,
dmsFk: dms.id
}, options);
}, myOptions);
promises.push(newClaimDms);
});
const resolvedPromises = await Promise.all(promises);
await tx.commit();
if (tx) await tx.commit();
return resolvedPromises;
} catch (err) {
await tx.rollback();
throw err;
} catch (e) {
if (tx) await tx.rollback();
throw e;
}
};
};

View File

@ -10,10 +10,10 @@ module.exports = Self => {
description: 'The routes ids to clone'
},
{
arg: 'started',
arg: 'created',
type: 'date',
required: true,
description: 'The started date for all routes'
description: 'The created date for all routes'
}
],
returns: {
@ -26,7 +26,7 @@ module.exports = Self => {
}
});
Self.clone = async(ids, started) => {
Self.clone = async(ids, created) => {
const tx = await Self.beginTransaction({});
try {
const options = {transaction: tx};
@ -39,8 +39,7 @@ module.exports = Self => {
throw new Error(`The amount of routes found don't match`);
const routes = originalRoutes.map(route => {
route.started = started;
route.created = new Date();
route.created = created;
return route;
});

View File

@ -38,10 +38,10 @@ module.exports = Self => {
for (let zoneAgencyMode of zoneAgencyModes)
zoneIds.push(zoneAgencyMode.zoneFk);
const minDate = new Date(route.finished);
const minDate = new Date(route.created);
minDate.setHours(0, 0, 0, 0);
const maxDate = new Date(route.finished);
const maxDate = new Date(route.created);
maxDate.setHours(23, 59, 59, 59);
let tickets = await Self.app.models.Ticket.find({
@ -49,7 +49,7 @@ module.exports = Self => {
agencyModeFk: route.agencyModeFk,
zoneFk: {inq: zoneIds},
id: {nin: idsToExclude},
landed: {between: [minDate, maxDate]}
created: {between: [minDate, maxDate]}
},
include: [
{

View File

@ -1,14 +1,14 @@
const app = require('vn-loopback/server/server');
describe('route clone()', () => {
const startDate = new Date();
const createdDate = new Date();
it('should throw an error if the amount of ids pased to the clone function do no match the database', async() => {
const ids = [996, 997, 998, 999];
let error;
try {
await app.models.Route.clone(ids, startDate);
await app.models.Route.clone(ids, createdDate);
} catch (e) {
error = e;
}
@ -20,7 +20,7 @@ describe('route clone()', () => {
it('should clone two routes', async() => {
const ids = [1, 2];
const clones = await app.models.Route.clone(ids, startDate);
const clones = await app.models.Route.clone(ids, createdDate);
expect(clones.length).toEqual(2);

View File

@ -105,8 +105,7 @@
<vn-horizontal>
<vn-date-picker
label="Starting date"
ng-model="$ctrl.startedDate"
required="true">
ng-model="$ctrl.createdDate">
</vn-date-picker>
</vn-horizontal>
</tpl-body>

View File

@ -41,19 +41,26 @@ export default class Controller extends Section {
}
openClonationDialog() {
this.startedDate = new Date();
this.$.clonationDialog.show();
this.createdDate = new Date();
}
cloneSelectedRoutes() {
try {
if (!this.createdDate)
throw new Error(`The date can't be empty`);
const routesIds = [];
for (let route of this.checked)
routesIds.push(route.id);
return this.$http.post('Routes/clone', {ids: routesIds, started: this.startedDate}).then(() => {
return this.$http.post('Routes/clone', {ids: routesIds, created: this.createdDate}).then(() => {
this.$.model.refresh();
this.vnApp.showSuccess(this.$t('Data saved!'));
});
} catch (e) {
this.vnApp.showError(this.$t(e.message));
}
}
onDrop($event) {

View File

@ -62,7 +62,7 @@ describe('Component vnRouteIndex', () => {
describe('cloneSelectedRoutes()', () => {
it('should perform an http request to Routes/clone', () => {
controller.startedDate = new Date();
controller.createdDate = new Date();
$httpBackend.expect('POST', 'Routes/clone').respond();
controller.cloneSelectedRoutes();

View File

@ -1,5 +1,5 @@
Vehicle: Vehículo
Download selected routes as PDF: Descargar rutas seleccionadas como PDF
Clone selected routes: Clonar rutas seleccionadas
Select the starting date: Seleccione fecha de inicio
The date can't be empty: La fecha no puede estar vacía
Starting date: Fecha de inicio

View File

@ -10,7 +10,7 @@
data="contacts"
form="form">
</vn-watcher>
<form name="form" ng-submit="$ctrl.onSubmit()" class="vn-w-md">
<form name="form" ng-submit="$ctrl.onSubmit()" class="vn-w-lg">
<vn-card class="vn-pa-lg">
<div ng-repeat="contact in contacts" class="contact">
<vn-vertical>

View File

@ -1,7 +1,6 @@
const app = require('vn-loopback/server/server');
// #2868 Excluded until database export
xdescribe('ticket filter()', () => {
describe('ticket filter()', () => {
it('should return the tickets matching the filter', async() => {
const ctx = {req: {accessToken: {userId: 9}}, args: {}};
const filter = {order: 'id DESC'};
@ -24,7 +23,7 @@ xdescribe('ticket filter()', () => {
const filter = {};
const result = await app.models.Ticket.filter(ctx, filter);
expect(result.length).toEqual(4);
expect(result.length).toEqual(3);
});
it('should return the tickets matching the problems on false', async() => {
@ -42,7 +41,7 @@ xdescribe('ticket filter()', () => {
const filter = {};
const result = await app.models.Ticket.filter(ctx, filter);
expect(result.length).toEqual(10);
expect(result.length).toEqual(7);
});
it('should return the tickets matching the problems on null', async() => {