Merge branch 'dev' into 2428-anchor_directive
gitea/salix/pipeline/head This commit looks good Details

This commit is contained in:
Joan Sanchez 2020-10-21 08:32:31 +00:00
commit 3c37f0e993
95 changed files with 7856 additions and 2194 deletions

View File

@ -3,15 +3,23 @@ const app = require('vn-loopback/server/server');
describe('account login()', () => { describe('account login()', () => {
describe('when credentials are correct', () => { describe('when credentials are correct', () => {
it('should return the token', async() => { it('should return the token', async() => {
let response = await app.models.Account.login('employee', 'nightmare'); let login = await app.models.Account.login('salesAssistant', 'nightmare');
let accessToken = await app.models.AccessToken.findById(login.token);
let ctx = {req: {accessToken: accessToken}};
expect(response.token).toBeDefined(); expect(login.token).toBeDefined();
await app.models.Account.logout(ctx);
}); });
it('should return the token if the user doesnt exist but the client does', async() => { it('should return the token if the user doesnt exist but the client does', async() => {
let response = await app.models.Account.login('PetterParker', 'nightmare'); let login = await app.models.Account.login('PetterParker', 'nightmare');
let accessToken = await app.models.AccessToken.findById(login.token);
let ctx = {req: {accessToken: accessToken}};
expect(response.token).toBeDefined(); expect(login.token).toBeDefined();
await app.models.Account.logout(ctx);
}); });
}); });

View File

@ -2,15 +2,15 @@ const app = require('vn-loopback/server/server');
describe('account logout()', () => { describe('account logout()', () => {
it('should logout and remove token after valid login', async() => { it('should logout and remove token after valid login', async() => {
let loginResponse = await app.models.Account.login('employee', 'nightmare'); let loginResponse = await app.models.Account.login('buyer', 'nightmare');
let accessToken = await app.models.AccessToken.findById(loginResponse.token); let accessToken = await app.models.AccessToken.findById(loginResponse.token);
let ctx = {req: {accessToken: accessToken}}; let ctx = {req: {accessToken: accessToken}};
let response = await app.models.Account.logout(ctx); let logoutResponse = await app.models.Account.logout(ctx);
let afterToken = await app.models.AccessToken.findById(loginResponse.token); let tokenAfterLogout = await app.models.AccessToken.findById(loginResponse.token);
expect(response).toBeTruthy(); expect(logoutResponse).toBeTrue();
expect(afterToken).toBeNull(); expect(tokenAfterLogout).toBeNull();
}); });
it('should throw a 401 error when token is invalid', async() => { it('should throw a 401 error when token is invalid', async() => {

View File

@ -1,55 +1,17 @@
const app = require('vn-loopback/server/server'); const app = require('vn-loopback/server/server');
describe('chat sendCheckingPresence()', () => { describe('chat sendCheckingPresence()', () => {
const today = new Date();
today.setHours(6, 0);
const ctx = {req: {accessToken: {userId: 1}}};
const chatModel = app.models.Chat;
const departmentId = 23; const departmentId = 23;
const workerId = 107; const workerId = 107;
let timeEntry;
afterAll(async done => { it(`should call send() method with the worker name if he's currently working then return a response`, async() => {
const department = await app.models.Department.findById(departmentId);
await department.updateAttribute('chatName', null);
await app.models.WorkerTimeControl.destroyById(timeEntry.id);
done();
});
it(`should call to send() method with the worker username when no department channel is specified
and then return a "Fake notification sent" as response`, async() => {
const ctx = {req: {accessToken: {userId: 1}}};
const chatModel = app.models.Chat;
spyOn(chatModel, 'send').and.callThrough(); spyOn(chatModel, 'send').and.callThrough();
const response = await chatModel.sendCheckingPresence(ctx, workerId, 'I changed something'); const timeEntry = await app.models.WorkerTimeControl.create({
expect(response.statusCode).toEqual(200);
expect(response.message).toEqual('Fake notification sent');
expect(chatModel.send).toHaveBeenCalledWith(ctx, '@HankPym', 'I changed something');
});
it(`should call to send() method with the worker department channel if is specified
and then return a "Fake notification sent" as response`, async() => {
const ctx = {req: {accessToken: {userId: 1}}};
const chatModel = app.models.Chat;
spyOn(chatModel, 'send').and.callThrough();
const department = await app.models.Department.findById(departmentId);
await department.updateAttribute('chatName', 'cooler');
const response = await chatModel.sendCheckingPresence(ctx, workerId, 'I changed something');
expect(response.statusCode).toEqual(200);
expect(response.message).toEqual('Fake notification sent');
expect(chatModel.send).toHaveBeenCalledWith(ctx, '#cooler', '@HankPym => I changed something');
});
it(`should call to send() method with the worker username when the worker is working`, async() => {
const ctx = {req: {accessToken: {userId: 1}}};
const chatModel = app.models.Chat;
spyOn(chatModel, 'send').and.callThrough();
const today = new Date();
today.setHours(6, 0);
timeEntry = await app.models.WorkerTimeControl.create({
userFk: workerId, userFk: workerId,
timed: today, timed: today,
manual: false, manual: false,
@ -61,5 +23,24 @@ describe('chat sendCheckingPresence()', () => {
expect(response.statusCode).toEqual(200); expect(response.statusCode).toEqual(200);
expect(response.message).toEqual('Fake notification sent'); expect(response.message).toEqual('Fake notification sent');
expect(chatModel.send).toHaveBeenCalledWith(ctx, '@HankPym', 'I changed something'); expect(chatModel.send).toHaveBeenCalledWith(ctx, '@HankPym', 'I changed something');
// restores
await app.models.WorkerTimeControl.destroyById(timeEntry.id);
});
it(`should call to send() method with the worker department channel if he's not currently working then return a response`, async() => {
spyOn(chatModel, 'send').and.callThrough();
const department = await app.models.Department.findById(departmentId);
await department.updateAttribute('chatName', 'cooler');
const response = await chatModel.sendCheckingPresence(ctx, workerId, 'I changed something');
expect(response.statusCode).toEqual(200);
expect(response.message).toEqual('Fake notification sent');
expect(chatModel.send).toHaveBeenCalledWith(ctx, '#cooler', '@HankPym => I changed something');
// restores
await department.updateAttribute('chatName', null);
}); });
}); });

View File

@ -1,6 +1,7 @@
const app = require('vn-loopback/server/server'); const app = require('vn-loopback/server/server');
describe('updateCollectionSale()', () => { // #2495 updateCollectionSale reparar polución de unitarios
xdescribe('updateCollectionSale()', () => {
it('return a new collection', async() => { it('return a new collection', async() => {
let ctx = {req: {accessToken: {userId: 106}}}; let ctx = {req: {accessToken: {userId: 106}}};
let response = await app.models.Collection.updateCollectionSale(ctx, 1, 5, 5, 5, 1, 4, false, 'UXN', 1, 1); let response = await app.models.Collection.updateCollectionSale(ctx, 1, 5, 5, 5, 1, 4, false, 'UXN', 1, 1);

View File

@ -110,13 +110,15 @@ BEGIN
SELECT `name` FROM `user` WHERE id = OLD.id; SELECT `name` FROM `user` WHERE id = OLD.id;
END$$ END$$
CREATE TRIGGER role_beforeInsert DROP TRIGGER IF EXISTS account.role_beforeInsert$$
CREATE DEFINER=`root`@`%` TRIGGER role_beforeInsert
BEFORE INSERT ON `role` FOR EACH ROW BEFORE INSERT ON `role` FOR EACH ROW
BEGIN BEGIN
CALL role_checkName(NEW.`name`); CALL role_checkName(NEW.`name`);
END$$ END
CREATE TRIGGER role_beforeUpdate DROP TRIGGER IF EXISTS account.role_beforeUpdate$$
CREATE DEFINER=`root`@`%` TRIGGER role_beforeUpdate
BEFORE UPDATE ON `role` FOR EACH ROW BEFORE UPDATE ON `role` FOR EACH ROW
BEGIN BEGIN
IF !(NEW.`name` <=> OLD.`name`) THEN IF !(NEW.`name` <=> OLD.`name`) THEN

View File

@ -1,7 +1,7 @@
DROP PROCEDURE IF EXISTS account.role_checkName; DROP PROCEDURE IF EXISTS account.role_checkName;
DELIMITER $$ DELIMITER $$
CREATE PROCEDURE account.role_checkName(vRoleName VARCHAR(255)) CREATE DEFINER=`root`@`%` PROCEDURE account.role_checkName(vRoleName VARCHAR(255))
BEGIN BEGIN
/** /**
* Checks that role name meets the necessary syntax requirements, otherwise it * Checks that role name meets the necessary syntax requirements, otherwise it

View File

@ -0,0 +1,3 @@
UPDATE `salix`.`ACL` SET `principalId` = 'deliveryBoss' WHERE (`id` = '194');
INSERT INTO `salix`.`ACL` (`model`, `property`, `accessType`, `permission`, `principalType`, `principalId`) VALUES ('Town', '*', 'WRITE', 'ALLOW', 'ROLE', 'deliveryBoss');
INSERT INTO `salix`.`ACL` (`model`, `property`, `accessType`, `permission`, `principalType`, `principalId`) VALUES ('Province', '*', 'WRITE', 'ALLOW', 'ROLE', 'deliveryBoss');

File diff suppressed because one or more lines are too long

View File

@ -2116,4 +2116,18 @@ INSERT INTO `vn`.`tabletDepartment`(`tabletFk`, `departmentFk`)
(1, 23), (1, 23),
(2, 1); (2, 1);
INSERT INTO `vn`.`campaign`(`code`, `dated`)
VALUES
('valentinesDay', CONCAT(YEAR(CURDATE()), '-02-14')),
('valentinesDay', CONCAT(YEAR(DATE_ADD(CURDATE(), INTERVAL -1 YEAR)), '-02-14')),
('valentinesDay', CONCAT(YEAR(DATE_ADD(CURDATE(), INTERVAL -2 YEAR)), '-02-14')),
('valentinesDay', CONCAT(YEAR(DATE_ADD(CURDATE(), INTERVAL -3 YEAR)), '-02-14')),
('mothersDay', CONCAT(YEAR(CURDATE()), '-05-05')),
('mothersDay', CONCAT(YEAR(DATE_ADD(CURDATE(), INTERVAL -1 YEAR)), '-05-05')),
('mothersDay', CONCAT(YEAR(DATE_ADD(CURDATE(), INTERVAL -2 YEAR)), '-05-05')),
('mothersDay', CONCAT(YEAR(DATE_ADD(CURDATE(), INTERVAL -3 YEAR)), '-05-05')),
('allSaints', CONCAT(YEAR(CURDATE()), '-11-01')),
('allSaints', CONCAT(YEAR(DATE_ADD(CURDATE(), INTERVAL -1 YEAR)), '-11-01')),
('allSaints', CONCAT(YEAR(DATE_ADD(CURDATE(), INTERVAL -2 YEAR)), '-11-01')),
('allSaints', CONCAT(YEAR(DATE_ADD(CURDATE(), INTERVAL -3 YEAR)), '-11-01'));

File diff suppressed because it is too large Load Diff

View File

@ -82,3 +82,10 @@ TABLES=(
workcenter workcenter
) )
dump_tables ${TABLES[@]} dump_tables ${TABLES[@]}
TABLES=(
sage
TiposIva
TiposTransacciones
)
dump_tables ${TABLES[@]}

View File

@ -9,6 +9,7 @@ SCHEMAS=(
nst nst
pbx pbx
postgresql postgresql
sage
salix salix
stock stock
util util

View File

@ -42,13 +42,24 @@ export default {
taxNumber: 'vn-client-create vn-textfield[ng-model="$ctrl.client.fi"]', taxNumber: 'vn-client-create vn-textfield[ng-model="$ctrl.client.fi"]',
socialName: 'vn-client-create vn-textfield[ng-model="$ctrl.client.socialName"]', socialName: 'vn-client-create vn-textfield[ng-model="$ctrl.client.socialName"]',
street: 'vn-client-create vn-textfield[ng-model="$ctrl.client.street"]', street: 'vn-client-create vn-textfield[ng-model="$ctrl.client.street"]',
addPostCode: 'vn-client-create vn-datalist[ng-model="$ctrl.client.postcode"] vn-icon-button[icon="add_circle"]',
addProvince: 'vn-autocomplete[ng-model="$ctrl.location.provinceFk"] vn-icon-button[icon="add_circle"]',
addCity: 'vn-autocomplete[ng-model="$ctrl.location.townFk"] vn-icon-button[icon="add_circle"]',
newProvinceName: 'vn-textfield[ng-model="$ctrl.province.name"]',
newCityName: 'vn-textfield[ng-model="$ctrl.city.name"]',
newCityProvince: 'vn-autocomplete[ng-model="$ctrl.city.provinceFk"]',
newPostcode: 'vn-textfield[ng-model="$ctrl.location.code"]',
postcode: 'vn-client-create vn-datalist[ng-model="$ctrl.client.postcode"]', postcode: 'vn-client-create vn-datalist[ng-model="$ctrl.client.postcode"]',
city: 'vn-client-create vn-datalist[ng-model="$ctrl.client.city"]', city: 'vn-client-create vn-datalist[ng-model="$ctrl.client.city"]',
province: 'vn-client-create vn-autocomplete[ng-model="$ctrl.client.provinceFk"]', province: 'vn-autocomplete[ng-model="$ctrl.client.provinceFk"]',
country: 'vn-client-create vn-autocomplete[ng-model="$ctrl.client.countryFk"]', country: 'vn-client-create vn-autocomplete[ng-model="$ctrl.client.countryFk"]',
dialogCountry: 'vn-autocomplete[ng-model="$ctrl.province.countryFk"]',
userName: 'vn-client-create vn-textfield[ng-model="$ctrl.client.userName"]', userName: 'vn-client-create vn-textfield[ng-model="$ctrl.client.userName"]',
email: 'vn-client-create vn-textfield[ng-model="$ctrl.client.email"]', email: 'vn-client-create vn-textfield[ng-model="$ctrl.client.email"]',
salesPerson: 'vn-client-create vn-autocomplete[ng-model="$ctrl.client.salesPersonFk"]', salesPerson: 'vn-client-create vn-autocomplete[ng-model="$ctrl.client.salesPersonFk"]',
saveNewProvicenButton: '#saveProvince',
saveNewCityButton: '#saveCity',
saveNewPoscode: '#savePostcode',
createButton: 'vn-client-create button[type=submit]' createButton: 'vn-client-create button[type=submit]'
}, },
clientDescriptor: { clientDescriptor: {

View File

@ -8,7 +8,7 @@ describe('Client create path', () => {
beforeAll(async() => { beforeAll(async() => {
browser = await getBrowser(); browser = await getBrowser();
page = browser.page; page = browser.page;
await page.loginAndModule('employee', 'client'); await page.loginAndModule(' deliveryBoss', 'client');
}); });
afterAll(async() => { afterAll(async() => {
@ -45,14 +45,40 @@ describe('Client create path', () => {
expect(message.text).toBe('Some fields are invalid'); expect(message.text).toBe('Some fields are invalid');
}); });
it(`should create a new province`, async() => {
await page.waitToClick(selectors.createClientView.addPostCode);
await page.waitToClick(selectors.createClientView.addProvince);
await page.write(selectors.createClientView.newProvinceName, 'Massachusetts');
await page.autocompleteSearch(selectors.createClientView.dialogCountry, 'España');
await page.waitToClick(selectors.createClientView.saveNewProvicenButton);
const message = await page.waitForSnackbar();
expect(message.text).toBe('The province has been created');
});
it(`should create a new city`, async() => {
await page.waitToClick(selectors.createClientView.addCity);
await page.write(selectors.createClientView.newCityName, 'Boston');
await page.autocompleteSearch(selectors.createClientView.newCityProvince, 'Massachusetts');
await page.waitToClick(selectors.createClientView.saveNewCityButton);
const message = await page.waitForSnackbar();
expect(message.text).toBe('The city has been created');
});
it(`should create a new post code`, async() => {
await page.write(selectors.createClientView.newPostcode, '61616');
await page.waitToClick(selectors.createClientView.saveNewPoscode);
const message = await page.waitForSnackbar();
expect(message.text).toBe('The postcode has been created. You can save the data now');
});
it(`should attempt to create a new user with all it's data but wrong email`, async() => { it(`should attempt to create a new user with all it's data but wrong email`, async() => {
await page.write(selectors.createClientView.name, 'Carol Danvers'); await page.write(selectors.createClientView.name, 'Carol Danvers');
await page.write(selectors.createClientView.socialName, 'AVG tax'); await page.write(selectors.createClientView.socialName, 'AVG tax');
await page.write(selectors.createClientView.street, 'Many places'); await page.write(selectors.createClientView.street, 'Many places');
await page.autocompleteSearch(selectors.createClientView.country, 'España');
await page.autocompleteSearch(selectors.createClientView.province, 'Province one');
await page.write(selectors.createClientView.city, 'Valencia');
await page.write(selectors.createClientView.postcode, '46000');
await page.clearInput(selectors.createClientView.email); await page.clearInput(selectors.createClientView.email);
await page.write(selectors.createClientView.email, 'incorrect email format'); await page.write(selectors.createClientView.email, 'incorrect email format');
await page.waitToClick(selectors.createClientView.createButton); await page.waitToClick(selectors.createClientView.createButton);
@ -82,14 +108,14 @@ describe('Client create path', () => {
const clientCountry = await page const clientCountry = await page
.waitToGetProperty(selectors.createClientView.country, 'value'); .waitToGetProperty(selectors.createClientView.country, 'value');
expect(clientCity).toEqual('Valencia'); expect(clientCity).toEqual('Boston');
expect(clientProvince).toContain('Province one'); expect(clientProvince).toContain('Massachusetts');
expect(clientCountry).toEqual('España'); expect(clientCountry).toEqual('España');
}); });
it(`should create a new user with all correct data`, async() => { it(`should create a new user with all correct data`, async() => {
await page.clearInput(selectors.createClientView.postcode); await page.clearInput(selectors.createClientView.postcode);
await page.write(selectors.createClientView.postcode, '46000'); await page.write(selectors.createClientView.postcode, '61616');
await page.waitToClick(selectors.createClientView.createButton); await page.waitToClick(selectors.createClientView.createButton);
const message = await page.waitForSnackbar(); const message = await page.waitForSnackbar();

View File

@ -32,7 +32,7 @@ describe('Item regularize path', () => {
expect(userLocalWarehouse).toContain('Warehouse Four'); expect(userLocalWarehouse).toContain('Warehouse Four');
}); });
it('should search for an specific item', async() => { it('should search for a specific item', async() => {
await page.accessToSearchResult('Ranged weapon pistol 9mm'); await page.accessToSearchResult('Ranged weapon pistol 9mm');
await page.waitForState('item.card.summary'); await page.waitForState('item.card.summary');
}); });

View File

@ -26,7 +26,9 @@ describe('Entry create path', () => {
await page.autocompleteSearch(selectors.entryIndex.newEntryCompany, 'ORN'); await page.autocompleteSearch(selectors.entryIndex.newEntryCompany, 'ORN');
await page.waitToClick(selectors.entryIndex.saveNewEntry); await page.waitToClick(selectors.entryIndex.saveNewEntry);
await page.waitFor(500); await page.waitForNavigation({
waitUntil: 'load',
});
await page.waitForState('entry.card.basicData'); await page.waitForState('entry.card.basicData');
}); });
}); });

View File

@ -95,9 +95,7 @@ async function launchBackTest(done) {
let options = { let options = {
errorOnFail: false, errorOnFail: false,
config: { config: {}
random: false
}
}; };
if (argv.ci) { if (argv.ci) {

View File

@ -2,41 +2,37 @@ const app = require('vn-loopback/server/server');
describe('Model crud()', () => { describe('Model crud()', () => {
let insertId; let insertId;
let ItemBarcode = app.models.ItemBarcode; const barcodeModel = app.models.ItemBarcode;
it('should inherit crud method from VnModel', () => { it('should inherit crud method from VnModel', () => {
expect(ItemBarcode.crud).toBeDefined(); expect(barcodeModel.crud).toBeDefined();
}); });
it('should create a new instance', async() => { it('should create, edit and delete an instance', async() => {
let data = {code: '500', itemFk: '1'}; let barcodeData = {code: '500', itemFk: '1'};
let creates = [data]; let creates = [barcodeData];
await ItemBarcode.crud(null, null, creates); await barcodeModel.crud(null, null, creates);
let instance = await ItemBarcode.findOne({where: data}); let instance = await barcodeModel.findOne({where: barcodeData});
insertId = instance.id; insertId = instance.id;
expect(instance).not.toEqual(null); expect(instance).not.toEqual(null);
expect(instance.code).toEqual('500'); expect(instance.code).toEqual('500');
});
it('should update the instance', async() => {
let updates = [{ let updates = [{
where: {id: insertId}, where: {id: insertId},
data: {code: '501', itemFk: 1} data: {code: '501', itemFk: 1}
}]; }];
await ItemBarcode.crud(null, updates); await barcodeModel.crud(null, updates);
let instance = await ItemBarcode.findById(insertId); let editedInstance = await barcodeModel.findById(insertId);
expect(instance.code).toEqual('501'); expect(editedInstance.code).toEqual('501');
});
it('should delete the created instance', async() => {
let deletes = [insertId]; let deletes = [insertId];
await ItemBarcode.crud(deletes); await barcodeModel.crud(deletes);
let instance = await ItemBarcode.findById(insertId); let deletedInstance = await barcodeModel.findById(insertId);
expect(instance).toEqual(null); expect(deletedInstance).toEqual(null);
}); });
}); });

View File

@ -57,12 +57,12 @@
"The postcode doesn't exist. Please enter a correct one": "The postcode doesn't exist. Please enter a correct one", "The postcode doesn't exist. Please enter a correct one": "The postcode doesn't exist. Please enter a correct one",
"Can't create stowaway for this ticket": "Can't create stowaway for this ticket", "Can't create stowaway for this ticket": "Can't create stowaway for this ticket",
"Swift / BIC can't be empty": "Swift / BIC can't be empty", "Swift / BIC can't be empty": "Swift / BIC can't be empty",
"MESSAGE_BOUGHT_UNITS": "Bought {{quantity}} units of {{concept}} (#{{itemId}}) for the ticket id [#{{ticketId}}]({{{url}}})", "MESSAGE_BOUGHT_UNITS": "Bought {{quantity}} units of {{concept}} ({{itemId}}) for the ticket id [{{ticketId}}]({{{url}}})",
"MESSAGE_INSURANCE_CHANGE": "I have changed the insurence credit of client [{{clientName}} (#{{clientId}})]({{{url}}}) to *{{credit}} €*", "MESSAGE_INSURANCE_CHANGE": "I have changed the insurence credit of client [{{clientName}} ({{clientId}})]({{{url}}}) to *{{credit}} €*",
"MESSAGE_CHANGED_PAYMETHOD": "I have changed the pay method for client [{{clientName}} (#{{clientId}})]({{{url}}})", "MESSAGE_CHANGED_PAYMETHOD": "I have changed the pay method for client [{{clientName}} ({{clientId}})]({{{url}}})",
"Sent units from ticket": "I sent *{{quantity}}* units of [{{concept}} (#{{itemId}})]({{{itemUrl}}}) to *\"{{nickname}}\"* coming from ticket id [#{{ticketId}}]({{{ticketUrl}}})", "Sent units from ticket": "I sent *{{quantity}}* units of [{{concept}} ({{itemId}})]({{{itemUrl}}}) to *\"{{nickname}}\"* coming from ticket id [{{ticketId}}]({{{ticketUrl}}})",
"Claim will be picked": "The product from the claim (#{{claimId}})]({{{claimUrl}}}) from the client *{{clientName}}* will be picked", "Claim will be picked": "The product from the claim ({{claimId}})]({{{claimUrl}}}) from the client *{{clientName}}* will be picked",
"This ticket is not an stowaway anymore": "The ticket id [#{{ticketId}}]({{{ticketUrl}}}) is not an stowaway anymore", "This ticket is not an stowaway anymore": "The ticket id [{{ticketId}}]({{{ticketUrl}}}) is not an stowaway anymore",
"Customs agent is required for a non UEE member": "Customs agent is required for a non UEE member", "Customs agent is required for a non UEE member": "Customs agent is required for a non UEE member",
"Incoterms is required for a non UEE member": "Incoterms is required for a non UEE member", "Incoterms is required for a non UEE member": "Incoterms is required for a non UEE member",
"Client checked as validated despite of duplication": "Client checked as validated despite of duplication from client id {{clientId}}", "Client checked as validated despite of duplication": "Client checked as validated despite of duplication from client id {{clientId}}",
@ -70,6 +70,7 @@
"NOT_ZONE_WITH_THIS_PARAMETERS": "There's no zone available for this day", "NOT_ZONE_WITH_THIS_PARAMETERS": "There's no zone available for this day",
"Created absence": "The worker <strong>{{author}}</strong> has added an absence of type '{{absenceType}}' to <a href='{{{workerUrl}}}'><strong>{{employee}}</strong></a> for day {{dated}}.", "Created absence": "The worker <strong>{{author}}</strong> has added an absence of type '{{absenceType}}' to <a href='{{{workerUrl}}}'><strong>{{employee}}</strong></a> for day {{dated}}.",
"Deleted absence": "The worker <strong>{{author}}</strong> has deleted an absence of type '{{absenceType}}' to <a href='{{{workerUrl}}}'><strong>{{employee}}</strong></a> for day {{dated}}.", "Deleted absence": "The worker <strong>{{author}}</strong> has deleted an absence of type '{{absenceType}}' to <a href='{{{workerUrl}}}'><strong>{{employee}}</strong></a> for day {{dated}}.",
"I have deleted the ticket id": "I have deleted the ticket id [#{{id}}]({{{url}}})", "I have deleted the ticket id": "I have deleted the ticket id [{{id}}]({{{url}}})",
"I have restored the ticket id": "I have restored the ticket id [#{{id}}]({{{url}}})" "I have restored the ticket id": "I have restored the ticket id [{{id}}]({{{url}}})",
"Changed this data from the ticket": "I have changed the data from the ticket [{{ticketId}}]({{{ticketUrl}}}): ```{{{changes}}}```"
} }

View File

@ -121,12 +121,12 @@
"Swift / BIC can't be empty": "Swift / BIC no puede estar vacío", "Swift / BIC can't be empty": "Swift / BIC no puede estar vacío",
"Customs agent is required for a non UEE member": "El agente de aduanas es requerido para los clientes extracomunitarios", "Customs agent is required for a non UEE member": "El agente de aduanas es requerido para los clientes extracomunitarios",
"Incoterms is required for a non UEE member": "El incoterms es requerido para los clientes extracomunitarios", "Incoterms is required for a non UEE member": "El incoterms es requerido para los clientes extracomunitarios",
"MESSAGE_BOUGHT_UNITS": "Se ha comprado {{quantity}} unidades de {{concept}} (#{{itemId}}) para el ticket id [#{{ticketId}}]({{{url}}})", "MESSAGE_BOUGHT_UNITS": "Se ha comprado {{quantity}} unidades de {{concept}} (#{{itemId}}) para el ticket id [{{ticketId}}]({{{url}}})",
"MESSAGE_INSURANCE_CHANGE": "He cambiado el crédito asegurado del cliente [{{clientName}} (#{{clientId}})]({{{url}}}) a *{{credit}} €*", "MESSAGE_INSURANCE_CHANGE": "He cambiado el crédito asegurado del cliente [{{clientName}} (#{{clientId}})]({{{url}}}) a *{{credit}} €*",
"MESSAGE_CHANGED_PAYMETHOD": "He cambiado la forma de pago del cliente [{{clientName}} (#{{clientId}})]({{{url}}})", "MESSAGE_CHANGED_PAYMETHOD": "He cambiado la forma de pago del cliente [{{clientName}} (#{{clientId}})]({{{url}}})",
"Sent units from ticket": "Envio *{{quantity}}* unidades de [{{concept}} (#{{itemId}})]({{{itemUrl}}}) a *\"{{nickname}}\"* provenientes del ticket id [#{{ticketId}}]({{{ticketUrl}}})", "Sent units from ticket": "Envio *{{quantity}}* unidades de [{{concept}} (#{{itemId}})]({{{itemUrl}}}) a *\"{{nickname}}\"* provenientes del ticket id [{{ticketId}}]({{{ticketUrl}}})",
"Claim will be picked": "Se recogerá el género de la reclamación (#{{claimId}})]({{{claimUrl}}}) del cliente *{{clientName}}*", "Claim will be picked": "Se recogerá el género de la reclamación (#{{claimId}})]({{{claimUrl}}}) del cliente *{{clientName}}*",
"This ticket is not an stowaway anymore": "El ticket id [#{{ticketId}}]({{{ticketUrl}}}) ha dejado de ser un polizón", "This ticket is not an stowaway anymore": "El ticket id [{{ticketId}}]({{{ticketUrl}}}) ha dejado de ser un polizón",
"Client checked as validated despite of duplication": "Cliente comprobado a pesar de que existe el cliente id {{clientId}}", "Client checked as validated despite of duplication": "Cliente comprobado a pesar de que existe el cliente id {{clientId}}",
"ORDER_ROW_UNAVAILABLE": "No hay disponibilidad de este producto", "ORDER_ROW_UNAVAILABLE": "No hay disponibilidad de este producto",
"Distance must be lesser than 1000": "La distancia debe ser inferior a 1000", "Distance must be lesser than 1000": "La distancia debe ser inferior a 1000",
@ -146,7 +146,8 @@
"Absence change notification on the labour calendar": "Notificacion de cambio de ausencia en el calendario laboral", "Absence change notification on the labour calendar": "Notificacion de cambio de ausencia en el calendario laboral",
"Created absence": "El empleado <strong>{{author}}</strong> ha añadido una ausencia de tipo '{{absenceType}}' a <a href='{{{workerUrl}}}'><strong>{{employee}}</strong></a> para el día {{dated}}.", "Created absence": "El empleado <strong>{{author}}</strong> ha añadido una ausencia de tipo '{{absenceType}}' a <a href='{{{workerUrl}}}'><strong>{{employee}}</strong></a> para el día {{dated}}.",
"Deleted absence": "El empleado <strong>{{author}}</strong> ha eliminado una ausencia de tipo '{{absenceType}}' a <a href='{{{workerUrl}}}'><strong>{{employee}}</strong></a> del día {{dated}}.", "Deleted absence": "El empleado <strong>{{author}}</strong> ha eliminado una ausencia de tipo '{{absenceType}}' a <a href='{{{workerUrl}}}'><strong>{{employee}}</strong></a> del día {{dated}}.",
"I have deleted the ticket id": "He eliminado el ticket id [#{{id}}]({{{url}}})", "I have deleted the ticket id": "He eliminado el ticket id [{{id}}]({{{url}}})",
"I have restored the ticket id": "He restaurado el ticket id [#{{id}}]({{{url}}})", "I have restored the ticket id": "He restaurado el ticket id [{{id}}]({{{url}}})",
"You can only restore a ticket within the first hour after deletion": "Únicamente puedes restaurar el ticket dentro de la primera hora después de su eliminación" "You can only restore a ticket within the first hour after deletion": "Únicamente puedes restaurar el ticket dentro de la primera hora después de su eliminación",
"Changed this data from the ticket": "He cambiado estos datos del ticket [{{ticketId}}]({{{ticketUrl}}}): ```{{{changes}}}```"
} }

View File

@ -1,26 +1,35 @@
const app = require('vn-loopback/server/server'); const app = require('vn-loopback/server/server');
const LoopBackContext = require('loopback-context');
describe('claimBeginning', () => { describe('claimBeginning', () => {
const salesAssistantId = 21;
let ticket; let ticket;
let refundTicketSales; let refundTicketSales;
let salesInsertedInClaimEnd; let salesInsertedInClaimEnd;
afterAll(async() => { const activeCtx = {
let promises = []; accessToken: {userId: salesAssistantId},
promises.push(app.models.Ticket.destroyById(ticket.id)); };
const ctx = {req: activeCtx};
promises.push(app.models.Ticket.rawSql(`DELETE FROM vn.orderTicket WHERE ticketFk ='${ticket.id}';`)); afterAll(async done => {
try {
await app.models.Ticket.destroyById(ticket.id);
await app.models.Ticket.rawSql(`DELETE FROM vn.orderTicket WHERE ticketFk ='${ticket.id}';`);
} catch (error) {
console.error(error);
}
await Promise.all(promises); done();
}); });
describe('importToNewRefundTicket()', () => { describe('importToNewRefundTicket()', () => {
it('should create a new ticket with negative sales and insert the negative sales into claimEnd', async() => { it('should create a new ticket with negative sales and insert the negative sales into claimEnd', async() => {
let ctxOfSalesAssistant = {req: {accessToken: {userId: 21}}}; spyOn(LoopBackContext, 'getCurrentContext').and.returnValue({
active: activeCtx
});
let claimId = 1; let claimId = 1;
ticket = await app.models.ClaimBeginning.importToNewRefundTicket(ctxOfSalesAssistant, claimId); ticket = await app.models.ClaimBeginning.importToNewRefundTicket(ctx, claimId);
await app.models.Ticket.findById(ticket.id);
refundTicketSales = await app.models.Sale.find({where: {ticketFk: ticket.id}}); refundTicketSales = await app.models.Sale.find({where: {ticketFk: ticket.id}});
salesInsertedInClaimEnd = await app.models.ClaimEnd.find({where: {claimFk: claimId}}); salesInsertedInClaimEnd = await app.models.ClaimEnd.find({where: {claimFk: claimId}});

View File

@ -1,14 +1,6 @@
const app = require('vn-loopback/server/server'); const app = require('vn-loopback/server/server');
describe('Claim createFromSales()', () => { describe('Claim createFromSales()', () => {
let createdClaimFk;
afterAll(async done => {
await app.models.Claim.destroyById(createdClaimFk);
done();
});
const ticketId = 2; const ticketId = 2;
const newSale = [{ const newSale = [{
id: 3, id: 3,
@ -27,10 +19,16 @@ describe('Claim createFromSales()', () => {
expect(claimBeginning.saleFk).toEqual(newSale[0].id); expect(claimBeginning.saleFk).toEqual(newSale[0].id);
expect(claimBeginning.quantity).toEqual(newSale[0].quantity); expect(claimBeginning.quantity).toEqual(newSale[0].quantity);
createdClaimFk = claim.id; const createdClaimId = claim.id;
// restores
await app.models.Claim.destroyById(createdClaimId);
}); });
it('should not be able to create a claim if exists that sale', async() => { 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;
let error; let error;
await app.models.Claim.createFromSales(ctx, ticketId, newSale) await app.models.Claim.createFromSales(ctx, ticketId, newSale)
@ -40,5 +38,8 @@ describe('Claim createFromSales()', () => {
}); });
expect(error.toString()).toContain(`A claim with that sale already exists`); expect(error.toString()).toContain(`A claim with that sale already exists`);
// restores
await app.models.Claim.destroyById(createdClaimId);
}); });
}); });

View File

@ -1,6 +1,17 @@
const app = require('vn-loopback/server/server'); const app = require('vn-loopback/server/server');
describe('regularizeClaim()', () => { describe('regularizeClaim()', () => {
const ctx = {
req: {
accessToken: {userId: 18},
headers: {origin: 'http://localhost'}
}
};
ctx.req.__ = (value, params) => {
return params.nickname;
};
const chatModel = app.models.Chat;
const claimFk = 1; const claimFk = 1;
const pendentState = 1; const pendentState = 1;
const resolvedState = 3; const resolvedState = 3;
@ -10,33 +21,27 @@ describe('regularizeClaim()', () => {
let claimEnds = []; let claimEnds = [];
let trashTicket; let trashTicket;
afterAll(async done => { afterEach(async done => {
let claim = await app.models.Claim.findById(claimFk); try {
await claim.updateAttributes({ let claim = await app.models.Claim.findById(claimFk);
claimStateFk: pendentState, await claim.updateAttributes({
hasToPickUp: false claimStateFk: pendentState,
}); hasToPickUp: false
await app.models.Ticket.destroyById(trashTicket.id); });
claimEnds.forEach(async line => { for (claimEnd of claimEnds)
await line.destroy(); await claimEnd.destroy();
});
if (trashTicket)
await app.models.Ticket.destroyById(trashTicket.id);
} catch (error) {
console.error(error);
}
done(); done();
}); });
it('should send a chat message with value "Trash" and then change claim state to resolved', async() => { it('should send a chat message with value "Trash" and then change claim state to resolved', async() => {
const ctx = {
req: {
accessToken: {userId: 18},
headers: {origin: 'http://localhost'}
}
};
ctx.req.__ = (value, params) => {
return params.nickname;
};
const chatModel = app.models.Chat;
spyOn(chatModel, 'sendCheckingPresence').and.callThrough(); spyOn(chatModel, 'sendCheckingPresence').and.callThrough();
claimEnds = await app.models.ClaimEnd.importTicketSales(ctx, { claimEnds = await app.models.ClaimEnd.importTicketSales(ctx, {
@ -61,23 +66,16 @@ describe('regularizeClaim()', () => {
}); });
it('should send a chat message with value "Bueno" and then change claim state to resolved', async() => { it('should send a chat message with value "Bueno" and then change claim state to resolved', async() => {
const ctx = {
req: {
accessToken: {userId: 18},
headers: {origin: 'http://localhost'}
}
};
ctx.req.__ = (value, params) => {
return params.nickname;
};
const chatModel = app.models.Chat;
spyOn(chatModel, 'sendCheckingPresence').and.callThrough(); spyOn(chatModel, 'sendCheckingPresence').and.callThrough();
claimEnds.forEach(async claimEnd => { claimEnds = await app.models.ClaimEnd.importTicketSales(ctx, {
claimEnd.updateAttributes({claimDestinationFk: okDestination}); claimFk: claimFk,
ticketFk: 1
}); });
for (claimEnd of claimEnds)
await claimEnd.updateAttributes({claimDestinationFk: okDestination});
await app.models.Claim.regularizeClaim(ctx, claimFk); await app.models.Claim.regularizeClaim(ctx, claimFk);
expect(chatModel.sendCheckingPresence).toHaveBeenCalledWith(ctx, 18, 'Bueno'); expect(chatModel.sendCheckingPresence).toHaveBeenCalledWith(ctx, 18, 'Bueno');
@ -85,23 +83,16 @@ describe('regularizeClaim()', () => {
}); });
it('should send a chat message to the salesPerson when claim isPickUp is enabled', async() => { it('should send a chat message to the salesPerson when claim isPickUp is enabled', async() => {
const ctx = {
req: {
accessToken: {userId: 18},
headers: {origin: 'http://localhost'}
}
};
ctx.req.__ = (value, params) => {
return params.nickname;
};
const chatModel = app.models.Chat;
spyOn(chatModel, 'sendCheckingPresence').and.callThrough(); spyOn(chatModel, 'sendCheckingPresence').and.callThrough();
claimEnds.forEach(async claimEnd => { claimEnds = await app.models.ClaimEnd.importTicketSales(ctx, {
claimEnd.updateAttributes({claimDestinationFk: okDestination}); claimFk: claimFk,
ticketFk: 1
}); });
for (claimEnd of claimEnds)
await claimEnd.updateAttributes({claimDestinationFk: okDestination});
await app.models.Claim.regularizeClaim(ctx, claimFk); await app.models.Claim.regularizeClaim(ctx, claimFk);
expect(chatModel.sendCheckingPresence).toHaveBeenCalledWith(ctx, 18, 'Bueno'); expect(chatModel.sendCheckingPresence).toHaveBeenCalledWith(ctx, 18, 'Bueno');

View File

@ -2,8 +2,7 @@ const app = require('vn-loopback/server/server');
describe('Update Claim', () => { describe('Update Claim', () => {
let newDate = new Date(); let newDate = new Date();
let newInstance; const originalData = {
let original = {
ticketFk: 3, ticketFk: 3,
clientFk: 101, clientFk: 101,
ticketCreated: newDate, ticketCreated: newDate,
@ -14,22 +13,11 @@ describe('Update Claim', () => {
observation: 'observation' 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 throw an error as the user doesn't have rights`, async() => { it(`should throw an error as the user doesn't have rights`, async() => {
let newClaim = await app.models.Claim.create(originalData);
const forbiddenState = 3; const forbiddenState = 3;
const salesPersonId = 18; const salesPersonId = 18;
let ctx = { const ctx = {
req: { req: {
accessToken: { accessToken: {
userId: salesPersonId userId: salesPersonId
@ -40,18 +28,23 @@ describe('Update Claim', () => {
observation: 'valid observation' observation: 'valid observation'
} }
}; };
await app.models.Claim.updateClaim(ctx, newInstance.id) await app.models.Claim.updateClaim(ctx, newClaim.id)
.catch(e => { .catch(e => {
error = e; error = e;
}); });
expect(error.message).toEqual(`You don't have enough privileges to change that field`); 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() => { it(`should success to update the claim within privileges `, async() => {
let newClaim = await app.models.Claim.create(originalData);
const correctState = 4; const correctState = 4;
const salesPersonId = 18; const salesPersonId = 18;
let ctx = { const ctx = {
req: { req: {
accessToken: { accessToken: {
userId: salesPersonId userId: salesPersonId
@ -63,14 +56,18 @@ describe('Update Claim', () => {
hasToPickUp: false hasToPickUp: false
} }
}; };
await app.models.Claim.updateClaim(ctx, newInstance.id); await app.models.Claim.updateClaim(ctx, newClaim.id,);
let claimUpdated = await app.models.Claim.findById(newInstance.id); let updatedClaim = await app.models.Claim.findById(newClaim.id);
expect(claimUpdated.observation).toEqual(ctx.args.observation); expect(updatedClaim.observation).toEqual(ctx.args.observation);
// restores
await app.models.Claim.destroyById(newClaim.id);
}); });
it('should change some sensible fields as salesAssistant', async() => { it('should change some sensible fields as salesAssistant', async() => {
let newClaim = await app.models.Claim.create(originalData);
const chatModel = app.models.Chat; const chatModel = app.models.Chat;
spyOn(chatModel, 'sendCheckingPresence').and.callThrough(); spyOn(chatModel, 'sendCheckingPresence').and.callThrough();
@ -90,13 +87,16 @@ describe('Update Claim', () => {
ctx.req.__ = (value, params) => { ctx.req.__ = (value, params) => {
return params.nickname; return params.nickname;
}; };
await app.models.Claim.updateClaim(ctx, newInstance.id); await app.models.Claim.updateClaim(ctx, newClaim.id);
let claimUpdated = await app.models.Claim.findById(newInstance.id); let updatedClaim = await app.models.Claim.findById(newClaim.id);
expect(claimUpdated.observation).toEqual(ctx.args.observation); expect(updatedClaim.observation).toEqual(ctx.args.observation);
expect(claimUpdated.claimStateFk).toEqual(ctx.args.claimStateFk); expect(updatedClaim.claimStateFk).toEqual(ctx.args.claimStateFk);
expect(claimUpdated.workerFk).toEqual(ctx.args.workerFk); expect(updatedClaim.workerFk).toEqual(ctx.args.workerFk);
expect(chatModel.sendCheckingPresence).toHaveBeenCalled(); expect(chatModel.sendCheckingPresence).toHaveBeenCalled();
// restores
await app.models.Claim.destroyById(newClaim.id);
}); });
}); });

View File

@ -7,7 +7,7 @@ describe('Client activeWorkersWithRole', () => {
let isSalesPerson = await app.models.Account.hasRole(result[0].id, 'salesPerson'); let isSalesPerson = await app.models.Account.hasRole(result[0].id, 'salesPerson');
expect(result.length).toEqual(16); expect(result.length).toEqual(17);
expect(isSalesPerson).toBeTruthy(); expect(isSalesPerson).toBeTruthy();
}); });

View File

@ -5,15 +5,6 @@ describe('Address createAddress', () => {
const provinceId = 5; const provinceId = 5;
const incotermsId = 'FAS'; const incotermsId = 'FAS';
const customAgentOneId = 1; const customAgentOneId = 1;
let address;
let client;
afterAll(async done => {
await client.updateAttributes({defaultAddressFk: 1});
await address.destroy();
done();
});
it('should throw a non uee member error if no incoterms is defined', async() => { it('should throw a non uee member error if no incoterms is defined', async() => {
const expectedResult = 'My edited address'; const expectedResult = 'My edited address';
@ -49,7 +40,6 @@ describe('Address createAddress', () => {
} }
}; };
try { try {
await app.models.Client.createAddress(ctx, clientId); await app.models.Client.createAddress(ctx, clientId);
} catch (e) { } catch (e) {
@ -61,7 +51,7 @@ describe('Address createAddress', () => {
}); });
it('should verify that client defaultAddressFk is untainted', async() => { it('should verify that client defaultAddressFk is untainted', async() => {
client = await app.models.Client.findById(clientId); const client = await app.models.Client.findById(clientId);
expect(client.defaultAddressFk).toEqual(1); expect(client.defaultAddressFk).toEqual(1);
}); });
@ -79,9 +69,13 @@ describe('Address createAddress', () => {
} }
}; };
address = await app.models.Client.createAddress(ctx, clientId); const address = await app.models.Client.createAddress(ctx, clientId);
client = await app.models.Client.findById(clientId); const client = await app.models.Client.findById(clientId);
expect(client.defaultAddressFk).toEqual(address.id); expect(client.defaultAddressFk).toEqual(address.id);
// restores
await client.updateAttributes({defaultAddressFk: 1});
await address.destroy();
}); });
}); });

View File

@ -4,13 +4,20 @@ describe('Client Create', () => {
const clientName = 'Wade'; const clientName = 'Wade';
const AccountName = 'Deadpool'; const AccountName = 'Deadpool';
afterAll(async done => { afterEach(async done => {
let address = await app.models.Address.findOne({where: {nickname: clientName}}); let address = await app.models.Address.findOne({where: {nickname: clientName}});
let client = await app.models.Client.findOne({where: {name: clientName}}); let client = await app.models.Client.findOne({where: {name: clientName}});
let account = await app.models.Account.findOne({where: {name: AccountName}}); let account = await app.models.Account.findOne({where: {name: AccountName}});
await app.models.Address.destroyById(address.id);
await app.models.Client.destroyById(client.id); if (address && client && account) {
await app.models.Account.destroyById(account.id); try {
await app.models.Address.destroyById(address.id);
await app.models.Client.destroyById(client.id);
await app.models.Account.destroyById(account.id);
} catch (error) {
console.error(error);
}
}
done(); done();
}); });
@ -46,13 +53,8 @@ describe('Client Create', () => {
expect(client.socialName).toEqual(newAccount.socialName); expect(client.socialName).toEqual(newAccount.socialName);
}); });
it('should find an existing account', async() => {
let account = await app.models.Account.findOne({where: {name: newAccount.userName}});
expect(account.name).toEqual(newAccount.userName);
});
it('should not be able to create a user if exists', async() => { it('should not be able to create a user if exists', async() => {
await app.models.Client.createWithUser(newAccount);
try { try {
let client = await app.models.Client.createWithUser(newAccount); let client = await app.models.Client.createWithUser(newAccount);

View File

@ -6,7 +6,7 @@ describe('Client listWorkers', () => {
.then(result => { .then(result => {
let amountOfEmployees = Object.keys(result).length; let amountOfEmployees = Object.keys(result).length;
expect(amountOfEmployees).toEqual(51); expect(amountOfEmployees).toEqual(53);
done(); done();
}) })
.catch(done.fail); .catch(done.fail);

View File

@ -6,26 +6,11 @@ describe('Address updateAddress', () => {
const provinceId = 5; const provinceId = 5;
const incotermsId = 'FAS'; const incotermsId = 'FAS';
const customAgentOneId = 1; const customAgentOneId = 1;
let oldAddress;
let address;
afterAll(async done => { it('should throw the non uee member error if no incoterms is defined', async() => {
await address.updateAttributes({
nickname: oldAddress.nickname,
provinceFk: 1,
customsAgentFk: null,
incotermsFk: null
});
done();
});
it('should throw a non uee member error if no incoterms is defined', async() => {
const expectedResult = 'My edited address';
const ctx = { const ctx = {
args: { args: {
provinceFk: provinceId, provinceFk: provinceId,
nickname: expectedResult,
customsAgentFk: customAgentOneId customsAgentFk: customAgentOneId
} }
}; };
@ -41,16 +26,13 @@ describe('Address updateAddress', () => {
}); });
it('should throw a non uee member error if no customsAgent is defined', async() => { it('should throw a non uee member error if no customsAgent is defined', async() => {
const expectedResult = 'My edited address';
const ctx = { const ctx = {
args: { args: {
provinceFk: provinceId, provinceFk: provinceId,
nickname: expectedResult,
incotermsFk: incotermsId incotermsFk: incotermsId
} }
}; };
try { try {
await app.models.Client.updateAddress(ctx, clientId, addressId); await app.models.Client.updateAddress(ctx, clientId, addressId);
} catch (e) { } catch (e) {
@ -72,13 +54,21 @@ describe('Address updateAddress', () => {
} }
}; };
oldAddress = await app.models.Address.findById(addressId); let oldAddress = await app.models.Address.findById(addressId);
await app.models.Client.updateAddress(ctx, clientId, addressId); await app.models.Client.updateAddress(ctx, clientId, addressId);
address = await app.models.Address.findById(addressId); let address = await app.models.Address.findById(addressId);
expect(address.nickname).toEqual(expectedResult); expect(address.nickname).toEqual(expectedResult);
// restores
await address.updateAttributes({
nickname: oldAddress.nickname,
provinceFk: oldAddress.provinceFk,
customsAgentFk: null,
incotermsFk: null
});
}); });
it('should update the address', async() => { it('should update the address', async() => {
@ -96,5 +86,13 @@ describe('Address updateAddress', () => {
address = await app.models.Address.findById(addressId); address = await app.models.Address.findById(addressId);
expect(address.nickname).toEqual(expectedResult); expect(address.nickname).toEqual(expectedResult);
// restores
await address.updateAttributes({
nickname: oldAddress.nickname,
provinceFk: oldAddress.provinceFk,
customsAgentFk: null,
incotermsFk: null
});
}); });
}); });

View File

@ -2,7 +2,6 @@ const app = require('vn-loopback/server/server');
const LoopBackContext = require('loopback-context'); const LoopBackContext = require('loopback-context');
describe('Client createWithInsurance', () => { describe('Client createWithInsurance', () => {
let classificationId;
const activeCtx = { const activeCtx = {
accessToken: {userId: 101}, accessToken: {userId: 101},
http: { http: {
@ -16,12 +15,6 @@ describe('Client createWithInsurance', () => {
return value; return value;
}; };
afterAll(async done => {
await app.models.CreditClassification.destroyById(classificationId);
done();
});
it('should verify the classifications and insurances are untainted', async() => { it('should verify the classifications and insurances are untainted', async() => {
let classifications = await app.models.CreditClassification.find(); let classifications = await app.models.CreditClassification.find();
let insurances = await app.models.CreditInsurance.find(); let insurances = await app.models.CreditInsurance.find();
@ -57,8 +50,6 @@ describe('Client createWithInsurance', () => {
}); });
let result = await app.models.CreditClassification.createWithInsurance(data, ctx); let result = await app.models.CreditClassification.createWithInsurance(data, ctx);
classificationId = result.id;
expect(result.client).toEqual(101); expect(result.client).toEqual(101);
let classifications = await app.models.CreditClassification.find(); let classifications = await app.models.CreditClassification.find();
@ -66,5 +57,8 @@ describe('Client createWithInsurance', () => {
expect(classifications.length).toEqual(6); expect(classifications.length).toEqual(6);
expect(insurances.length).toEqual(4); expect(insurances.length).toEqual(4);
// restore
await app.models.CreditClassification.destroyById(result.id);
}); });
}); });

View File

@ -153,9 +153,9 @@
</form> </form>
<!-- New postcode dialog --> <!-- New postcode dialog -->
<vn-client-postcode vn-id="postcode" <vn-geo-postcode vn-id="postcode"
on-response="$ctrl.onResponse($response)"> on-response="$ctrl.onResponse($response)">
</vn-client-postcode> </vn-geo-postcode>
<!-- Create custom agent dialog --> <!-- Create custom agent dialog -->
<vn-dialog class="edit" <vn-dialog class="edit"

View File

@ -74,6 +74,8 @@ export default class Controller extends Section {
onResponse(response) { onResponse(response) {
this.address.postalCode = response.code; this.address.postalCode = response.code;
this.address.city = response.city;
this.address.provinceFk = response.provinceFk;
} }
} }

View File

@ -191,9 +191,9 @@
</form> </form>
<!-- New postcode dialog --> <!-- New postcode dialog -->
<vn-client-postcode vn-id="postcode" <vn-geo-postcode vn-id="postcode"
on-response="$ctrl.onResponse($response)"> on-response="$ctrl.onResponse($response)">
</vn-client-postcode> </vn-geo-postcode>
<!-- Create custom agent dialog --> <!-- Create custom agent dialog -->
<vn-dialog class="edit" <vn-dialog class="edit"

View File

@ -78,6 +78,8 @@ export default class Controller extends Section {
onResponse(response) { onResponse(response) {
this.address.postalCode = response.code; this.address.postalCode = response.code;
this.address.city = response.city;
this.address.provinceFk = response.provinceFk;
} }
} }

View File

@ -5,12 +5,6 @@
insert-mode="true" insert-mode="true"
form="form"> form="form">
</vn-watcher> </vn-watcher>
<vn-crud-model
auto-load="true"
url="Towns/location"
data="townLocations"
order="name">
</vn-crud-model>
<form name="form" vn-http-submit="$ctrl.onSubmit()" class="vn-w-md"> <form name="form" vn-http-submit="$ctrl.onSubmit()" class="vn-w-md">
<vn-card class="vn-pa-lg"> <vn-card class="vn-pa-lg">
<vn-horizontal> <vn-horizontal>
@ -82,7 +76,7 @@
label="City" label="City"
ng-model="$ctrl.client.city" ng-model="$ctrl.client.city"
selection="$ctrl.town" selection="$ctrl.town"
data="townsLocation" url="Towns/location"
fields="['id', 'name', 'provinceFk']" fields="['id', 'name', 'provinceFk']"
value-field="name"> value-field="name">
<tpl-item> <tpl-item>
@ -136,7 +130,7 @@
</vn-button-bar> </vn-button-bar>
</form> </form>
<!-- New postcode dialog --> <!-- New postcode dialog -->
<vn-client-postcode <vn-geo-postcode
vn-id="postcode" vn-id="postcode"
on-response="$ctrl.onResponse($response)"> on-response="$ctrl.onResponse($response)">
</vn-client-postcode> </vn-geo-postcode>

View File

@ -81,6 +81,9 @@ export default class Controller extends Section {
onResponse(response) { onResponse(response) {
this.client.postcode = response.code; this.client.postcode = response.code;
this.client.city = response.city;
this.client.provinceFk = response.provinceFk;
this.client.countryFk = response.countryFk;
} }
} }

View File

@ -173,7 +173,7 @@
on-accept="$ctrl.onAcceptDuplication()"> on-accept="$ctrl.onAcceptDuplication()">
</vn-confirm> </vn-confirm>
<!-- New postcode dialog --> <!-- New postcode dialog -->
<vn-client-postcode <vn-geo-postcode
vn-id="postcode" vn-id="postcode"
on-response="$ctrl.onResponse($response)"> on-response="$ctrl.onResponse($response)">
</vn-client-postcode> </vn-geo-postcode>

View File

@ -159,6 +159,9 @@ export default class Controller extends Section {
onResponse(response) { onResponse(response) {
this.client.postcode = response.code; this.client.postcode = response.code;
this.client.city = response.city;
this.client.provinceFk = response.provinceFk;
this.client.countryFk = response.countryFk;
} }
} }

View File

@ -37,6 +37,8 @@ import './web-payment';
import './log'; import './log';
import './sms'; import './sms';
import './postcode'; import './postcode';
import './postcode/province';
import './postcode/city';
import './dms/index'; import './dms/index';
import './dms/create'; import './dms/create';
import './dms/edit'; import './dms/edit';

View File

@ -0,0 +1,27 @@
<vn-dialog class="edit"
vn-id="cityDialog"
on-open="$ctrl.onOpen()"
on-accept="$ctrl.onAccept()"
message="New city">
<tpl-body>
<p translate>Please, ensure you put the correct data!</p>
<vn-horizontal>
<vn-textfield vn-one
vn-focus
ng-model="$ctrl.city.name"
label="Name">
</vn-textfield>
<vn-autocomplete vn-one
ng-model="$ctrl.city.provinceFk"
url="Provinces"
show-field="name"
value-field="id"
label="Province">
</vn-autocomplete>
</vn-horizontal>
</tpl-body>
<tpl-buttons>
<input type="button" response="cancel" translate-attr="{value: 'Cancel'}"/>
<button id="saveCity" response="accept" translate>Save</button>
</tpl-buttons>
</vn-dialog>

View File

@ -0,0 +1,37 @@
import ngModule from '../../module';
import Component from 'core/lib/component';
class Controller extends Component { // Comprobar funcionamiento añadir ciudad
open($event) {
if ($event.defaultPrevented) return;
this.$.cityDialog.show();
$event.preventDefault();
}
onAccept() {
try {
if (!this.city.name)
throw new Error(`The city name can't be empty`);
if (!this.city.provinceFk)
throw new Error(`The province can't be empty`);
this.$http.patch(`towns`, this.city).then(res => {
this.vnApp.showMessage(this.$t('The city has been created'));
this.emit('response', {$response: res.data});
});
} catch (e) {
this.vnApp.showError(this.$t(e.message));
return false;
}
return true;
}
}
ngModule.vnComponent('vnGeoCity', {
template: require('./index.html'),
controller: Controller,
bindings: {
data: '<',
}
});

View File

@ -0,0 +1,34 @@
import './index';
describe('Client', () => {
describe('Component vnGeoCity', () => {
let controller;
let $httpBackend;
let $scope;
beforeEach(ngModule('client'));
beforeEach(inject(($componentController, _$httpBackend_, $rootScope) => {
$httpBackend = _$httpBackend_;
$scope = $rootScope.$new();
const $element = angular.element('<vn-dialog></vn-dialog>');
controller = $componentController('vnGeoCity', {$element, $scope});
controller.client = {id: 101};
}));
describe('onAccept()', () => {
it('should perform a POST query and show a success snackbar', () => {
let params = {name: 'Gotham City', provinceFk: 1};
controller.city = {name: 'Gotham City', provinceFk: 1};
jest.spyOn(controller.vnApp, 'showMessage');
$httpBackend.expect('PATCH', `towns`, params).respond(200, params);
controller.onAccept();
$httpBackend.flush();
expect(controller.vnApp.showMessage).toHaveBeenCalledWith('The city has been created');
});
});
});
});

View File

@ -11,7 +11,7 @@
vn-focus vn-focus
vn-id="postcode" vn-id="postcode"
label="Postcode" label="Postcode"
ng-model="$ctrl.data.code" ng-model="$ctrl.location.code"
required="true"> required="true">
</vn-textfield> </vn-textfield>
<vn-autocomplete vn-one <vn-autocomplete vn-one
@ -19,23 +19,39 @@
url="Towns/location" url="Towns/location"
show-field="name" show-field="name"
value-field="id" value-field="id"
ng-model="$ctrl.data.townFk" ng-model="$ctrl.location.townFk"
selection="$ctrl.townSelection" selection="$ctrl.townSelection"
required="true"> required="true">
<append>
<vn-icon-button
icon="add_circle"
vn-tooltip="New city"
ng-click="city.open($event)"
vn-acl="deliveryBoss"
vn-acl-action="remove">
</vn-icon-button>
</append>
</vn-autocomplete> </vn-autocomplete>
</vn-horizontal> </vn-horizontal>
<vn-horizontal> <vn-horizontal>
<vn-autocomplete vn-one <vn-autocomplete vn-one
disabled="true" ng-model="$ctrl.location.provinceFk"
ng-model="$ctrl.data.provinceFk"
url="Provinces" url="Provinces"
show-field="name" show-field="name"
value-field="id" value-field="id"
label="Province"> label="Province">
<append>
<vn-icon-button
icon="add_circle"
vn-tooltip="New province"
ng-click="province.open($event)"
vn-acl="deliveryBoss"
vn-acl-action="remove">
</vn-icon-button>
</append>
</vn-autocomplete> </vn-autocomplete>
<vn-autocomplete vn-one <vn-autocomplete vn-one
disabled="true" ng-model="$ctrl.location.countryFk"
ng-model="$ctrl.data.countryFk"
url="Countries" url="Countries"
show-field="country" show-field="country"
value-field="id" value-field="id"
@ -45,6 +61,16 @@
</tpl-body> </tpl-body>
<tpl-buttons> <tpl-buttons>
<input type="button" response="cancel" translate-attr="{value: 'Cancel'}"/> <input type="button" response="cancel" translate-attr="{value: 'Cancel'}"/>
<button response="accept" translate>Save</button> <button id= "savePostcode" response="accept" translate>Save</button>
</tpl-buttons> </tpl-buttons>
</vn-dialog> </vn-dialog>
<!-- New province dialog -->
<vn-geo-province
vn-id="province"
on-response="$ctrl.onProvinceResponse($response)">
</vn-geo-province>
<!-- New city dialog -->
<vn-geo-city
vn-id="city"
on-response="$ctrl.onCityResponse($response)">
</vn-geo-city>

View File

@ -15,8 +15,9 @@ class Controller extends Component {
const province = selection.province; const province = selection.province;
const country = province.country; const country = province.country;
this.data.provinceFk = province.id; this.location.city = selection.name;
this.data.countryFk = country.id; this.location.provinceFk = province.id;
this.location.countryFk = country.id;
} }
open() { open() {
@ -24,19 +25,35 @@ class Controller extends Component {
} }
onOpen() { onOpen() {
this.location = {};
this.$.postcode.focus(); this.$.postcode.focus();
} }
onProvinceResponse(response) {
this.location.provinceFk = response.id;
this.location.countryFk = response.countryFk;
}
onCityResponse(response) {
this.location.townFk = response.id;
this.location.provinceFk = response.provinceFk;
this.location.countryFk = response.countryFk;
}
onAccept() { onAccept() {
try { try {
if (!this.data.code) if (!this.location.code)
throw new Error(`The postcode can't be empty`); throw new Error(`The postcode can't be empty`);
if (!this.data.townFk) if (!this.location.townFk)
throw new Error(`The town can't be empty`); throw new Error(`The town can't be empty`);
if (!this.location.provinceFk)
throw new Error(`The province can't be empty`);
if (!this.location.provinceFk)
throw new Error(`The country can't be empty`);
this.$http.patch(`postcodes`, this.data).then(res => { this.$http.patch(`postcodes`, this.location).then(() => {
this.vnApp.showMessage(this.$t('The postcode has been saved')); this.vnApp.showMessage(this.$t('The postcode has been created. You can save the data now'));
this.emit('response', {$response: res.data}); this.emit('response', {$response: this.location});
}); });
} catch (e) { } catch (e) {
this.vnApp.showError(this.$t(e.message)); this.vnApp.showError(this.$t(e.message));
@ -46,7 +63,7 @@ class Controller extends Component {
} }
} }
ngModule.vnComponent('vnClientPostcode', { ngModule.vnComponent('vnGeoPostcode', {
template: require('./index.html'), template: require('./index.html'),
controller: Controller, controller: Controller,
bindings: { bindings: {

View File

@ -1,7 +1,7 @@
import './index'; import './index';
describe('Client', () => { describe('Client', () => {
describe('Component vnClientPostcode', () => { describe('Component vnGeoPostcode', () => {
let controller; let controller;
let $httpBackend; let $httpBackend;
let $scope; let $scope;
@ -12,14 +12,14 @@ describe('Client', () => {
$httpBackend = _$httpBackend_; $httpBackend = _$httpBackend_;
$scope = $rootScope.$new(); $scope = $rootScope.$new();
const $element = angular.element('<vn-dialog></vn-dialog>'); const $element = angular.element('<vn-dialog></vn-dialog>');
controller = $componentController('vnClientPostcode', {$element, $scope}); controller = $componentController('vnGeoPostcode', {$element, $scope});
controller.client = {id: 101}; controller.client = {id: 101};
})); }));
describe('onAccept()', () => { describe('onAccept()', () => {
it('should perform a POST query and show a success snackbar', () => { it('should perform a POST query and show a success snackbar', () => {
let params = {townFk: 1, provinceFk: 1, countryFk: 1, code: '46460'}; let params = {townFk: 1, provinceFk: 1, countryFk: 1, code: '46460'};
controller.data = {townFk: 1, provinceFk: 1, countryFk: 1, code: '46460'}; controller.location = {townFk: 1, provinceFk: 1, countryFk: 1, code: '46460'};
jest.spyOn(controller.vnApp, 'showMessage'); jest.spyOn(controller.vnApp, 'showMessage');
$httpBackend.expect('PATCH', `postcodes`, params).respond(200, params); $httpBackend.expect('PATCH', `postcodes`, params).respond(200, params);
@ -27,7 +27,7 @@ describe('Client', () => {
controller.onAccept(); controller.onAccept();
$httpBackend.flush(); $httpBackend.flush();
expect(controller.vnApp.showMessage).toHaveBeenCalledWith('The postcode has been saved'); expect(controller.vnApp.showMessage).toHaveBeenCalledWith('The postcode has been created. You can save the data now');
}); });
}); });
}); });

View File

@ -1,5 +1,11 @@
New postcode: Nuevo código postal New postcode: Nuevo código postal
New city: Nueva ciudad
New province: Nueva provincia
Please, ensure you put the correct data!: ¡Por favor, asegúrate de poner los datos correctos! Please, ensure you put the correct data!: ¡Por favor, asegúrate de poner los datos correctos!
The postcode can't be empty: El código postal no puede quedar vacío The postcode can't be empty: El código postal no puede quedar vacío
The town can't be empty: La población no puede quedar vacía The town can't be empty: La población no puede quedar vacía
The postcode has been saved: El código postal ha sido guardado The province can't be empty: La provincia no puede quedar vacía
The country can't be empty: El país no puede quedar vacío
The postcode has been created. You can save the data now: Se ha creado el código postal. Ahora puedes guardar los datos
The city has been created: Se ha creado la ciudad
The province has been created: Se ha creado la provincia

View File

@ -0,0 +1,27 @@
<vn-dialog class="edit"
vn-id="provinceDialog"
on-open="$ctrl.onOpen()"
on-accept="$ctrl.onAccept()"
message="New province">
<tpl-body>
<p translate>Please, ensure you put the correct data!</p>
<vn-horizontal>
<vn-textfield vn-one
vn-focus
ng-model="$ctrl.province.name"
label="Name">
</vn-textfield>
<vn-autocomplete vn-one
ng-model="$ctrl.province.countryFk"
url="Countries"
show-field="country"
value-field="id"
label="Country">
</vn-autocomplete>
</vn-horizontal>
</tpl-body>
<tpl-buttons>
<input type="button" response="cancel" translate-attr="{value: 'Cancel'}"/>
<button id="saveProvince" response="accept" translate>Save</button>
</tpl-buttons>
</vn-dialog>

View File

@ -0,0 +1,37 @@
import ngModule from '../../module';
import Component from 'core/lib/component';
class Controller extends Component {
open($event) {
if ($event.defaultPrevented) return;
this.$.provinceDialog.show();
$event.preventDefault();
}
onAccept() {
try {
if (!this.province.name)
throw new Error(`The province name can't be empty`);
if (!this.province.countryFk)
throw new Error(`The country can't be empty`);
this.$http.patch(`provinces`, this.province).then(res => {
this.vnApp.showMessage(this.$t('The province has been created'));
this.emit('response', {$response: res.data});
});
} catch (e) {
this.vnApp.showError(this.$t(e.message));
return false;
}
return true;
}
}
ngModule.vnComponent('vnGeoProvince', {
template: require('./index.html'),
controller: Controller,
bindings: {
data: '<',
}
});

View File

@ -0,0 +1,34 @@
import './index';
describe('Client', () => {
describe('Component vnGeoProvince', () => {
let controller;
let $httpBackend;
let $scope;
beforeEach(ngModule('client'));
beforeEach(inject(($componentController, _$httpBackend_, $rootScope) => {
$httpBackend = _$httpBackend_;
$scope = $rootScope.$new();
const $element = angular.element('<vn-dialog></vn-dialog>');
controller = $componentController('vnGeoProvince', {$element, $scope});
controller.client = {id: 101};
}));
describe('onAccept()', () => {
it('should perform a POST query and show a success snackbar', () => {
let params = {name: 'New Jersey', countryFk: 1};
controller.province = {name: 'New Jersey', countryFk: 1};
jest.spyOn(controller.vnApp, 'showMessage');
$httpBackend.expect('PATCH', `provinces`, params).respond(200, params);
controller.onAccept();
$httpBackend.flush();
expect(controller.vnApp.showMessage).toHaveBeenCalledWith('The province has been created');
});
});
});
});

View File

@ -1,6 +1,6 @@
@import "variables"; @import "variables";
vn-client-postcode { vn-geo-postcode {
vn-dialog { vn-dialog {
p { p {
color: $color-alert color: $color-alert

View File

@ -137,3 +137,4 @@ describe('Buy latests buys filter()', () => {
expect(results.length).toBe(1); expect(results.length).toBe(1);
}); });
}); });

View File

@ -143,6 +143,9 @@
</vn-fetched-tags> </vn-fetched-tags>
</td> </td>
</tr> </tr>
<tr class="empty-row">
<td colspan="10"></td>
</tr>
</tbody> </tbody>
</table> </table>
</vn-auto> </vn-auto>

View File

@ -8,8 +8,18 @@ vn-entry-summary .summary {
background-color: lighten($color-marginal, 10%); background-color: lighten($color-marginal, 10%);
} }
tbody { tbody tr:nth-child(1) {
border: 2px solid $color-marginal; border-top: 1px solid $color-marginal;
}
tbody tr:nth-child(1),
tbody tr:nth-child(2) {
border-left: 1px solid $color-marginal;
border-right: 1px solid $color-marginal
}
tbody tr:nth-child(3) {
height: inherit
} }
tr { tr {

View File

@ -1,7 +1,7 @@
module.exports = Self => { module.exports = Self => {
Self.remoteMethod('book', { Self.remoteMethod('book', {
description: 'Book a invoiceOut', description: 'Book an invoiceOut',
accessType: 'WRITE', accessType: 'WRITE',
accepts: { accepts: {
arg: 'ref', arg: 'ref',

View File

@ -2,38 +2,31 @@ const app = require('vn-loopback/server/server');
describe('invoiceOut book()', () => { describe('invoiceOut book()', () => {
const invoiceOutId = 5; const invoiceOutId = 5;
let bookedDate;
let OriginalInvoiceOut;
let updatedInvoiceOut;
afterAll(async done => {
updatedInvoiceOut.updateAttributes({
booked: OriginalInvoiceOut.booked,
hasPdf: OriginalInvoiceOut.hasPdf,
amount: OriginalInvoiceOut.amount
});
done();
});
it('should check that invoice out booked is untainted', async() => { it('should check that invoice out booked is untainted', async() => {
const invoiceOut = await app.models.InvoiceOut.findById(invoiceOutId); const invoiceOut = await app.models.InvoiceOut.findById(invoiceOutId);
bookedDate = invoiceOut.booked;
expect(invoiceOut.booked).toBeDefined(); expect(invoiceOut.booked).toBeDefined();
expect(invoiceOut.hasPdf).toBeTruthy(); expect(invoiceOut.hasPdf).toBeTruthy();
}); });
it(`should confirm the book property have been updated`, async() => { it('should update the booked property', async() => {
OriginalInvoiceOut = await app.models.InvoiceOut.findById(invoiceOutId); const originalInvoiceOut = await app.models.InvoiceOut.findById(invoiceOutId);
let invoiceOutRef = OriginalInvoiceOut.ref; const bookedDate = originalInvoiceOut.booked;
const invoiceOutRef = originalInvoiceOut.ref;
await app.models.InvoiceOut.book(invoiceOutRef); await app.models.InvoiceOut.book(invoiceOutRef);
updatedInvoiceOut = await app.models.InvoiceOut.findById(invoiceOutId); const updatedInvoiceOut = await app.models.InvoiceOut.findById(invoiceOutId);
expect(updatedInvoiceOut.booked).not.toEqual(bookedDate); expect(updatedInvoiceOut.booked).not.toEqual(bookedDate);
expect(updatedInvoiceOut.hasPdf).toBeFalsy(); expect(updatedInvoiceOut.hasPdf).toBeFalsy();
// restores
await updatedInvoiceOut.updateAttributes({
booked: originalInvoiceOut.booked,
hasPdf: originalInvoiceOut.hasPdf,
amount: originalInvoiceOut.amount
});
}); });
}); });

View File

@ -1,20 +1,14 @@
const app = require('vn-loopback/server/server'); const app = require('vn-loopback/server/server');
const LoopBackContext = require('loopback-context');
describe('invoiceOut delete()', () => { describe('invoiceOut delete()', () => {
const invoiceOutId = 2; const invoiceOutId = 2;
let originalInvoiceOut; let originalInvoiceOut;
let originalTicket; let originalTicket;
const userId = 106;
afterAll(async done => { const activeCtx = {
const newInvoiceOut = await app.models.InvoiceOut.create(originalInvoiceOut); accessToken: {userId: userId},
await newInvoiceOut.updateAttribute('ref', originalInvoiceOut.ref); };
const promises = [];
promises.push(originalTicket.updateAttribute('refFk', newInvoiceOut.ref));
Promise.all(promises);
done();
});
it('should check that there is one ticket in the target invoiceOut', async() => { it('should check that there is one ticket in the target invoiceOut', async() => {
const invoiceOut = await app.models.InvoiceOut.findById(invoiceOutId); const invoiceOut = await app.models.InvoiceOut.findById(invoiceOutId);
@ -25,6 +19,9 @@ describe('invoiceOut delete()', () => {
}); });
it(`should delete the target invoiceOut then check the ticket doesn't have a refFk anymore`, async() => { it(`should delete the target invoiceOut then check the ticket doesn't have a refFk anymore`, async() => {
spyOn(LoopBackContext, 'getCurrentContext').and.returnValue({
active: activeCtx
});
originalInvoiceOut = await app.models.InvoiceOut.findById(invoiceOutId); originalInvoiceOut = await app.models.InvoiceOut.findById(invoiceOutId);
await app.models.InvoiceOut.delete(invoiceOutId); await app.models.InvoiceOut.delete(invoiceOutId);
originalTicket = await app.models.Ticket.findById(3); originalTicket = await app.models.Ticket.findById(3);
@ -32,5 +29,10 @@ describe('invoiceOut delete()', () => {
expect(deletedInvoiceOut).toBeNull(); expect(deletedInvoiceOut).toBeNull();
expect(originalTicket.refFk).toBeNull(); expect(originalTicket.refFk).toBeNull();
// restores
const restoredInvoiceOut = await app.models.InvoiceOut.create(originalInvoiceOut);
await restoredInvoiceOut.updateAttribute('ref', originalInvoiceOut.ref);
await originalTicket.updateAttribute('refFk', restoredInvoiceOut.ref);
}); });
}); });

View File

@ -4,16 +4,6 @@ describe('invoiceOut regenerate()', () => {
const invoiceReportFk = 30; const invoiceReportFk = 30;
const invoiceOutId = 1; const invoiceOutId = 1;
afterAll(async done => {
const invoiceOut = await app.models.InvoiceOut.findById(invoiceOutId);
await invoiceOut.updateAttributes({hasPdf: true});
await app.models.InvoiceOut.rawSql(`
DELETE FROM vn.printServerQueue
WHERE reportFk = ?`, [invoiceReportFk]);
done();
});
it('should check that the invoice has a PDF and is not in print generation queue', async() => { it('should check that the invoice has a PDF and is not in print generation queue', async() => {
const invoiceOut = await app.models.InvoiceOut.findById(invoiceOutId); const invoiceOut = await app.models.InvoiceOut.findById(invoiceOutId);
const [queue] = await app.models.InvoiceOut.rawSql(` const [queue] = await app.models.InvoiceOut.rawSql(`
@ -35,5 +25,12 @@ describe('invoiceOut regenerate()', () => {
expect(invoiceOut.hasPdf).toBeFalsy(); expect(invoiceOut.hasPdf).toBeFalsy();
expect(queue.total).toEqual(1); expect(queue.total).toEqual(1);
// restores
const invoiceOutToRestore = await app.models.InvoiceOut.findById(invoiceOutId);
await invoiceOutToRestore.updateAttributes({hasPdf: true});
await app.models.InvoiceOut.rawSql(`
DELETE FROM vn.printServerQueue
WHERE reportFk = ?`, [invoiceReportFk]);
}); });
}); });

View File

@ -2,22 +2,12 @@ const app = require('vn-loopback/server/server');
describe('item getWasteDetail()', () => { describe('item getWasteDetail()', () => {
it('should check for the waste breakdown for every worker', async() => { it('should check for the waste breakdown for every worker', async() => {
let result = await app.models.Item.getWasteDetail(); const result = await app.models.Item.getWasteDetail();
const firstBuyer = result[0].buyer; const length = result.length;
const firstBuyerLines = result[0].lines; const anyResult = result[Math.floor(Math.random() * Math.floor(length))];
const secondBuyer = result[1].buyer;
const secondBuyerLines = result[1].lines;
const thirdBuyer = result[2].buyer;
const thirdBuyerLines = result[2].lines;
expect(result.length).toEqual(3); expect(anyResult.buyer).toMatch(/(CharlesXavier|HankPym|DavidCharlesHaller)/);
expect(firstBuyer).toEqual('CharlesXavier'); expect(anyResult.lines.length).toBeGreaterThanOrEqual(3);
expect(firstBuyerLines.length).toEqual(4);
expect(secondBuyer).toEqual('HankPym');
expect(secondBuyerLines.length).toEqual(3);
expect(thirdBuyer).toEqual('DavidCharlesHaller');
expect(thirdBuyerLines.length).toEqual(3);
}); });
}); });

View File

@ -4,12 +4,15 @@ describe('item new()', () => {
let item; let item;
afterAll(async done => { afterAll(async done => {
let sql = 'DELETE FROM vn.itemLog WHERE originFk = ?'; try {
await app.models.Item.rawSql(sql, [item.id]); let sql = 'DELETE FROM vn.itemLog WHERE originFk = ?';
await app.models.Item.rawSql(sql, [item.id]);
sql = 'DELETE FROM vn.item WHERE id = ?';
await app.models.Item.rawSql(sql, [item.id]);
sql = 'DELETE FROM vn.item WHERE id = ?';
await app.models.Item.rawSql(sql, [item.id]);
} catch (error) {
console.error(error);
}
done(); done();
}); });

View File

@ -1,13 +1,7 @@
const app = require('vn-loopback/server/server'); const app = require('vn-loopback/server/server');
describe('item updateTaxes()', () => { describe('item updateTaxes()', () => {
afterAll(async done => { let taxesInFixtures = [{id: 3, taxClassFk: 1}];
let taxesInFixtures = [{id: 3, taxClassFk: 1}];
await app.models.Item.updateTaxes(taxesInFixtures);
done();
});
it('should throw an error if the taxClassFk is blank', async() => { it('should throw an error if the taxClassFk is blank', async() => {
let error; let error;
@ -29,14 +23,12 @@ describe('item updateTaxes()', () => {
let taxes = [{id: 3, taxClassFk: 2}]; let taxes = [{id: 3, taxClassFk: 2}];
let result = await app.models.Item.updateTaxes(taxes); await app.models.Item.updateTaxes(taxes);
taxCountry = await app.models.ItemTaxCountry.findById(3);
expect(result).toBeTruthy();
});
it('should confirm the tax class was updated', async() => {
let taxCountry = await app.models.ItemTaxCountry.findById(3);
expect(taxCountry.taxClassFk).toEqual(2); expect(taxCountry.taxClassFk).toEqual(2);
// restores
await app.models.Item.updateTaxes(taxesInFixtures);
}); });
}); });

View File

@ -138,34 +138,34 @@ module.exports = Self => {
stmt = new ParameterizedSQL( stmt = new ParameterizedSQL(
`SELECT `SELECT
o.id, o.id,
o.total, o.total,
o.date_send landed, o.date_send landed,
o.date_make created, o.date_make created,
o.customer_id clientFk, o.customer_id clientFk,
o.agency_id agencyModeFk, o.agency_id agencyModeFk,
o.address_id addressFk, o.address_id addressFk,
o.company_id companyFk, o.company_id companyFk,
o.source_app sourceApp, o.source_app sourceApp,
o.confirmed isConfirmed, o.confirmed isConfirmed,
c.name clientName, c.name clientName,
c.salesPersonFk, c.salesPersonFk,
u.nickname workerNickname, u.nickname workerNickname,
u.name name, u.name name,
co.code companyCode, co.code companyCode,
zed.zoneFk, zed.zoneFk,
zed.hourTheoretical, zed.hourTheoretical,
zed.hourEffective zed.hourEffective
FROM hedera.order o FROM hedera.order o
LEFT JOIN address a ON a.id = o.address_id LEFT JOIN address a ON a.id = o.address_id
LEFT JOIN agencyMode am ON am.id = o.agency_id LEFT JOIN agencyMode am ON am.id = o.agency_id
LEFT JOIN client c ON c.id = o.customer_id LEFT JOIN client c ON c.id = o.customer_id
LEFT JOIN worker wk ON wk.id = c.salesPersonFk LEFT JOIN worker wk ON wk.id = c.salesPersonFk
LEFT JOIN account.user u ON u.id = wk.userFk LEFT JOIN account.user u ON u.id = wk.userFk
LEFT JOIN company co ON co.id = o.company_id LEFT JOIN company co ON co.id = o.company_id
LEFT JOIN orderTicket ot ON ot.orderFk = o.id LEFT JOIN orderTicket ot ON ot.orderFk = o.id
LEFT JOIN ticket t ON t.id = ot.ticketFk LEFT JOIN ticket t ON t.id = ot.ticketFk
LEFT JOIN zoneEstimatedDelivery zed ON zed.zoneFk = t.zoneFk`); LEFT JOIN zoneEstimatedDelivery zed ON zed.zoneFk = t.zoneFk`);
if (args && args.ticketFk) { if (args && args.ticketFk) {
stmt.merge({ stmt.merge({

View File

@ -39,6 +39,8 @@ describe('order filter()', () => {
}); });
expect(hasEmptyLines).toBeFalsy(); expect(hasEmptyLines).toBeFalsy();
ctx.args = {showEmpty: null};
}); });
it('should return the orders matching the showEmpty on true', async() => { it('should return the orders matching the showEmpty on true', async() => {
@ -50,5 +52,7 @@ describe('order filter()', () => {
}); });
expect(hasEmptyLines).toBeTruthy(); expect(hasEmptyLines).toBeTruthy();
ctx.args = {showEmpty: null};
}); });
}); });

View File

@ -14,18 +14,9 @@ describe('route guessPriority()', () => {
done(); done();
}); });
it('should confirm the tickets in the target route have no priority yet', async() => { it('should call guessPriority() and then check the tickets in the target route now have their priorities defined', async() => {
routeTicketsToRestore = await app.models.Ticket.find({where: {routeFk: targetRouteId}}); routeTicketsToRestore = await app.models.Ticket.find({where: {routeFk: targetRouteId}});
expect(routeTicketsToRestore.length).toEqual(2);
expect(routeTicketsToRestore[0].id).toEqual(23);
expect(routeTicketsToRestore[0].priority).toBeNull();
expect(routeTicketsToRestore[1].id).toEqual(24);
expect(routeTicketsToRestore[1].priority).toBeNull();
});
it('should call guessPriority() and then check the tickets in the target route now have their priorities defined', async() => {
await app.models.Route.guessPriority(targetRouteId); await app.models.Route.guessPriority(targetRouteId);
let routeTickets = await app.models.Ticket.find({where: {routeFk: targetRouteId}, fields: ['id', 'priority']}); let routeTickets = await app.models.Ticket.find({where: {routeFk: targetRouteId}, fields: ['id', 'priority']});

View File

@ -1,46 +1,43 @@
const app = require('vn-loopback/server/server'); const app = require('vn-loopback/server/server');
const LoopBackContext = require('loopback-context');
describe('route updateVolume()', () => { describe('route updateVolume()', () => {
const routeId = 1; const routeId = 1;
const workerFk = 9; const userId = 50;
const ctx = {req: {accessToken: {userId: workerFk}}}; const activeCtx = {
let originalRoute; accessToken: {userId: userId},
let ticketToRestore; };
let logIdToDestroy; const ctx = {req: activeCtx};
afterAll(async done => { it('should confirm the route volume is updated and logged when a ticket is added', async() => {
await originalRoute.updateAttributes({m3: 1.8}); spyOn(LoopBackContext, 'getCurrentContext').and.returnValue({
await ticketToRestore.updateAttributes({routeFk: null}); active: activeCtx
await app.models.RouteLog.destroyById(logIdToDestroy); });
done(); const route = await app.models.Route.findById(routeId);
});
it('should confirm the original volume of the route is the expected', async() => { expect(route.m3).toEqual(1.8);
originalRoute = await app.models.Route.findById(routeId);
expect(originalRoute.m3).toEqual(1.8); const ticket = await app.models.Ticket.findById(14);
});
it('should confirm the route volume is updated when a ticket is added', async() => { await ticket.updateAttributes({routeFk: routeId});
ticketToRestore = await app.models.Ticket.findById(14);
let updatedTicket = await app.models.Ticket.findById(14);
await updatedTicket.updateAttributes({routeFk: routeId});
await app.models.Route.updateVolume(ctx, routeId); await app.models.Route.updateVolume(ctx, routeId);
let updatedRoute = await app.models.Route.findById(routeId); const updatedRoute = await app.models.Route.findById(routeId);
expect(updatedRoute.m3).not.toEqual(originalRoute.m3); expect(updatedRoute.m3).not.toEqual(route.m3);
});
it('should confirm the change is logged', async() => { const logs = await app.models.RouteLog.find({fields: ['id', 'newInstance']});
let logs = await app.models.RouteLog.find({fields: ['id', 'newInstance']});
let m3Log = logs.filter(log => { const m3Log = logs.filter(log => {
return log.newInstance.m3 === 1.9; return log.newInstance.m3 === updatedRoute.m3;
}); });
logIdToDestroy = m3Log[0].id; const logIdToDestroy = m3Log[0].id;
expect(m3Log.length).toEqual(1); expect(m3Log.length).toEqual(1);
// restores
await ticket.updateAttributes({routeFk: null});
await route.updateAttributes({m3: 1.8});
await app.models.RouteLog.destroyById(logIdToDestroy);
}); });
}); });

View File

@ -1,7 +1,7 @@
const app = require('vn-loopback/server/server'); const app = require('vn-loopback/server/server');
describe('ticket listPackaging()', () => { describe('ticket listPackaging()', () => {
it('should call the listPackaging method and return the response', async() => { it('should return the packaging', async() => {
let filter = {where: {packagingFk: 1}}; let filter = {where: {packagingFk: 1}};
let response = await app.models.Packaging.listPackaging(filter); let response = await app.models.Packaging.listPackaging(filter);

View File

@ -22,22 +22,22 @@ module.exports = Self => {
Self.listSaleTracking = async filter => { Self.listSaleTracking = async filter => {
let stmt = new ParameterizedSQL(` let stmt = new ParameterizedSQL(`
SELECT SELECT
st.id, st.id,
s.ticketFk, s.ticketFk,
s.quantity, s.quantity,
s.concept, s.concept,
s.itemFk, s.itemFk,
st.originalQuantity, st.originalQuantity,
st.created, st.created,
st.workerFk, st.workerFk,
u.nickname userNickname, u.nickname userNickname,
ste.name AS state ste.name AS state
FROM saleTracking st FROM saleTracking st
JOIN sale s ON s.id = st.saleFk JOIN sale s ON s.id = st.saleFk
JOIN worker w ON w.id = st.workerFk JOIN worker w ON w.id = st.workerFk
JOIN account.user u ON u.id = w.userFk JOIN account.user u ON u.id = w.userFk
JOIN state ste ON ste.id = st.stateFk`); JOIN state ste ON ste.id = st.stateFk`);
stmt.merge(Self.makeSuffix(filter)); stmt.merge(Self.makeSuffix(filter));

View File

@ -5,7 +5,7 @@ describe('ticket listSaleTracking()', () => {
let filter = {where: {ticketFk: 1}}; let filter = {where: {ticketFk: 1}};
let result = await app.models.SaleTracking.listSaleTracking(filter); let result = await app.models.SaleTracking.listSaleTracking(filter);
expect(result[0].concept).toEqual('Ranged weapon longbow 2m'); expect(result.length).toEqual(4);
}); });
it(`should call the listSaleTracking method and return zero if doesn't have lines`, async() => { it(`should call the listSaleTracking method and return zero if doesn't have lines`, async() => {

View File

@ -2,12 +2,16 @@ const app = require('vn-loopback/server/server');
describe('sale deleteSales()', () => { describe('sale deleteSales()', () => {
let sale; let sale;
let newsale; let newSale;
beforeAll(async done => { beforeAll(async done => {
sale = await app.models.Sale.findOne({where: {id: 9}}); try {
sale.id = null; sale = await app.models.Sale.findOne({where: {id: 9}});
newsale = await app.models.Sale.create(sale); sale.id = null;
newSale = await app.models.Sale.create(sale);
} catch (error) {
console.error(error);
}
done(); done();
}); });
@ -28,10 +32,10 @@ describe('sale deleteSales()', () => {
expect(error).toEqual(new Error(`The sales of this ticket can't be modified`)); expect(error).toEqual(new Error(`The sales of this ticket can't be modified`));
}); });
it('should delete the sales', async() => { it('should delete the sale', async() => {
let ctx = {req: {accessToken: {userId: 9}}}; let ctx = {req: {accessToken: {userId: 9}}};
const sales = [{id: newsale.id, instance: 0}]; const sales = [{id: newSale.id, instance: 0}];
const ticketId = 16; const ticketId = 16;
let res = await app.models.Sale.deleteSales(ctx, sales, ticketId); let res = await app.models.Sale.deleteSales(ctx, sales, ticketId);

View File

@ -16,14 +16,6 @@ describe('sale updatePrice()', () => {
done(); done();
}); });
afterAll(async done => {
await originalSale.save();
await app.models.SaleComponent.updateAll({componentFk: manaComponentId, saleFk: saleId}, {value: 0});
await originalSalesPersonMana.save();
done();
});
it('should throw an error if the ticket is not editable', async() => { it('should throw an error if the ticket is not editable', async() => {
let ctx = {req: {accessToken: {userId: 18}}}; let ctx = {req: {accessToken: {userId: 18}}};
let immutableSaleId = 1; let immutableSaleId = 1;
@ -43,36 +35,49 @@ describe('sale updatePrice()', () => {
let price = ''; let price = '';
await app.models.Sale.updatePrice(ctx, saleId, price); await app.models.Sale.updatePrice(ctx, saleId, price);
let saleUpdated = await app.models.Sale.findById(saleId); let updatedSale = await app.models.Sale.findById(saleId);
expect(saleUpdated.price).toEqual(0); expect(updatedSale.price).toEqual(0);
// restores
await originalSale.updateAttributes(originalSale);
await app.models.SaleComponent.updateAll({componentFk: manaComponentId, saleFk: saleId}, {value: 0});
await originalSalesPersonMana.updateAttributes(originalSalesPersonMana);
}); });
it('should now set price as a decimal number in a string', async() => { it('should now set price as a number in a string', async() => {
let ctx = {req: {accessToken: {userId: 18}}}; let ctx = {req: {accessToken: {userId: 18}}};
let price = '8'; let price = '8';
await app.models.Sale.updatePrice(ctx, saleId, price); await app.models.Sale.updatePrice(ctx, saleId, price);
let saleUpdated = await app.models.Sale.findById(saleId); let updatedSale = await app.models.Sale.findById(saleId);
expect(saleUpdated.price).toEqual(8); expect(updatedSale.price).toEqual(8);
// restores
await originalSale.updateAttributes(originalSale);
await app.models.SaleComponent.updateAll({componentFk: manaComponentId, saleFk: saleId}, {value: 0});
await originalSalesPersonMana.updateAttributes(originalSalesPersonMana);
}); });
it('should set price as a decimal number and check the sale has the mana component', async() => { it('should set price as a decimal number and check the sale has the mana component changing the salesPersonMana', async() => {
let ctx = {req: {accessToken: {userId: 18}}}; let ctx = {req: {accessToken: {userId: 18}}};
let price = 5.4; let price = 5.4;
await app.models.Sale.updatePrice(ctx, saleId, price); await app.models.Sale.updatePrice(ctx, saleId, price);
let saleUpdated = await app.models.Sale.findById(saleId); let updatedSale = await app.models.Sale.findById(saleId);
createdSaleComponent = await app.models.SaleComponent.findOne({where: {saleFk: saleId, componentFk: manaComponentId}}); createdSaleComponent = await app.models.SaleComponent.findOne({where: {saleFk: saleId, componentFk: manaComponentId}});
expect(saleUpdated.price).toBe(price); expect(updatedSale.price).toBe(price);
expect(createdSaleComponent.value).toEqual(-2.04); expect(createdSaleComponent.value).toEqual(-2.04);
});
it('should check that the mana of salesPerson changed', async() => {
let updatedSalesPersonMana = await app.models.WorkerMana.findById(18); let updatedSalesPersonMana = await app.models.WorkerMana.findById(18);
expect(updatedSalesPersonMana.amount).not.toEqual(originalSalesPersonMana.amount); expect(updatedSalesPersonMana.amount).not.toEqual(originalSalesPersonMana.amount);
// restores
await originalSale.updateAttributes(originalSale);
await app.models.SaleComponent.updateAll({componentFk: manaComponentId, saleFk: saleId}, {value: 0});
await originalSalesPersonMana.updateAttributes(originalSalesPersonMana);
}); });
}); });

View File

@ -12,7 +12,7 @@ describe('ticket editableStates()', () => {
expect(deliveredState).toBeTruthy(); expect(deliveredState).toBeTruthy();
}); });
it(`should returns the expected states by a specific role`, async() => { it(`should return the expected states by a specific role`, async() => {
const productionRole = 18; const productionRole = 18;
const ctx = {req: {accessToken: {userId: productionRole}}}; const ctx = {req: {accessToken: {userId: productionRole}}};
let result = await app.models.State.editableStates(ctx, filter); let result = await app.models.State.editableStates(ctx, filter);

View File

@ -48,31 +48,26 @@ module.exports = Self => {
include: {relation: 'ticket'} include: {relation: 'ticket'}
}, options); }, options);
const res = await models.Item.getVisibleAvailable(ctx.args.itemFk, request.ticket().warehouseFk, request.ticket().shipped); const itemStock = await models.Item.getVisibleAvailable(ctx.args.itemFk, request.ticket().warehouseFk, request.ticket().shipped);
const isAvailable = itemStock.available > 0;
if (res.available < 0) if (!isAvailable)
throw new UserError(`This item is not available`); throw new UserError(`This item is not available`);
if (request.saleFk) { if (request.saleFk)
sale = await models.Sale.findById(request.saleFk, null, options); throw new UserError(`This request already contains a sale`);
await sale.updateAttributes({
itemFk: ctx.args.itemFk, sale = await models.Sale.create({
quantity: ctx.args.quantity, ticketFk: request.ticketFk,
concept: item.name, itemFk: ctx.args.itemFk,
}, options); quantity: ctx.args.quantity,
} else { concept: item.name
sale = await models.Sale.create({ }, options);
ticketFk: request.ticketFk, await request.updateAttributes({
itemFk: ctx.args.itemFk, saleFk: sale.id,
quantity: ctx.args.quantity, itemFk: sale.itemFk,
concept: item.name isOk: true
}, options); }, options);
await request.updateAttributes({
saleFk: sale.id,
itemFk: sale.itemFk,
isOk: true
}, options);
}
query = `CALL vn.sale_calculateComponent(?, NULL)`; query = `CALL vn.sale_calculateComponent(?, NULL)`;
await Self.rawSql(query, [sale.id], options); await Self.rawSql(query, [sale.id], options);

View File

@ -1,9 +1,7 @@
const app = require('vn-loopback/server/server'); const app = require('vn-loopback/server/server');
describe('ticket-request confirm()', () => { // #2512 confirm.spec pollutes other tests
let originalRequest; xdescribe('ticket-request confirm()', () => {
let originalSale;
let createdSaleId;
let ctx = { let ctx = {
req: { req: {
accessToken: {userId: 9}, accessToken: {userId: 9},
@ -14,13 +12,6 @@ describe('ticket-request confirm()', () => {
return value; return value;
}; };
afterAll(async done => {
await originalRequest.updateAttributes(originalRequest);
await originalSale.updateAttributes(originalSale);
await app.models.Sale.destroyById(createdSaleId);
done();
});
it(`should throw an error if the item doesn't exist`, async() => { it(`should throw an error if the item doesn't exist`, async() => {
ctx.args = {itemFk: 999}; ctx.args = {itemFk: 999};
@ -56,9 +47,8 @@ describe('ticket-request confirm()', () => {
expect(error.message).toEqual(`This item is not available`); expect(error.message).toEqual(`This item is not available`);
}); });
it(`should update the sale details if the request already contains a sale id`, async() => { it(`should throw if there's a sale id`, async() => {
const requestId = 4; const requestId = 4;
const saleId = 11;
const itemId = 1; const itemId = 1;
const quantity = 10; const quantity = 10;
@ -68,17 +58,30 @@ describe('ticket-request confirm()', () => {
quantity: quantity quantity: quantity
}; };
originalRequest = await app.models.TicketRequest.findById(requestId);
originalSale = await app.models.Sale.findById(saleId);
const request = await app.models.TicketRequest.findById(requestId); const request = await app.models.TicketRequest.findById(requestId);
await request.updateAttributes({saleFk: saleId});
await app.models.TicketRequest.confirm(ctx);
let updatedSale = await app.models.Sale.findById(saleId); expect(request.saleFk).toBeNull();
expect(updatedSale.itemFk).toEqual(itemId); await request.updateAttributes({saleFk: 2});
expect(updatedSale.quantity).toEqual(quantity);
ctx.args = {
itemFk: itemId,
id: requestId,
quantity: quantity
};
let error;
try {
await app.models.TicketRequest.confirm(ctx);
} catch (err) {
error = err;
}
expect(error.message).toEqual(`This request already contains a sale`);
// restores
await request.updateAttributes({saleFk: null});
}); });
it(`should create a new sale for the the request if there's no sale id`, async() => { it(`should create a new sale for the the request if there's no sale id`, async() => {
@ -86,6 +89,8 @@ describe('ticket-request confirm()', () => {
const itemId = 1; const itemId = 1;
const quantity = 10; const quantity = 10;
const originalRequest = await app.models.TicketRequest.findById(requestId);
ctx.args = { ctx.args = {
itemFk: itemId, itemFk: itemId,
id: requestId, id: requestId,
@ -96,11 +101,19 @@ describe('ticket-request confirm()', () => {
await request.updateAttributes({saleFk: null}); await request.updateAttributes({saleFk: null});
await app.models.TicketRequest.confirm(ctx); await app.models.TicketRequest.confirm(ctx);
let updatedRequest = await app.models.TicketRequest.findById(requestId); const updatedRequest = await app.models.TicketRequest.findById(requestId);
createdSaleId = updatedRequest.saleFk; const createdSaleId = updatedRequest.saleFk;
expect(updatedRequest.saleFk).toEqual(createdSaleId); expect(updatedRequest.saleFk).toEqual(createdSaleId);
expect(updatedRequest.isOk).toEqual(true); expect(updatedRequest.isOk).toEqual(true);
expect(updatedRequest.itemFk).toEqual(itemId); expect(updatedRequest.itemFk).toEqual(itemId);
// restores
await originalRequest.updateAttributes(originalRequest);
await app.models.Sale.destroyById(createdSaleId);
await app.models.Item.rawSql(`
TRUNCATE TABLE cache.last_buy
`);
}); });
}); });

View File

@ -1,24 +1,58 @@
const app = require('vn-loopback/server/server'); const app = require('vn-loopback/server/server');
const LoopBackContext = require('loopback-context');
describe('ticket changeState()', () => { describe('ticket changeState()', () => {
const salesPersonId = 18;
const employeeId = 1;
const productionId = 49;
let activeCtx = {
accessToken: {userId: 9},
};
let ctx = {req: activeCtx};
let ticket; let ticket;
beforeAll(async done => { beforeAll(async done => {
let originalTicket = await app.models.Ticket.findOne({where: {id: 16}}); spyOn(LoopBackContext, 'getCurrentContext').and.returnValue({
originalTicket.id = null; active: activeCtx
ticket = await app.models.Ticket.create(originalTicket); });
done();
});
beforeEach(async done => {
try {
let originalTicket = await app.models.Ticket.findOne({where: {id: 16}});
originalTicket.id = null;
ticket = await app.models.Ticket.create(originalTicket);
} catch (error) {
console.error(error);
}
done();
});
afterEach(async done => {
try {
await app.models.Ticket.destroyById(ticket.id);
} catch (error) {
console.error(error);
}
done(); done();
}); });
afterAll(async done => { afterAll(async done => {
await app.models.Ticket.destroyById(ticket.id); try {
await app.models.Ticket.destroyById(ticket.id);
} catch (error) {
console.error(error);
}
done(); done();
}); });
it('should throw if the ticket is not editable and the user isnt production', async() => { it('should throw if the ticket is not editable and the user isnt production', async() => {
let ctx = {req: {accessToken: {userId: 18}}}; activeCtx.accessToken.userId = salesPersonId;
let params = {ticketFk: 2, stateFk: 3}; let params = {ticketFk: 2, stateFk: 3};
let errCode; let errCode;
@ -32,7 +66,7 @@ describe('ticket changeState()', () => {
}); });
it('should throw an error if a worker with employee role attemps to a forbidden state', async() => { it('should throw an error if a worker with employee role attemps to a forbidden state', async() => {
let ctx = {req: {accessToken: {userId: 1}}}; activeCtx.accessToken.userId = employeeId;
let params = {ticketFk: 11, stateFk: 13}; let params = {ticketFk: 11, stateFk: 13};
let errCode; let errCode;
@ -46,29 +80,21 @@ describe('ticket changeState()', () => {
}); });
it('should be able to create a ticket tracking line for a not editable ticket if the user has the production role', async() => { it('should be able to create a ticket tracking line for a not editable ticket if the user has the production role', async() => {
let ctx = {req: {accessToken: {userId: 49}}}; activeCtx.accessToken.userId = productionId;
let params = {ticketFk: ticket.id, stateFk: 3}; let params = {ticketFk: ticket.id, stateFk: 3};
let res = await app.models.TicketTracking.changeState(ctx, params); let ticketTracking = await app.models.TicketTracking.changeState(ctx, params);
expect(res.__data.ticketFk).toBe(params.ticketFk); expect(ticketTracking.__data.ticketFk).toBe(params.ticketFk);
expect(res.__data.stateFk).toBe(params.stateFk); expect(ticketTracking.__data.stateFk).toBe(params.stateFk);
expect(res.__data.workerFk).toBe(49); expect(ticketTracking.__data.workerFk).toBe(49);
expect(res.__data.id).toBeDefined(); expect(ticketTracking.__data.id).toBeDefined();
// restores
await app.models.TicketTracking.destroyById(ticketTracking.__data.id);
}); });
it('should return an array with the created ticket tracking line', async() => { it('should update the ticket tracking line when the user is salesperson, uses the state assigned and a valid worker id', async() => {
let ctx = {req: {accessToken: {userId: 49}}};
let params = {ticketFk: ticket.id, stateFk: 3};
let res = await app.models.TicketTracking.changeState(ctx, params);
expect(res.__data.ticketFk).toBe(params.ticketFk);
expect(res.__data.stateFk).toBe(params.stateFk);
expect(res.__data.workerFk).toBe(49);
expect(res.__data.id).toBeDefined();
});
it('should return an array with the created ticket tracking line when the user is salesperson, uses the state assigned and thes a workerFk given', async() => {
let ctx = {req: {accessToken: {userId: 18}}}; let ctx = {req: {accessToken: {userId: 18}}};
let assignedState = await app.models.State.findOne({where: {code: 'PICKER_DESIGNED'}}); let assignedState = await app.models.State.findOne({where: {code: 'PICKER_DESIGNED'}});
let params = {ticketFk: ticket.id, stateFk: assignedState.id, workerFk: 1}; let params = {ticketFk: ticket.id, stateFk: assignedState.id, workerFk: 1};

View File

@ -1,24 +1,42 @@
const app = require('vn-loopback/server/server'); const app = require('vn-loopback/server/server');
const LoopBackContext = require('loopback-context');
describe('ticket setDelivered()', () => { describe('ticket setDelivered()', () => {
const userId = 50;
const activeCtx = {
accessToken: {userId: userId},
};
let ticketOne; let ticketOne;
let ticketTwo; let ticketTwo;
beforeAll(async done => { beforeAll(async done => {
let originalTicketOne = await app.models.Ticket.findById(8); spyOn(LoopBackContext, 'getCurrentContext').and.returnValue({
let originalTicketTwo = await app.models.Ticket.findById(10); active: activeCtx
});
try {
let originalTicketOne = await app.models.Ticket.findById(8);
let originalTicketTwo = await app.models.Ticket.findById(10);
originalTicketOne.id = null; originalTicketOne.id = null;
originalTicketTwo.id = null; originalTicketTwo.id = null;
ticketOne = await app.models.Ticket.create(originalTicketOne);
ticketTwo = await app.models.Ticket.create(originalTicketTwo); ticketOne = await app.models.Ticket.create(originalTicketOne);
ticketTwo = await app.models.Ticket.create(originalTicketTwo);
} catch (error) {
console.error(error);
}
done(); done();
}); });
afterAll(async done => { afterAll(async done => {
await app.models.Ticket.destroyById(ticketOne.id); try {
await app.models.Ticket.destroyById(ticketTwo.id); await app.models.Ticket.destroyById(ticketOne.id);
await app.models.Ticket.destroyById(ticketTwo.id);
} catch (error) {
console.error(error);
}
done(); done();
}); });
@ -31,6 +49,8 @@ describe('ticket setDelivered()', () => {
let state = await app.models.TicketTracking.setDelivered(ctx, params); let state = await app.models.TicketTracking.setDelivered(ctx, params);
expect(state.id).toEqual(delivered.id); expect(state.id).toEqual(delivered.id);
// restores
await app.models.TicketTracking.destroyById(state.id);
}); });
}); });

View File

@ -76,6 +76,7 @@ module.exports = Self => {
companyFk, shipped, landed, isDeleted, option) => { companyFk, shipped, landed, isDeleted, option) => {
const userId = ctx.req.accessToken.userId; const userId = ctx.req.accessToken.userId;
const models = Self.app.models; const models = Self.app.models;
const $t = ctx.req.__; // $translate
const isEditable = await models.Ticket.isEditable(ctx, id); const isEditable = await models.Ticket.isEditable(ctx, id);
if (!isEditable) if (!isEditable)
@ -88,29 +89,26 @@ module.exports = Self => {
if (!zoneShipped || zoneShipped.zoneFk != zoneFk) if (!zoneShipped || zoneShipped.zoneFk != zoneFk)
throw new UserError(`You don't have privileges to change the zone`); throw new UserError(`You don't have privileges to change the zone`);
} }
const originalTicket = await models.Ticket.findById(id, {fields: const originalTicket = await models.Ticket.findById(id, {
['id', 'clientFk', 'agencyModeFk', 'addressFk', 'zoneFk', include: {
relation: 'client',
scope: {
fields: 'salesPersonFk'
}
},
fields: ['id', 'clientFk', 'agencyModeFk', 'addressFk', 'zoneFk',
'warehouseFk', 'companyFk', 'shipped', 'landed', 'isDeleted'] 'warehouseFk', 'companyFk', 'shipped', 'landed', 'isDeleted']
}); });
const updatedTicket = Object.assign({}, ctx.args); const updatedTicket = Object.assign({}, ctx.args);
delete updatedTicket.ctx; delete updatedTicket.ctx;
delete updatedTicket.option; delete updatedTicket.option;
// Force unroute // Force unroute
const hasToBeUnrouted = true; const hasToBeUnrouted = true;
const propertiesChange = diff(originalTicket, updatedTicket); const changedProperties = diff(originalTicket, updatedTicket);
let logRecord = { const query = 'CALL vn.ticket_componentMakeUpdate(?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)';
originFk: id, const res = await Self.rawSql(query, [
userFk: userId,
action: 'update',
changedModel: 'Ticket',
changedModelId: id,
oldInstance: originalTicket,
newInstance: propertiesChange
};
let query = 'CALL vn.ticket_componentMakeUpdate(?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)';
let res = await Self.rawSql(query, [
id, id,
clientFk, clientFk,
agencyModeFk, agencyModeFk,
@ -125,7 +123,45 @@ module.exports = Self => {
option option
]); ]);
await models.TicketLog.create(logRecord); await models.TicketLog.create({
originFk: id,
userFk: userId,
action: 'update',
changedModel: 'Ticket',
changedModelId: id,
oldInstance: originalTicket,
newInstance: changedProperties
});
const salesPersonId = originalTicket.client().salesPersonFk;
if (salesPersonId) {
const origin = ctx.req.headers.origin;
let changesMade = '';
for (let change in changedProperties) {
let value = changedProperties[change];
if (value instanceof Date) {
value = new Intl.DateTimeFormat('es', {
year: '2-digit',
month: '2-digit',
day: '2-digit',
hour: '2-digit',
minute: '2-digit',
second: '2-digit'
}).format(value);
}
changesMade += `${change}: ${value}\r\n`;
}
const message = $t('Changed this data from the ticket', {
ticketId: id,
ticketUrl: `${origin}/#!/ticket/${id}/summary`,
changes: changesMade
});
await models.Chat.sendCheckingPresence(ctx, salesPersonId, message);
}
return res; return res;
}; };
}; };

View File

@ -87,15 +87,14 @@ module.exports = Self => {
const map = new Map(); const map = new Map();
// Sale price component, one per sale // Sale price component, one per sale
difComponents.forEach(difComponent => { for (difComponent of difComponents)
map.set(difComponent.saleFk, difComponent); map.set(difComponent.saleFk, difComponent);
});
function round(value) { function round(value) {
return Math.round(value * 100) / 100; return Math.round(value * 100) / 100;
} }
salesObj.items.forEach(sale => { for (sale of salesObj.items) {
const difComponent = map.get(sale.id); const difComponent = map.get(sale.id);
if (difComponent) { if (difComponent) {
@ -110,7 +109,7 @@ module.exports = Self => {
salesObj.totalUnitPrice += sale.price; salesObj.totalUnitPrice += sale.price;
salesObj.totalUnitPrice = round(salesObj.totalUnitPrice); salesObj.totalUnitPrice = round(salesObj.totalUnitPrice);
}); }
return salesObj; return salesObj;
}; };

View File

@ -1,7 +1,8 @@
const app = require('vn-loopback/server/server'); const app = require('vn-loopback/server/server');
describe('ticket componentUpdate()', () => { describe('ticket componentUpdate()', () => {
const ticketId = 11; const userID = 101;
const ticketID = 11;
const today = new Date(); const today = new Date();
const tomorrow = new Date(); const tomorrow = new Date();
tomorrow.setDate(tomorrow.getDate() + 1); tomorrow.setDate(tomorrow.getDate() + 1);
@ -13,39 +14,50 @@ describe('ticket componentUpdate()', () => {
let componentOfSaleEight; let componentOfSaleEight;
beforeAll(async done => { beforeAll(async done => {
let deliveryComponenet = await app.models.Component.findOne({where: {code: 'delivery'}}); try {
deliveryComponentId = deliveryComponenet.id; let deliveryComponenet = await app.models.Component.findOne({where: {code: 'delivery'}});
componentOfSaleSeven = `SELECT value FROM vn.saleComponent WHERE saleFk = 7 AND componentFk = ${deliveryComponentId}`; deliveryComponentId = deliveryComponenet.id;
componentOfSaleEight = `SELECT value FROM vn.saleComponent WHERE saleFk = 8 AND componentFk = ${deliveryComponentId}`; componentOfSaleSeven = `SELECT value FROM vn.saleComponent WHERE saleFk = 7 AND componentFk = ${deliveryComponentId}`;
componentOfSaleEight = `SELECT value FROM vn.saleComponent WHERE saleFk = 8 AND componentFk = ${deliveryComponentId}`;
[componentValue] = await app.models.SaleComponent.rawSql(componentOfSaleSeven); [componentValue] = await app.models.SaleComponent.rawSql(componentOfSaleSeven);
firstvalueBeforeChange = componentValue.value; firstvalueBeforeChange = componentValue.value;
[componentValue] = await app.models.SaleComponent.rawSql(componentOfSaleEight); [componentValue] = await app.models.SaleComponent.rawSql(componentOfSaleEight);
secondvalueBeforeChange = componentValue.value; secondvalueBeforeChange = componentValue.value;
} catch (error) {
console.error(error);
}
done(); done();
}); });
it('should change the agencyMode to modify the sale components value', async() => { it('should change the agencyMode to modify the sale components value and then undo the changes', async() => {
const clientId = 102; const clientID = 102;
const addressId = 122; const addressID = 122;
const agencyModeId = 8; const agencyModeID = 8;
const warehouseId = 1; const warehouseID = 1;
const zoneId = 5; const zoneID = 5;
const shipped = today; const shipped = today;
const companyId = 442; const companyID = 442;
const isDeleted = false; const isDeleted = false;
const landed = tomorrow; const landed = tomorrow;
const option = 1; const option = 1;
let ctx = { let ctx = {
args: {clientFk: 102, args: {clientFk: clientID,
agencyModeFk: 8}, agencyModeFk: agencyModeID},
req: {accessToken: {userId: 101}}}; req: {
accessToken: {userId: userID},
headers: {origin: 'http://localhost'},
__: (value, params) => {
return params.nickname;
}
}
};
await app.models.Ticket.componentUpdate(ctx, ticketId, clientId, agencyModeId, addressId, await app.models.Ticket.componentUpdate(ctx, ticketID, clientID, agencyModeID, addressID,
zoneId, warehouseId, companyId, shipped, landed, isDeleted, option); zoneID, warehouseID, companyID, shipped, landed, isDeleted, option);
[componentValue] = await app.models.SaleComponent.rawSql(componentOfSaleSeven); [componentValue] = await app.models.SaleComponent.rawSql(componentOfSaleSeven);
let firstvalueAfterChange = componentValue.value; let firstvalueAfterChange = componentValue.value;
@ -55,33 +67,32 @@ describe('ticket componentUpdate()', () => {
expect(firstvalueBeforeChange).not.toEqual(firstvalueAfterChange); expect(firstvalueBeforeChange).not.toEqual(firstvalueAfterChange);
expect(secondvalueBeforeChange).not.toEqual(secondvalueAfterChange); expect(secondvalueBeforeChange).not.toEqual(secondvalueAfterChange);
});
it('should change the agencyMode to go back to the originals sale components value', async() => { // restores
const clientId = 102; const restores = {
const addressId = 122; clientID: 102,
const agencyModeId = 7; addressID: 122,
const warehouseId = 1; agencyModeID: 7,
const zoneId = 3; warehouseID: 1,
const shipped = today; zoneID: 3,
const companyId = 442; shipped: today,
const isDeleted = false; companyID: 442,
const landed = tomorrow; isDeleted: false,
const option = 1; landed: tomorrow,
option: 1,
};
let ctx = { ctx.clientFk = restores.clientID;
args: {clientFk: 102, ctx.agencyModeFk = restores.agencyModeID;
agencyModeFk: 7},
req: {accessToken: {userId: 101}}};
await app.models.Ticket.componentUpdate(ctx, ticketId, clientId, agencyModeId, addressId, await app.models.Ticket.componentUpdate(ctx, ticketID, restores.clientID, restores.agencyModeID, restores.addressID,
zoneId, warehouseId, companyId, shipped, landed, isDeleted, option); restores.zoneID, restores.warehouseID, restores.companyID, restores.shipped, restores.landed, restores.isDeleted, restores.option);
[componentValue] = await app.models.SaleComponent.rawSql(componentOfSaleSeven); [componentValue] = await app.models.SaleComponent.rawSql(componentOfSaleSeven);
let firstvalueAfterChange = componentValue.value; firstvalueAfterChange = componentValue.value;
[componentValue] = await app.models.SaleComponent.rawSql(componentOfSaleEight); [componentValue] = await app.models.SaleComponent.rawSql(componentOfSaleEight);
let secondvalueAfterChange = componentValue.value; secondvalueAfterChange = componentValue.value;
expect(firstvalueBeforeChange).toEqual(firstvalueAfterChange); expect(firstvalueBeforeChange).toEqual(firstvalueAfterChange);
expect(secondvalueBeforeChange).toEqual(secondvalueAfterChange); expect(secondvalueBeforeChange).toEqual(secondvalueAfterChange);

View File

@ -13,90 +13,52 @@ describe('ticket deleteStowaway()', () => {
return params.nickname; return params.nickname;
}; };
afterAll(async() => { it(`should create an stowaway, delete it and see the states of both stowaway and ship go back to the last states`, async() => {
await app.models.Stowaway.rawSql(
`CALL ticketStateUpdate(?, ?)`, [shipId, 'OK']);
await app.models.Stowaway.rawSql(
`CALL ticketStateUpdate(?, ?)`, [stowawayId, 'FREE']);
});
it('should create an stowaway', async() => {
await app.models.Stowaway.rawSql(` await app.models.Stowaway.rawSql(`
INSERT INTO stowaway (id, shipFk) VALUES (?, ?) INSERT INTO stowaway (id, shipFk) VALUES (?, ?)
`, [stowawayId, shipId]); `, [stowawayId, shipId]);
await app.models.Stowaway.rawSql( await app.models.Stowaway.rawSql(
`CALL ticketStateUpdate(?, ?)`, [shipId, 'BOARDING']); `CALL ticketStateUpdate(?, ?)`, [shipId, 'BOARDING']);
await app.models.Stowaway.rawSql(
`CALL ticketStateUpdate(?, ?)`, [stowawayId, 'BOARDING']);
const stowawayExists = await app.models.Stowaway.count({id: stowawayId, shipFk: shipId}); let createdStowaways = await app.models.Stowaway.count({id: stowawayId, shipFk: shipId});
expect(stowawayExists).toEqual(1); expect(createdStowaways).toEqual(1);
});
it('should confirm that the ship ticket is on "BOARDING" state', async() => { let shipState = await app.models.TicketLastState.findOne({
const shipState = await app.models.TicketLastState.findOne({
where: { where: {
ticketFk: shipId ticketFk: shipId
} }
}); });
let stowawayState = await app.models.TicketLastState.findOne({
where: {
ticketFk: stowawayId
}
});
expect(shipState.name).toEqual('Embarcando'); expect(shipState.name).toEqual('Embarcando');
}); expect(stowawayState.name).toEqual('Embarcando');
it('should delete the stowaway from the ship ticket', async() => {
await app.models.Ticket.deleteStowaway(ctx, shipId); await app.models.Ticket.deleteStowaway(ctx, shipId);
await app.models.Ticket.deleteStowaway(ctx, stowawayId);
const stowawayExists = await app.models.Stowaway.count({id: stowawayId, shipFk: shipId}); createdStowaways = await app.models.Stowaway.count({id: stowawayId, shipFk: shipId});
expect(stowawayExists).toEqual(0); expect(createdStowaways).toEqual(0);
});
it('should confirm that the ship ticket is not on "BOARDING" state anymore', async() => { shipState = await app.models.TicketLastState.findOne({
const shipState = await app.models.TicketLastState.findOne({
where: { where: {
ticketFk: shipId ticketFk: shipId
} }
}); });
stowawayState = await app.models.TicketLastState.findOne({
where: {
ticketFk: stowawayId
}
});
expect(shipState.name).toEqual('OK'); expect(shipState.name).toEqual('OK');
}); expect(stowawayState.name).toEqual('Libre');
it('should create again an stowaway', async() => {
await app.models.Stowaway.rawSql(`
INSERT INTO stowaway (id, shipFk) VALUES (?, ?)
`, [shipId, stowawayId]);
await app.models.Stowaway.rawSql(
`CALL ticketStateUpdate(?, ?)`, [stowawayId, 'BOARDING']);
const stowawayExists = await app.models.Stowaway.count({id: shipId, shipFk: stowawayId});
expect(stowawayExists).toEqual(1);
});
it('should confirm that the stowaway ticket is on "BOARDING" state', async() => {
const shipState = await app.models.TicketLastState.findOne({
where: {
ticketFk: stowawayId
}
});
expect(shipState.name).toEqual('Embarcando');
});
it('should delete the stowaway from the stowaway ticket', async() => {
await app.models.Ticket.deleteStowaway(ctx, stowawayId);
const stowawayExists = await app.models.Stowaway.count({id: shipId, shipFk: stowawayId});
expect(stowawayExists).toEqual(0);
});
it('should confirm that the stowaway ticket is not on "BOARDING" state anymore', async() => {
const shipState = await app.models.TicketLastState.findOne({
where: {
ticketFk: stowawayId
}
});
expect(shipState.name).toEqual('Libre');
}); });
}); });

View File

@ -5,9 +5,8 @@ describe('ticket filter()', () => {
const ctx = {req: {accessToken: {userId: 9}}, args: {}}; const ctx = {req: {accessToken: {userId: 9}}, args: {}};
const filter = {order: 'id DESC'}; const filter = {order: 'id DESC'};
const result = await app.models.Ticket.filter(ctx, filter); const result = await app.models.Ticket.filter(ctx, filter);
const ticketId = result[0].id;
expect(ticketId).toEqual(24); expect(result.length).toEqual(24);
}); });
it('should return the tickets matching the problems on true', async() => { it('should return the tickets matching the problems on true', async() => {
@ -71,7 +70,7 @@ describe('ticket filter()', () => {
const length = result.length; const length = result.length;
const anyResult = result[Math.floor(Math.random() * Math.floor(length))]; const anyResult = result[Math.floor(Math.random() * Math.floor(length))];
expect(result.length).toEqual(7); expect(length).toEqual(7);
expect(anyResult.state).toMatch(/(Libre|Arreglar)/); expect(anyResult.state).toMatch(/(Libre|Arreglar)/);
}); });

View File

@ -1,41 +1,53 @@
const app = require('vn-loopback/server/server'); const app = require('vn-loopback/server/server');
const LoopBackContext = require('loopback-context');
describe('ticket makeInvoice()', () => { describe('ticket makeInvoice()', () => {
const userId = 19;
const activeCtx = {
accessToken: {userId: userId},
};
const ctx = {req: activeCtx};
let invoice; let invoice;
let ticketId = 11; let ticketId = 11;
const okState = 3; const okState = 3;
afterAll(async done => { beforeAll(async done => {
let ticket = await app.models.Ticket.findById(11); spyOn(LoopBackContext, 'getCurrentContext').and.returnValue({
await ticket.updateAttributes({refFk: null}); active: activeCtx
let ticketTrackings = await app.models.TicketTracking.find({
where: {
ticketFk: ticketId,
stateFk: {neq: okState}
},
order: 'id DESC'
}); });
});
for (let state of ticketTrackings) afterAll(async done => {
await state.destroy(); try {
let ticket = await app.models.Ticket.findById(11);
await ticket.updateAttributes({refFk: null});
let invoiceOut = await app.models.InvoiceOut.findById(invoice.invoiceFk); let ticketTrackings = await app.models.TicketTracking.find({
await invoiceOut.destroy(); where: {
ticketFk: ticketId,
stateFk: {neq: okState}
},
order: 'id DESC'
});
for (let state of ticketTrackings)
await state.destroy();
let invoiceOut = await app.models.InvoiceOut.findById(invoice.invoiceFk);
await invoiceOut.destroy();
} catch (error) {
console.error(error);
}
done(); done();
}); });
it('should invoice a ticket', async() => { it('should invoice a ticket, then try again to fail', async() => {
let ctx = {req: {accessToken: {userId: 9}}};
invoice = await app.models.Ticket.makeInvoice(ctx, ticketId); invoice = await app.models.Ticket.makeInvoice(ctx, ticketId);
expect(invoice.invoiceFk).toBeDefined(); expect(invoice.invoiceFk).toBeDefined();
expect(invoice.serial).toEqual('T'); expect(invoice.serial).toEqual('T');
});
it('should not invoice an already invoiced ticket', async() => {
let ctx = {req: {accessToken: {userId: 9}}};
let error; let error;
await app.models.Ticket.makeInvoice(ctx, ticketId).catch(e => { await app.models.Ticket.makeInvoice(ctx, ticketId).catch(e => {
@ -43,5 +55,7 @@ describe('ticket makeInvoice()', () => {
}).finally(() => { }).finally(() => {
expect(error.message).toEqual(`This ticket can't be invoiced`); expect(error.message).toEqual(`This ticket can't be invoiced`);
}); });
expect(error).toBeDefined();
}); });
}); });

View File

@ -1,20 +1,24 @@
const app = require('vn-loopback/server/server'); const app = require('vn-loopback/server/server');
const LoopBackContext = require('loopback-context');
const models = app.models; const models = app.models;
describe('ticket restore()', () => { describe('ticket restore()', () => {
const employeeUser = 110; const employeeUser = 110;
const ctx = { const activeCtx = {
req: { accessToken: {userId: employeeUser},
accessToken: {userId: employeeUser}, headers: {
headers: { origin: 'http://localhost:5000'
origin: 'http://localhost:5000' },
}, __: () => {}
__: () => {}
}
}; };
const ctx = {req: activeCtx};
let createdTicket; let createdTicket;
beforeEach(async done => { beforeEach(async done => {
spyOn(LoopBackContext, 'getCurrentContext').and.returnValue({
active: activeCtx
});
try { try {
const sampleTicket = await models.Ticket.findById(11); const sampleTicket = await models.Ticket.findById(11);
sampleTicket.id = undefined; sampleTicket.id = undefined;

View File

@ -1,18 +1,17 @@
const app = require('vn-loopback/server/server'); const app = require('vn-loopback/server/server');
const LoopBackContext = require('loopback-context');
const models = app.models; const models = app.models;
describe('ticket setDeleted()', () => { describe('ticket setDeleted()', () => {
const userId = 106;
const employeeUser = 110;
const activeCtx = {
accessToken: {userId: userId},
};
it('should throw an error if the given ticket has a claim', async() => { it('should throw an error if the given ticket has a claim', async() => {
const ctx = {req: activeCtx};
const ticketId = 16; const ticketId = 16;
const ctx = {
req: {
accessToken: {userId: 106},
headers: {
origin: 'http://localhost:5000'
},
__: () => {}
}
};
let error; let error;
try { try {
@ -26,7 +25,9 @@ describe('ticket setDeleted()', () => {
}); });
it('should delete the ticket, remove the stowaway link and change the stowaway ticket state to "FIXING" and get rid of the itemshelving', async() => { it('should delete the ticket, remove the stowaway link and change the stowaway ticket state to "FIXING" and get rid of the itemshelving', async() => {
const employeeUser = 110; spyOn(LoopBackContext, 'getCurrentContext').and.returnValue({
active: activeCtx
});
const ctx = { const ctx = {
req: { req: {
accessToken: {userId: employeeUser}, accessToken: {userId: employeeUser},
@ -96,7 +97,8 @@ describe('ticket setDeleted()', () => {
expect(stowaway).toBeNull(); expect(stowaway).toBeNull();
expect(stowawayTicketState.code).toEqual('FIXING'); expect(stowawayTicketState.code).toEqual('FIXING');
await shipTicket.destroy(); // restores
await stowawayTicket.destroy(); await models.Ticket.destroyById(shipTicket.id);
await models.Ticket.destroyById(stowawayTicket.id);
}); });
}); });

View File

@ -1,18 +1,36 @@
const app = require('vn-loopback/server/server'); const app = require('vn-loopback/server/server');
const LoopBackContext = require('loopback-context');
describe('sale transferSales()', () => { describe('sale transferSales()', () => {
let createdTicketId; const userId = 101;
const activeCtx = {
accessToken: {userId: userId},
};
const ctx = {req: activeCtx};
let createdTicketsIds = []; let createdTicketsIds = [];
afterAll(async done => { beforeAll(() => {
createdTicketsIds.forEach(async createdTicketId => { spyOn(LoopBackContext, 'getCurrentContext').and.returnValue({
await app.models.Ticket.destroyById(createdTicketId); active: activeCtx
}); });
});
afterEach(async done => {
if (createdTicketsIds.length) {
try {
createdTicketsIds.forEach(async createdTicketId => {
await app.models.Ticket.destroyById(createdTicketId);
});
} catch (error) {
console.error(error);
}
}
done(); done();
}); });
it('should throw an error as the ticket is not editable', async() => { it('should throw an error as the ticket is not editable', async() => {
const ctx = {req: {accessToken: {userId: 101}}};
let error; let error;
const currentTicketId = 1; const currentTicketId = 1;
@ -29,7 +47,6 @@ describe('sale transferSales()', () => {
}); });
it('should throw an error if the receiving ticket is not editable', async() => { it('should throw an error if the receiving ticket is not editable', async() => {
const ctx = {req: {accessToken: {userId: 101}}};
let error; let error;
const currentTicketId = 16; const currentTicketId = 16;
@ -45,60 +62,41 @@ describe('sale transferSales()', () => {
expect(error).toBeDefined(); expect(error).toBeDefined();
}); });
it('should transfer the sales from one ticket to a new one', async() => { it('should transfer the sales from one ticket to a new one then send them back and delete the created ticket', async() => {
const ctx = {req: {accessToken: {userId: 101}}}; const formerTicketId = 11;
let currentTicket = await app.models.Ticket.findById(11); let createdTicketId = undefined;
let originalTicketSales = await app.models.Ticket.getSales(currentTicket.id);
salesToRestone = originalTicketSales;
expect(originalTicketSales.length).toEqual(2); let formerTicketSales = await app.models.Ticket.getSales(formerTicketId);
const currentTicketId = currentTicket.id; expect(formerTicketSales.length).toEqual(2);
const receiverTicketId = undefined;
let createdTicket = await app.models.Ticket.transferSales( let createdTicket = await app.models.Ticket.transferSales(
ctx, currentTicketId, receiverTicketId, originalTicketSales); ctx, formerTicketId, createdTicketId, formerTicketSales);
createdTicketId = createdTicket.id; createdTicketId = createdTicket.id;
createdTicketsIds.push(createdTicket.id); createdTicketsIds.push(createdTicketId);
originalTicketSales = await app.models.Ticket.getSales(currentTicket.id); formerTicketSales = await app.models.Ticket.getSales(formerTicketId);
receiverTicketSales = await app.models.Ticket.getSales(createdTicket.id); createdTicketSales = await app.models.Ticket.getSales(createdTicketId);
expect(originalTicketSales.length).toEqual(0); expect(formerTicketSales.length).toEqual(0);
expect(receiverTicketSales.length).toEqual(2);
});
it('should transfer back the sales and set the created ticket as deleted', async() => {
const ctx = {req: {accessToken: {userId: 101}}};
const currentTicket = await app.models.Ticket.findById(createdTicketId);
const receiverTicketId = 11;
let createdTicket = await app.models.Ticket.findById(createdTicketId);
let createdTicketSales = await app.models.Ticket.getSales(createdTicketId);
let receiverTicketSales = await app.models.Ticket.getSales(receiverTicketId);
const currentTicketId = currentTicket.id;
const sales = createdTicketSales;
expect(createdTicket.isDeleted).toBeFalsy();
expect(createdTicketSales.length).toEqual(2); expect(createdTicketSales.length).toEqual(2);
expect(receiverTicketSales.length).toEqual(0);
await app.models.Ticket.transferSales( await app.models.Ticket.transferSales(
ctx, currentTicketId, receiverTicketId, sales); ctx, createdTicketId, formerTicketId, createdTicketSales);
formerTicketSales = await app.models.Ticket.getSales(formerTicketId);
createdTicketSales = await app.models.Ticket.getSales(createdTicketId);
createdTicket = await app.models.Ticket.findById(createdTicketId); createdTicket = await app.models.Ticket.findById(createdTicketId);
createdTicketSales = await app.models.Ticket.getSales(createdTicketId);
receiverTicketSales = await app.models.Ticket.getSales(receiverTicketId);
expect(createdTicket.isDeleted).toBeTruthy(); expect(createdTicket.isDeleted).toBeTruthy();
expect(formerTicketSales.length).toEqual(2);
expect(createdTicketSales.length).toEqual(0); expect(createdTicketSales.length).toEqual(0);
expect(receiverTicketSales.length).toEqual(2);
}); });
describe('sale transferPartialSales()', () => { describe('sale transferPartialSales()', () => {
it('should throw an error in the quantity to transfer exceeds the amount from the original sale', async() => { it('should throw an error in the quantity to transfer exceeds the amount from the original sale', async() => {
const ctx = {req: {accessToken: {userId: 101}}};
let error; let error;
let currentTicket = await app.models.Ticket.findById(11); let currentTicket = await app.models.Ticket.findById(11);
let currentTicketSales = await app.models.Ticket.getSales(currentTicket.id); let currentTicketSales = await app.models.Ticket.getSales(currentTicket.id);
@ -119,44 +117,51 @@ describe('sale transferSales()', () => {
}); });
it('should transfer two sales to a new ticket but one shall be partial', async() => { it('should transfer two sales to a new ticket but one shall be partial', async() => {
const ctx = {req: {accessToken: {userId: 101}}}; const formerTicketId = 11;
const originalTicketId = 11; let createdTicketId = undefined;
const receiverTicketId = undefined;
let currentTicketSales = await app.models.Ticket.getSales(originalTicketId); let formerTicketSales = await app.models.Ticket.getSales(formerTicketId);
const originalPartialSaleId = currentTicketSales[0].id; const partialSaleId = formerTicketSales[0].id;
const originalCompleteSaleId = currentTicketSales[1].id; const completeSaleId = formerTicketSales[1].id;
let originalQuantity = currentTicketSales[0].quantity; let partialSaleTotalQuantity = formerTicketSales[0].quantity;
currentTicketSales[0].quantity = 1;
expect(partialSaleTotalQuantity).toEqual(15);
formerTicketSales[0].quantity = 1;
let createdTicket = await app.models.Ticket.transferSales( let createdTicket = await app.models.Ticket.transferSales(
ctx, originalTicketId, receiverTicketId, currentTicketSales); ctx, formerTicketId, createdTicketId, formerTicketSales);
createdTicketId = createdTicket.id; createdTicketId = createdTicket.id;
createdTicketsIds.push(createdTicket.id); createdTicketsIds.push(createdTicket.id);
currentTicketSales = await app.models.Ticket.getSales(originalTicketId); formerTicketSales = await app.models.Ticket.getSales(formerTicketId);
receiverTicketSales = await app.models.Ticket.getSales(createdTicketId); createdTicketSales = await app.models.Ticket.getSales(createdTicketId);
const [createdPartialSale] = receiverTicketSales.filter(sale => { const [createdPartialSale] = createdTicketSales.filter(sale => {
return sale.id != originalCompleteSaleId; return sale.id != completeSaleId;
}); });
expect(currentTicketSales.length).toEqual(1); expect(formerTicketSales.length).toEqual(1);
expect(currentTicketSales[0].quantity).toEqual(originalQuantity -= 1); expect(formerTicketSales[0].quantity).toEqual(partialSaleTotalQuantity - 1);
expect(receiverTicketSales.length).toEqual(2); expect(createdTicketSales.length).toEqual(2);
expect(createdPartialSale.quantity).toEqual(1); expect(createdPartialSale.quantity).toEqual(1);
let saleToRestore = await app.models.Sale.findById(originalPartialSaleId); let saleToRestore = await app.models.Sale.findById(partialSaleId);
await saleToRestore.updateAttribute('quantity', originalQuantity); await saleToRestore.updateAttribute('quantity', partialSaleTotalQuantity);
let saleToReturnToTicket = await app.models.Sale.findById(originalCompleteSaleId); let saleToReturnToTicket = await app.models.Sale.findById(completeSaleId);
await saleToReturnToTicket.updateAttribute('ticketFk', originalTicketId); await saleToReturnToTicket.updateAttribute('ticketFk', formerTicketId);
currentTicketSales = await app.models.Ticket.getSales(originalTicketId); formerTicketSales = await app.models.Ticket.getSales(formerTicketId);
expect(currentTicketSales.length).toEqual(2); const [returningPartialSale] = formerTicketSales.filter(sale => {
return sale.id == partialSaleId;
});
expect(returningPartialSale.quantity).toEqual(partialSaleTotalQuantity);
expect(formerTicketSales.length).toEqual(2);
}); });
}); });
}); });

View File

@ -7,21 +7,29 @@ describe('sale updateDiscount()', () => {
let salesPersonMana; let salesPersonMana;
beforeAll(async done => { beforeAll(async done => {
originalSale = await app.models.Sale.findById(originalSaleId); try {
let manaDiscount = await app.models.Component.findOne({where: {code: 'buyerDiscount'}}); originalSale = await app.models.Sale.findById(originalSaleId);
componentId = manaDiscount.id; let manaDiscount = await app.models.Component.findOne({where: {code: 'buyerDiscount'}});
componentId = manaDiscount.id;
let ticket = await app.models.Ticket.findById(originalSale.ticketFk); let ticket = await app.models.Ticket.findById(originalSale.ticketFk);
let client = await app.models.Client.findById(ticket.clientFk); let client = await app.models.Client.findById(ticket.clientFk);
salesPersonMana = await app.models.WorkerMana.findById(client.salesPersonFk); salesPersonMana = await app.models.WorkerMana.findById(client.salesPersonFk);
} catch (error) {
console.error(error);
}
done(); done();
}); });
afterAll(async done => { afterAll(async done => {
await originalSale.save(); try {
await app.models.SaleComponent.updateAll({componentFk: componentId, saleFk: originalSaleId}, {value: 0}); await originalSale.save();
await salesPersonMana.save(); await app.models.SaleComponent.updateAll({componentFk: componentId, saleFk: originalSaleId}, {value: 0});
await salesPersonMana.save();
} catch (error) {
console.error(error);
}
done(); done();
}); });

View File

@ -1,4 +1,11 @@
const app = require('vn-loopback/server/server'); const app = require('vn-loopback/server/server');
const LoopBackContext = require('loopback-context');
const userId = 9;
const activeCtx = {
accessToken: {userId: userId},
};
const ctx = {req: activeCtx};
describe('ticket updateEditableTicket()', () => { describe('ticket updateEditableTicket()', () => {
const validTicketId = 12; const validTicketId = 12;
@ -7,7 +14,6 @@ describe('ticket updateEditableTicket()', () => {
const originalData = {addressFk: 123}; const originalData = {addressFk: 123};
afterAll(async done => { afterAll(async done => {
let ctx = {req: {accessToken: {userId: 9}}};
await app.models.Ticket.updateEditableTicket(ctx, validTicketId, originalData); await app.models.Ticket.updateEditableTicket(ctx, validTicketId, originalData);
done(); done();
@ -15,7 +21,6 @@ describe('ticket updateEditableTicket()', () => {
it('should now throw an error if the ticket is not editable', async() => { it('should now throw an error if the ticket is not editable', async() => {
let error; let error;
let ctx = {req: {accessToken: {userId: 9}}};
await app.models.Ticket.updateEditableTicket(ctx, invalidTicketId, data).catch(e => { await app.models.Ticket.updateEditableTicket(ctx, invalidTicketId, data).catch(e => {
error = e; error = e;
@ -27,8 +32,9 @@ describe('ticket updateEditableTicket()', () => {
}); });
it('should edit the ticket address', async() => { it('should edit the ticket address', async() => {
let ctx = {req: {accessToken: {userId: 9}}}; spyOn(LoopBackContext, 'getCurrentContext').and.returnValue({
active: activeCtx
});
await app.models.Ticket.updateEditableTicket(ctx, validTicketId, data); await app.models.Ticket.updateEditableTicket(ctx, validTicketId, data);
let updatedTicket = await app.models.Ticket.findById(validTicketId); let updatedTicket = await app.models.Ticket.findById(validTicketId);

View File

@ -1,4 +1,6 @@
const LoopBackContext = require('loopback-context');
module.exports = Self => { module.exports = Self => {
require('../methods/ticket/changeWorker')(Self); require('../methods/ticket/changeWorker')(Self);
require('../methods/ticket/getVolume')(Self); require('../methods/ticket/getVolume')(Self);
@ -35,13 +37,16 @@ module.exports = Self => {
require('../methods/ticket/getComponentsSum')(Self); require('../methods/ticket/getComponentsSum')(Self);
Self.observe('before save', async function(ctx) { Self.observe('before save', async function(ctx) {
const loopBackContext = LoopBackContext.getCurrentContext();
const httpCtx = loopBackContext.active;
if (ctx.isNewInstance) return; if (ctx.isNewInstance) return;
let changes = ctx.data || ctx.instance; let changes = ctx.data || ctx.instance;
if (changes.routeFk === null && ctx.currentInstance.routeFk != null) { if (changes.routeFk === null && ctx.currentInstance.routeFk != null) {
let instance = JSON.parse(JSON.stringify(ctx.currentInstance)); let instance = JSON.parse(JSON.stringify(ctx.currentInstance));
let userId = ctx.options.accessToken.userId; let userId = httpCtx.accessToken.userId;
let logRecord = { let logRecord = {
originFk: ctx.currentInstance.routeFk, originFk: ctx.currentInstance.routeFk,
userFk: userId, userFk: userId,

View File

@ -56,8 +56,16 @@ class Controller extends Component {
set warehouseId(value) { set warehouseId(value) {
if (value != this.ticket.warehouseFk) { if (value != this.ticket.warehouseFk) {
this.ticket.warehouseFk = value; this.ticket.warehouseFk = value;
this.ticket.agencyModeFk = null;
this.ticket.zoneFk = null; this.getShipped({
landed: this.ticket.landed,
addressFk: this.ticket.addressFk,
agencyModeFk: this.ticket.agencyModeFk,
warehouseFk: value
}).then(() => {
if (this.zoneId == null)
this.ticket.agencyModeFk = null;
});
} }
} }
@ -177,8 +185,8 @@ class Controller extends Component {
); );
} }
let query = `tickets/${this.ticket.id}/priceDifference`; const query = `tickets/${this.ticket.id}/priceDifference`;
let params = { const params = {
landed: this.ticket.landed, landed: this.ticket.landed,
addressId: this.ticket.addressFk, addressId: this.ticket.addressFk,
agencyModeId: this.ticket.agencyModeFk, agencyModeId: this.ticket.agencyModeFk,
@ -202,9 +210,13 @@ class Controller extends Component {
* Returns a landing date * Returns a landing date
*/ */
getLanded(params) { getLanded(params) {
const validParams = this.shipped && this.addressId
&& this.agencyModeId && this.warehouseId;
if (!validParams) return this.$q.resolve();
this.ticket.zoneFk = null; this.ticket.zoneFk = null;
const query = `Agencies/getLanded`; const query = `Agencies/getLanded`;
this.$http.get(query, {params}).then(res => { return this.$http.get(query, {params}).then(res => {
if (res.data) { if (res.data) {
this.ticket.zoneFk = res.data.zoneFk; this.ticket.zoneFk = res.data.zoneFk;
this.ticket.landed = res.data.landed; this.ticket.landed = res.data.landed;
@ -221,9 +233,13 @@ class Controller extends Component {
* Returns a shipment date * Returns a shipment date
*/ */
getShipped(params) { getShipped(params) {
const validParams = this.landed && this.addressId
&& this.agencyModeId && this.warehouseId;
if (!validParams) return this.$q.resolve();
this.ticket.zoneFk = null; this.ticket.zoneFk = null;
const query = `Agencies/getShipped`; const query = `Agencies/getShipped`;
this.$http.get(query, {params}).then(res => { return this.$http.get(query, {params}).then(res => {
if (res.data) { if (res.data) {
this.ticket.zoneFk = res.data.zoneFk; this.ticket.zoneFk = res.data.zoneFk;
this.ticket.landed = params.landed; this.ticket.landed = params.landed;

View File

@ -323,6 +323,7 @@ describe('Ticket', () => {
warehouseFk: 1 warehouseFk: 1
}; };
const serializedParams = $httpParamSerializer(params); const serializedParams = $httpParamSerializer(params);
controller.ticket.shipped = shipped;
$httpBackend.expect('GET', `Agencies/getLanded?${serializedParams}`).respond(200, expectedResult); $httpBackend.expect('GET', `Agencies/getLanded?${serializedParams}`).respond(200, expectedResult);
controller.getLanded(params); controller.getLanded(params);
@ -344,6 +345,7 @@ describe('Ticket', () => {
warehouseFk: 1 warehouseFk: 1
}; };
const serializedParams = $httpParamSerializer(params); const serializedParams = $httpParamSerializer(params);
controller.ticket.landed = landed;
$httpBackend.expect('GET', `Agencies/getShipped?${serializedParams}`).respond(200, expectedResult); $httpBackend.expect('GET', `Agencies/getShipped?${serializedParams}`).respond(200, expectedResult);
controller.getShipped(params); controller.getShipped(params);

View File

@ -18,7 +18,7 @@ describe('Termograph createThermograph()', () => {
done(); done();
}); });
it(`should create a thermograph which is saved in both thermograph and travelThermograph`, async() => { it(`should be able to create just once a thermograph which is saved in both thermograph and travelThermograph`, async() => {
let createdTravelThermograpth = await models.TravelThermograph.findOne({where: {thermographFk: thermographId}}); let createdTravelThermograpth = await models.TravelThermograph.findOne({where: {thermographFk: thermographId}});
expect(createdTravelThermograpth).toBeNull(); expect(createdTravelThermograpth).toBeNull();
@ -32,9 +32,7 @@ describe('Termograph createThermograph()', () => {
expect(createdTravelThermograpth.warehouseFk).toEqual(warehouseId); expect(createdTravelThermograpth.warehouseFk).toEqual(warehouseId);
expect(createdTravelThermograpth.temperature).toEqual(temperature); expect(createdTravelThermograpth.temperature).toEqual(temperature);
});
it(`should not be able to created duplicated entries`, async() => {
let error; let error;
try { try {

View File

@ -1,12 +1,37 @@
const app = require('vn-loopback/server/server'); const app = require('vn-loopback/server/server');
const LoopBackContext = require('loopback-context');
describe('workerTimeControl add/delete timeEntry()', () => {
const HHRRId = 37;
const teamBossId = 13;
const employeeId = 1;
let activeCtx = {
accessToken: {userId: 50},
};
let ctx = {req: activeCtx};
describe('workerTimeControl addTimeEntry()', () => {
let timeEntry; let timeEntry;
let createdTimeEntry; let createdTimeEntry;
afterEach(async() => {
if (createdTimeEntry) {
try {
await app.models.WorkerTimeControl.destroyById(createdTimeEntry.id);
} catch (error) {
console.error(error);
}
}
});
beforeAll(() => {
spyOn(LoopBackContext, 'getCurrentContext').and.returnValue({
active: activeCtx
});
});
it('should fail to add a time entry if the target user is not a subordinate', async() => { it('should fail to add a time entry if the target user is not a subordinate', async() => {
activeCtx.accessToken.userId = employeeId;
let error; let error;
let ctx = {req: {accessToken: {userId: 1}}};
let data = { let data = {
workerFk: 2, workerFk: 2,
timed: new Date() timed: new Date()
@ -24,8 +49,8 @@ describe('workerTimeControl addTimeEntry()', () => {
}); });
it('should fail to add if the current and the target user are the same and is not team boss', async() => { it('should fail to add if the current and the target user are the same and is not team boss', async() => {
activeCtx.accessToken.userId = employeeId;
let error; let error;
let ctx = {req: {accessToken: {userId: 1}}};
let data = { let data = {
workerFk: 1, workerFk: 1,
timed: new Date() timed: new Date()
@ -42,12 +67,11 @@ describe('workerTimeControl addTimeEntry()', () => {
expect(error.message).toBe(`You don't have enough privileges`); expect(error.message).toBe(`You don't have enough privileges`);
}); });
it('should add if the current user is team boss and the target user is a subordinate', async() => { it('should add if the current user is team boss and the target user is a himself', async() => {
todayAtSix = new Date(); activeCtx.accessToken.userId = teamBossId;
let todayAtSix = new Date();
todayAtSix.setHours(18, 30, 0, 0); todayAtSix.setHours(18, 30, 0, 0);
let teamBossId = 13;
let ctx = {req: {accessToken: {userId: teamBossId}}};
let data = { let data = {
workerFk: teamBossId, workerFk: teamBossId,
timed: todayAtSix timed: todayAtSix
@ -60,10 +84,20 @@ describe('workerTimeControl addTimeEntry()', () => {
expect(createdTimeEntry).toBeDefined(); expect(createdTimeEntry).toBeDefined();
}); });
it('should try but fail to delete the created time entry for the team boss as team boss', async() => { it('should try but fail to delete his own time entry', async() => {
activeCtx.accessToken.userId = teamBossId;
let error; let error;
let teamBossId = 13; let todayAtSeven = new Date();
let ctx = {req: {accessToken: {userId: teamBossId}}}; todayAtSeven.setHours(19, 30, 0, 0);
let data = {
workerFk: teamBossId,
timed: todayAtSeven
};
timeEntry = await app.models.WorkerTimeControl.addTimeEntry(ctx, data);
createdTimeEntry = await app.models.WorkerTimeControl.findById(timeEntry.id);
try { try {
await app.models.WorkerTimeControl.deleteTimeEntry(ctx, createdTimeEntry.id); await app.models.WorkerTimeControl.deleteTimeEntry(ctx, createdTimeEntry.id);
@ -77,13 +111,23 @@ describe('workerTimeControl addTimeEntry()', () => {
}); });
it('should delete the created time entry for the team boss as HHRR', async() => { it('should delete the created time entry for the team boss as HHRR', async() => {
let HHRRId = 37; activeCtx.accessToken.userId = HHRRId;
let ctx = {req: {accessToken: {userId: HHRRId}}};
let todayAtFive = new Date();
todayAtFive.setHours(17, 30, 0, 0);
let data = {
workerFk: teamBossId,
timed: todayAtFive
};
timeEntry = await app.models.WorkerTimeControl.addTimeEntry(ctx, data);
createdTimeEntry = await app.models.WorkerTimeControl.findById(timeEntry.id);
expect(createdTimeEntry).toBeDefined(); expect(createdTimeEntry).toBeDefined();
ctx.req.accessToken.userId = HHRRId; await app.models.WorkerTimeControl.deleteTimeEntry(ctx, createdTimeEntry.id);
await app.models.WorkerTimeControl.deleteTimeEntry(ctx, timeEntry.id);
createdTimeEntry = await app.models.WorkerTimeControl.findById(timeEntry.id); createdTimeEntry = await app.models.WorkerTimeControl.findById(timeEntry.id);

View File

@ -37,9 +37,12 @@ module.exports = Self => {
Self.getLanded = async(ctx, shipped, addressFk, agencyModeFk, warehouseFk) => { Self.getLanded = async(ctx, shipped, addressFk, agencyModeFk, warehouseFk) => {
const userId = ctx.req.accessToken.userId; const userId = ctx.req.accessToken.userId;
const models = Self.app.models; const models = Self.app.models;
const isProductionBoss = await models.Account.hasRole(userId, 'productionBoss'); const roles = await models.Account.getRoles(userId);
const canSeeExpired = roles.filter(role =>
role == 'productionBoss' || role == 'administrative'
);
let showExpired = false; let showExpired = false;
if (isProductionBoss) showExpired = true; if (canSeeExpired.length) showExpired = true;
let stmts = []; let stmts = [];
stmts.push(new ParameterizedSQL( stmts.push(new ParameterizedSQL(

View File

@ -49,7 +49,7 @@ module.exports = Self => {
await models.Ticket.rawSql('UPDATE ticket SET zoneFk = NULL WHERE zoneFk = ?', [id], options); await models.Ticket.rawSql('UPDATE ticket SET zoneFk = NULL WHERE zoneFk = ?', [id], options);
ticketList.forEach(ticket => { for (ticket of ticketList) {
if (ticket.ticketState().alertLevel == 0) { if (ticket.ticketState().alertLevel == 0) {
promises.push(models.TicketTracking.create({ promises.push(models.TicketTracking.create({
ticketFk: ticket.id, ticketFk: ticket.id,
@ -57,7 +57,7 @@ module.exports = Self => {
workerFk: worker.id workerFk: worker.id
}, options)); }, options));
} }
}); }
await Promise.all(promises); await Promise.all(promises);
await models.Zone.destroyById(id, options); await models.Zone.destroyById(id, options);
await tx.commit(); await tx.commit();

View File

@ -32,9 +32,12 @@ module.exports = Self => {
&& where.agencyModeFk && where.warehouseFk; && where.agencyModeFk && where.warehouseFk;
if (filterByAvailability) { if (filterByAvailability) {
const isProductionBoss = await models.Account.hasRole(userId, 'productionBoss'); const roles = await models.Account.getRoles(userId);
const canSeeExpired = roles.filter(role =>
role == 'productionBoss' || role == 'administrative'
);
let showExpired = false; let showExpired = false;
if (isProductionBoss) showExpired = true; if (canSeeExpired.length) showExpired = true;
stmt = new ParameterizedSQL(`CALL vn.zone_getLanded(?, ?, ?, ?, ?)`, [ stmt = new ParameterizedSQL(`CALL vn.zone_getLanded(?, ?, ?, ?, ?)`, [
where.shipped, where.shipped,

View File

@ -1,62 +1,81 @@
const app = require('vn-loopback/server/server'); const app = require('vn-loopback/server/server');
const LoopBackContext = require('loopback-context');
describe('zone deletezone()', () => { describe('zone deletezone()', () => {
const userId = 9;
const activeCtx = {
accessToken: {userId: userId},
};
const ctx = {req: activeCtx};
let zoneId = 9; let zoneId = 9;
let originalZoneTickets;
let originalZone; let originalZone;
let originalZoneWarehouses; let originalZoneWarehouses;
let originalTickets;
let ticketIDs;
let originalZoneIncluded; let originalZoneIncluded;
let ticketsId; let originalTicketStates;
let originalTicketsState;
beforeAll(async done => { beforeAll(async done => {
originalZone = await app.models.Zone.findById(zoneId); spyOn(LoopBackContext, 'getCurrentContext').and.returnValue({
originalZoneTickets = await app.models.Ticket.find({where: {zoneFk: zoneId}}); active: activeCtx
originalZoneWarehouses = await app.models.ZoneWarehouse.findById(zoneId); });
originalZoneIncluded = await app.models.ZoneIncluded.find({where: {zoneFk: zoneId}}); try {
ticketsId = originalZoneTickets.map(originalZoneTickets => originalZoneTickets.id); originalZone = await app.models.Zone.findById(zoneId);
originalTicketsState = await app.models.TicketState.find({where: { originalZoneWarehouses = await app.models.ZoneWarehouse.findById(zoneId);
ticketFk: {inq: ticketsId}, originalTickets = await app.models.Ticket.find({
code: 'FIXING'}}); where: {
zoneFk: zoneId
}
});
ticketIDs = originalTickets.map(ticket => ticket.id);
originalZoneIncluded = await app.models.ZoneIncluded.find({where: {zoneFk: zoneId}});
originalTicketStates = await app.models.TicketState.find({where: {
ticketFk: {inq: ticketIDs},
code: 'FIXING'}});
} catch (error) {
console.error(error);
}
done(); done();
}); });
afterAll(async done => { afterAll(async done => {
await originalZone.save(); try {
await app.models.ZoneWarehouse.create(originalZoneWarehouses); await originalZone.save();
await app.models.ZoneWarehouse.create(originalZoneWarehouses);
for (ticket of originalZoneTickets) for (ticket of originalTickets)
await ticket.updateAttributes({zoneFk: zoneId}); await ticket.updateAttributes({zoneFk: zoneId});
for (zoneIncluded of originalZoneIncluded) for (zoneIncluded of originalZoneIncluded)
await zoneIncluded.save(); await zoneIncluded.save();
const fixingStateId = 1; const fixingStateId = 1;
const ticketIds = originalZoneTickets.map(ticket => ticket.id); const trackings = await app.models.TicketTracking.find({where: {
const trackings = await app.models.TicketTracking.find({where: { ticketFk: {inq: ticketIDs},
ticketFk: {inq: ticketIds}, stateFk: fixingStateId}});
stateFk: fixingStateId}});
for (let tracking of trackings) for (let tracking of trackings)
await app.models.TicketTracking.destroyById(tracking.id); await app.models.TicketTracking.destroyById(tracking.id);
} catch (error) {
console.error(error);
}
done(); done();
}); });
it('should delete a zone and update their tickets', async() => { it('should delete a zone and update their tickets', async() => {
const ctx = {req: {accessToken: {userId: 9}}};
await app.models.Zone.deleteZone(ctx, zoneId); await app.models.Zone.deleteZone(ctx, zoneId);
let updatedZone = await app.models.Zone.findById(zoneId); const updatedZone = await app.models.Zone.findById(zoneId);
let zoneUpdatedTicket = await app.models.Ticket.findById(originalZoneTickets[0].id); const anUpdatedTicket = await app.models.Ticket.findById(ticketIDs[0]);
let ticketsId = originalZoneTickets.map(originalZoneTickets => originalZoneTickets.id);
let updatedTicketState = await app.models.TicketState.find({where: { const updatedTicketStates = await app.models.TicketState.find({where: {
ticketFk: {inq: ticketsId}, ticketFk: {inq: ticketIDs},
code: 'FIXING'}}); code: 'FIXING'}});
expect(updatedZone).toBeNull(); expect(updatedZone).toBeNull();
expect(zoneUpdatedTicket.zoneFk).not.toBe(zoneId); expect(anUpdatedTicket.zoneFk).toBeNull();
expect(originalTicketsState.length).not.toBeGreaterThan(updatedTicketState.length); expect(updatedTicketStates.length).toBeGreaterThan(originalTicketStates.length);
}); });
}); });

View File

@ -3,6 +3,7 @@ const Component = require(`${appPath}/core/component`);
const reportHeader = new Component('report-header'); const reportHeader = new Component('report-header');
const reportFooter = new Component('report-footer'); const reportFooter = new Component('report-footer');
const md5 = require('md5'); const md5 = require('md5');
const fs = require('fs-extra');
module.exports = { module.exports = {
name: 'delivery-note', name: 'delivery-note',
@ -24,11 +25,14 @@ module.exports = {
}, },
computed: { computed: {
dmsPath() { dmsPath() {
const pathHash = md5(this.signature.id.toString()).substring(0, 3); if (!this.signature) return;
const hostPath = `file:///${config.storage.root}/${pathHash}`;
if (this.signature && this.signature.id) const hash = md5(this.signature.id.toString()).substring(0, 3);
return `${hostPath}/${this.signature.id}.png`; const file = `${config.storage.root}/${hash}/${this.signature.id}.png`;
const src = fs.readFileSync(file);
const base64 = Buffer.from(src, 'utf8').toString('base64');
return `data:image/png;base64, ${base64}`;
}, },
serviceTotal() { serviceTotal() {
let total = 0.00; let total = 0.00;