3092-module_transactions #740

Merged
joan merged 41 commits from 3092-module_transactions into dev 2021-10-18 07:42:24 +00:00
114 changed files with 44364 additions and 1570 deletions

View File

@ -11,7 +11,6 @@ Required applications.
* Node.js = 14.x LTS * Node.js = 14.x LTS
* Docker * Docker
* Git * Git
* Docker
You will need to install globally the following items. You will need to install globally the following items.
``` ```

View File

@ -89,6 +89,7 @@ module.exports = Self => {
await uploadNewFile(ctx, dms, myOptions); await uploadNewFile(ctx, dms, myOptions);
if (tx) await tx.commit(); if (tx) await tx.commit();
return dms; return dms;
} catch (e) { } catch (e) {
if (tx) await tx.rollback(); if (tx) await tx.rollback();

View File

@ -96,6 +96,7 @@ module.exports = Self => {
} }
if (tx) await tx.commit(); if (tx) await tx.commit();
return addedDms; return addedDms;
} catch (e) { } catch (e) {
if (tx) await tx.rollback(); if (tx) await tx.rollback();

View File

@ -151,6 +151,7 @@ module.exports = Self => {
await fs.unlink(srcFilePath); await fs.unlink(srcFilePath);
await tx.commit(); await tx.commit();
return newImage; return newImage;
} catch (e) { } catch (e) {
await tx.rollback(); await tx.rollback();

View File

@ -108,7 +108,7 @@ let actions = {
}, },
getState: async function() { getState: async function() {
return await this.evaluate(() => { return this.evaluate(() => {
let $state = angular.element(document.body).injector().get('$state'); let $state = angular.element(document.body).injector().get('$state');
return $state.current.name; return $state.current.name;
}); });
@ -194,7 +194,7 @@ let actions = {
}, },
getProperty: async function(selector, property) { getProperty: async function(selector, property) {
return await this.evaluate((selector, property) => { return this.evaluate((selector, property) => {
return document.querySelector(selector)[property].replace(/\s+/g, ' ').trim(); return document.querySelector(selector)[property].replace(/\s+/g, ' ').trim();
}, selector, property); }, selector, property);
}, },
@ -202,7 +202,7 @@ let actions = {
getClassName: async function(selector) { getClassName: async function(selector) {
const element = await this.$(selector); const element = await this.$(selector);
const handle = await element.getProperty('className'); const handle = await element.getProperty('className');
return await handle.jsonValue(); return handle.jsonValue();
}, },
waitPropertyLength: async function(selector, property, minLength) { waitPropertyLength: async function(selector, property, minLength) {
@ -210,7 +210,7 @@ let actions = {
const element = document.querySelector(selector); const element = document.querySelector(selector);
return element && element[property] != null && element[property] !== '' && element[property].length >= minLength; return element && element[property] != null && element[property] !== '' && element[property].length >= minLength;
}, {}, selector, property, minLength); }, {}, selector, property, minLength);
return await this.getProperty(selector, property); return this.getProperty(selector, property);
}, },
expectPropertyValue: async function(selector, property, value) { expectPropertyValue: async function(selector, property, value) {
@ -219,7 +219,7 @@ let actions = {
builtSelector = await this.selectorFormater(selector); builtSelector = await this.selectorFormater(selector);
try { try {
return await this.waitForFunction((selector, property, value) => { return this.waitForFunction((selector, property, value) => {
const element = document.querySelector(selector); const element = document.querySelector(selector);
return element[property] == value; return element[property] == value;
}, {}, builtSelector, property, value); }, {}, builtSelector, property, value);
@ -239,7 +239,7 @@ let actions = {
return element && element[property] != null && element[property] !== ''; return element && element[property] != null && element[property] !== '';
}, {}, builtSelector, property); }, {}, builtSelector, property);
return await this.getProperty(builtSelector, property); return this.getProperty(builtSelector, property);
} catch (error) { } catch (error) {
throw new Error(`couldn't get property: ${property} of ${builtSelector}, ${error}`); throw new Error(`couldn't get property: ${property} of ${builtSelector}, ${error}`);
} }
@ -261,7 +261,7 @@ let actions = {
await this.waitForSelector(selector); await this.waitForSelector(selector);
await this.waitForFunction(checkVisibility, {}, selector); await this.waitForFunction(checkVisibility, {}, selector);
return await this.click(selector); return this.click(selector);
}, },
writeOnEditableTD: async function(selector, text) { writeOnEditableTD: async function(selector, text) {
@ -274,7 +274,7 @@ let actions = {
focusElement: async function(selector) { focusElement: async function(selector) {
await this.waitForSelector(selector); await this.waitForSelector(selector);
return await this.evaluate(selector => { return this.evaluate(selector => {
let element = document.querySelector(selector); let element = document.querySelector(selector);
element.focus(); element.focus();
}, selector); }, selector);
@ -282,19 +282,19 @@ let actions = {
isVisible: async function(selector) { isVisible: async function(selector) {
await this.waitForSelector(selector); await this.waitForSelector(selector);
return await this.evaluate(checkVisibility, selector); return this.evaluate(checkVisibility, selector);
}, },
waitImgLoad: async function(selector) { waitImgLoad: async function(selector) {
await this.waitForSelector(selector); await this.waitForSelector(selector);
return await this.waitForFunction(selector => { return this.waitForFunction(selector => {
const imageReady = document.querySelector(selector).complete; const imageReady = document.querySelector(selector).complete;
return imageReady; return imageReady;
}, {}, selector); }, {}, selector);
}, },
countElement: async function(selector) { countElement: async function(selector) {
return await this.evaluate(selector => { return this.evaluate(selector => {
return document.querySelectorAll(selector).length; return document.querySelectorAll(selector).length;
}, selector); }, selector);
}, },
@ -312,7 +312,7 @@ let actions = {
waitForClassNotPresent: async function(selector, className) { waitForClassNotPresent: async function(selector, className) {
await this.waitForSelector(selector); await this.waitForSelector(selector);
return await this.waitForFunction((selector, className) => { return this.waitForFunction((selector, className) => {
if (!document.querySelector(selector).classList.contains(className)) if (!document.querySelector(selector).classList.contains(className))
return true; return true;
}, {}, selector, className); }, {}, selector, className);
@ -320,7 +320,7 @@ let actions = {
waitForClassPresent: async function(selector, className) { waitForClassPresent: async function(selector, className) {
await this.waitForSelector(selector); await this.waitForSelector(selector);
return await this.waitForFunction((elementSelector, targetClass) => { return this.waitForFunction((elementSelector, targetClass) => {
if (document.querySelector(elementSelector).classList.contains(targetClass)) if (document.querySelector(elementSelector).classList.contains(targetClass))
return true; return true;
}, {}, selector, className); }, {}, selector, className);
@ -387,13 +387,13 @@ let actions = {
const innerText = document.querySelector(selector).innerText; const innerText = document.querySelector(selector).innerText;
return innerText != null && innerText != ''; return innerText != null && innerText != '';
}, {}, selector); }, {}, selector);
return await this.evaluate(selector => { return this.evaluate(selector => {
return document.querySelector(selector).innerText; return document.querySelector(selector).innerText;
}, selector); }, selector);
}, },
waitForEmptyInnerText: async function(selector) { waitForEmptyInnerText: async function(selector) {
return await this.waitFunction(selector => { return this.waitFunction(selector => {
return document.querySelector(selector).innerText == ''; return document.querySelector(selector).innerText == '';
}, selector); }, selector);
}, },
@ -521,7 +521,7 @@ let actions = {
checkboxState: async function(selector) { checkboxState: async function(selector) {
await this.waitForSelector(selector); await this.waitForSelector(selector);
return await this.evaluate(selector => { return this.evaluate(selector => {
let checkbox = document.querySelector(selector); let checkbox = document.querySelector(selector);
switch (checkbox.$ctrl.field) { switch (checkbox.$ctrl.field) {
case null: case null:
@ -536,14 +536,14 @@ let actions = {
isDisabled: async function(selector) { isDisabled: async function(selector) {
await this.waitForSelector(selector); await this.waitForSelector(selector);
return await this.evaluate(selector => { return this.evaluate(selector => {
let element = document.querySelector(selector); let element = document.querySelector(selector);
return element.$ctrl.disabled; return element.$ctrl.disabled;
}, selector); }, selector);
}, },
waitForStylePresent: async function(selector, property, value) { waitForStylePresent: async function(selector, property, value) {
return await this.waitForFunction((selector, property, value) => { return this.waitForFunction((selector, property, value) => {
const element = document.querySelector(selector); const element = document.querySelector(selector);
return element.style[property] == value; return element.style[property] == value;
}, {}, selector, property, value); }, {}, selector, property, value);
@ -631,7 +631,7 @@ export function extendPage(page) {
for (let name in actions) { for (let name in actions) {
page[name] = async(...args) => { page[name] = async(...args) => {
try { try {
return await actions[name].apply(page, args); return actions[name].apply(page, args);
} catch (err) { } catch (err) {
let stringArgs = args let stringArgs = args
.map(i => typeof i == 'function' ? 'Function' : i) .map(i => typeof i == 'function' ? 'Function' : i)

View File

@ -5,11 +5,16 @@ import {url as defaultURL} from './config';
export async function getBrowser() { export async function getBrowser() {
const args = [ const args = [
`--no-sandbox`, '--no-sandbox',
`--window-size=${ 1920 },${ 1080 }` `--window-size=${ 1920 },${ 1080 }`,
'--single-process',
'--no-zygote'
// '--disable-dev-shm-usage'
// '--full-memory-crash-report',
// '--unlimited-storage'
]; ];
let env = process.env; const env = process.env;
if (env.E2E_DEBUG) { if (env.E2E_DEBUG) {
args.push('--auto-open-devtools-for-tabs'); args.push('--auto-open-devtools-for-tabs');
@ -22,6 +27,9 @@ export async function getBrowser() {
defaultViewport: null, defaultViewport: null,
headless: headless, headless: headless,
slowMo: 0, // slow down by ms slowMo: 0, // slow down by ms
// ignoreDefaultArgs: ['--disable-extensions'],
// executablePath: '/usr/bin/google-chrome-stable',
// executablePath: '/usr/bin/firefox-developer-edition',
}); });
let page = (await browser.pages())[0]; let page = (await browser.pages())[0];
@ -38,7 +46,7 @@ export async function getBrowser() {
}); });
}); });
page = extendPage(page); page = extendPage(page);
page.setDefaultTimeout(4000); page.setDefaultTimeout(5000);
await page.goto(defaultURL, {waitUntil: 'load'}); await page.goto(defaultURL, {waitUntil: 'load'});
return {page, close: browser.close.bind(browser)}; return {page, close: browser.close.bind(browser)};
} }

View File

@ -544,7 +544,7 @@ export default {
}, },
ticketSales: { ticketSales: {
setOk: 'vn-ticket-sale vn-tool-bar > vn-button[label="Ok"] > button', setOk: 'vn-ticket-sale vn-tool-bar > vn-button[label="Ok"] > button',
saleLine: 'vn-table div > vn-tbody > vn-tr', saleLine: 'vn-table div > vn-tbody > vn-tr vn-check',
saleDescriptorPopover: '.vn-popover.shown vn-item-descriptor', saleDescriptorPopover: '.vn-popover.shown vn-item-descriptor',
saleDescriptorPopoverSummaryButton: '.vn-popover.shown vn-item-descriptor a[ui-sref="item.card.summary({id: $ctrl.descriptor.id})"]', saleDescriptorPopoverSummaryButton: '.vn-popover.shown vn-item-descriptor a[ui-sref="item.card.summary({id: $ctrl.descriptor.id})"]',
newItemButton: 'vn-ticket-sale vn-card vn-icon-button[icon="add_circle"]', newItemButton: 'vn-ticket-sale vn-card vn-icon-button[icon="add_circle"]',

View File

@ -107,6 +107,7 @@ describe('Item regularize path', () => {
}); });
it('should regularize the item once more', async() => { it('should regularize the item once more', async() => {
await page.waitForTimeout(1000); // initialization of functionality takes about 1000ms to work
await page.waitToClick(selectors.itemDescriptor.moreMenu); await page.waitToClick(selectors.itemDescriptor.moreMenu);
await page.waitToClick(selectors.itemDescriptor.moreMenuRegularizeButton); await page.waitToClick(selectors.itemDescriptor.moreMenuRegularizeButton);
await page.write(selectors.itemDescriptor.regularizeQuantity, '100'); await page.write(selectors.itemDescriptor.regularizeQuantity, '100');

View File

@ -196,6 +196,7 @@ describe('Ticket Edit sale path', () => {
}); });
it('should select the third sale and create a claim of it', async() => { it('should select the third sale and create a claim of it', async() => {
await page.waitForTimeout(1000); // initialization of functionality takes about 1000ms to work
await page.waitToClick(selectors.ticketSales.thirdSaleCheckbox); await page.waitToClick(selectors.ticketSales.thirdSaleCheckbox);
await page.waitToClick(selectors.ticketSales.moreMenu); await page.waitToClick(selectors.ticketSales.moreMenu);
await page.waitToClick(selectors.ticketSales.moreMenuCreateClaim); await page.waitToClick(selectors.ticketSales.moreMenuCreateClaim);
@ -340,9 +341,10 @@ describe('Ticket Edit sale path', () => {
}); });
it('should confirm the new ticket received the line', async() => { it('should confirm the new ticket received the line', async() => {
const expectedLines = 1;
const result = await page.countElement(selectors.ticketSales.saleLine); const result = await page.countElement(selectors.ticketSales.saleLine);
expect(result).toEqual(1); expect(result).toEqual(expectedLines);
}); });
it('should check the first sale reserved icon isnt visible', async() => { it('should check the first sale reserved icon isnt visible', async() => {
@ -353,6 +355,7 @@ describe('Ticket Edit sale path', () => {
it('should mark the first sale as reserved', async() => { it('should mark the first sale as reserved', async() => {
await page.waitToClick(selectors.ticketSales.firstSaleCheckbox); await page.waitToClick(selectors.ticketSales.firstSaleCheckbox);
await page.waitForTimeout(1000); // initialization of functionality takes about 1000ms to work
await page.waitToClick(selectors.ticketSales.moreMenu); await page.waitToClick(selectors.ticketSales.moreMenu);
await page.waitToClick(selectors.ticketSales.moreMenuReserve); await page.waitToClick(selectors.ticketSales.moreMenuReserve);
await page.closePopup(); await page.closePopup();
@ -363,6 +366,7 @@ describe('Ticket Edit sale path', () => {
}); });
it('should unmark the first sale as reserved', async() => { it('should unmark the first sale as reserved', async() => {
await page.waitForTimeout(1000); // initialization of functionality takes about 1000ms to work
await page.waitToClick(selectors.ticketSales.moreMenu); await page.waitToClick(selectors.ticketSales.moreMenu);
await page.waitToClick(selectors.ticketSales.moreMenuUnmarkReseved); await page.waitToClick(selectors.ticketSales.moreMenuUnmarkReseved);
await page.waitForClassPresent(selectors.ticketSales.firstSaleReservedIcon, 'ng-hide'); await page.waitForClassPresent(selectors.ticketSales.firstSaleReservedIcon, 'ng-hide');

View File

@ -28,6 +28,7 @@ describe('Ticket descriptor path', () => {
}); });
it('should add the ticket to thursday turn using the descriptor more menu', async() => { it('should add the ticket to thursday turn using the descriptor more menu', async() => {
await page.waitForTimeout(1000); // initialization of functionality takes about 1000ms to work
await page.waitToClick(selectors.ticketDescriptor.moreMenu); await page.waitToClick(selectors.ticketDescriptor.moreMenu);
await page.waitToClick(selectors.ticketDescriptor.moreMenuAddToTurn); await page.waitToClick(selectors.ticketDescriptor.moreMenuAddToTurn);
await page.waitToClick(selectors.ticketDescriptor.thursdayButton); await page.waitToClick(selectors.ticketDescriptor.thursdayButton);
@ -63,6 +64,7 @@ describe('Ticket descriptor path', () => {
}); });
it('should add the ticket to saturday turn using the descriptor more menu', async() => { it('should add the ticket to saturday turn using the descriptor more menu', async() => {
await page.waitForTimeout(1000); // initialization of functionality takes about 1000ms to work
await page.waitToClick(selectors.ticketDescriptor.moreMenu); await page.waitToClick(selectors.ticketDescriptor.moreMenu);
await page.waitToClick(selectors.ticketDescriptor.moreMenuAddToTurn); await page.waitToClick(selectors.ticketDescriptor.moreMenuAddToTurn);
await page.waitToClick(selectors.ticketDescriptor.saturdayButton); await page.waitToClick(selectors.ticketDescriptor.saturdayButton);

View File

@ -22,6 +22,7 @@ describe('Ticket descriptor path', () => {
}); });
it(`should update the shipped hour using the descriptor menu`, async() => { it(`should update the shipped hour using the descriptor menu`, async() => {
await page.waitForTimeout(1000); // initialization of functionality takes about 1000ms to work
await page.waitToClick(selectors.ticketDescriptor.moreMenu); await page.waitToClick(selectors.ticketDescriptor.moreMenu);
await page.waitToClick(selectors.ticketDescriptor.moreMenuChangeShippedHour); await page.waitToClick(selectors.ticketDescriptor.moreMenuChangeShippedHour);
await page.pickTime(selectors.ticketDescriptor.changeShippedHour, '08:15'); await page.pickTime(selectors.ticketDescriptor.changeShippedHour, '08:15');
@ -65,6 +66,7 @@ describe('Ticket descriptor path', () => {
describe('Restore ticket', () => { describe('Restore ticket', () => {
it('should restore the ticket using the descriptor menu', async() => { it('should restore the ticket using the descriptor menu', async() => {
await page.waitForTimeout(1000); // initialization of functionality takes about 1000ms to work
await page.waitToClick(selectors.ticketDescriptor.moreMenu); await page.waitToClick(selectors.ticketDescriptor.moreMenu);
await page.waitToClick(selectors.ticketDescriptor.moreMenuRestoreTicket); await page.waitToClick(selectors.ticketDescriptor.moreMenuRestoreTicket);
await page.waitToClick(selectors.ticketDescriptor.acceptDialog); await page.waitToClick(selectors.ticketDescriptor.acceptDialog);
@ -82,6 +84,7 @@ describe('Ticket descriptor path', () => {
}); });
it('should open the add stowaway dialog', async() => { it('should open the add stowaway dialog', async() => {
await page.waitForTimeout(1000); // initialization of functionality takes about 1000ms to work
await page.waitForFunction(() => { await page.waitForFunction(() => {
let element = document.querySelector('vn-ticket-descriptor-menu'); let element = document.querySelector('vn-ticket-descriptor-menu');
return element.$ctrl.canShowStowaway === true; return element.$ctrl.canShowStowaway === true;
@ -114,6 +117,7 @@ describe('Ticket descriptor path', () => {
}); });
it('should delete the stowaway', async() => { it('should delete the stowaway', async() => {
await page.waitForTimeout(1000); // initialization of functionality takes about 1000ms to work
await page.waitToClick(selectors.ticketDescriptor.moreMenu); await page.waitToClick(selectors.ticketDescriptor.moreMenu);
await page.waitForContentLoaded(); await page.waitForContentLoaded();
await page.waitToClick(selectors.ticketDescriptor.moreMenuDeleteStowawayButton); await page.waitToClick(selectors.ticketDescriptor.moreMenuDeleteStowawayButton);
@ -145,6 +149,7 @@ describe('Ticket descriptor path', () => {
}); });
it('should invoice the ticket using the descriptor menu', async() => { it('should invoice the ticket using the descriptor menu', async() => {
await page.waitForTimeout(1000); // initialization of functionality takes about 1000ms to work
await page.waitToClick(selectors.ticketDescriptor.moreMenu); await page.waitToClick(selectors.ticketDescriptor.moreMenu);
await page.waitForContentLoaded(); await page.waitForContentLoaded();
await page.waitToClick(selectors.ticketDescriptor.moreMenuMakeInvoice); await page.waitToClick(selectors.ticketDescriptor.moreMenuMakeInvoice);
@ -176,6 +181,7 @@ describe('Ticket descriptor path', () => {
describe('SMS', () => { describe('SMS', () => {
it('should send the payment SMS using the descriptor menu', async() => { it('should send the payment SMS using the descriptor menu', async() => {
await page.waitForTimeout(2000); // initialization of functionality takes about 1000ms to work
await page.waitToClick(selectors.ticketDescriptor.moreMenu); await page.waitToClick(selectors.ticketDescriptor.moreMenu);
await page.waitForContentLoaded(); await page.waitForContentLoaded();
await page.waitToClick(selectors.ticketDescriptor.moreMenuPaymentSMS); await page.waitToClick(selectors.ticketDescriptor.moreMenuPaymentSMS);
@ -188,6 +194,7 @@ describe('Ticket descriptor path', () => {
}); });
it('should send the import SMS using the descriptor menu', async() => { it('should send the import SMS using the descriptor menu', async() => {
await page.waitForTimeout(1000); // initialization of functionality takes about 1000ms to work
await page.waitToClick(selectors.ticketDescriptor.moreMenu); await page.waitToClick(selectors.ticketDescriptor.moreMenu);
await page.waitForContentLoaded(); await page.waitForContentLoaded();
await page.waitToClick(selectors.ticketDescriptor.moreMenuSendImportSms); await page.waitToClick(selectors.ticketDescriptor.moreMenuSendImportSms);

View File

@ -62,6 +62,7 @@ describe('Ticket create path', () => {
}); });
it('should make the previously created ticket the stowaway of the current ticket', async() => { it('should make the previously created ticket the stowaway of the current ticket', async() => {
await page.waitForTimeout(1000); // initialization of functionality takes about 1000ms to work
await page.waitToClick(selectors.ticketDescriptor.moreMenu); await page.waitToClick(selectors.ticketDescriptor.moreMenu);
await page.waitToClick(selectors.ticketDescriptor.moreMenuAddStowaway); await page.waitToClick(selectors.ticketDescriptor.moreMenuAddStowaway);
await page.waitToClick(selectors.ticketDescriptor.addStowawayDialogFirstTicket); await page.waitToClick(selectors.ticketDescriptor.addStowawayDialogFirstTicket);
@ -71,6 +72,7 @@ describe('Ticket create path', () => {
}); });
it('should delete the current ticket', async() => { it('should delete the current ticket', async() => {
await page.waitForTimeout(1000); // initialization of functionality takes about 1000ms to work
await page.waitToClick(selectors.ticketDescriptor.moreMenu); await page.waitToClick(selectors.ticketDescriptor.moreMenu);
await page.waitToClick(selectors.ticketDescriptor.moreMenuDeleteTicket); await page.waitToClick(selectors.ticketDescriptor.moreMenuDeleteTicket);
await page.waitToClick(selectors.ticketDescriptor.acceptDialog); await page.waitToClick(selectors.ticketDescriptor.acceptDialog);

View File

@ -17,6 +17,7 @@ describe('Ticket create from client path', () => {
}); });
it('should click the create simple ticket on the descriptor menu', async() => { it('should click the create simple ticket on the descriptor menu', async() => {
await page.waitForTimeout(1000); // initialization of functionality takes about 1000ms to work
await page.waitToClick(selectors.clientDescriptor.moreMenu); await page.waitToClick(selectors.clientDescriptor.moreMenu);
await page.waitToClick(selectors.clientDescriptor.simpleTicketButton); await page.waitToClick(selectors.clientDescriptor.simpleTicketButton);
await page.waitForState('ticket.create'); await page.waitForState('ticket.create');

View File

@ -18,6 +18,7 @@ describe('InvoiceIn descriptor path', () => {
}); });
it('should clone the invoiceIn using the descriptor more menu', async() => { it('should clone the invoiceIn using the descriptor more menu', async() => {
await page.waitForTimeout(1000); // initialization of functionality takes about 1000ms to work
await page.waitToClick(selectors.invoiceInDescriptor.moreMenu); await page.waitToClick(selectors.invoiceInDescriptor.moreMenu);
await page.waitToClick(selectors.invoiceInDescriptor.moreMenuCloneInvoiceIn); await page.waitToClick(selectors.invoiceInDescriptor.moreMenuCloneInvoiceIn);
await page.waitToClick(selectors.invoiceInDescriptor.acceptButton); await page.waitToClick(selectors.invoiceInDescriptor.acceptButton);
@ -31,6 +32,7 @@ describe('InvoiceIn descriptor path', () => {
}); });
it('should delete the cloned invoiceIn using the descriptor more menu', async() => { it('should delete the cloned invoiceIn using the descriptor more menu', async() => {
await page.waitForTimeout(1000); // initialization of functionality takes about 1000ms to work
await page.waitToClick(selectors.invoiceInDescriptor.moreMenu); await page.waitToClick(selectors.invoiceInDescriptor.moreMenu);
await page.waitToClick(selectors.invoiceInDescriptor.moreMenuDeleteInvoiceIn); await page.waitToClick(selectors.invoiceInDescriptor.moreMenuDeleteInvoiceIn);
await page.waitToClick(selectors.invoiceInDescriptor.acceptButton); await page.waitToClick(selectors.invoiceInDescriptor.acceptButton);

View File

@ -38,6 +38,7 @@ describe('InvoiceOut descriptor path', () => {
}); });
it('should delete the invoiceOut using the descriptor more menu', async() => { it('should delete the invoiceOut using the descriptor more menu', async() => {
await page.waitForTimeout(1000); // initialization of functionality takes about 1000ms to work
await page.waitToClick(selectors.invoiceOutDescriptor.moreMenu); await page.waitToClick(selectors.invoiceOutDescriptor.moreMenu);
await page.waitToClick(selectors.invoiceOutDescriptor.moreMenuDeleteInvoiceOut); await page.waitToClick(selectors.invoiceOutDescriptor.moreMenuDeleteInvoiceOut);
await page.waitToClick(selectors.invoiceOutDescriptor.acceptDeleteButton); await page.waitToClick(selectors.invoiceOutDescriptor.acceptDeleteButton);

View File

@ -16,6 +16,7 @@ describe('InvoiceOut manual invoice path', () => {
}); });
it('should open the manual invoice form', async() => { it('should open the manual invoice form', async() => {
await page.waitForTimeout(1000); // initialization of functionality takes about 1000ms to work
await page.waitToClick(selectors.invoiceOutIndex.createInvoice); await page.waitToClick(selectors.invoiceOutIndex.createInvoice);
await page.waitToClick(selectors.invoiceOutIndex.createManualInvoice); await page.waitToClick(selectors.invoiceOutIndex.createManualInvoice);
await page.waitForSelector(selectors.invoiceOutIndex.manualInvoiceForm); await page.waitForSelector(selectors.invoiceOutIndex.manualInvoiceForm);
@ -44,6 +45,7 @@ describe('InvoiceOut manual invoice path', () => {
}); });
it('should now open the manual invoice form', async() => { it('should now open the manual invoice form', async() => {
await page.waitForTimeout(1000); // initialization of functionality takes about 1000ms to work
await page.waitToClick(selectors.invoiceOutIndex.createInvoice); await page.waitToClick(selectors.invoiceOutIndex.createInvoice);
await page.waitToClick(selectors.invoiceOutIndex.createManualInvoice); await page.waitToClick(selectors.invoiceOutIndex.createManualInvoice);
await page.waitForSelector(selectors.invoiceOutIndex.manualInvoiceForm); await page.waitForSelector(selectors.invoiceOutIndex.manualInvoiceForm);

View File

@ -34,6 +34,7 @@ describe('Travel descriptor path', () => {
}); });
it('should be redirected to the create entry view', async() => { it('should be redirected to the create entry view', async() => {
await page.waitForTimeout(1000); // initialization of functionality takes about 1000ms to work
await page.waitToClick(selectors.travelDescriptor.dotMenu); await page.waitToClick(selectors.travelDescriptor.dotMenu);
await page.waitToClick(selectors.travelDescriptor.dotMenuAddEntry); await page.waitToClick(selectors.travelDescriptor.dotMenuAddEntry);
await page.waitForState('entry.create'); await page.waitForState('entry.create');
@ -89,6 +90,7 @@ describe('Travel descriptor path', () => {
}); });
it('should be redirected to the create travel when using the clone option of the dot menu', async() => { it('should be redirected to the create travel when using the clone option of the dot menu', async() => {
await page.waitForTimeout(1000); // initialization of functionality takes about 1000ms to work
await page.waitToClick(selectors.travelDescriptor.dotMenu); await page.waitToClick(selectors.travelDescriptor.dotMenu);
await page.waitToClick(selectors.travelDescriptor.dotMenuClone); await page.waitToClick(selectors.travelDescriptor.dotMenuClone);
await page.respondToDialog('accept'); await page.respondToDialog('accept');
@ -114,6 +116,7 @@ describe('Travel descriptor path', () => {
}); });
it('should atempt to clone the travel and its entries using the descriptor menu but receive an error', async() => { it('should atempt to clone the travel and its entries using the descriptor menu but receive an error', async() => {
await page.waitForTimeout(1000); // initialization of functionality takes about 1000ms to work
await page.waitToClick(selectors.travelDescriptor.dotMenu); await page.waitToClick(selectors.travelDescriptor.dotMenu);
await page.waitToClick(selectors.travelDescriptor.dotMenuCloneWithEntries); await page.waitToClick(selectors.travelDescriptor.dotMenuCloneWithEntries);
await page.waitToClick(selectors.travelDescriptor.acceptClonation); await page.waitToClick(selectors.travelDescriptor.acceptClonation);

View File

@ -17,6 +17,7 @@ describe('Zone descriptor path', () => {
}); });
it('should eliminate the zone using the descriptor option', async() => { it('should eliminate the zone using the descriptor option', async() => {
await page.waitForTimeout(1000); // initialization of functionality takes about 1000ms to work
await page.waitToClick(selectors.zoneDescriptor.menu); await page.waitToClick(selectors.zoneDescriptor.menu);
await page.waitToClick(selectors.zoneDescriptor.deleteZone); await page.waitToClick(selectors.zoneDescriptor.deleteZone);
await page.respondToDialog('accept'); await page.respondToDialog('accept');

View File

@ -91,6 +91,7 @@ describe('Account create and basic data path', () => {
}); });
it('should activate the account using the descriptor menu', async() => { it('should activate the account using the descriptor menu', async() => {
await page.waitForTimeout(1000); // initialization of functionality takes about 1000ms to work
await page.waitToClick(selectors.accountDescriptor.menuButton); await page.waitToClick(selectors.accountDescriptor.menuButton);
await page.waitToClick(selectors.accountDescriptor.activateAccount); await page.waitToClick(selectors.accountDescriptor.activateAccount);
await page.waitToClick(selectors.accountDescriptor.acceptButton); await page.waitToClick(selectors.accountDescriptor.acceptButton);
@ -138,6 +139,7 @@ describe('Account create and basic data path', () => {
describe('Set password', () => { describe('Set password', () => {
it('should set the password using the descriptor menu', async() => { it('should set the password using the descriptor menu', async() => {
await page.waitForTimeout(1000); // initialization of functionality takes about 1000ms to work
await page.waitToClick(selectors.accountDescriptor.menuButton); await page.waitToClick(selectors.accountDescriptor.menuButton);
await page.waitToClick(selectors.accountDescriptor.setPassword); await page.waitToClick(selectors.accountDescriptor.setPassword);
await page.write(selectors.accountDescriptor.newPassword, 'quantum.crypt0graphy'); await page.write(selectors.accountDescriptor.newPassword, 'quantum.crypt0graphy');

View File

@ -94,8 +94,11 @@ async function launchBackTest(done) {
await new Promise((resolve, reject) => { await new Promise((resolve, reject) => {
const jasmine = require('gulp-jasmine'); const jasmine = require('gulp-jasmine');
let options = { const options = {
verbose: false,
includeStackTrace: false,
errorOnFail: false, errorOnFail: false,
timeout: 5000,
config: {} config: {}
}; };

View File

@ -116,5 +116,6 @@
"This client is not invoiceable": "This client is not invoiceable", "This client is not invoiceable": "This client is not invoiceable",
"INACTIVE_PROVIDER": "Inactive provider", "INACTIVE_PROVIDER": "Inactive provider",
"reference duplicated": "reference duplicated", "reference duplicated": "reference duplicated",
"The PDF document does not exists": "The PDF document does not exists. Try regenerating it from 'Regenerate invoice PDF' option" "The PDF document does not exists": "The PDF document does not exists. Try regenerating it from 'Regenerate invoice PDF' option",
"This item is not available": "This item is not available"
} }

View File

@ -8,12 +8,10 @@ describe('client canBeInvoiced()', () => {
accessToken: {userId: userId} accessToken: {userId: userId}
}; };
beforeAll(async done => { beforeAll(async() => {
spyOn(LoopBackContext, 'getCurrentContext').and.returnValue({ spyOn(LoopBackContext, 'getCurrentContext').and.returnValue({
active: activeCtx active: activeCtx
}); });
done();
}); });
it('should return falsy for a client without the data checked', async() => { it('should return falsy for a client without the data checked', async() => {

View File

@ -4,13 +4,11 @@ describe('loopback model address', () => {
let createdAddressId; let createdAddressId;
const clientId = 1101; const clientId = 1101;
afterAll(async done => { afterAll(async() => {
let client = await app.models.Client.findById(clientId); let client = await app.models.Client.findById(clientId);
await app.models.Address.destroyById(createdAddressId); await app.models.Address.destroyById(createdAddressId);
await client.updateAttribute('isEqualizated', false); await client.updateAttribute('isEqualizated', false);
done();
}); });
describe('observe()', () => { describe('observe()', () => {

View File

@ -41,6 +41,7 @@ module.exports = Self => {
const deleted = await Promise.all(promises); const deleted = await Promise.all(promises);
if (tx) await tx.commit(); if (tx) await tx.commit();
return deleted; return deleted;
} catch (e) { } catch (e) {
if (tx) await tx.rollback(); if (tx) await tx.rollback();

View File

@ -97,6 +97,7 @@ module.exports = Self => {
const sql = ParameterizedSQL.join(stmts, ';'); const sql = ParameterizedSQL.join(stmts, ';');
await conn.executeStmt(sql, myOptions); await conn.executeStmt(sql, myOptions);
if (tx) await tx.commit(); if (tx) await tx.commit();
} catch (e) { } catch (e) {
if (tx) await tx.rollback(); if (tx) await tx.rollback();

View File

@ -10,12 +10,10 @@ describe('entry import()', () => {
accessToken: {userId: buyerId}, accessToken: {userId: buyerId},
}; };
beforeAll(async done => { beforeAll(async() => {
spyOn(LoopBackContext, 'getCurrentContext').and.returnValue({ spyOn(LoopBackContext, 'getCurrentContext').and.returnValue({
active: activeCtx active: activeCtx
}); });
done();
}); });
it('should import the buy rows', async() => { it('should import the buy rows', async() => {

View File

@ -3,12 +3,10 @@ const LoopBackContext = require('loopback-context');
describe('entry importBuysPreview()', () => { describe('entry importBuysPreview()', () => {
const entryId = 1; const entryId = 1;
beforeAll(async done => { beforeAll(async() => {
spyOn(LoopBackContext, 'getCurrentContext').and.returnValue({ spyOn(LoopBackContext, 'getCurrentContext').and.returnValue({
active: activeCtx active: activeCtx
}); });
done();
}); });
it('should return the buys with the calculated packageFk', async() => { it('should return the buys with the calculated packageFk', async() => {

View File

@ -1,9 +1,3 @@
vn-entry-buy-import {
.vn-table > tbody td:nth-child(1) {
width: 250px
}
}
.itemFilter { .itemFilter {
vn-table.scrollable { vn-table.scrollable {
height: 500px height: 500px

View File

@ -40,7 +40,7 @@ module.exports = Self => {
if (!tax.taxClassFk) if (!tax.taxClassFk)
throw new UserError('Tax class cannot be blank'); throw new UserError('Tax class cannot be blank');
promises.push(Self.app.models.ItemTaxCountry.update( promises.push(Self.app.models.ItemTaxCountry.updateAll(
{id: tax.id}, {id: tax.id},
{taxClassFk: tax.taxClassFk} {taxClassFk: tax.taxClassFk}
), myOptions); ), myOptions);

View File

@ -1,15 +1,15 @@
const app = require('vn-loopback/server/server'); const models = require('vn-loopback/server/server').models;
describe('SalesMonitor salesFilter()', () => { describe('SalesMonitor salesFilter()', () => {
it('should return the tickets matching the filter', async() => { it('should now return the tickets matching the filter', async() => {
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.SalesMonitor.salesFilter(ctx, filter); const result = await models.SalesMonitor.salesFilter(ctx, filter);
expect(result.length).toEqual(24); expect(result.length).toEqual(24);
}); });
it('should return the tickets matching the problems on true', async() => { it('should now return the tickets matching the problems on true', async() => {
const yesterday = new Date(); const yesterday = new Date();
yesterday.setHours(0, 0, 0, 0); yesterday.setHours(0, 0, 0, 0);
const today = new Date(); const today = new Date();
@ -21,12 +21,12 @@ describe('SalesMonitor salesFilter()', () => {
to: today to: today
}}; }};
const filter = {}; const filter = {};
const result = await app.models.SalesMonitor.salesFilter(ctx, filter); const result = await models.SalesMonitor.salesFilter(ctx, filter);
expect(result.length).toEqual(13); expect(result.length).toEqual(13);
}); });
it('should return the tickets matching the problems on false', async() => { it('should now return the tickets matching the problems on false', async() => {
const yesterday = new Date(); const yesterday = new Date();
yesterday.setDate(yesterday.getDate() - 1); yesterday.setDate(yesterday.getDate() - 1);
yesterday.setHours(0, 0, 0, 0); yesterday.setHours(0, 0, 0, 0);
@ -39,33 +39,33 @@ describe('SalesMonitor salesFilter()', () => {
to: today to: today
}}; }};
const filter = {}; const filter = {};
const result = await app.models.SalesMonitor.salesFilter(ctx, filter); const result = await models.SalesMonitor.salesFilter(ctx, filter);
expect(result.length).toEqual(0); expect(result.length).toEqual(0);
}); });
it('should return the tickets matching the problems on null', async() => { it('should now return the tickets matching the problems on null', async() => {
const ctx = {req: {accessToken: {userId: 9}}, args: {problems: null}}; const ctx = {req: {accessToken: {userId: 9}}, args: {problems: null}};
const filter = {}; const filter = {};
const result = await app.models.SalesMonitor.salesFilter(ctx, filter); const result = await models.SalesMonitor.salesFilter(ctx, filter);
expect(result.length).toEqual(24); expect(result.length).toEqual(24);
}); });
it('should return the tickets matching the orderId 11', async() => { it('should now return the tickets matching the orderId 11', async() => {
const ctx = {req: {accessToken: {userId: 9}}, args: {orderFk: 11}}; const ctx = {req: {accessToken: {userId: 9}}, args: {orderFk: 11}};
const filter = {}; const filter = {};
const result = await app.models.SalesMonitor.salesFilter(ctx, filter); const result = await models.SalesMonitor.salesFilter(ctx, filter);
const firstRow = result[0]; const firstRow = result[0];
expect(result.length).toEqual(1); expect(result.length).toEqual(1);
expect(firstRow.id).toEqual(11); expect(firstRow.id).toEqual(11);
}); });
it('should return the tickets with grouped state "Pending" and not "Ok" nor "BOARDING"', async() => { it('should now return the tickets with grouped state "Pending" and not "Ok" nor "BOARDING"', async() => {
const ctx = {req: {accessToken: {userId: 9}}, args: {pending: true}}; const ctx = {req: {accessToken: {userId: 9}}, args: {pending: true}};
const filter = {}; const filter = {};
const result = await app.models.SalesMonitor.salesFilter(ctx, filter); const result = await models.SalesMonitor.salesFilter(ctx, 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))];
@ -74,10 +74,10 @@ describe('SalesMonitor salesFilter()', () => {
expect(anyResult.state).toMatch(/(Libre|Arreglar)/); expect(anyResult.state).toMatch(/(Libre|Arreglar)/);
}); });
it('should return the tickets that are not pending', async() => { it('should now return the tickets that are not pending', async() => {
const ctx = {req: {accessToken: {userId: 9}}, args: {pending: false}}; const ctx = {req: {accessToken: {userId: 9}}, args: {pending: false}};
const filter = {}; const filter = {};
const result = await app.models.SalesMonitor.salesFilter(ctx, filter); const result = await models.SalesMonitor.salesFilter(ctx, filter);
const firstRow = result[0]; const firstRow = result[0];
const secondRow = result[1]; const secondRow = result[1];
const thirdRow = result[2]; const thirdRow = result[2];
@ -88,18 +88,18 @@ describe('SalesMonitor salesFilter()', () => {
expect(thirdRow.state).toEqual('Entregado'); expect(thirdRow.state).toEqual('Entregado');
}); });
it('should return the tickets from the worker team', async() => { it('should now return the tickets from the worker team', async() => {
const ctx = {req: {accessToken: {userId: 18}}, args: {myTeam: true}}; const ctx = {req: {accessToken: {userId: 18}}, args: {myTeam: true}};
const filter = {}; const filter = {};
const result = await app.models.SalesMonitor.salesFilter(ctx, filter); const result = await models.SalesMonitor.salesFilter(ctx, filter);
expect(result.length).toEqual(20); expect(result.length).toEqual(20);
}); });
it('should return the tickets that are not from the worker team', async() => { it('should now return the tickets that are not from the worker team', async() => {
const ctx = {req: {accessToken: {userId: 18}}, args: {myTeam: false}}; const ctx = {req: {accessToken: {userId: 18}}, args: {myTeam: false}};
const filter = {}; const filter = {};
const result = await app.models.SalesMonitor.salesFilter(ctx, filter); const result = await models.SalesMonitor.salesFilter(ctx, filter);
expect(result.length).toEqual(4); expect(result.length).toEqual(4);
}); });
@ -113,7 +113,7 @@ describe('SalesMonitor salesFilter()', () => {
const ctx = {req: {accessToken: {userId: 18}}, args: {}}; const ctx = {req: {accessToken: {userId: 18}}, args: {}};
const filter = {order: 'totalProblems DESC'}; const filter = {order: 'totalProblems DESC'};
const result = await app.models.SalesMonitor.salesFilter(ctx, filter); const result = await models.SalesMonitor.salesFilter(ctx, filter);
const firstTicket = result.shift(); const firstTicket = result.shift();
const secondTicket = result.shift(); const secondTicket = result.shift();
@ -131,7 +131,7 @@ describe('SalesMonitor salesFilter()', () => {
const ctx = {req: {accessToken: {userId: 18}}, args: {}}; const ctx = {req: {accessToken: {userId: 18}}, args: {}};
const filter = {order: 'totalProblems ASC'}; const filter = {order: 'totalProblems ASC'};
const result = await app.models.SalesMonitor.salesFilter(ctx, filter); const result = await models.SalesMonitor.salesFilter(ctx, filter);
const firstTicket = result.shift(); const firstTicket = result.shift();
const secondTicket = result.shift(); const secondTicket = result.shift();

View File

@ -1,6 +1,5 @@
<vn-crud-model auto-load="true" <vn-crud-model
vn-id="model" vn-id="model"
params="::$ctrl.filterParams"
url="SalesMonitors/salesFilter" url="SalesMonitors/salesFilter"
limit="20" limit="20"
order="shippedDate DESC, preparationHour ASC, zoneLanding ASC, id"> order="shippedDate DESC, preparationHour ASC, zoneLanding ASC, id">

View File

@ -6,13 +6,30 @@ export default class Controller extends Section {
constructor($element, $) { constructor($element, $) {
super($element, $); super($element, $);
this.filterParams = this.fetchParams({ this.filterParams = this.fetchParams();
scopeDays: 1
});
} }
fetchParams($params) { $onInit() {
if (!Object.entries($params).length) if (!this.$params.q) {
this.$.$applyAsync(
() => this.$.model.applyFilter(null, this.filterParams));
}
}
fetchParams($params = {}) {
const excludedParams = [
'search',
'clientFk',
'orderFk',
'refFk',
'scopeDays'
];
const hasExcludedParams = excludedParams.some(param => {
return $params && $params[param];
});
const hasParams = Object.entries($params).length;
if (!hasParams || !hasExcludedParams)
$params.scopeDays = 1; $params.scopeDays = 1;
if (typeof $params.scopeDays === 'number') { if (typeof $params.scopeDays === 'number') {

View File

@ -3,10 +3,8 @@ const app = require('vn-loopback/server/server');
describe('order addToOrder()', () => { describe('order addToOrder()', () => {
const orderId = 8; const orderId = 8;
let rowToDelete; let rowToDelete;
afterAll(async done => { afterAll(async() => {
await app.models.OrderRow.removes({rows: [rowToDelete], actualOrderId: orderId}); await app.models.OrderRow.removes({rows: [rowToDelete], actualOrderId: orderId});
done();
}); });
it('should add a row to a given order', async() => { it('should add a row to a given order', async() => {

View File

@ -4,12 +4,10 @@ describe('order removes()', () => {
let row; let row;
let newRow; let newRow;
beforeAll(async done => { beforeAll(async() => {
row = await app.models.OrderRow.findOne({where: {id: 12}}); row = await app.models.OrderRow.findOne({where: {id: 12}});
row.id = null; row.id = null;
newRow = await app.models.OrderRow.create(row); newRow = await app.models.OrderRow.create(row);
done();
}); });
it('should throw an error if rows property is empty', async() => { it('should throw an error if rows property is empty', async() => {

View File

@ -4,10 +4,8 @@ let UserError = require('vn-loopback/util/user-error');
describe('order new()', () => { describe('order new()', () => {
let orderId; let orderId;
afterAll(async done => { afterAll(async() => {
await app.models.Order.destroyById(orderId); await app.models.Order.destroyById(orderId);
done();
}); });
it('should throw an error if the client isnt active', async() => { it('should throw an error if the client isnt active', async() => {

View File

@ -2,12 +2,10 @@ const app = require('vn-loopback/server/server');
describe('Order updateBasicData', () => { describe('Order updateBasicData', () => {
const orderId = 21; const orderId = 21;
afterAll(async done => { afterAll(async() => {
let validparams = {note: null}; let validparams = {note: null};
await app.models.Order.updateBasicData(orderId, validparams); await app.models.Order.updateBasicData(orderId, validparams);
done();
}); });
it('should return an error if the order is confirmed', async() => { it('should return an error if the order is confirmed', async() => {

View File

@ -4,14 +4,12 @@ describe('route guessPriority()', () => {
const targetRouteId = 7; const targetRouteId = 7;
let routeTicketsToRestore; let routeTicketsToRestore;
afterAll(async done => { afterAll(async() => {
let restoreFixtures = []; let restoreFixtures = [];
routeTicketsToRestore.forEach(ticket => { routeTicketsToRestore.forEach(ticket => {
restoreFixtures.push(ticket.updateAttribute('priority', null)); restoreFixtures.push(ticket.updateAttribute('priority', null));
}); });
await Promise.all(restoreFixtures); await Promise.all(restoreFixtures);
done();
}); });
it('should call guessPriority() and then check the tickets in the target route now have their priorities defined', async() => { it('should call guessPriority() and then check the tickets in the target route now have their priorities defined', async() => {

View File

@ -8,12 +8,10 @@ describe('route insertTicket()', () => {
accessToken: {userId: deliveryId}, accessToken: {userId: deliveryId},
}; };
beforeAll(async done => { beforeAll(async() => {
spyOn(LoopBackContext, 'getCurrentContext').and.returnValue({ spyOn(LoopBackContext, 'getCurrentContext').and.returnValue({
active: activeCtx active: activeCtx
}); });
done();
}); });
it('should add the ticket to a route', async() => { it('should add the ticket to a route', async() => {

View File

@ -4,18 +4,14 @@ describe('loopback model Supplier', () => {
let supplierOne; let supplierOne;
let supplierTwo; let supplierTwo;
beforeAll(async done => { beforeAll(async() => {
supplierOne = await app.models.Supplier.findById(1); supplierOne = await app.models.Supplier.findById(1);
supplierTwo = await app.models.Supplier.findById(442); supplierTwo = await app.models.Supplier.findById(442);
done();
}); });
afterAll(async done => { afterAll(async() => {
await supplierOne.updateAttribute('payMethodFk', supplierOne.payMethodFk); await supplierOne.updateAttribute('payMethodFk', supplierOne.payMethodFk);
await supplierTwo.updateAttribute('payMethodFk', supplierTwo.payMethodFk); await supplierTwo.updateAttribute('payMethodFk', supplierTwo.payMethodFk);
done();
}); });
describe('payMethodFk', () => { describe('payMethodFk', () => {

View File

@ -7,13 +7,13 @@ module.exports = Self => {
accepts: [ accepts: [
{ {
arg: 'filter', arg: 'filter',
type: 'Object', type: 'object',
description: 'Filter defining where, order, offset, and limit - must be a JSON-encoded string', description: 'Filter defining where, order, offset, and limit - must be a JSON-encoded string',
http: {source: 'query'}, http: {source: 'query'},
}, },
], ],
returns: { returns: {
type: ['Object'], type: ['object'],
root: true, root: true,
}, },
http: { http: {
@ -22,7 +22,12 @@ module.exports = Self => {
}, },
}); });
Self.filter = async filter => { Self.filter = async(filter, options) => {
const myOptions = {};
if (typeof options == 'object')
Object.assign(myOptions, options);
const stmt = new ParameterizedSQL( const stmt = new ParameterizedSQL(
`SELECT `SELECT
e.id, e.id,
@ -55,6 +60,6 @@ module.exports = Self => {
`); `);
stmt.merge(Self.buildSuffix(filter, 'e')); stmt.merge(Self.buildSuffix(filter, 'e'));
return await Self.rawStmt(stmt); return Self.rawStmt(stmt, myOptions);
}; };
}; };

View File

@ -0,0 +1,21 @@
const models = require('vn-loopback/server/server').models;
describe('expedition filter()', () => {
it('should return the expeditions matching the filter', async() => {
const tx = await models.Expedition.beginTransaction({});
try {
const options = {transaction: tx};
const filter = {where: {packagingFk: 1}};
const response = await models.Expedition.filter(filter, options);
expect(response.length).toEqual(10);
await tx.rollback();
} catch (e) {
await tx.rollback();
throw e;
}
});
});

View File

@ -7,13 +7,13 @@ module.exports = Self => {
accessType: 'READ', accessType: 'READ',
accepts: [{ accepts: [{
arg: 'filter', arg: 'filter',
type: 'Object', type: 'object',
required: false, required: false,
description: 'Filter defining where and paginated data', description: 'Filter defining where and paginated data',
http: {source: 'query'} http: {source: 'query'}
}], }],
returns: { returns: {
type: ['Object'], type: ['object'],
root: true root: true
}, },
http: { http: {
@ -22,9 +22,14 @@ module.exports = Self => {
} }
}); });
Self.listPackaging = async filter => { Self.listPackaging = async(filter, options) => {
let conn = Self.dataSource.connector; const conn = Self.dataSource.connector;
let stmt = new ParameterizedSQL( const myOptions = {};
if (typeof options == 'object')
Object.assign(myOptions, options);
const stmt = new ParameterizedSQL(
`SELECT name, itemFk, packagingFk `SELECT name, itemFk, packagingFk
FROM (SELECT i.name, i.id itemFk, p.id packagingFk FROM (SELECT i.name, i.id itemFk, p.id packagingFk
FROM item i FROM item i
@ -33,6 +38,7 @@ module.exports = Self => {
); );
stmt.merge(conn.makeSuffix(filter)); stmt.merge(conn.makeSuffix(filter));
return conn.executeStmt(stmt);
return conn.executeStmt(stmt, myOptions);
}; };
}; };

View File

@ -1,12 +1,22 @@
const app = require('vn-loopback/server/server'); const models = require('vn-loopback/server/server').models;
describe('ticket listPackaging()', () => { describe('ticket listPackaging()', () => {
it('should return the packaging', async() => { it('should return the packaging', async() => {
let filter = {where: {packagingFk: 1}}; const tx = await models.Packaging.beginTransaction({});
let response = await app.models.Packaging.listPackaging(filter);
try {
const options = {transaction: tx};
const filter = {where: {packagingFk: 1}};
const response = await models.Packaging.listPackaging(filter, options);
expect(response[0].name).toBeDefined();
expect(response[0].name).toEqual('Container ammo box 1m'); expect(response[0].name).toEqual('Container ammo box 1m');
await tx.rollback();
} catch (e) {
await tx.rollback();
throw e;
}
}); });
}); });

View File

@ -7,11 +7,11 @@ module.exports = Self => {
accessType: 'READ', accessType: 'READ',
accepts: [{ accepts: [{
arg: 'filter', arg: 'filter',
type: 'Object', type: 'object',
description: 'Filter defining where and paginated data' description: 'Filter defining where and paginated data'
}], }],
returns: { returns: {
type: ['Object'], type: ['object'],
root: true root: true
}, },
http: { http: {
@ -20,8 +20,13 @@ module.exports = Self => {
} }
}); });
Self.listSaleTracking = async filter => { Self.listSaleTracking = async(filter, options) => {
let stmt = new ParameterizedSQL(` const myOptions = {};
if (typeof options == 'object')
Object.assign(myOptions, options);
const stmt = new ParameterizedSQL(`
SELECT SELECT
st.id, st.id,
s.ticketFk, s.ticketFk,
@ -41,9 +46,9 @@ module.exports = Self => {
stmt.merge(Self.makeSuffix(filter)); stmt.merge(Self.makeSuffix(filter));
let trackings = await Self.rawStmt(stmt); const trackings = await Self.rawStmt(stmt, myOptions);
let salesFilter = { const salesFilter = {
include: [ include: [
{ {
relation: 'item' relation: 'item'
@ -52,14 +57,14 @@ module.exports = Self => {
where: {ticketFk: filter.where.ticketFk} where: {ticketFk: filter.where.ticketFk}
}; };
let sales = await Self.app.models.Sale.find(salesFilter); const sales = await Self.app.models.Sale.find(salesFilter, myOptions);
trackings.forEach(tracking => { for (const tracking of trackings) {
sales.forEach(sale => { for (const sale of sales) {
if (tracking.itemFk == sale.itemFk) if (tracking.itemFk == sale.itemFk)
tracking.item = sale.item(); tracking.item = sale.item();
}); }
}); }
return trackings; return trackings;
}; };

View File

@ -1,17 +1,39 @@
const app = require('vn-loopback/server/server'); const models = require('vn-loopback/server/server').models;
describe('ticket listSaleTracking()', () => { describe('ticket listSaleTracking()', () => {
it('should call the listSaleTracking method and return the response', async() => { it('should call the listSaleTracking method and return the response', async() => {
let filter = {where: {ticketFk: 1}}; const tx = await models.SaleTracking.beginTransaction({});
let result = await app.models.SaleTracking.listSaleTracking(filter);
try {
const options = {transaction: tx};
const filter = {where: {ticketFk: 1}};
const result = await models.SaleTracking.listSaleTracking(filter, options);
expect(result.length).toEqual(4); expect(result.length).toEqual(4);
await tx.rollback();
} catch (e) {
await tx.rollback();
throw e;
}
}); });
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() => {
let filter = {where: {ticketFk: 2}}; const tx = await models.SaleTracking.beginTransaction({});
let result = await app.models.SaleTracking.listSaleTracking(filter);
try {
const options = {transaction: tx};
const filter = {where: {ticketFk: 2}};
const result = await models.SaleTracking.listSaleTracking(filter, options);
expect(result.length).toEqual(0); expect(result.length).toEqual(0);
await tx.rollback();
} catch (e) {
await tx.rollback();
throw e;
}
}); });
}); });

View File

@ -26,11 +26,22 @@ module.exports = Self => {
} }
}); });
Self.deleteSales = async(ctx, sales, ticketId) => { Self.deleteSales = async(ctx, sales, ticketId, options) => {
const $t = ctx.req.__; // $translate const $t = ctx.req.__; // $translate
const models = Self.app.models; const models = Self.app.models;
const myOptions = {};
let tx;
const canEditSales = await models.Sale.canEdit(ctx, sales); if (typeof options == 'object')
Object.assign(myOptions, options);
if (!myOptions.transaction) {
tx = await Self.beginTransaction({});
myOptions.transaction = tx;
}
try {
const canEditSales = await models.Sale.canEdit(ctx, sales, myOptions);
const ticket = await models.Ticket.findById(ticketId, { const ticket = await models.Ticket.findById(ticketId, {
include: { include: {
@ -44,9 +55,9 @@ module.exports = Self => {
} }
} }
} }
}); }, myOptions);
const isTicketEditable = await models.Ticket.isEditable(ctx, ticketId); const isTicketEditable = await models.Ticket.isEditable(ctx, ticketId, myOptions);
if (!isTicketEditable) if (!isTicketEditable)
throw new UserError(`The sales of this ticket can't be modified`); throw new UserError(`The sales of this ticket can't be modified`);
@ -56,7 +67,7 @@ module.exports = Self => {
const promises = []; const promises = [];
let deletions = ''; let deletions = '';
for (let sale of sales) { for (let sale of sales) {
const deletedSale = models.Sale.destroyById(sale.id); const deletedSale = models.Sale.destroyById(sale.id, myOptions);
deletions += `\r\n-${sale.itemFk}: ${sale.concept} (${sale.quantity})`; deletions += `\r\n-${sale.itemFk}: ${sale.concept} (${sale.quantity})`;
promises.push(deletedSale); promises.push(deletedSale);
@ -71,9 +82,17 @@ module.exports = Self => {
ticketUrl: `${origin}/#!/ticket/${ticketId}/sale`, ticketUrl: `${origin}/#!/ticket/${ticketId}/sale`,
deletions: deletions deletions: deletions
}); });
await models.Chat.sendCheckingPresence(ctx, salesPerson.id, message); await models.Chat.sendCheckingPresence(ctx, salesPerson.id, message, myOptions);
} }
return Promise.all(promises); const deletedSales = await Promise.all(promises);
if (tx) await tx.commit();
return deletedSales;
} catch (e) {
if (tx) await tx.rollback();
throw e;
}
}; };
}; };

View File

@ -18,8 +18,14 @@ module.exports = Self => {
} }
}); });
Self.getClaimableFromTicket = async ticketFk => { Self.getClaimableFromTicket = async(ticketFk, options) => {
let query = `SELECT const myOptions = {};
if (typeof options == 'object')
Object.assign(myOptions, options);
const query = `
SELECT
s.id AS saleFk, s.id AS saleFk,
t.id AS ticketFk, t.id AS ticketFk,
t.landed, t.landed,
@ -37,7 +43,7 @@ module.exports = Self => {
AND t.id = ? AND cb.id IS NULL AND t.id = ? AND cb.id IS NULL
ORDER BY t.landed DESC, t.id DESC`; ORDER BY t.landed DESC, t.id DESC`;
let claimableSales = await Self.rawSql(query, [ticketFk]); const claimableSales = await Self.rawSql(query, [ticketFk], myOptions);
return claimableSales; return claimableSales;
}; };

View File

@ -20,20 +20,39 @@ module.exports = Self => {
} }
}); });
Self.recalculatePrice = async(ctx, id) => { Self.recalculatePrice = async(ctx, id, options) => {
const models = Self.app.models; const models = Self.app.models;
const myOptions = {};
let tx;
const sale = await Self.findById(id); if (typeof options == 'object')
const isEditable = await models.Ticket.isEditable(ctx, sale.ticketFk); Object.assign(myOptions, options);
if (!myOptions.transaction) {
tx = await Self.beginTransaction({});
myOptions.transaction = tx;
}
try {
const sale = await Self.findById(id, null, myOptions);
const isEditable = await models.Ticket.isEditable(ctx, sale.ticketFk, myOptions);
if (!isEditable) if (!isEditable)
throw new UserError(`The sales of this ticket can't be modified`); throw new UserError(`The sales of this ticket can't be modified`);
const canEditSale = await models.Sale.canEdit(ctx, [id]); const canEditSale = await models.Sale.canEdit(ctx, [id], myOptions);
if (!canEditSale) if (!canEditSale)
throw new UserError(`Sale(s) blocked, please contact production`); throw new UserError(`Sale(s) blocked, please contact production`);
return Self.rawSql('CALL vn.sale_calculateComponent(?, null)', [id]); const recalculation = await Self.rawSql('CALL vn.sale_calculateComponent(?, null)', [id], myOptions);
if (tx) await tx.commit();
return recalculation;
} catch (e) {
if (tx) await tx.rollback();
throw e;
}
}; };
}; };

View File

@ -34,15 +34,26 @@ module.exports = Self => {
} }
}); });
Self.reserve = async(ctx, ticketId, sales, reserved) => { Self.reserve = async(ctx, ticketId, sales, reserved, options) => {
const $t = ctx.req.__; // $translate const $t = ctx.req.__; // $translate
const models = Self.app.models; const models = Self.app.models;
const myOptions = {};
let tx;
const isTicketEditable = await models.Ticket.isEditable(ctx, ticketId); if (typeof options == 'object')
Object.assign(myOptions, options);
if (!myOptions.transaction) {
tx = await Self.beginTransaction({});
myOptions.transaction = tx;
}
try {
const isTicketEditable = await models.Ticket.isEditable(ctx, ticketId, myOptions);
if (!isTicketEditable) if (!isTicketEditable)
throw new UserError(`The sales of this ticket can't be modified`); throw new UserError(`The sales of this ticket can't be modified`);
const canEditSale = await models.Sale.canEdit(ctx, sales); const canEditSale = await models.Sale.canEdit(ctx, sales, myOptions);
if (!canEditSale) if (!canEditSale)
throw new UserError(`Sale(s) blocked, please contact production`); throw new UserError(`Sale(s) blocked, please contact production`);
@ -55,7 +66,7 @@ module.exports = Self => {
const oldState = sale.reserved ? 'reserved' : 'regular'; const oldState = sale.reserved ? 'reserved' : 'regular';
const newState = reserved ? 'reserved' : 'regular'; const newState = reserved ? 'reserved' : 'regular';
const reservedSale = models.Sale.update({id: sale.id}, {reserved: reserved}); const reservedSale = models.Sale.updateAll({id: sale.id}, {reserved: reserved}, myOptions);
promises.push(reservedSale); promises.push(reservedSale);
@ -77,7 +88,7 @@ module.exports = Self => {
} }
} }
} }
}); }, myOptions);
const salesPerson = ticket.client().salesPersonUser(); const salesPerson = ticket.client().salesPersonUser();
if (salesPerson) { if (salesPerson) {
@ -88,9 +99,15 @@ module.exports = Self => {
ticketUrl: `${origin}/#!/ticket/${ticketId}/sale`, ticketUrl: `${origin}/#!/ticket/${ticketId}/sale`,
changes: changesMade changes: changesMade
}); });
await models.Chat.sendCheckingPresence(ctx, salesPerson.id, message); await models.Chat.sendCheckingPresence(ctx, salesPerson.id, message, myOptions);
} }
if (tx) await tx.commit();
return result; return result;
} catch (e) {
if (tx) await tx.rollback();
throw e;
}
}; };
}; };

View File

@ -1,36 +1,69 @@
const app = require('vn-loopback/server/server'); const models = require('vn-loopback/server/server').models;
describe('sale canEdit()', () => { describe('sale canEdit()', () => {
it('should return true if the role is production regardless of the saleTrackings', async() => { it('should return true if the role is production regardless of the saleTrackings', async() => {
const tx = await models.Sale.beginTransaction({});
try {
const options = {transaction: tx};
const productionUserID = 49; const productionUserID = 49;
let ctx = {req: {accessToken: {userId: productionUserID}}}; const ctx = {req: {accessToken: {userId: productionUserID}}};
const sales = [{id: 3}]; const sales = [{id: 3}];
const result = await app.models.Sale.canEdit(ctx, sales); const result = await models.Sale.canEdit(ctx, sales, options);
expect(result).toEqual(true); expect(result).toEqual(true);
await tx.rollback();
} catch (e) {
await tx.rollback();
throw e;
}
}); });
it('should return true if the role is not production and none of the sales has saleTracking', async() => { it('should return true if the role is not production and none of the sales has saleTracking', async() => {
const tx = await models.Sale.beginTransaction({});
try {
const options = {transaction: tx};
const salesPersonUserID = 18; const salesPersonUserID = 18;
let ctx = {req: {accessToken: {userId: salesPersonUserID}}}; const ctx = {req: {accessToken: {userId: salesPersonUserID}}};
const sales = [{id: 10}]; const sales = [{id: 10}];
const result = await app.models.Sale.canEdit(ctx, sales); const result = await models.Sale.canEdit(ctx, sales, options);
expect(result).toEqual(true); expect(result).toEqual(true);
await tx.rollback();
} catch (e) {
await tx.rollback();
throw e;
}
}); });
it('should return false if any of the sales has a saleTracking record', async() => { it('should return false if any of the sales has a saleTracking record', async() => {
const tx = await models.Sale.beginTransaction({});
try {
const options = {transaction: tx};
const salesPersonUserID = 18; const salesPersonUserID = 18;
let ctx = {req: {accessToken: {userId: salesPersonUserID}}}; const ctx = {req: {accessToken: {userId: salesPersonUserID}}};
const sales = [{id: 3}]; const sales = [{id: 3}];
const result = await app.models.Sale.canEdit(ctx, sales); const result = await models.Sale.canEdit(ctx, sales, options);
expect(result).toEqual(false); expect(result).toEqual(false);
await tx.rollback();
} catch (e) {
await tx.rollback();
throw e;
}
}); });
}); });

View File

@ -1,23 +1,14 @@
const app = require('vn-loopback/server/server'); const models = require('vn-loopback/server/server').models;
describe('sale deleteSales()', () => { describe('sale deleteSales()', () => {
let sale;
let newSale;
beforeAll(async done => {
try {
sale = await app.models.Sale.findOne({where: {id: 9}});
sale.id = null;
newSale = await app.models.Sale.create(sale);
} catch (error) {
console.error(error);
}
done();
});
it('should throw an error if the ticket of the given sales is not editable', async() => { it('should throw an error if the ticket of the given sales is not editable', async() => {
let ctx = { const tx = await models.Sale.beginTransaction({});
let error;
try {
const options = {transaction: tx};
const ctx = {
req: { req: {
accessToken: {userId: 9}, accessToken: {userId: 9},
headers: {origin: 'localhost:5000'}, headers: {origin: 'localhost:5000'},
@ -25,14 +16,14 @@ describe('sale deleteSales()', () => {
} }
}; };
let error;
const sales = [{id: 1, instance: 0}, {id: 2, instance: 1}]; const sales = [{id: 1, instance: 0}, {id: 2, instance: 1}];
const ticketId = 2; const ticketId = 2;
try { await models.Sale.deleteSales(ctx, sales, ticketId, options);
await app.models.Sale.deleteSales(ctx, sales, ticketId);
await tx.rollback();
} catch (e) { } catch (e) {
await tx.rollback();
error = e; error = e;
} }
@ -40,19 +31,33 @@ describe('sale deleteSales()', () => {
}); });
it('should delete the sale', async() => { it('should delete the sale', async() => {
let ctx = { const tx = await models.Sale.beginTransaction({});
try {
const options = {transaction: tx};
const ctx = {
req: { req: {
accessToken: {userId: 9}, accessToken: {userId: 9},
headers: {origin: 'localhost:5000'}, headers: {origin: 'localhost:5000'},
__: () => {} __: () => {}
} }
}; };
const sale = await models.Sale.findOne({where: {id: 9}}, options);
sale.id = null;
const newSale = await models.Sale.create(sale, options);
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); const deletions = await models.Sale.deleteSales(ctx, sales, ticketId, options);
expect(res).toEqual([{count: 1}]); expect(deletions).toEqual([{count: 1}]);
await tx.rollback();
} catch (e) {
await tx.rollback();
throw e;
}
}); });
}); });

View File

@ -1,10 +1,21 @@
const app = require('vn-loopback/server/server'); const models = require('vn-loopback/server/server').models;
describe('sale getClaimableFromTicket()', () => { describe('sale getClaimableFromTicket()', () => {
it('should return the claimable sales of a given ticket', async() => { it('should return the claimable sales of a given ticket', async() => {
let claimableFromTicket = await app.models.Sale.getClaimableFromTicket(16); const tx = await models.Sale.beginTransaction({});
try {
const options = {transaction: tx};
const claimableFromTicket = await models.Sale.getClaimableFromTicket(16, options);
expect(claimableFromTicket[0].concept).toBe('Ranged weapon longbow 2m'); expect(claimableFromTicket[0].concept).toBe('Ranged weapon longbow 2m');
expect(claimableFromTicket.length).toBe(3); expect(claimableFromTicket.length).toBe(3);
await tx.rollback();
} catch (e) {
await tx.rollback();
throw e;
}
}); });
}); });

View File

@ -1,24 +1,43 @@
const app = require('vn-loopback/server/server'); const models = require('vn-loopback/server/server').models;
describe('sale recalculatePrice()', () => { describe('sale recalculatePrice()', () => {
const saleId = 7; const saleId = 7;
it('should update the sale price', async() => { it('should update the sale price', async() => {
const tx = await models.Sale.beginTransaction({});
try {
const options = {transaction: tx};
const ctx = {req: {accessToken: {userId: 9}}}; const ctx = {req: {accessToken: {userId: 9}}};
const response = await app.models.Sale.recalculatePrice(ctx, saleId); const response = await models.Sale.recalculatePrice(ctx, saleId, options);
expect(response.affectedRows).toBeDefined(); expect(response.affectedRows).toBeDefined();
await tx.rollback();
} catch (e) {
await tx.rollback();
throw e;
}
}); });
it('should throw an error if the ticket is not editable', async() => { it('should throw an error if the ticket is not editable', async() => {
const tx = await models.Sale.beginTransaction({});
let error;
try {
const options = {transaction: tx};
const ctx = {req: {accessToken: {userId: 9}}}; const ctx = {req: {accessToken: {userId: 9}}};
const immutableSaleId = 1; const immutableSaleId = 1;
await app.models.Sale.recalculatePrice(ctx, immutableSaleId) await models.Sale.recalculatePrice(ctx, immutableSaleId, options);
.catch(response => {
expect(response).toEqual(new Error(`The sales of this ticket can't be modified`));
error = response;
});
expect(error).toBeDefined(); await tx.rollback();
} catch (e) {
await tx.rollback();
error = e;
}
expect(error).toEqual(new Error(`The sales of this ticket can't be modified`));
}); });
}); });

View File

@ -1,4 +1,4 @@
const app = require('vn-loopback/server/server'); const models = require('vn-loopback/server/server').models;
describe('sale reserve()', () => { describe('sale reserve()', () => {
const ctx = { const ctx = {
@ -9,38 +9,35 @@ describe('sale reserve()', () => {
} }
}; };
afterAll(async done => {
let ctx = {req: {accessToken: {userId: 9}}};
let params = {
sales: [
{id: 7},
{id: 8}],
ticketFk: 11,
reserved: false
};
await app.models.Sale.reserve(ctx, params);
done();
});
it('should throw an error if the ticket can not be modified', async() => { it('should throw an error if the ticket can not be modified', async() => {
const tx = await models.Sale.beginTransaction({});
let error; let error;
try {
const options = {transaction: tx};
const ticketId = 2; const ticketId = 2;
const sales = [{id: 5}]; const sales = [{id: 5}];
const reserved = false; const reserved = false;
await app.models.Sale.reserve(ctx, ticketId, sales, reserved) await models.Sale.reserve(ctx, ticketId, sales, reserved, options);
.catch(response => {
expect(response).toEqual(new Error(`The sales of this ticket can't be modified`));
error = response;
});
expect(error).toBeDefined(); await tx.rollback();
} catch (e) {
await tx.rollback();
error = e;
}
expect(error).toEqual(new Error(`The sales of this ticket can't be modified`));
}); });
it('should update the given sales of a ticket to reserved', async() => { it('should update the given sales of a ticket to reserved', async() => {
originalTicketSales = await app.models.Ticket.getSales(11); const tx = await models.Sale.beginTransaction({});
try {
const options = {transaction: tx};
originalTicketSales = await models.Ticket.getSales(11, options);
expect(originalTicketSales[0].reserved).toEqual(false); expect(originalTicketSales[0].reserved).toEqual(false);
expect(originalTicketSales[1].reserved).toEqual(false); expect(originalTicketSales[1].reserved).toEqual(false);
@ -49,11 +46,17 @@ describe('sale reserve()', () => {
const sales = [{id: 7}, {id: 8}]; const sales = [{id: 7}, {id: 8}];
const reserved = true; const reserved = true;
await app.models.Sale.reserve(ctx, ticketId, sales, reserved); await models.Sale.reserve(ctx, ticketId, sales, reserved, options);
const reservedTicketSales = await app.models.Ticket.getSales(ticketId); const reservedTicketSales = await models.Ticket.getSales(ticketId, options);
expect(reservedTicketSales[0].reserved).toEqual(true); expect(reservedTicketSales[0].reserved).toEqual(true);
expect(reservedTicketSales[1].reserved).toEqual(true); expect(reservedTicketSales[1].reserved).toEqual(true);
await tx.rollback();
} catch (e) {
await tx.rollback();
throw e;
}
}); });
}); });

View File

@ -1,40 +1,45 @@
const app = require('vn-loopback/server/server'); const models = require('vn-loopback/server/server').models;
describe('sale updateConcept()', () => { describe('sale updateConcept()', () => {
const ctx = {req: {accessToken: {userId: 9}}}; const ctx = {req: {accessToken: {userId: 9}}};
const saleId = 1; const saleId = 1;
let originalSale;
beforeAll(async done => {
originalSale = await app.models.Sale.findById(saleId);
done();
});
afterAll(async done => {
await originalSale.save();
done();
});
it('should throw if ID was undefined', async() => { it('should throw if ID was undefined', async() => {
let err; const tx = await models.Sale.beginTransaction({});
const newConcept = 'I am he new concept';
let error;
try { try {
await app.models.Sale.updateConcept(ctx, undefined, newConcept); const options = {transaction: tx};
const newConcept = 'not going to heppen';
await models.Sale.updateConcept(ctx, undefined, newConcept, options);
await tx.rollback();
} catch (e) { } catch (e) {
err = e; await tx.rollback();
error = e;
} }
expect(err).toBeDefined(); expect(error).toBeDefined();
}); });
it('should update the sale concept', async() => { it('should update the sale concept', async() => {
const tx = await models.Sale.beginTransaction({});
try {
const options = {transaction: tx};
const newConcept = 'I am the new concept'; const newConcept = 'I am the new concept';
let response = await app.models.Sale.updateConcept(ctx, saleId, newConcept); let response = await models.Sale.updateConcept(ctx, saleId, newConcept, options);
expect(response.concept).toEqual(newConcept); expect(response.concept).toEqual(newConcept);
await tx.rollback();
} catch (e) {
await tx.rollback();
throw e;
}
}); });
}); });

View File

@ -1,22 +1,6 @@
const app = require('vn-loopback/server/server'); const models = require('vn-loopback/server/server').models;
describe('sale updatePrice()', () => { describe('sale updatePrice()', () => {
let originalSale;
let originalSalesPersonMana;
let createdSaleComponent;
let saleId = 7;
let manaComponentId;
beforeAll(async done => {
let component = await app.models.Component.findOne({where: {code: 'mana'}});
manaComponentId = component.id;
originalSale = await app.models.Sale.findById(saleId);
originalSalesPersonMana = await app.models.WorkerMana.findById(18);
done();
});
it('should throw an error if the ticket is not editable', async() => {
const ctx = { const ctx = {
req: { req: {
accessToken: {userId: 18}, accessToken: {userId: 18},
@ -24,81 +8,93 @@ describe('sale updatePrice()', () => {
__: () => {} __: () => {}
} }
}; };
let immutableSaleId = 1; const saleId = 7;
let price = 5;
await app.models.Sale.updatePrice(ctx, immutableSaleId, price) it('should throw an error if the ticket is not editable', async() => {
.catch(response => { const tx = await models.Sale.beginTransaction({});
expect(response).toEqual(new Error(`The sales of this ticket can't be modified`));
error = response;
});
expect(error).toBeDefined(); try {
const options = {transaction: tx};
const immutableSaleId = 1;
const price = 5;
await models.Sale.updatePrice(ctx, immutableSaleId, price, options);
await tx.rollback();
} catch (e) {
await tx.rollback();
error = e;
}
expect(error).toEqual(new Error(`The sales of this ticket can't be modified`));
}); });
it('should return 0 if the price is an empty string', async() => { it('should return 0 if the price is an empty string', async() => {
const ctx = { const tx = await models.Sale.beginTransaction({});
req: {
accessToken: {userId: 18},
headers: {origin: 'localhost:5000'},
__: () => {}
}
};
let price = ''; try {
const options = {transaction: tx};
await app.models.Sale.updatePrice(ctx, saleId, price); const price = '';
let updatedSale = await app.models.Sale.findById(saleId);
await models.Sale.updatePrice(ctx, saleId, price, options);
const updatedSale = await models.Sale.findById(saleId, null, options);
expect(updatedSale.price).toEqual(0); expect(updatedSale.price).toEqual(0);
// restores await tx.rollback();
await originalSale.updateAttributes(originalSale); } catch (e) {
await app.models.SaleComponent.updateAll({componentFk: manaComponentId, saleFk: saleId}, {value: 0}); await tx.rollback();
await originalSalesPersonMana.updateAttributes(originalSalesPersonMana); throw e;
}
}); });
it('should now set price as a number in a string', async() => { it('should now set price as a number in a string', async() => {
const ctx = { const tx = await models.Sale.beginTransaction({});
req: {
accessToken: {userId: 18},
headers: {origin: 'localhost:5000'},
__: () => {}
}
};
let price = '8'; try {
const options = {transaction: tx};
await app.models.Sale.updatePrice(ctx, saleId, price); const price = '8';
let updatedSale = await app.models.Sale.findById(saleId);
await models.Sale.updatePrice(ctx, saleId, price, options);
const updatedSale = await models.Sale.findById(saleId, null, options);
expect(updatedSale.price).toEqual(8); expect(updatedSale.price).toEqual(8);
// restores await tx.rollback();
await originalSale.updateAttributes(originalSale); } catch (e) {
await app.models.SaleComponent.updateAll({componentFk: manaComponentId, saleFk: saleId}, {value: 0}); await tx.rollback();
await originalSalesPersonMana.updateAttributes(originalSalesPersonMana); throw e;
}
}); });
// #2736 sale updatePrice() returns inconsistent values it('should set price as a decimal number and check the sale has the mana component changing the salesPersonMana', async() => {
xit('should set price as a decimal number and check the sale has the mana component changing the salesPersonMana', async() => { const tx = await models.Sale.beginTransaction({});
let ctx = {req: {accessToken: {userId: 18}}};
let price = 5.4;
await app.models.Sale.updatePrice(ctx, saleId, price); try {
let updatedSale = await app.models.Sale.findById(saleId); const options = {transaction: tx};
createdSaleComponent = await app.models.SaleComponent.findOne({where: {saleFk: saleId, componentFk: manaComponentId}});
const price = 5.4;
const originalSalesPersonMana = await models.WorkerMana.findById(18, null, options);
const manaComponent = await models.Component.findOne({where: {code: 'mana'}}, options);
await models.Sale.updatePrice(ctx, saleId, price, options);
const updatedSale = await models.Sale.findById(saleId, null, options);
createdSaleComponent = await models.SaleComponent.findOne({where: {saleFk: saleId, componentFk: manaComponent.id}}, options);
expect(updatedSale.price).toBe(price); expect(updatedSale.price).toBe(price);
expect(createdSaleComponent.value).toEqual(-2.04); expect(createdSaleComponent.value).toEqual(-2.04);
let updatedSalesPersonMana = await app.models.WorkerMana.findById(18); const updatedSalesPersonMana = await models.WorkerMana.findById(18, null, options);
expect(updatedSalesPersonMana.amount).not.toEqual(originalSalesPersonMana.amount); expect(updatedSalesPersonMana.amount).not.toEqual(originalSalesPersonMana.amount);
// restores await tx.rollback();
await originalSale.updateAttributes(originalSale); } catch (e) {
await app.models.SaleComponent.updateAll({componentFk: manaComponentId, saleFk: saleId}, {value: 0}); await tx.rollback();
await originalSalesPersonMana.updateAttributes(originalSalesPersonMana); throw e;
}
}); });
}); });

View File

@ -1,4 +1,4 @@
const app = require('vn-loopback/server/server'); const models = require('vn-loopback/server/server').models;
describe('sale updateQuantity()', () => { describe('sale updateQuantity()', () => {
const ctx = { const ctx = {
@ -10,44 +10,61 @@ describe('sale updateQuantity()', () => {
}; };
it('should throw an error if the quantity is not a number', async() => { it('should throw an error if the quantity is not a number', async() => {
const tx = await models.Sale.beginTransaction({});
let error; let error;
try {
const options = {transaction: tx};
await app.models.Sale.updateQuantity(ctx, 1, 'wrong quantity!') await models.Sale.updateQuantity(ctx, 1, 'wrong quantity!', options);
.catch(response => {
expect(response).toEqual(new Error('The value should be a number'));
error = response;
});
expect(error).toBeDefined(); await tx.rollback();
} catch (e) {
await tx.rollback();
error = e;
}
expect(error).toEqual(new Error('The value should be a number'));
}); });
it('should throw an error if the quantity is greater than it should be', async() => { it('should throw an error if the quantity is greater than it should be', async() => {
const tx = await models.Sale.beginTransaction({});
let error; let error;
try {
const options = {transaction: tx};
await app.models.Sale.updateQuantity(ctx, 1, 99) await models.Sale.updateQuantity(ctx, 1, 99, options);
.catch(response => {
expect(response).toEqual(new Error('The new quantity should be smaller than the old one'));
error = response;
});
expect(error).toBeDefined(); await tx.rollback();
} catch (e) {
await tx.rollback();
error = e;
}
expect(error).toEqual(new Error('The new quantity should be smaller than the old one'));
}); });
it('should update the quantity of a given sale current line', async() => { it('should update the quantity of a given sale current line', async() => {
let originalLineData = await app.models.Sale.findOne({where: {id: 1}, fields: ['quantity']}); const tx = await models.Sale.beginTransaction({});
expect(originalLineData.quantity).toEqual(5); try {
const options = {transaction: tx};
await app.models.Sale.updateQuantity(ctx, 1, 4); const originalLine = await models.Sale.findOne({where: {id: 1}, fields: ['quantity']}, options);
let modifiedLineData = await app.models.Sale.findOne({where: {id: 1}, fields: ['quantity']}); expect(originalLine.quantity).toEqual(5);
expect(modifiedLineData.quantity).toEqual(4); await models.Sale.updateQuantity(ctx, 1, 4, options);
await app.models.Sale.update({id: 1}, {quantity: 5}); const modifiedLine = await models.Sale.findOne({where: {id: 1}, fields: ['quantity']}, options);
let resetLineDataValues = await app.models.Sale.findOne({where: {id: 1}, fields: ['quantity']}); expect(modifiedLine.quantity).toEqual(4);
expect(resetLineDataValues.quantity).toEqual(5); await tx.rollback();
} catch (e) {
await tx.rollback();
throw e;
}
}); });
}); });

View File

@ -24,15 +24,35 @@ module.exports = Self => {
} }
}); });
Self.updateConcept = async(ctx, id, newConcept) => { Self.updateConcept = async(ctx, id, newConcept, options) => {
const models = Self.app.models; const models = Self.app.models;
const currentLine = await models.Sale.findById(id); const myOptions = {};
let tx;
const canEditSale = await models.Sale.canEdit(ctx, [id]); if (typeof options == 'object')
Object.assign(myOptions, options);
if (!myOptions.transaction) {
tx = await Self.beginTransaction({});
myOptions.transaction = tx;
}
try {
const currentLine = await models.Sale.findById(id, null, myOptions);
const canEditSale = await models.Sale.canEdit(ctx, [id], myOptions);
if (!canEditSale) if (!canEditSale)
throw new UserError(`Sale(s) blocked, please contact production`); throw new UserError(`Sale(s) blocked, please contact production`);
return await currentLine.updateAttributes({concept: newConcept}); const line = await currentLine.updateAttributes({concept: newConcept}, myOptions);
if (tx) await tx.commit();
return line;
} catch (e) {
if (tx) await tx.rollback();
throw e;
}
}; };
}; };

View File

@ -28,14 +28,21 @@ module.exports = Self => {
} }
}); });
Self.updatePrice = async(ctx, id, newPrice) => { Self.updatePrice = async(ctx, id, newPrice, options) => {
const $t = ctx.req.__; // $translate const $t = ctx.req.__; // $translate
const models = Self.app.models; const models = Self.app.models;
const tx = await Self.beginTransaction({}); const myOptions = {};
let tx;
if (typeof options == 'object')
Object.assign(myOptions, options);
if (!myOptions.transaction) {
tx = await Self.beginTransaction({});
myOptions.transaction = tx;
}
try { try {
const options = {transaction: tx};
const filter = { const filter = {
include: { include: {
relation: 'ticket', relation: 'ticket',
@ -57,22 +64,22 @@ module.exports = Self => {
} }
}; };
const sale = await models.Sale.findById(id, filter, options); const sale = await models.Sale.findById(id, filter, myOptions);
const isEditable = await models.Ticket.isEditable(ctx, sale.ticketFk); const isEditable = await models.Ticket.isEditable(ctx, sale.ticketFk, myOptions);
if (!isEditable) if (!isEditable)
throw new UserError(`The sales of this ticket can't be modified`); throw new UserError(`The sales of this ticket can't be modified`);
const canEditSale = await models.Sale.canEdit(ctx, [id]); const canEditSale = await models.Sale.canEdit(ctx, [id], myOptions);
if (!canEditSale) if (!canEditSale)
throw new UserError(`Sale(s) blocked, please contact production`); throw new UserError(`Sale(s) blocked, please contact production`);
const oldPrice = sale.price; const oldPrice = sale.price;
const userId = ctx.req.accessToken.userId; const userId = ctx.req.accessToken.userId;
const usesMana = await models.WorkerMana.findOne({where: {workerFk: userId}, fields: 'amount'}, options); const usesMana = await models.WorkerMana.findOne({where: {workerFk: userId}, fields: 'amount'}, myOptions);
const componentCode = usesMana ? 'mana' : 'buyerDiscount'; const componentCode = usesMana ? 'mana' : 'buyerDiscount';
const discount = await models.Component.findOne({where: {code: componentCode}}, options); const discount = await models.Component.findOne({where: {code: componentCode}}, myOptions);
const componentId = discount.id; const componentId = discount.id;
const componentValue = newPrice - sale.price; const componentValue = newPrice - sale.price;
@ -80,23 +87,23 @@ module.exports = Self => {
componentFk: componentId, componentFk: componentId,
saleFk: id saleFk: id
}; };
const saleComponent = await models.SaleComponent.findOne({where}, options); const saleComponent = await models.SaleComponent.findOne({where}, myOptions);
if (saleComponent) { if (saleComponent) {
await models.SaleComponent.updateAll(where, { await models.SaleComponent.updateAll(where, {
value: saleComponent.value + componentValue value: saleComponent.value + componentValue
}, options); }, myOptions);
} else { } else {
await models.SaleComponent.create({ await models.SaleComponent.create({
saleFk: id, saleFk: id,
componentFk: componentId, componentFk: componentId,
value: componentValue value: componentValue
}, options); }, myOptions);
} }
await sale.updateAttributes({price: newPrice}, options); await sale.updateAttributes({price: newPrice}, myOptions);
query = `CALL vn.manaSpellersRequery(?)`; query = `CALL vn.manaSpellersRequery(?)`;
await Self.rawSql(query, [userId], options); await Self.rawSql(query, [userId], myOptions);
const salesPerson = sale.ticket().client().salesPersonUser(); const salesPerson = sale.ticket().client().salesPersonUser();
if (salesPerson) { if (salesPerson) {
@ -111,14 +118,14 @@ module.exports = Self => {
ticketUrl: `${origin}/#!/ticket/${sale.ticket().id}/sale`, ticketUrl: `${origin}/#!/ticket/${sale.ticket().id}/sale`,
itemUrl: `${origin}/#!/item/${sale.itemFk}/summary` itemUrl: `${origin}/#!/item/${sale.itemFk}/summary`
}); });
await models.Chat.sendCheckingPresence(ctx, salesPerson.id, message); await models.Chat.sendCheckingPresence(ctx, salesPerson.id, message, myOptions);
} }
await tx.commit(); if (tx) await tx.commit();
return sale; return sale;
} catch (error) { } catch (error) {
await tx.rollback(); if (tx) await tx.rollback();
throw error; throw error;
} }
}; };

View File

@ -26,11 +26,22 @@ module.exports = Self => {
} }
}); });
Self.updateQuantity = async(ctx, id, newQuantity) => { Self.updateQuantity = async(ctx, id, newQuantity, options) => {
const $t = ctx.req.__; // $translate
const models = Self.app.models; const models = Self.app.models;
const $t = ctx.req.__; // $translate
const myOptions = {};
let tx;
const canEditSale = await models.Sale.canEdit(ctx, [id]); if (typeof options == 'object')
Object.assign(myOptions, options);
if (!myOptions.transaction) {
tx = await Self.beginTransaction({});
myOptions.transaction = tx;
}
try {
const canEditSale = await models.Sale.canEdit(ctx, [id], myOptions);
if (!canEditSale) if (!canEditSale)
throw new UserError(`Sale(s) blocked, please contact production`); throw new UserError(`Sale(s) blocked, please contact production`);
@ -57,13 +68,13 @@ module.exports = Self => {
} }
}; };
const sale = await models.Sale.findById(id, filter); const sale = await models.Sale.findById(id, filter, myOptions);
if (newQuantity > sale.quantity) if (newQuantity > sale.quantity)
throw new UserError('The new quantity should be smaller than the old one'); throw new UserError('The new quantity should be smaller than the old one');
const oldQuantity = sale.quantity; const oldQuantity = sale.quantity;
const result = await sale.updateAttributes({quantity: newQuantity}); const result = await sale.updateAttributes({quantity: newQuantity}, myOptions);
const salesPerson = sale.ticket().client().salesPersonUser(); const salesPerson = sale.ticket().client().salesPersonUser();
if (salesPerson) { if (salesPerson) {
@ -77,9 +88,15 @@ module.exports = Self => {
ticketUrl: `${origin}/#!/ticket/${sale.ticket().id}/sale`, ticketUrl: `${origin}/#!/ticket/${sale.ticket().id}/sale`,
itemUrl: `${origin}/#!/item/${sale.itemFk}/summary` itemUrl: `${origin}/#!/item/${sale.itemFk}/summary`
}); });
await models.Chat.sendCheckingPresence(ctx, salesPerson.id, message); await models.Chat.sendCheckingPresence(ctx, salesPerson.id, message, myOptions);
} }
if (tx) await tx.commit();
return result; return result;
} catch (error) {
if (tx) await tx.rollback();
throw error;
}
}; };
}; };

View File

@ -1,4 +1,3 @@
module.exports = Self => { module.exports = Self => {
Self.remoteMethodCtx('editableStates', { Self.remoteMethodCtx('editableStates', {
description: 'Gets the editable states according the user role ', description: 'Gets the editable states according the user role ',
@ -8,7 +7,7 @@ module.exports = Self => {
type: 'object' type: 'object'
}, },
returns: { returns: {
type: ['Object'], type: ['object'],
root: true root: true
}, },
http: { http: {
@ -17,14 +16,18 @@ module.exports = Self => {
} }
}); });
Self.editableStates = async(ctx, filter) => { Self.editableStates = async(ctx, filter, options) => {
let userId = ctx.req.accessToken.userId; const models = Self.app.models;
let models = Self.app.models; const userId = ctx.req.accessToken.userId;
let statesList = await models.State.find({where: filter.where}); const myOptions = {};
let isProduction = await models.Account.hasRole(userId, 'production'); if (typeof options == 'object')
let isSalesPerson = await models.Account.hasRole(userId, 'salesPerson'); Object.assign(myOptions, options);
let isAdministrative = await models.Account.hasRole(userId, 'administrative');
let statesList = await models.State.find({where: filter.where}, myOptions);
const isProduction = await models.Account.hasRole(userId, 'production', myOptions);
const isSalesPerson = await models.Account.hasRole(userId, 'salesPerson', myOptions);
const isAdministrative = await models.Account.hasRole(userId, 'administrative', myOptions);
if (isProduction || isAdministrative) if (isProduction || isAdministrative)
return statesList; return statesList;

View File

@ -18,19 +18,23 @@ module.exports = Self => {
} }
}); });
Self.isEditable = async(ctx, stateId) => { Self.isEditable = async(ctx, stateId, options) => {
const accessToken = ctx.req.accessToken; const accessToken = ctx.req.accessToken;
const models = Self.app.models; const models = Self.app.models;
const userId = accessToken.userId; const userId = accessToken.userId;
const myOptions = {};
let isProduction = await models.Account.hasRole(userId, 'production'); if (typeof options == 'object')
let isSalesPerson = await models.Account.hasRole(userId, 'salesPerson'); Object.assign(myOptions, options);
let isAdministrative = await models.Account.hasRole(userId, 'administrative');
let state = await models.State.findById(stateId);
let salesPersonAllowed = (isSalesPerson && (state.code == 'PICKER_DESIGNED' || state.code == 'PRINTED')); const isProduction = await models.Account.hasRole(userId, 'production', myOptions);
const isSalesPerson = await models.Account.hasRole(userId, 'salesPerson', myOptions);
const isAdministrative = await models.Account.hasRole(userId, 'administrative', myOptions);
const state = await models.State.findById(stateId, null, myOptions);
let isAllowed = isProduction || isAdministrative || salesPersonAllowed || state.alertLevel == 0; const salesPersonAllowed = (isSalesPerson && (state.code == 'PICKER_DESIGNED' || state.code == 'PRINTED'));
const isAllowed = isProduction || isAdministrative || salesPersonAllowed || state.alertLevel == 0;
return isAllowed; return isAllowed;
}; };
}; };

View File

@ -1,34 +1,73 @@
const app = require('vn-loopback/server/server'); const models = require('vn-loopback/server/server').models;
describe('ticket editableStates()', () => { describe('ticket editableStates()', () => {
const filter = {where: {name: {like: '%%'}}}; const filter = {where: {name: {like: '%%'}}};
it('should return the expected state for the given role', async() => { it('should return the expected state for the given role', async() => {
const tx = await models.State.beginTransaction({});
try {
const options = {transaction: tx};
const productionRole = 49; const productionRole = 49;
const ctx = {req: {accessToken: {userId: productionRole}}}; const ctx = {req: {accessToken: {userId: productionRole}}};
let result = await app.models.State.editableStates(ctx, filter); const editableStates = await models.State.editableStates(ctx, filter, options);
let deliveredState = result.some(state => state.code == 'DELIVERED');
const deliveredState = editableStates.some(state => state.code == 'DELIVERED');
expect(deliveredState).toBeTruthy(); expect(deliveredState).toBeTruthy();
await tx.rollback();
} catch (e) {
await tx.rollback();
throw e;
}
}); });
it(`should return the expected states by a specific role`, async() => { it(`should return the expected states by a specific role`, async() => {
const tx = await models.State.beginTransaction({});
try {
const options = {transaction: tx};
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 deliveredState = result.some(state => state.code == 'DELIVERED'); const editableStates = await models.State.editableStates(ctx, filter, options);
let pickerDesignedState = result.some(state => state.code == 'PICKER_DESIGNED');
const deliveredState = editableStates.some(state => state.code == 'DELIVERED');
const pickerDesignedState = editableStates.some(state => state.code == 'PICKER_DESIGNED');
expect(deliveredState).toBeFalsy(); expect(deliveredState).toBeFalsy();
expect(pickerDesignedState).toBeTruthy(); expect(pickerDesignedState).toBeTruthy();
await tx.rollback();
} catch (e) {
await tx.rollback();
throw e;
}
}); });
it(`should return again the expected state by a specific role`, async() => { it(`should return again the expected state by a specific role`, async() => {
const tx = await models.State.beginTransaction({});
try {
const options = {transaction: tx};
const employeeRole = 1; const employeeRole = 1;
const ctx = {req: {accessToken: {userId: employeeRole}}}; const ctx = {req: {accessToken: {userId: employeeRole}}};
let result = await app.models.State.editableStates(ctx, filter);
let pickerDesignedState = result.some(state => state.code == 'PICKER_DESIGNED'); const editableStates = await models.State.editableStates(ctx, filter, options);
const pickerDesignedState = editableStates.some(state => state.code == 'PICKER_DESIGNED');
expect(pickerDesignedState).toBeTruthy(); expect(pickerDesignedState).toBeTruthy();
await tx.rollback();
} catch (e) {
await tx.rollback();
throw e;
}
}); });
}); });

View File

@ -1,61 +1,127 @@
const app = require('vn-loopback/server/server'); const models = require('vn-loopback/server/server').models;
describe('state isEditable()', () => { describe('state isEditable()', () => {
it('should return false if the state is not editable by a specific role', async() => { it('should return false if the state is not editable by a specific role', async() => {
const tx = await models.State.beginTransaction({});
try {
const options = {transaction: tx};
const salesPersonRole = 18; const salesPersonRole = 18;
const onDeliveryState = 13; const onDeliveryState = 13;
let ctx = {req: {accessToken: {userId: salesPersonRole}}}; const ctx = {req: {accessToken: {userId: salesPersonRole}}};
let result = await app.models.State.isEditable(ctx, onDeliveryState); const result = await models.State.isEditable(ctx, onDeliveryState, options);
expect(result).toBe(false); expect(result).toBe(false);
await tx.rollback();
} catch (e) {
await tx.rollback();
throw e;
}
}); });
it('should return true if the state is editable by a specific role', async() => { it('should return true if the state is editable by a specific role', async() => {
const tx = await models.State.beginTransaction({});
try {
const options = {transaction: tx};
const salesPersonRole = 18; const salesPersonRole = 18;
const asignedState = 20; const asignedState = 20;
let ctx = {req: {accessToken: {userId: salesPersonRole}}}; const ctx = {req: {accessToken: {userId: salesPersonRole}}};
let result = await app.models.State.isEditable(ctx, asignedState); const result = await models.State.isEditable(ctx, asignedState, options);
expect(result).toBe(true); expect(result).toBe(true);
await tx.rollback();
} catch (e) {
await tx.rollback();
throw e;
}
}); });
it('should return true again if the state is editable by a specific role', async() => { it('should return true again if the state is editable by a specific role', async() => {
const tx = await models.State.beginTransaction({});
try {
const options = {transaction: tx};
const employeeRole = 1; const employeeRole = 1;
const fixingState = 1; const fixingState = 1;
let ctx = {req: {accessToken: {userId: employeeRole}}}; const ctx = {req: {accessToken: {userId: employeeRole}}};
let result = await app.models.State.isEditable(ctx, fixingState); const result = await models.State.isEditable(ctx, fixingState, options);
expect(result).toBe(true); expect(result).toBe(true);
await tx.rollback();
} catch (e) {
await tx.rollback();
throw e;
}
}); });
it('should return false if the state is not editable for the given role', async() => { it('should return false if the state is not editable for the given role', async() => {
const tx = await models.State.beginTransaction({});
try {
const options = {transaction: tx};
const employeeRole = 1; const employeeRole = 1;
const asignedState = 13; const asignedState = 13;
let ctx = {req: {accessToken: {userId: employeeRole}}}; const ctx = {req: {accessToken: {userId: employeeRole}}};
let result = await app.models.State.isEditable(ctx, asignedState); const result = await models.State.isEditable(ctx, asignedState, options);
expect(result).toBe(false); expect(result).toBe(false);
await tx.rollback();
} catch (e) {
await tx.rollback();
throw e;
}
}); });
it('should return true if the state is editable for the given role', async() => { it('should return true if the state is editable for the given role', async() => {
const tx = await models.State.beginTransaction({});
try {
const options = {transaction: tx};
const productionRole = 49; const productionRole = 49;
const onDeliveryState = 13; const onDeliveryState = 13;
let ctx = {req: {accessToken: {userId: productionRole}}}; const ctx = {req: {accessToken: {userId: productionRole}}};
let result = await app.models.State.isEditable(ctx, onDeliveryState); const result = await models.State.isEditable(ctx, onDeliveryState, options);
expect(result).toBe(true); expect(result).toBe(true);
await tx.rollback();
} catch (e) {
await tx.rollback();
throw e;
}
}); });
it('should return true if the ticket is editable, the role is salesPerson and the ticket state is printed', async() => { it('should return true if the ticket is editable, the role is salesPerson and the ticket state is printed', async() => {
const tx = await models.State.beginTransaction({});
try {
const options = {transaction: tx};
const salesPersonRole = 18; const salesPersonRole = 18;
const printedState = 4; const printedState = 4;
const okState = 3; const okState = 3;
const ctx = {req: {accessToken: {userId: salesPersonRole}}}; const ctx = {req: {accessToken: {userId: salesPersonRole}}};
let canEditCurrent = await app.models.State.isEditable(ctx, printedState); const canEditCurrent = await models.State.isEditable(ctx, printedState, options);
let canAsignNew = await app.models.State.isEditable(ctx, okState); const canAsignNew = await models.State.isEditable(ctx, okState, options);
let result = canEditCurrent && canAsignNew; const result = canEditCurrent && canAsignNew;
expect(result).toBe(true); expect(result).toBe(true);
await tx.rollback();
} catch (e) {
await tx.rollback();
throw e;
}
}); });
}); });

View File

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

View File

@ -1,18 +1,25 @@
const app = require('vn-loopback/server/server'); const models = require('vn-loopback/server/server').models;
describe('TicketDms removeFile()', () => { describe('TicketDms removeFile()', () => {
const ticketDmsId = 1; const ticketDmsId = 1;
it(`should return an error for a user without enough privileges`, async() => { it(`should return an error for a user without enough privileges`, async() => {
let clientId = 1101; const tx = await models.TicketDms.beginTransaction({});
let ctx = {req: {accessToken: {userId: clientId}}};
let error; let error;
await app.models.TicketDms.removeFile(ctx, ticketDmsId).catch(e => { try {
const options = {transaction: tx};
const clientId = 1101;
const ctx = {req: {accessToken: {userId: clientId}}};
await models.TicketDms.removeFile(ctx, ticketDmsId, options);
await tx.rollback();
} catch (e) {
await tx.rollback();
error = e; error = e;
}).finally(() => { }
expect(error.message).toEqual(`You don't have enough privileges`); expect(error.message).toEqual(`You don't have enough privileges`);
}); });
expect(error).toBeDefined();
});
}); });

View File

@ -6,22 +6,22 @@ module.exports = Self => {
accessType: 'WRITE', accessType: 'WRITE',
accepts: [{ accepts: [{
arg: 'id', arg: 'id',
type: 'Integer', type: 'number',
required: true, required: true,
description: 'The request ID', description: 'The request ID',
}, { }, {
arg: 'itemFk', arg: 'itemFk',
type: 'Integer', type: 'number',
required: true, required: true,
description: 'The requested item ID', description: 'The requested item ID',
}, { }, {
arg: 'quantity', arg: 'quantity',
type: 'Integer', type: 'number',
required: true, required: true,
description: 'The requested item quantity', description: 'The requested item quantity',
}], }],
returns: { returns: {
type: 'Object', type: 'object',
root: true root: true
}, },
http: { http: {
@ -30,25 +30,37 @@ module.exports = Self => {
} }
}); });
Self.confirm = async ctx => { Self.confirm = async(ctx, options) => {
const userId = ctx.req.accessToken.userId; const userId = ctx.req.accessToken.userId;
const models = Self.app.models; const models = Self.app.models;
const tx = await Self.beginTransaction({});
const $t = ctx.req.__; // $translate const $t = ctx.req.__; // $translate
let sale; const myOptions = {};
let tx;
if (typeof options == 'object')
Object.assign(myOptions, options);
if (!myOptions.transaction) {
tx = await Self.beginTransaction({});
myOptions.transaction = tx;
}
try { try {
let options = {transaction: tx}; const item = await models.Item.findById(ctx.args.itemFk, null, myOptions);
let item = await models.Item.findById(ctx.args.itemFk, null, options);
if (!item) if (!item)
throw new UserError(`That item doesn't exists`); throw new UserError(`That item doesn't exists`);
let request = await models.TicketRequest.findById(ctx.args.id, { const request = await models.TicketRequest.findById(ctx.args.id, {
include: {relation: 'ticket'} include: {relation: 'ticket'}
}, options); }, myOptions);
const itemStock = await models.Item.getVisibleAvailable(
ctx.args.itemFk,
request.ticket().warehouseFk,
request.ticket().shipped,
myOptions
);
const itemStock = await models.Item.getVisibleAvailable(ctx.args.itemFk, request.ticket().warehouseFk, request.ticket().shipped);
const isAvailable = itemStock.available > 0; const isAvailable = itemStock.available > 0;
if (!isAvailable) if (!isAvailable)
@ -57,23 +69,24 @@ module.exports = Self => {
if (request.saleFk) if (request.saleFk)
throw new UserError(`This request already contains a sale`); throw new UserError(`This request already contains a sale`);
sale = await models.Sale.create({ const sale = await models.Sale.create({
ticketFk: request.ticketFk, ticketFk: request.ticketFk,
itemFk: ctx.args.itemFk, itemFk: ctx.args.itemFk,
quantity: ctx.args.quantity, quantity: ctx.args.quantity,
concept: item.name concept: item.name
}, options); }, myOptions);
await request.updateAttributes({ await request.updateAttributes({
saleFk: sale.id, saleFk: sale.id,
itemFk: sale.itemFk, itemFk: sale.itemFk,
isOk: true isOk: true
}, options); }, myOptions);
query = `CALL vn.sale_calculateComponent(?, NULL)`; const query = `CALL vn.sale_calculateComponent(?, NULL)`;
await Self.rawSql(query, [sale.id], options); await Self.rawSql(query, [sale.id], myOptions);
const origin = ctx.req.headers.origin; const origin = ctx.req.headers.origin;
const requesterId = request.requesterFk; const requesterId = request.requesterFk;
const message = $t('Bought units from buy request', { const message = $t('Bought units from buy request', {
quantity: sale.quantity, quantity: sale.quantity,
concept: sale.concept, concept: sale.concept,
@ -82,10 +95,9 @@ module.exports = Self => {
url: `${origin}/#!/ticket/${sale.ticketFk}/summary`, url: `${origin}/#!/ticket/${sale.ticketFk}/summary`,
urlItem: `${origin}/#!/item/${sale.itemFk}/summary` urlItem: `${origin}/#!/item/${sale.itemFk}/summary`
}); });
await models.Chat.sendCheckingPresence(ctx, requesterId, message); await models.Chat.sendCheckingPresence(ctx, requesterId, message, myOptions);
// log const logRecord = {
let logRecord = {
originFk: sale.ticketFk, originFk: sale.ticketFk,
userFk: userId, userFk: userId,
action: 'update', action: 'update',
@ -99,14 +111,14 @@ module.exports = Self => {
} }
}; };
await Self.app.models.TicketLog.create(logRecord); await Self.app.models.TicketLog.create(logRecord, myOptions);
await tx.commit(); if (tx) await tx.commit();
return sale; return sale;
} catch (error) { } catch (e) {
await tx.rollback(); if (tx) await tx.rollback();
throw error; throw e;
} }
}; };
}; };

View File

@ -4,7 +4,7 @@ module.exports = Self => {
accessType: 'WRITE', accessType: 'WRITE',
accepts: [{ accepts: [{
arg: 'id', arg: 'id',
type: 'Integer', type: 'number',
required: true, required: true,
description: 'The request ID', description: 'The request ID',
}, { }, {
@ -23,17 +23,37 @@ module.exports = Self => {
} }
}); });
Self.deny = async ctx => { Self.deny = async(ctx, options) => {
let userId = ctx.req.accessToken.userId; const myOptions = {};
let worker = await Self.app.models.Worker.findOne({where: {userFk: userId}}); let tx;
let params = { if (typeof options == 'object')
Object.assign(myOptions, options);
if (!myOptions.transaction) {
tx = await Self.beginTransaction({});
myOptions.transaction = tx;
}
try {
const userId = ctx.req.accessToken.userId;
const worker = await Self.app.models.Worker.findOne({where: {userFk: userId}}, myOptions);
const params = {
isOk: false, isOk: false,
attenderFk: worker.id, attenderFk: worker.id,
response: ctx.args.observation, response: ctx.args.observation,
}; };
let request = await Self.app.models.TicketRequest.findById(ctx.args.id); const request = await Self.app.models.TicketRequest.findById(ctx.args.id, null, myOptions);
return request.updateAttributes(params); await request.updateAttributes(params, myOptions);
if (tx) await tx.commit();
return request;
} catch (e) {
if (tx) await tx.rollback();
throw e;
}
}; };
}; };

View File

@ -68,9 +68,13 @@ module.exports = Self => {
} }
}); });
Self.filter = async(ctx, filter) => { Self.filter = async(ctx, filter, options) => {
let conn = Self.dataSource.connector; const conn = Self.dataSource.connector;
let userId = ctx.req.accessToken.userId; const userId = ctx.req.accessToken.userId;
const myOptions = {};
if (typeof options == 'object')
Object.assign(myOptions, options);
if (ctx.args.mine) if (ctx.args.mine)
ctx.args.attenderFk = userId; ctx.args.attenderFk = userId;
@ -111,9 +115,7 @@ module.exports = Self => {
filter = mergeFilters(filter, {where}); filter = mergeFilters(filter, {where});
let stmt; const stmt = new ParameterizedSQL(
stmt = new ParameterizedSQL(
`SELECT `SELECT
tr.id, tr.id,
tr.ticketFk, tr.ticketFk,
@ -149,8 +151,6 @@ module.exports = Self => {
LEFT JOIN account.user ua ON ua.id = wka.userFk`); LEFT JOIN account.user ua ON ua.id = wka.userFk`);
stmt.merge(conn.makeSuffix(filter)); stmt.merge(conn.makeSuffix(filter));
let result = await conn.executeStmt(stmt); return conn.executeStmt(stmt, myOptions);
return result;
}; };
}; };

View File

@ -1,4 +1,4 @@
const app = require('vn-loopback/server/server'); const models = require('vn-loopback/server/server').models;
describe('ticket-request confirm()', () => { describe('ticket-request confirm()', () => {
let ctx = { let ctx = {
@ -12,19 +12,31 @@ describe('ticket-request confirm()', () => {
}; };
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}; const tx = await models.TicketRequest.beginTransaction({});
let error; let error;
try { try {
await app.models.TicketRequest.confirm(ctx); const options = {transaction: tx};
} catch (err) {
error = err; ctx.args = {itemFk: 999};
await models.TicketRequest.confirm(ctx, options);
await tx.rollback();
} catch (e) {
await tx.rollback();
error = e;
} }
expect(error.message).toEqual(`That item doesn't exists`); expect(error.message).toEqual(`That item doesn't exists`);
}); });
it('should throw an error if the item is not available', async() => { it('should throw an error if the item is not available', async() => {
const tx = await models.TicketRequest.beginTransaction({});
let error;
try {
const options = {transaction: tx};
const requestId = 5; const requestId = 5;
const itemId = 4; const itemId = 4;
const quantity = 99999; const quantity = 99999;
@ -35,18 +47,24 @@ describe('ticket-request confirm()', () => {
quantity: quantity quantity: quantity
}; };
let error; await models.TicketRequest.confirm(ctx, options);
try { await tx.rollback();
await app.models.TicketRequest.confirm(ctx); } catch (e) {
} catch (err) { await tx.rollback();
error = err; error = e;
} }
expect(error.message).toEqual(`This item is not available`); expect(error.message).toEqual(`This item is not available`);
}); });
it(`should throw if there's a sale id`, async() => { it(`should throw if there's a sale id`, async() => {
const tx = await models.TicketRequest.beginTransaction({});
let error;
try {
const options = {transaction: tx};
const requestId = 4; const requestId = 4;
const itemId = 1; const itemId = 1;
const quantity = 10; const quantity = 10;
@ -57,11 +75,11 @@ describe('ticket-request confirm()', () => {
quantity: quantity quantity: quantity
}; };
const request = await app.models.TicketRequest.findById(requestId); const request = await models.TicketRequest.findById(requestId, null, options);
expect(request.saleFk).toBeNull(); expect(request.saleFk).toBeNull();
await request.updateAttributes({saleFk: 2}); await request.updateAttributes({saleFk: 2}, options);
ctx.args = { ctx.args = {
itemFk: itemId, itemFk: itemId,
@ -69,17 +87,14 @@ describe('ticket-request confirm()', () => {
quantity: quantity quantity: quantity
}; };
let error; await models.TicketRequest.confirm(ctx, options);
try { await tx.rollback();
await app.models.TicketRequest.confirm(ctx); } catch (e) {
} catch (err) { await tx.rollback();
error = err; error = e;
} }
expect(error.message).toEqual(`This request already contains a sale`); expect(error.message).toEqual(`This request already contains a sale`);
// restores
await request.updateAttributes({saleFk: null});
}); });
}); });

View File

@ -1,25 +1,22 @@
const app = require('vn-loopback/server/server'); const models = require('vn-loopback/server/server').models;
describe('ticket-request deny()', () => { describe('ticket-request deny()', () => {
let request; it('should return the dinied ticket request', async() => {
afterAll(async done => { const tx = await models.TicketRequest.beginTransaction({});
let params = {
isOk: null,
attenderFk: request.attenderFk,
response: null,
};
await request.updateAttributes(params); try {
done(); const options = {transaction: tx};
});
it('should return all ticket requests', async() => { const ctx = {req: {accessToken: {userId: 9}}, args: {id: 4, observation: 'my observation'}};
let ctx = {req: {accessToken: {userId: 9}}, args: {id: 4, observation: 'my observation'}};
request = await app.models.TicketRequest.findById(ctx.args.id); const result = await models.TicketRequest.deny(ctx, options);
let result = await app.models.TicketRequest.deny(ctx);
expect(result.id).toEqual(4); expect(result.id).toEqual(4);
await tx.rollback();
} catch (e) {
await tx.rollback();
throw e;
}
}); });
}); });

View File

@ -1,85 +1,184 @@
const app = require('vn-loopback/server/server'); const models = require('vn-loopback/server/server').models;
describe('ticket-request filter()', () => { describe('ticket-request filter()', () => {
const userId = 9; const userId = 9;
let ctx = {req: {accessToken: {userId: userId}}}; let ctx = {req: {accessToken: {userId: userId}}};
it('should now return all ticket requests', async() => { it('should now return all ticket requests', async() => {
const tx = await models.TicketRequest.beginTransaction({});
try {
const options = {transaction: tx};
ctx.args = {}; ctx.args = {};
const result = await app.models.TicketRequest.filter(ctx); const result = await models.TicketRequest.filter(ctx, options);
expect(result.length).toEqual(3); expect(result.length).toEqual(3);
await tx.rollback();
} catch (e) {
await tx.rollback();
throw e;
}
}); });
it('should return the ticket request matching a generic search value which is the ticket ID', async() => { it('should return the ticket request matching a generic search value which is the ticket ID', async() => {
const tx = await models.TicketRequest.beginTransaction({});
try {
const options = {transaction: tx};
ctx.args = {search: 11}; ctx.args = {search: 11};
const result = await app.models.TicketRequest.filter(ctx); const result = await models.TicketRequest.filter(ctx, options);
const requestId = result[0].id; const requestId = result[0].id;
expect(requestId).toEqual(4); expect(requestId).toEqual(4);
await tx.rollback();
} catch (e) {
await tx.rollback();
throw e;
}
}); });
it('should return the ticket request matching a generic search value which is the client address alias', async() => { it('should return the ticket request matching a generic search value which is the client address alias', async() => {
const tx = await models.TicketRequest.beginTransaction({});
try {
const options = {transaction: tx};
ctx.args = {search: 'NY roofs'}; ctx.args = {search: 'NY roofs'};
const result = await app.models.TicketRequest.filter(ctx); const result = await models.TicketRequest.filter(ctx, options);
const requestId = result[0].id; const requestId = result[0].id;
expect(requestId).toEqual(4); expect(requestId).toEqual(4);
await tx.rollback();
} catch (e) {
await tx.rollback();
throw e;
}
}); });
it('should return the ticket request matching the ticket ID', async() => { it('should return the ticket request matching the ticket ID', async() => {
const tx = await models.TicketRequest.beginTransaction({});
try {
const options = {transaction: tx};
ctx.args = {ticketFk: 11}; ctx.args = {ticketFk: 11};
const result = await app.models.TicketRequest.filter(ctx); const result = await models.TicketRequest.filter(ctx, options);
const requestId = result[0].id; const requestId = result[0].id;
expect(requestId).toEqual(4); expect(requestId).toEqual(4);
await tx.rollback();
} catch (e) {
await tx.rollback();
throw e;
}
}); });
it('should return the ticket request matching the atender ID', async() => { it('should return the ticket request matching the atender ID', async() => {
const tx = await models.TicketRequest.beginTransaction({});
try {
const options = {transaction: tx};
ctx.args = {attenderFk: 35}; ctx.args = {attenderFk: 35};
const result = await app.models.TicketRequest.filter(ctx); const result = await models.TicketRequest.filter(ctx, options);
const requestId = result[0].id; const requestId = result[0].id;
expect(requestId).toEqual(3); expect(requestId).toEqual(3);
await tx.rollback();
} catch (e) {
await tx.rollback();
throw e;
}
}); });
it('should return the ticket request matching the isOk triple-state', async() => { it('should return the ticket request matching the isOk triple-state', async() => {
const tx = await models.TicketRequest.beginTransaction({});
try {
const options = {transaction: tx};
ctx.args = {isOk: null}; ctx.args = {isOk: null};
const result = await app.models.TicketRequest.filter(ctx); const result = await models.TicketRequest.filter(ctx, options);
const requestId = result[0].id; const requestId = result[0].id;
expect(requestId).toEqual(3); expect(requestId).toEqual(3);
await tx.rollback();
} catch (e) {
await tx.rollback();
throw e;
}
}); });
it('should return the ticket request matching the client ID', async() => { it('should return the ticket request matching the client ID', async() => {
const tx = await models.TicketRequest.beginTransaction({});
try {
const options = {transaction: tx};
ctx.args = {clientFk: 1102}; ctx.args = {clientFk: 1102};
const result = await app.models.TicketRequest.filter(ctx); const result = await models.TicketRequest.filter(ctx, options);
const requestId = result[0].id; const requestId = result[0].id;
expect(requestId).toEqual(4); expect(requestId).toEqual(4);
await tx.rollback();
} catch (e) {
await tx.rollback();
throw e;
}
}); });
it('should return the ticket request matching the warehouse ID', async() => { it('should return the ticket request matching the warehouse ID', async() => {
const tx = await models.TicketRequest.beginTransaction({});
try {
const options = {transaction: tx};
ctx.args = {warehouse: 1}; ctx.args = {warehouse: 1};
const result = await app.models.TicketRequest.filter(ctx, {order: 'id'}); const result = await models.TicketRequest.filter(ctx, {order: 'id'}, options);
const requestId = result[0].id; const requestId = result[0].id;
expect(requestId).toEqual(3); expect(requestId).toEqual(3);
await tx.rollback();
} catch (e) {
await tx.rollback();
throw e;
}
}); });
it('should return the ticket request matching the salesPerson ID', async() => { it('should return the ticket request matching the salesPerson ID', async() => {
const tx = await models.TicketRequest.beginTransaction({});
try {
const options = {transaction: tx};
ctx.args = {salesPersonFk: 18}; ctx.args = {salesPersonFk: 18};
const result = await app.models.TicketRequest.filter(ctx); const result = await models.TicketRequest.filter(ctx, options);
const requestId = result[0].id; const requestId = result[0].id;
expect(requestId).toEqual(3); expect(requestId).toEqual(3);
await tx.rollback();
} catch (e) {
await tx.rollback();
throw e;
}
}); });
}); });

View File

@ -23,38 +23,64 @@ module.exports = Self => {
} }
}); });
Self.changeState = async(ctx, params) => { Self.changeState = async(ctx, params, options) => {
let userId = ctx.req.accessToken.userId; const models = Self.app.models;
let models = Self.app.models; const myOptions = {};
let tx;
if (typeof options == 'object')
Object.assign(myOptions, options);
if (!myOptions.transaction) {
tx = await Self.beginTransaction({});
myOptions.transaction = tx;
}
try {
const userId = ctx.req.accessToken.userId;
if (!params.stateFk && !params.code) if (!params.stateFk && !params.code)
throw new UserError('State cannot be blank'); throw new UserError('State cannot be blank');
if (params.code) { if (params.code) {
let state = await models.State.findOne({where: {code: params.code}, fields: ['id']}); const state = await models.State.findOne({
where: {code: params.code},
fields: ['id']
}, myOptions);
params.stateFk = state.id; params.stateFk = state.id;
} }
if (!params.workerFk) { if (!params.workerFk) {
let worker = await models.Worker.findOne({where: {userFk: userId}}); const worker = await models.Worker.findOne({
where: {userFk: userId}
}, myOptions);
params.workerFk = worker.id; params.workerFk = worker.id;
} }
let ticketState = await models.TicketState.findById( const ticketState = await models.TicketState.findById(params.ticketFk, {
params.ticketFk, fields: ['stateFk']
{fields: ['stateFk']} }, myOptions);
);
let oldStateAllowed; let oldStateAllowed;
if (ticketState) if (ticketState)
oldStateAllowed = await models.State.isEditable(ctx, ticketState.stateFk); oldStateAllowed = await models.State.isEditable(ctx, ticketState.stateFk, myOptions);
let newStateAllowed = await models.State.isEditable(ctx, params.stateFk); const newStateAllowed = await models.State.isEditable(ctx, params.stateFk, myOptions);
let isAllowed = (!ticketState || oldStateAllowed == true) && newStateAllowed == true; const isAllowed = (!ticketState || oldStateAllowed == true) && newStateAllowed == true;
if (!isAllowed) if (!isAllowed)
throw new UserError(`You don't have enough privileges`, 'ACCESS_DENIED'); throw new UserError(`You don't have enough privileges`, 'ACCESS_DENIED');
return models.TicketTracking.create(params); const ticketTracking = await models.TicketTracking.create(params, myOptions);
if (tx) await tx.commit();
return ticketTracking;
} catch (e) {
if (tx) await tx.rollback();
throw e;
}
}; };
}; };

View File

@ -6,13 +6,13 @@ module.exports = Self => {
{ {
arg: 'ticketIds', arg: 'ticketIds',
description: 'the array of ticket ids to set as delivered', description: 'the array of ticket ids to set as delivered',
type: ['Number'], type: ['number'],
required: true, required: true,
http: {source: 'body'} http: {source: 'body'}
} }
], ],
returns: { returns: {
type: 'Object', type: 'object',
root: true root: true
}, },
http: { http: {
@ -21,30 +21,47 @@ module.exports = Self => {
} }
}); });
Self.setDelivered = async(ctx, ticketIds) => { Self.setDelivered = async(ctx, ticketIds, options) => {
let userId = ctx.req.accessToken.userId; const userId = ctx.req.accessToken.userId;
let models = Self.app.models; const models = Self.app.models;
const myOptions = {};
let tx;
let state = await models.State.findOne({ if (typeof options == 'object')
Object.assign(myOptions, options);
if (!myOptions.transaction) {
tx = await Self.beginTransaction({});
myOptions.transaction = tx;
}
try {
const state = await models.State.findOne({
where: { where: {
code: 'delivered' code: 'delivered'
}, },
fields: ['id', 'name', 'alertLevel', 'code'] fields: ['id', 'name', 'alertLevel', 'code']
}); }, myOptions);
let worker = await models.Worker.findOne({where: {userFk: userId}}); const worker = await models.Worker.findOne({where: {userFk: userId}}, myOptions);
let promises = []; const promises = [];
for (let id of ticketIds) { for (const id of ticketIds) {
let promise = models.TicketTracking.changeState(ctx, { const promise = models.TicketTracking.changeState(ctx, {
stateFk: state.id, stateFk: state.id,
workerFk: worker.id, workerFk: worker.id,
ticketFk: id ticketFk: id
}); }, myOptions);
promises.push(promise); promises.push(promise);
} }
await Promise.all(promises); await Promise.all(promises);
if (tx) await tx.commit();
return state; return state;
} catch (e) {
if (tx) await tx.rollback();
throw e;
}
}; };
}; };

View File

@ -1,110 +1,133 @@
const app = require('vn-loopback/server/server'); const models = require('vn-loopback/server/server').models;
const LoopBackContext = require('loopback-context'); const LoopBackContext = require('loopback-context');
describe('ticket changeState()', () => { describe('ticket changeState()', () => {
const salesPersonId = 18; const salesPersonId = 18;
const employeeId = 1; const employeeId = 1;
const productionId = 49; const productionId = 49;
let activeCtx = { const activeCtx = {
accessToken: {userId: 9}, accessToken: {userId: 9},
}; };
let ctx = {req: activeCtx}; const ctx = {req: activeCtx};
let ticket; const now = new Date();
const sampleTicket = {
shipped: now,
landed: now,
nickname: 'Many Places',
packages: 0,
updated: now,
priority: 1,
zoneFk: 3,
zonePrice: 5,
zoneBonus: 1,
totalWithVat: 120,
totalWithoutVat: 100,
clientFk: 1106,
warehouseFk: 1,
addressFk: 126,
routeFk: 6,
companyFk: 442,
agencyModeFk: 7
};
beforeAll(async done => { beforeAll(async() => {
spyOn(LoopBackContext, 'getCurrentContext').and.returnValue({ spyOn(LoopBackContext, 'getCurrentContext').and.returnValue({
active: activeCtx active: activeCtx
}); });
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();
});
afterAll(async done => {
try {
await app.models.Ticket.destroyById(ticket.id);
} catch (error) {
console.error(error);
}
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() => {
activeCtx.accessToken.userId = salesPersonId; const tx = await models.TicketTracking.beginTransaction({});
let params = {ticketFk: 2, stateFk: 3};
let error;
let errCode;
try { try {
await app.models.TicketTracking.changeState(ctx, params); const options = {transaction: tx};
activeCtx.accessToken.userId = salesPersonId;
const params = {ticketFk: 2, stateFk: 3};
await models.TicketTracking.changeState(ctx, params, options);
await tx.rollback();
} catch (e) { } catch (e) {
errCode = e.code; await tx.rollback();
error = e;
} }
expect(errCode).toBe('ACCESS_DENIED'); expect(error.code).toBe('ACCESS_DENIED');
}); });
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() => {
activeCtx.accessToken.userId = employeeId; const tx = await models.TicketTracking.beginTransaction({});
let params = {ticketFk: 11, stateFk: 13};
let error;
let errCode;
try { try {
await app.models.TicketTracking.changeState(ctx, params); const options = {transaction: tx};
activeCtx.accessToken.userId = employeeId;
const params = {ticketFk: 11, stateFk: 13};
await models.TicketTracking.changeState(ctx, params, options);
await tx.rollback();
} catch (e) { } catch (e) {
errCode = e.code; await tx.rollback();
error = e;
} }
expect(errCode).toBe('ACCESS_DENIED'); expect(error.code).toBe('ACCESS_DENIED');
}); });
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() => {
activeCtx.accessToken.userId = productionId; const tx = await models.TicketTracking.beginTransaction({});
let params = {ticketFk: ticket.id, stateFk: 3};
let ticketTracking = await app.models.TicketTracking.changeState(ctx, params); try {
const options = {transaction: tx};
const ticket = await models.Ticket.create(sampleTicket, options);
activeCtx.accessToken.userId = productionId;
const params = {ticketFk: ticket.id, stateFk: 3};
const ticketTracking = await models.TicketTracking.changeState(ctx, params, options);
expect(ticketTracking.__data.ticketFk).toBe(params.ticketFk); expect(ticketTracking.__data.ticketFk).toBe(params.ticketFk);
expect(ticketTracking.__data.stateFk).toBe(params.stateFk); expect(ticketTracking.__data.stateFk).toBe(params.stateFk);
expect(ticketTracking.__data.workerFk).toBe(49); expect(ticketTracking.__data.workerFk).toBe(49);
expect(ticketTracking.__data.id).toBeDefined(); expect(ticketTracking.__data.id).toBeDefined();
// restores await tx.rollback();
await app.models.TicketTracking.destroyById(ticketTracking.__data.id); } catch (e) {
await tx.rollback();
throw e;
}
}); });
it('should update the ticket tracking line when the user is salesperson, uses the state assigned and a valid worker id', 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: 18}}}; const tx = await models.TicketTracking.beginTransaction({});
let assignedState = await app.models.State.findOne({where: {code: 'PICKER_DESIGNED'}});
let params = {ticketFk: ticket.id, stateFk: assignedState.id, workerFk: 1}; try {
let res = await app.models.TicketTracking.changeState(ctx, params); const options = {transaction: tx};
const ticket = await models.Ticket.create(sampleTicket, options);
const ctx = {req: {accessToken: {userId: 18}}};
const assignedState = await models.State.findOne({where: {code: 'PICKER_DESIGNED'}}, options);
const params = {ticketFk: ticket.id, stateFk: assignedState.id, workerFk: 1};
const res = await models.TicketTracking.changeState(ctx, params, options);
expect(res.__data.ticketFk).toBe(params.ticketFk); expect(res.__data.ticketFk).toBe(params.ticketFk);
expect(res.__data.stateFk).toBe(params.stateFk); expect(res.__data.stateFk).toBe(params.stateFk);
expect(res.__data.workerFk).toBe(params.workerFk); expect(res.__data.workerFk).toBe(params.workerFk);
expect(res.__data.workerFk).toBe(1); expect(res.__data.workerFk).toBe(1);
expect(res.__data.id).toBeDefined(); expect(res.__data.id).toBeDefined();
await tx.rollback();
} catch (e) {
await tx.rollback();
throw e;
}
}); });
}); });

View File

@ -1,4 +1,4 @@
const app = require('vn-loopback/server/server'); const models = require('vn-loopback/server/server').models;
const LoopBackContext = require('loopback-context'); const LoopBackContext = require('loopback-context');
describe('ticket setDelivered()', () => { describe('ticket setDelivered()', () => {
@ -7,50 +7,40 @@ describe('ticket setDelivered()', () => {
accessToken: {userId: userId}, accessToken: {userId: userId},
}; };
let ticketOne; beforeAll(async() => {
let ticketTwo;
beforeAll(async done => {
spyOn(LoopBackContext, 'getCurrentContext').and.returnValue({ spyOn(LoopBackContext, 'getCurrentContext').and.returnValue({
active: activeCtx active: activeCtx
}); });
});
it('should return the state which has been applied to the given tickets', async() => {
const tx = await models.TicketTracking.beginTransaction({});
try { try {
let originalTicketOne = await app.models.Ticket.findById(8); const options = {transaction: tx};
let originalTicketTwo = await app.models.Ticket.findById(10);
const ctx = {req: {accessToken: {userId: 49}}};
const originalTicketOne = await models.Ticket.findById(8, null, options);
const originalTicketTwo = await models.Ticket.findById(10, null, options);
originalTicketOne.id = null; originalTicketOne.id = null;
originalTicketTwo.id = null; originalTicketTwo.id = null;
ticketOne = await app.models.Ticket.create(originalTicketOne); const ticketOne = await models.Ticket.create(originalTicketOne, options);
ticketTwo = await app.models.Ticket.create(originalTicketTwo); const ticketTwo = await models.Ticket.create(originalTicketTwo, options);
} catch (error) {
console.error(error);
}
done(); const delivered = await models.State.findOne({where: {code: 'delivered'}, fields: ['id']}, options);
});
afterAll(async done => { const params = [ticketOne.id, ticketTwo.id];
try { const state = await models.TicketTracking.setDelivered(ctx, params, options);
await app.models.Ticket.destroyById(ticketOne.id);
await app.models.Ticket.destroyById(ticketTwo.id);
} catch (error) {
console.error(error);
}
done();
});
it('should return the state which has been applied to the given tickets', async() => {
let ctx = {req: {accessToken: {userId: 49}}};
let delivered = await app.models.State.findOne({where: {code: 'delivered'}, fields: ['id']});
let params = [ticketOne.id, ticketTwo.id];
let state = await app.models.TicketTracking.setDelivered(ctx, params);
expect(state.id).toEqual(delivered.id); expect(state.id).toEqual(delivered.id);
// restores await tx.rollback();
await app.models.TicketTracking.destroyById(state.id); } catch (e) {
await tx.rollback();
throw e;
}
}); });
}); });

View File

@ -10,18 +10,18 @@ module.exports = Self => {
accepts: [ accepts: [
{ {
arg: 'filter', arg: 'filter',
type: 'Object', type: 'object',
description: 'Filter defining where, order, offset, and limit - must be a JSON-encoded string', description: 'Filter defining where, order, offset, and limit - must be a JSON-encoded string',
http: {source: 'query'} http: {source: 'query'}
}, { }, {
arg: 'search', arg: 'search',
type: 'String', type: 'string',
description: `If it's and integer searchs by id, otherwise it searchs by client id`, description: `If it's and integer searchs by id, otherwise it searchs by client id`,
http: {source: 'query'} http: {source: 'query'}
} }
], ],
returns: { returns: {
type: ['Object'], type: ['object'],
root: true root: true
}, },
http: { http: {
@ -30,10 +30,14 @@ module.exports = Self => {
} }
}); });
Self.filter = async(ctx, filter) => { Self.filter = async(ctx, filter, options) => {
let conn = Self.dataSource.connector; const conn = Self.dataSource.connector;
const myOptions = {};
let where = buildFilter(ctx.args, (param, value) => { if (typeof options == 'object')
Object.assign(myOptions, options);
const where = buildFilter(ctx.args, (param, value) => {
switch (param) { switch (param) {
case 'search': case 'search':
return {or: [ return {or: [
@ -46,10 +50,9 @@ module.exports = Self => {
filter = mergeFilters(ctx.args.filter, {where}); filter = mergeFilters(ctx.args.filter, {where});
let stmts = []; const stmts = [];
let stmt;
stmt = new ParameterizedSQL( const stmt = new ParameterizedSQL(
`SELECT t.id AS ticketFk, c.id AS clientFk, c.name AS clientName, tw.weekDay, `SELECT t.id AS ticketFk, c.id AS clientFk, c.name AS clientName, tw.weekDay,
wh.name AS warehouseName, u.id AS workerFk, u.name AS userName, u.nickName, tw.agencyModeFk wh.name AS warehouseName, u.id AS workerFk, u.name AS userName, u.nickName, tw.agencyModeFk
FROM ticketWeekly tw FROM ticketWeekly tw
@ -60,10 +63,10 @@ module.exports = Self => {
); );
stmt.merge(conn.makeSuffix(filter)); stmt.merge(conn.makeSuffix(filter));
let itemsIndex = stmts.push(stmt) - 1; const itemsIndex = stmts.push(stmt) - 1;
let sql = ParameterizedSQL.join(stmts, ';'); const sql = ParameterizedSQL.join(stmts, ';');
let result = await conn.executeStmt(sql); const result = await conn.executeStmt(sql, myOptions);
return itemsIndex === 0 ? result : result[itemsIndex]; return itemsIndex === 0 ? result : result[itemsIndex];
}; };
}; };

View File

@ -1,43 +1,89 @@
const app = require('vn-loopback/server/server'); const models = require('vn-loopback/server/server').models;
describe('ticket-weekly filter()', () => { describe('ticket-weekly filter()', () => {
const authUserId = 9; const authUserId = 9;
it('should all return the tickets matching the filter', async() => { it('should all return the tickets matching the filter', async() => {
const tx = await models.TicketWeekly.beginTransaction({});
try {
const options = {transaction: tx};
const filter = {order: 't.id ASC'}; const filter = {order: 't.id ASC'};
const ctx = {req: {accessToken: {userId: authUserId}}, args: {filter: filter}}; const ctx = {req: {accessToken: {userId: authUserId}}, args: {filter: filter}};
const result = await app.models.TicketWeekly.filter(ctx);
const result = await models.TicketWeekly.filter(ctx, null, options);
const firstRow = result[0]; const firstRow = result[0];
expect(firstRow.ticketFk).toEqual(1); expect(firstRow.ticketFk).toEqual(1);
expect(result.length).toEqual(5); expect(result.length).toEqual(5);
await tx.rollback();
} catch (e) {
await tx.rollback();
throw e;
}
}); });
it('should return the ticket with id one', async() => { it('should return the ticket with id one', async() => {
const tx = await models.TicketWeekly.beginTransaction({});
try {
const options = {transaction: tx};
const ctx = {req: {accessToken: {userId: authUserId}}, args: {search: 2}}; const ctx = {req: {accessToken: {userId: authUserId}}, args: {search: 2}};
const filter = {};
const result = await app.models.TicketWeekly.filter(ctx, filter); const result = await models.TicketWeekly.filter(ctx, null, options);
const firstRow = result[0]; const firstRow = result[0];
expect(firstRow.ticketFk).toEqual(2); expect(firstRow.ticketFk).toEqual(2);
await tx.rollback();
} catch (e) {
await tx.rollback();
throw e;
}
}); });
it('should return the ticket matching the client name', async() => { it('should return the ticket matching the client name', async() => {
const tx = await models.TicketWeekly.beginTransaction({});
try {
const options = {transaction: tx};
const ctx = {req: {accessToken: {userId: authUserId}}, args: {search: 'bruce'}}; const ctx = {req: {accessToken: {userId: authUserId}}, args: {search: 'bruce'}};
const filter = {};
const result = await app.models.TicketWeekly.filter(ctx, filter); const result = await models.TicketWeekly.filter(ctx, null, options);
const firstRow = result[0]; const firstRow = result[0];
expect(firstRow.clientName).toEqual('Bruce Wayne'); expect(firstRow.clientName).toEqual('Bruce Wayne');
await tx.rollback();
} catch (e) {
await tx.rollback();
throw e;
}
}); });
it('should return the ticket matching the client id', async() => { it('should return the ticket matching the client id', async() => {
const tx = await models.TicketWeekly.beginTransaction({});
try {
const options = {transaction: tx};
const ctx = {req: {accessToken: {userId: authUserId}}, args: {search: 1101}}; const ctx = {req: {accessToken: {userId: authUserId}}, args: {search: 1101}};
const filter = {};
const result = await app.models.TicketWeekly.filter(ctx, filter); const result = await models.TicketWeekly.filter(ctx, null, options);
const firstRow = result[0]; const firstRow = result[0];
expect(firstRow.clientFk).toEqual(1101); expect(firstRow.clientFk).toEqual(1101);
expect(firstRow.clientName).toEqual('Bruce Wayne'); expect(firstRow.clientName).toEqual('Bruce Wayne');
await tx.rollback();
} catch (e) {
await tx.rollback();
throw e;
}
}); });
}); });

View File

@ -117,9 +117,6 @@ module.exports = Self => {
if (!zoneShipped || zoneShipped.zoneFk != args.zoneFk) if (!zoneShipped || zoneShipped.zoneFk != args.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 observationTypeDelivery = await models.ObservationType.findOne({
where: {code: 'delivery'}
}, myOptions);
const originalTicket = await models.Ticket.findOne({ const originalTicket = await models.Ticket.findOne({
where: {id: args.id}, where: {id: args.id},

View File

@ -12,12 +12,12 @@ module.exports = Self => {
}, },
{ {
arg: 'shipped', arg: 'shipped',
type: 'Date', type: 'date',
description: `The shipment date filter` description: `The shipment date filter`
}, },
{ {
arg: 'landed', arg: 'landed',
type: 'Date', type: 'date',
description: `The landing date filter` description: `The landing date filter`
}, },
{ {
@ -142,7 +142,7 @@ module.exports = Self => {
if (tx) await tx.commit(); if (tx) await tx.commit();
return await ticket; return ticket;
} catch (e) { } catch (e) {
if (tx) await tx.rollback(); if (tx) await tx.rollback();
throw e; throw e;

View File

@ -11,7 +11,7 @@ module.exports = Self => {
http: {source: 'path'} http: {source: 'path'}
}], }],
returns: { returns: {
type: 'Number', type: 'number',
root: true root: true
}, },
http: { http: {
@ -20,12 +20,32 @@ module.exports = Self => {
} }
}); });
Self.recalculateComponents = async(ctx, id) => { Self.recalculateComponents = async(ctx, id, options) => {
const isEditable = await Self.isEditable(ctx, id); const myOptions = {};
let tx;
if (typeof options == 'object')
Object.assign(myOptions, options);
if (!myOptions.transaction) {
tx = await Self.beginTransaction({});
myOptions.transaction = tx;
}
try {
const isEditable = await Self.isEditable(ctx, id, myOptions);
if (!isEditable) if (!isEditable)
throw new UserError(`The current ticket can't be modified`); throw new UserError(`The current ticket can't be modified`);
return Self.rawSql('CALL vn.ticket_recalcComponents(?, NULL)', [id]); const recalculation = await Self.rawSql('CALL vn.ticket_recalcComponents(?, NULL)', [id], myOptions);
if (tx) await tx.commit();
return recalculation;
} catch (e) {
if (tx) await tx.rollback();
throw e;
}
}; };
}; };

View File

@ -6,7 +6,7 @@ module.exports = Self => {
accessType: 'WRITE', accessType: 'WRITE',
accepts: [{ accepts: [{
arg: 'id', arg: 'id',
type: 'Number', type: 'number',
required: true, required: true,
description: 'The ticket id', description: 'The ticket id',
http: {source: 'path'} http: {source: 'path'}

View File

@ -5,23 +5,23 @@ module.exports = Self => {
accessType: 'WRITE', accessType: 'WRITE',
accepts: [{ accepts: [{
arg: 'id', arg: 'id',
type: 'Number', type: 'number',
required: true, required: true,
description: 'The ticket id', description: 'The ticket id',
http: {source: 'path'} http: {source: 'path'}
}, },
{ {
arg: 'destination', arg: 'destination',
type: 'String', type: 'string',
required: true, required: true,
}, },
{ {
arg: 'message', arg: 'message',
type: 'String', type: 'string',
required: true, required: true,
}], }],
returns: { returns: {
type: 'Object', type: 'object',
root: true root: true
}, },
http: { http: {
@ -30,11 +30,23 @@ module.exports = Self => {
} }
}); });
Self.sendSms = async(ctx, id, destination, message) => { Self.sendSms = async(ctx, id, destination, message, options) => {
const myOptions = {};
let tx;
if (typeof options == 'object')
Object.assign(myOptions, options);
if (!myOptions.transaction) {
tx = await Self.beginTransaction({});
myOptions.transaction = tx;
}
const userId = ctx.req.accessToken.userId; const userId = ctx.req.accessToken.userId;
let sms = await Self.app.models.Sms.send(ctx, id, destination, message); try {
let logRecord = { const sms = await Self.app.models.Sms.send(ctx, id, destination, message);
const logRecord = {
originFk: id, originFk: id,
userFk: userId, userFk: userId,
action: 'insert', action: 'insert',
@ -48,10 +60,16 @@ module.exports = Self => {
} }
}; };
const ticketLog = await Self.app.models.TicketLog.create(logRecord); const ticketLog = await Self.app.models.TicketLog.create(logRecord, myOptions);
sms.logId = ticketLog.id; sms.logId = ticketLog.id;
if (tx) await tx.commit();
return sms; return sms;
} catch (e) {
if (tx) await tx.rollback();
throw e;
}
}; };
}; };

View File

@ -6,7 +6,7 @@ module.exports = Self => {
accessType: 'WRITE', accessType: 'WRITE',
accepts: [{ accepts: [{
arg: 'id', arg: 'id',
type: 'Number', type: 'number',
required: true, required: true,
description: 'The ticket id', description: 'The ticket id',
http: {source: 'path'} http: {source: 'path'}
@ -21,21 +21,33 @@ module.exports = Self => {
} }
}); });
Self.setDeleted = async(ctx, id) => { Self.setDeleted = async(ctx, id, options) => {
const models = Self.app.models; const models = Self.app.models;
const userId = ctx.req.accessToken.userId;
const isEditable = await Self.isEditable(ctx, id);
const $t = ctx.req.__; // $translate const $t = ctx.req.__; // $translate
const myOptions = {};
let tx;
if (typeof options == 'object')
Object.assign(myOptions, options);
if (!myOptions.transaction) {
tx = await Self.beginTransaction({});
myOptions.transaction = tx;
}
try {
const userId = ctx.req.accessToken.userId;
const isEditable = await Self.isEditable(ctx, id, myOptions);
if (!isEditable) if (!isEditable)
throw new UserError(`The sales of this ticket can't be modified`); throw new UserError(`The sales of this ticket can't be modified`);
// Check if has sales with shelving // Check if has sales with shelving
const isSalesAssistant = await models.Account.hasRole(userId, 'salesAssistant'); const isSalesAssistant = await models.Account.hasRole(userId, 'salesAssistant', myOptions);
const sales = await models.Sale.find({ const sales = await models.Sale.find({
include: {relation: 'itemShelvingSale'}, include: {relation: 'itemShelvingSale'},
where: {ticketFk: id} where: {ticketFk: id}
}); }, myOptions);
const hasItemShelvingSales = sales.some(sale => { const hasItemShelvingSales = sales.some(sale => {
return sale.itemShelvingSale(); return sale.itemShelvingSale();
}); });
@ -44,7 +56,7 @@ module.exports = Self => {
throw new UserError(`You cannot delete a ticket that part of it is being prepared`); throw new UserError(`You cannot delete a ticket that part of it is being prepared`);
// Check for existing claim // Check for existing claim
const claimOfATicket = await models.Claim.findOne({where: {ticketFk: id}}); const claimOfATicket = await models.Claim.findOne({where: {ticketFk: id}}, myOptions);
if (claimOfATicket) if (claimOfATicket)
throw new UserError('You must delete the claim id %d first', 'DELETE_CLAIM_FIRST', claimOfATicket.id); throw new UserError('You must delete the claim id %d first', 'DELETE_CLAIM_FIRST', claimOfATicket.id);
@ -52,7 +64,7 @@ module.exports = Self => {
const hasPurchaseRequests = await models.TicketRequest.count({ const hasPurchaseRequests = await models.TicketRequest.count({
ticketFk: id, ticketFk: id,
isOk: true isOk: true
}); }, myOptions);
if (hasPurchaseRequests) if (hasPurchaseRequests)
throw new UserError('You must delete all the buy requests first'); throw new UserError('You must delete all the buy requests first');
@ -63,7 +75,7 @@ module.exports = Self => {
for (let sale of sales) { for (let sale of sales) {
if (sale.itemShelvingSale()) { if (sale.itemShelvingSale()) {
const itemShelvingSale = sale.itemShelvingSale(); const itemShelvingSale = sale.itemShelvingSale();
const destroyedShelving = models.ItemShelvingSale.destroyById(itemShelvingSale.id); const destroyedShelving = models.ItemShelvingSale.destroyById(itemShelvingSale.id, myOptions);
promises.push(destroyedShelving); promises.push(destroyedShelving);
} }
} }
@ -71,15 +83,15 @@ module.exports = Self => {
} }
// Remove ticket greuges // Remove ticket greuges
const ticketGreuges = await models.Greuge.find({where: {ticketFk: id}}); const ticketGreuges = await models.Greuge.find({where: {ticketFk: id}}, myOptions);
const ownGreuges = ticketGreuges.every(greuge => { const ownGreuges = ticketGreuges.every(greuge => {
return greuge.ticketFk == id; return greuge.ticketFk == id;
}); });
if (ownGreuges) { if (ownGreuges) {
for (const greuge of ticketGreuges) { for (const greuge of ticketGreuges) {
const instance = await models.Greuge.findById(greuge.id); const instance = await models.Greuge.findById(greuge.id, null, myOptions);
await instance.destroy(); await instance.destroy(myOptions);
} }
} }
@ -100,7 +112,7 @@ module.exports = Self => {
}, { }, {
relation: 'stowaway' relation: 'stowaway'
}] }]
}); }, myOptions);
// Change state to "fixing" if contains an stowaway and remove the link between them // Change state to "fixing" if contains an stowaway and remove the link between them
let otherTicketId; let otherTicketId;
@ -110,11 +122,11 @@ module.exports = Self => {
otherTicketId = ticket.ship().id; otherTicketId = ticket.ship().id;
if (otherTicketId) { if (otherTicketId) {
await models.Ticket.deleteStowaway(ctx, otherTicketId); await models.Ticket.deleteStowaway(ctx, otherTicketId, myOptions);
await models.TicketTracking.changeState(ctx, { await models.TicketTracking.changeState(ctx, {
ticketFk: otherTicketId, ticketFk: otherTicketId,
code: 'FIXING' code: 'FIXING'
}); }, myOptions);
} }
// Send notification to salesPerson // Send notification to salesPerson
@ -128,6 +140,14 @@ module.exports = Self => {
await models.Chat.send(ctx, `@${salesPersonUser.name}`, message); await models.Chat.send(ctx, `@${salesPersonUser.name}`, message);
} }
return ticket.updateAttribute('isDeleted', true); const updatedTicket = await ticket.updateAttribute('isDeleted', true, myOptions);
if (tx) await tx.commit();
return updatedTicket;
} catch (e) {
if (tx) await tx.rollback();
throw e;
}
}; };
}; };

View File

@ -8,12 +8,10 @@ describe('ticket canBeInvoiced()', () => {
accessToken: {userId: userId} accessToken: {userId: userId}
}; };
beforeAll(async done => { beforeAll(async() => {
spyOn(LoopBackContext, 'getCurrentContext').and.returnValue({ spyOn(LoopBackContext, 'getCurrentContext').and.returnValue({
active: activeCtx active: activeCtx
}); });
done();
}); });
it('should return falsy for an already invoiced ticket', async() => { it('should return falsy for an already invoiced ticket', async() => {

View File

@ -11,12 +11,10 @@ describe('ticket makeInvoice()', () => {
}; };
const ctx = {req: activeCtx}; const ctx = {req: activeCtx};
beforeAll(async done => { beforeAll(async() => {
spyOn(LoopBackContext, 'getCurrentContext').and.returnValue({ spyOn(LoopBackContext, 'getCurrentContext').and.returnValue({
active: activeCtx active: activeCtx
}); });
done();
}); });
it('should throw an error when invoicing tickets from multiple clients', async() => { it('should throw an error when invoicing tickets from multiple clients', async() => {

View File

@ -1,24 +1,43 @@
const app = require('vn-loopback/server/server'); const models = require('vn-loopback/server/server').models;
describe('ticket recalculateComponents()', () => { describe('ticket recalculateComponents()', () => {
const ticketId = 11; const ticketId = 11;
it('should update the ticket components', async() => { it('should update the ticket components', async() => {
const tx = await models.Ticket.beginTransaction({});
try {
const options = {transaction: tx};
const ctx = {req: {accessToken: {userId: 9}}}; const ctx = {req: {accessToken: {userId: 9}}};
const response = await app.models.Ticket.recalculateComponents(ctx, ticketId); const response = await models.Ticket.recalculateComponents(ctx, ticketId, options);
expect(response.affectedRows).toBeDefined(); expect(response.affectedRows).toBeDefined();
await tx.rollback();
} catch (e) {
await tx.rollback();
throw e;
}
}); });
it('should throw an error if the ticket is not editable', async() => { it('should throw an error if the ticket is not editable', async() => {
const tx = await models.Ticket.beginTransaction({});
let error;
try {
const options = {transaction: tx};
const ctx = {req: {accessToken: {userId: 9}}}; const ctx = {req: {accessToken: {userId: 9}}};
const immutableTicketId = 1; const immutableTicketId = 1;
await app.models.Ticket.recalculateComponents(ctx, immutableTicketId) await models.Ticket.recalculateComponents(ctx, immutableTicketId, options);
.catch(response => {
expect(response).toEqual(new Error(`The current ticket can't be modified`));
error = response;
});
expect(error).toBeDefined(); await tx.rollback();
} catch (e) {
await tx.rollback();
error = e;
}
expect(error).toEqual(new Error(`The current ticket can't be modified`));
}); });
}); });

View File

@ -1,29 +1,30 @@
const app = require('vn-loopback/server/server'); const models = require('vn-loopback/server/server').models;
const soap = require('soap'); const soap = require('soap');
describe('ticket sendSms()', () => { describe('ticket sendSms()', () => {
let logId;
afterAll(async done => {
await app.models.TicketLog.destroyById(logId);
done();
});
it('should send a message and log it', async() => { it('should send a message and log it', async() => {
const tx = await models.Ticket.beginTransaction({});
try {
const options = {transaction: tx};
spyOn(soap, 'createClientAsync').and.returnValue('a so fake client'); spyOn(soap, 'createClientAsync').and.returnValue('a so fake client');
let ctx = {req: {accessToken: {userId: 9}}}; const ctx = {req: {accessToken: {userId: 9}}};
let id = 11; const id = 11;
let destination = 222222222; const destination = 222222222;
let message = 'this is the message created in a test'; const message = 'this is the message created in a test';
let sms = await app.models.Ticket.sendSms(ctx, id, destination, message); const sms = await models.Ticket.sendSms(ctx, id, destination, message, options);
logId = sms.logId; const createdLog = await models.TicketLog.findById(sms.logId, null, options);
const json = JSON.parse(JSON.stringify(createdLog.newInstance));
let createdLog = await app.models.TicketLog.findById(logId);
let json = JSON.parse(JSON.stringify(createdLog.newInstance));
expect(json.message).toEqual(message); expect(json.message).toEqual(message);
await tx.rollback();
} catch (e) {
await tx.rollback();
throw e;
}
}); });
}); });

View File

@ -1,6 +1,5 @@
const app = require('vn-loopback/server/server'); const models = require('vn-loopback/server/server').models;
const LoopBackContext = require('loopback-context'); const LoopBackContext = require('loopback-context');
const models = app.models;
describe('ticket setDeleted()', () => { describe('ticket setDeleted()', () => {
const userId = 1106; const userId = 1106;
@ -10,21 +9,32 @@ describe('ticket setDeleted()', () => {
}; };
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 tx = await models.Ticket.beginTransaction({});
let error;
try {
const options = {transaction: tx};
const ctx = {req: activeCtx}; const ctx = {req: activeCtx};
const ticketId = 16; const ticketId = 16;
let error;
try { await models.Ticket.setDeleted(ctx, ticketId, options);
await app.models.Ticket.setDeleted(ctx, ticketId);
await tx.rollback();
} catch (e) { } catch (e) {
await tx.rollback();
error = e; error = e;
} }
expect(error.translateArgs[0]).toEqual(2);
expect(error.message).toEqual('You must delete the claim id %d first'); expect(error.message).toEqual('You must delete the claim id %d first');
}); });
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 tx = await models.Ticket.beginTransaction({});
try {
const options = {transaction: tx};
spyOn(LoopBackContext, 'getCurrentContext').and.returnValue({ spyOn(LoopBackContext, 'getCurrentContext').and.returnValue({
active: activeCtx active: activeCtx
}); });
@ -38,67 +48,71 @@ describe('ticket setDeleted()', () => {
} }
}; };
let sampleTicket = await models.Ticket.findById(12); const sampleTicket = await models.Ticket.findById(12);
let sampleStowaway = await models.Ticket.findById(13); const sampleStowaway = await models.Ticket.findById(13);
sampleTicket.id = undefined; sampleTicket.id = undefined;
let shipTicket = await models.Ticket.create(sampleTicket); const shipTicket = await models.Ticket.create(sampleTicket, options);
sampleStowaway.id = undefined; sampleStowaway.id = undefined;
let stowawayTicket = await models.Ticket.create(sampleStowaway); const stowawayTicket = await models.Ticket.create(sampleStowaway, options);
await models.Stowaway.rawSql(` await models.Stowaway.rawSql(`
INSERT INTO vn.stowaway(id, shipFk) INSERT INTO vn.stowaway(id, shipFk)
VALUES (?, ?)`, [stowawayTicket.id, shipTicket.id]); VALUES (?, ?)`, [stowawayTicket.id, shipTicket.id], options);
const boardingState = await models.State.findOne({ const boardingState = await models.State.findOne({
where: { where: {
code: 'BOARDING' code: 'BOARDING'
} }
}); }, options);
await models.TicketTracking.create({ await models.TicketTracking.create({
ticketFk: stowawayTicket.id, ticketFk: stowawayTicket.id,
stateFk: boardingState.id, stateFk: boardingState.id,
workerFk: ctx.req.accessToken.userId workerFk: ctx.req.accessToken.userId
}); }, options);
const okState = await models.State.findOne({ const okState = await models.State.findOne({
where: { where: {
code: 'OK' code: 'OK'
} }
}); }, options);
await models.TicketTracking.create({ await models.TicketTracking.create({
ticketFk: shipTicket.id, ticketFk: shipTicket.id,
stateFk: okState.id, stateFk: okState.id,
workerFk: ctx.req.accessToken.userId workerFk: ctx.req.accessToken.userId
}); }, options);
let stowawayTicketState = await models.TicketState.findOne({ let stowawayTicketState = await models.TicketState.findOne({
where: { where: {
ticketFk: stowawayTicket.id ticketFk: stowawayTicket.id
} }
}); }, options);
let stowaway = await models.Stowaway.findById(shipTicket.id); let stowaway = await models.Stowaway.findById(shipTicket.id, null, options);
expect(stowaway).toBeDefined(); expect(stowaway).toBeDefined();
expect(stowawayTicketState.code).toEqual('BOARDING'); expect(stowawayTicketState.code).toEqual('BOARDING');
await models.Ticket.setDeleted(ctx, shipTicket.id); await models.Ticket.setDeleted(ctx, shipTicket.id, options);
stowawayTicketState = await models.TicketState.findOne({ stowawayTicketState = await models.TicketState.findOne({
where: { where: {
ticketFk: stowawayTicket.id ticketFk: stowawayTicket.id
} }
}); }, options);
stowaway = await models.Stowaway.findById(shipTicket.id); stowaway = await models.Stowaway.findById(shipTicket.id, null, options);
expect(stowaway).toBeNull(); expect(stowaway).toBeNull();
expect(stowawayTicketState.code).toEqual('FIXING'); expect(stowawayTicketState.code).toEqual('FIXING');
// restores await tx.rollback();
await models.Ticket.destroyById(shipTicket.id); } catch (e) {
await models.Ticket.destroyById(stowawayTicket.id); await tx.rollback();
throw e;
}
}); });
}); });

View File

@ -1,28 +1,72 @@
const app = require('vn-loopback/server/server'); const models = require('vn-loopback/server/server').models;
describe('ticket summary()', () => { describe('ticket summary()', () => {
it('should return a summary object containing data from 1 ticket', async() => { it('should return a summary object containing data from 1 ticket', async() => {
let result = await app.models.Ticket.summary(1); const tx = await models.Ticket.beginTransaction({});
try {
const options = {transaction: tx};
const result = await models.Ticket.summary(1, options);
expect(result.id).toEqual(1); expect(result.id).toEqual(1);
expect(result.nickname).toEqual('Bat cave'); expect(result.nickname).toEqual('Bat cave');
await tx.rollback();
} catch (e) {
await tx.rollback();
throw e;
}
}); });
it('should return a summary object containing sales from 1 ticket', async() => { it('should return a summary object containing sales from 1 ticket', async() => {
let result = await app.models.Ticket.summary(1); const tx = await models.Ticket.beginTransaction({});
try {
const options = {transaction: tx};
const result = await models.Ticket.summary(1, options);
expect(result.sales.length).toEqual(4); expect(result.sales.length).toEqual(4);
await tx.rollback();
} catch (e) {
await tx.rollback();
throw e;
}
}); });
it('should return a summary object containing totalWithoutVat for 1 ticket', async() => { it('should return a summary object containing totalWithoutVat for 1 ticket', async() => {
let result = await app.models.Ticket.summary(1); const tx = await models.Ticket.beginTransaction({});
try {
const options = {transaction: tx};
const result = await models.Ticket.summary(1, options);
expect(result.totalWithoutVat).toEqual(jasmine.any(Number)); expect(result.totalWithoutVat).toEqual(jasmine.any(Number));
await tx.rollback();
} catch (e) {
await tx.rollback();
throw e;
}
}); });
it('should return a summary object containing total for 1 ticket', async() => { it('should return a summary object containing total for 1 ticket', async() => {
let result = await app.models.Ticket.summary(1); const tx = await models.Ticket.beginTransaction({});
try {
const options = {transaction: tx};
const result = await models.Ticket.summary(1, options);
expect(result.totalWithVat).toEqual(jasmine.any(Number)); expect(result.totalWithVat).toEqual(jasmine.any(Number));
await tx.rollback();
} catch (e) {
await tx.rollback();
throw e;
}
}); });
}); });

View File

@ -1,4 +1,4 @@
const app = require('vn-loopback/server/server'); const models = require('vn-loopback/server/server').models;
const LoopBackContext = require('loopback-context'); const LoopBackContext = require('loopback-context');
describe('sale transferSales()', () => { describe('sale transferSales()', () => {
@ -8,121 +8,138 @@ describe('sale transferSales()', () => {
}; };
const ctx = {req: activeCtx}; const ctx = {req: activeCtx};
let createdTicketsIds = [];
beforeAll(() => { beforeAll(() => {
spyOn(LoopBackContext, 'getCurrentContext').and.returnValue({ spyOn(LoopBackContext, 'getCurrentContext').and.returnValue({
active: activeCtx 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();
});
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 tx = await models.Ticket.beginTransaction({});
let error; let error;
try {
const options = {transaction: tx};
const currentTicketId = 1; const currentTicketId = 1;
const receiverTicketId = undefined; const receiverTicketId = undefined;
const sales = []; const sales = [];
await app.models.Ticket.transferSales(ctx, currentTicketId, receiverTicketId, sales) await models.Ticket.transferSales(ctx, currentTicketId, receiverTicketId, sales, options);
.catch(response => {
expect(response.message).toEqual(`The sales of this ticket can't be modified`);
error = response;
});
expect(error).toBeDefined(); await tx.rollback();
} catch (e) {
await tx.rollback();
error = e;
}
expect(error.message).toEqual(`The sales of this ticket can't be modified`);
}); });
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 tx = await models.Ticket.beginTransaction({});
let error; let error;
try {
const options = {transaction: tx};
const currentTicketId = 16; const currentTicketId = 16;
const receiverTicketId = 1; const receiverTicketId = 1;
const sales = []; const sales = [];
await app.models.Ticket.transferSales(ctx, currentTicketId, receiverTicketId, sales) await models.Ticket.transferSales(ctx, currentTicketId, receiverTicketId, sales, options);
.catch(response => {
expect(response.message).toEqual(`The sales of the receiver ticket can't be modified`);
error = response;
});
expect(error).toBeDefined(); await tx.rollback();
} catch (e) {
await tx.rollback();
error = e;
}
expect(error.message).toEqual(`The sales of the receiver ticket can't be modified`);
}); });
it('should transfer the sales from one ticket to a new one then send them back and delete the created ticket', async() => { it('should transfer the sales from one ticket to a new one then send them back and delete the created ticket', async() => {
const tx = await models.Ticket.beginTransaction({});
try {
const options = {transaction: tx};
const formerTicketId = 11; const formerTicketId = 11;
let createdTicketId = undefined; let createdTicketId = undefined;
let formerTicketSales = await app.models.Ticket.getSales(formerTicketId); let formerTicketSales = await models.Ticket.getSales(formerTicketId, options);
expect(formerTicketSales.length).toEqual(2); expect(formerTicketSales.length).toEqual(2);
let createdTicket = await app.models.Ticket.transferSales( let createdTicket = await models.Ticket.transferSales(
ctx, formerTicketId, createdTicketId, formerTicketSales); ctx, formerTicketId, createdTicketId, formerTicketSales, options);
createdTicketId = createdTicket.id; createdTicketId = createdTicket.id;
createdTicketsIds.push(createdTicketId);
formerTicketSales = await app.models.Ticket.getSales(formerTicketId); formerTicketSales = await models.Ticket.getSales(formerTicketId, options);
createdTicketSales = await app.models.Ticket.getSales(createdTicketId); createdTicketSales = await models.Ticket.getSales(createdTicketId, options);
expect(formerTicketSales.length).toEqual(0); expect(formerTicketSales.length).toEqual(0);
expect(createdTicketSales.length).toEqual(2); expect(createdTicketSales.length).toEqual(2);
await app.models.Ticket.transferSales( await models.Ticket.transferSales(
ctx, createdTicketId, formerTicketId, createdTicketSales); ctx, createdTicketId, formerTicketId, createdTicketSales, options);
formerTicketSales = await app.models.Ticket.getSales(formerTicketId); formerTicketSales = await models.Ticket.getSales(formerTicketId, options);
createdTicketSales = await app.models.Ticket.getSales(createdTicketId); createdTicketSales = await models.Ticket.getSales(createdTicketId, options);
createdTicket = await app.models.Ticket.findById(createdTicketId); createdTicket = await models.Ticket.findById(createdTicketId, null, options);
expect(createdTicket.isDeleted).toBeTruthy(); expect(createdTicket.isDeleted).toBeTruthy();
expect(formerTicketSales.length).toEqual(2); expect(formerTicketSales.length).toEqual(2);
expect(createdTicketSales.length).toEqual(0); expect(createdTicketSales.length).toEqual(0);
await tx.rollback();
} catch (e) {
await tx.rollback();
throw e;
}
}); });
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 tx = await models.Ticket.beginTransaction({});
let error; let error;
let currentTicket = await app.models.Ticket.findById(11); try {
let currentTicketSales = await app.models.Ticket.getSales(currentTicket.id); const options = {transaction: tx};
const currentTicket = await models.Ticket.findById(11, null, options);
const currentTicketSales = await models.Ticket.getSales(currentTicket.id, options);
const currentTicketId = currentTicket.id; const currentTicketId = currentTicket.id;
const receiverTicketId = undefined; const receiverTicketId = undefined;
currentTicketSales[0].quantity = 99; currentTicketSales[0].quantity = 99;
await app.models.Ticket.transferSales( await models.Ticket.transferSales(
ctx, currentTicketId, receiverTicketId, currentTicketSales) ctx, currentTicketId, receiverTicketId, currentTicketSales, options);
.catch(response => {
expect(response.message).toEqual(`Invalid quantity`);
error = response;
});
expect(error).toBeDefined(); await tx.rollback();
} catch (e) {
await tx.rollback();
error = e;
}
expect(error.message).toEqual(`Invalid quantity`);
}); });
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 tx = await models.Ticket.beginTransaction({});
try {
const options = {transaction: tx};
const formerTicketId = 11; const formerTicketId = 11;
let createdTicketId = undefined; let createdTicketId = undefined;
let formerTicketSales = await app.models.Ticket.getSales(formerTicketId); let formerTicketSales = await models.Ticket.getSales(formerTicketId, options);
const partialSaleId = formerTicketSales[0].id;
const completeSaleId = formerTicketSales[1].id; const completeSaleId = formerTicketSales[1].id;
let partialSaleTotalQuantity = formerTicketSales[0].quantity; let partialSaleTotalQuantity = formerTicketSales[0].quantity;
@ -130,14 +147,13 @@ describe('sale transferSales()', () => {
formerTicketSales[0].quantity = 1; formerTicketSales[0].quantity = 1;
let createdTicket = await app.models.Ticket.transferSales( let createdTicket = await models.Ticket.transferSales(
ctx, formerTicketId, createdTicketId, formerTicketSales); ctx, formerTicketId, createdTicketId, formerTicketSales, options);
createdTicketId = createdTicket.id; createdTicketId = createdTicket.id;
createdTicketsIds.push(createdTicket.id);
formerTicketSales = await app.models.Ticket.getSales(formerTicketId); formerTicketSales = await models.Ticket.getSales(formerTicketId, options);
createdTicketSales = await app.models.Ticket.getSales(createdTicketId); createdTicketSales = await models.Ticket.getSales(createdTicketId, options);
const [createdPartialSale] = createdTicketSales.filter(sale => { const [createdPartialSale] = createdTicketSales.filter(sale => {
return sale.id != completeSaleId; return sale.id != completeSaleId;
@ -148,20 +164,11 @@ describe('sale transferSales()', () => {
expect(createdTicketSales.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(partialSaleId); await tx.rollback();
await saleToRestore.updateAttribute('quantity', partialSaleTotalQuantity); } catch (e) {
await tx.rollback();
let saleToReturnToTicket = await app.models.Sale.findById(completeSaleId); throw e;
await saleToReturnToTicket.updateAttribute('ticketFk', formerTicketId); }
formerTicketSales = await app.models.Ticket.getSales(formerTicketId);
const [returningPartialSale] = formerTicketSales.filter(sale => {
return sale.id == partialSaleId;
});
expect(returningPartialSale.quantity).toEqual(partialSaleTotalQuantity);
expect(formerTicketSales.length).toEqual(2);
}); });
}); });
}); });

View File

@ -1,40 +1,15 @@
const app = require('vn-loopback/server/server'); const models = require('vn-loopback/server/server').models;
describe('sale updateDiscount()', () => { describe('sale updateDiscount()', () => {
const originalSaleId = 8; const originalSaleId = 8;
let componentId;
let originalSale;
let salesPersonMana;
beforeAll(async done => {
try {
originalSale = await app.models.Sale.findById(originalSaleId);
let manaDiscount = await app.models.Component.findOne({where: {code: 'buyerDiscount'}});
componentId = manaDiscount.id;
let ticket = await app.models.Ticket.findById(originalSale.ticketFk);
let client = await app.models.Client.findById(ticket.clientFk);
salesPersonMana = await app.models.WorkerMana.findById(client.salesPersonFk);
} catch (error) {
console.error(error);
}
done();
});
afterAll(async done => {
try {
await originalSale.save();
await app.models.SaleComponent.updateAll({componentFk: componentId, saleFk: originalSaleId}, {value: 0});
await salesPersonMana.save();
} catch (error) {
console.error(error);
}
done();
});
it('should throw an error if no sales were selected', async() => { it('should throw an error if no sales were selected', async() => {
const tx = await models.Ticket.beginTransaction({});
let error;
try {
const options = {transaction: tx};
const ctx = { const ctx = {
req: { req: {
accessToken: {userId: 9}, accessToken: {userId: 9},
@ -42,21 +17,28 @@ describe('sale updateDiscount()', () => {
__: () => {} __: () => {}
} }
}; };
let error;
const ticketId = 11; const ticketId = 11;
const sales = []; const sales = [];
const newDiscount = 10; const newDiscount = 10;
try { await models.Ticket.updateDiscount(ctx, ticketId, sales, newDiscount, options);
await app.models.Ticket.updateDiscount(ctx, ticketId, sales, newDiscount);
} catch (err) { await tx.rollback();
error = err; } catch (e) {
await tx.rollback();
error = e;
} }
expect(error.message).toEqual('Please select at least one sale'); expect(error.message).toEqual('Please select at least one sale');
}); });
it('should throw an error if no sales belong to different tickets', async() => { it('should throw an error if no sales belong to different tickets', async() => {
const tx = await models.Ticket.beginTransaction({});
let error;
try {
const options = {transaction: tx};
const ctx = { const ctx = {
req: { req: {
accessToken: {userId: 9}, accessToken: {userId: 9},
@ -64,21 +46,28 @@ describe('sale updateDiscount()', () => {
__: () => {} __: () => {}
} }
}; };
let error;
const ticketId = 11; const ticketId = 11;
const sales = [1, 14]; const sales = [1, 14];
const newDiscount = 10; const newDiscount = 10;
try { await models.Ticket.updateDiscount(ctx, ticketId, sales, newDiscount, options);
await app.models.Ticket.updateDiscount(ctx, ticketId, sales, newDiscount);
} catch (err) { await tx.rollback();
error = err; } catch (e) {
await tx.rollback();
error = e;
} }
expect(error.message).toEqual('All sales must belong to the same ticket'); expect(error.message).toEqual('All sales must belong to the same ticket');
}); });
it('should throw an error if the ticket is invoiced already', async() => { it('should throw an error if the ticket is invoiced already', async() => {
const tx = await models.Ticket.beginTransaction({});
let error;
try {
const options = {transaction: tx};
const ctx = { const ctx = {
req: { req: {
accessToken: {userId: 9}, accessToken: {userId: 9},
@ -86,21 +75,27 @@ describe('sale updateDiscount()', () => {
__: () => {} __: () => {}
} }
}; };
let error;
const ticketId = 1; const ticketId = 1;
const sales = [1]; const sales = [1];
const newDiscount = 100; const newDiscount = 100;
try { await models.Ticket.updateDiscount(ctx, ticketId, sales, newDiscount, options);
await app.models.Ticket.updateDiscount(ctx, ticketId, sales, newDiscount);
} catch (err) { await tx.rollback();
error = err; } catch (e) {
await tx.rollback();
error = e;
} }
expect(error.message).toEqual(`The sales of this ticket can't be modified`); expect(error.message).toEqual(`The sales of this ticket can't be modified`);
}); });
it('should update the discount if the salesPerson has mana', async() => { it('should update the discount if the salesPerson has mana', async() => {
const tx = await models.Ticket.beginTransaction({});
try {
const options = {transaction: tx};
const ctx = { const ctx = {
req: { req: {
accessToken: {userId: 18}, accessToken: {userId: 18},
@ -111,24 +106,35 @@ describe('sale updateDiscount()', () => {
const ticketId = 11; const ticketId = 11;
const sales = [originalSaleId]; const sales = [originalSaleId];
const newDiscount = 100; const newDiscount = 100;
let manaDiscount = await app.models.Component.findOne({where: {code: 'mana'}}); const manaDiscount = await models.Component.findOne({where: {code: 'mana'}}, options);
componentId = manaDiscount.id; const componentId = manaDiscount.id;
await app.models.Ticket.updateDiscount(ctx, ticketId, sales, newDiscount); await models.Ticket.updateDiscount(ctx, ticketId, sales, newDiscount, options);
let updatedSale = await app.models.Sale.findById(originalSaleId); const updatedSale = await models.Sale.findById(originalSaleId, null, options);
let createdSaleComponent = await app.models.SaleComponent.findOne({ const createdSaleComponent = await models.SaleComponent.findOne({
where: { where: {
componentFk: componentId, componentFk: componentId,
saleFk: originalSaleId saleFk: originalSaleId
} }
}); }, options);
expect(createdSaleComponent.componentFk).toEqual(componentId); expect(createdSaleComponent.componentFk).toEqual(componentId);
expect(updatedSale.discount).toEqual(100); expect(updatedSale.discount).toEqual(100);
await tx.rollback();
} catch (e) {
await tx.rollback();
throw e;
}
}); });
it('should update the discount and add company discount component if the worker does not have mana', async() => { it('should update the discount and add company discount component if the worker does not have mana', async() => {
const tx = await models.Ticket.beginTransaction({});
try {
const options = {transaction: tx};
const ctx = { const ctx = {
req: { req: {
accessToken: {userId: 9}, accessToken: {userId: 9},
@ -140,17 +146,26 @@ describe('sale updateDiscount()', () => {
const sales = [originalSaleId]; const sales = [originalSaleId];
const newDiscount = 100; const newDiscount = 100;
await app.models.Ticket.updateDiscount(ctx, ticketId, sales, newDiscount); await models.Ticket.updateDiscount(ctx, ticketId, sales, newDiscount, options);
let updatedSale = await app.models.Sale.findById(originalSaleId); const updatedSale = await models.Sale.findById(originalSaleId, null, options);
let createdSaleComponent = await app.models.SaleComponent.findOne({ const manaDiscount = await models.Component.findOne({where: {code: 'buyerDiscount'}}, options);
const componentId = manaDiscount.id;
const createdSaleComponent = await models.SaleComponent.findOne({
where: { where: {
componentFk: componentId, componentFk: componentId,
saleFk: originalSaleId saleFk: originalSaleId
} }
}); }, options);
expect(createdSaleComponent.componentFk).toEqual(componentId); expect(createdSaleComponent.componentFk).toEqual(componentId);
expect(updatedSale.discount).toEqual(100); expect(updatedSale.discount).toEqual(100);
await tx.rollback();
} catch (e) {
await tx.rollback();
throw e;
}
}); });
}); });

View File

@ -1,4 +1,4 @@
const app = require('vn-loopback/server/server'); const models = require('vn-loopback/server/server').models;
const LoopBackContext = require('loopback-context'); const LoopBackContext = require('loopback-context');
const userId = 9; const userId = 9;
@ -11,34 +11,44 @@ describe('ticket updateEditableTicket()', () => {
const validTicketId = 12; const validTicketId = 12;
const invalidTicketId = 1; const invalidTicketId = 1;
const data = {addressFk: 1}; const data = {addressFk: 1};
const originalData = {addressFk: 123};
afterAll(async done => {
await app.models.Ticket.updateEditableTicket(ctx, validTicketId, originalData);
done();
});
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; const tx = await models.Ticket.beginTransaction({});
await app.models.Ticket.updateEditableTicket(ctx, invalidTicketId, data).catch(e => { let error;
try {
const options = {transaction: tx};
await models.Ticket.updateEditableTicket(ctx, invalidTicketId, data, options);
await tx.rollback();
} catch (e) {
await tx.rollback();
error = e; error = e;
}).finally(() => { }
expect(error.message).toEqual('This ticket can not be modified'); expect(error.message).toEqual('This ticket can not be modified');
}); });
expect(error).toBeDefined();
});
it('should edit the ticket address', async() => { it('should edit the ticket address', async() => {
const tx = await models.Ticket.beginTransaction({});
try {
const options = {transaction: tx};
spyOn(LoopBackContext, 'getCurrentContext').and.returnValue({ spyOn(LoopBackContext, 'getCurrentContext').and.returnValue({
active: activeCtx active: activeCtx
}); });
await app.models.Ticket.updateEditableTicket(ctx, validTicketId, data); await models.Ticket.updateEditableTicket(ctx, validTicketId, data, options);
let updatedTicket = await app.models.Ticket.findById(validTicketId); const updatedTicket = await models.Ticket.findById(validTicketId, null, options);
expect(updatedTicket.addressFk).toEqual(1); expect(updatedTicket.addressFk).toEqual(1);
await tx.rollback();
} catch (e) {
await tx.rollback();
throw e;
}
}); });
}); });

View File

@ -1,19 +1,26 @@
const app = require('vn-loopback/server/server'); const models = require('vn-loopback/server/server').models;
describe('Ticket uploadFile()', () => { describe('Ticket uploadFile()', () => {
it(`should return an error for a user without enough privileges`, async() => { it(`should return an error for a user without enough privileges`, async() => {
let ticketId = 15; const tx = await models.Ticket.beginTransaction({});
let currentUserId = 1101;
let ticketTypeId = 14;
let ctx = {req: {accessToken: {userId: currentUserId}}, args: {dmsTypeId: ticketTypeId}};
let error; let error;
await app.models.Ticket.uploadFile(ctx, ticketId).catch(e => { try {
const options = {transaction: tx};
const ticketId = 15;
const currentUserId = 1101;
const ticketTypeId = 14;
const ctx = {req: {accessToken: {userId: currentUserId}}, args: {dmsTypeId: ticketTypeId}};
await models.Ticket.uploadFile(ctx, ticketId, options);
await tx.rollback();
} catch (e) {
await tx.rollback();
error = e; error = e;
}).finally(() => { }
expect(error.message).toEqual(`You don't have enough privileges`); expect(error.message).toEqual(`You don't have enough privileges`);
}); });
expect(error).toBeDefined();
});
}); });

View File

@ -19,10 +19,17 @@ module.exports = Self => {
} }
}); });
Self.summary = async ticketFk => { Self.summary = async(ticketFk, options) => {
let models = Self.app.models; const models = Self.app.models;
let summaryObj = await getTicketData(Self, ticketFk); const myOptions = {};
summaryObj.sales = await models.Ticket.getSales(ticketFk);
if (typeof options == 'object')
Object.assign(myOptions, options);
const summaryObj = await getTicketData(Self, ticketFk, myOptions);
summaryObj.sales = await models.Ticket.getSales(ticketFk, myOptions);
summaryObj.packagings = await models.TicketPackaging.find({ summaryObj.packagings = await models.TicketPackaging.find({
where: {ticketFk: ticketFk}, where: {ticketFk: ticketFk},
include: [{relation: 'packaging', include: [{relation: 'packaging',
@ -33,24 +40,25 @@ module.exports = Self => {
} }
} }
}] }]
}); }, myOptions);
summaryObj.requests = await getRequests(Self, ticketFk);
summaryObj.requests = await getRequests(Self, ticketFk, myOptions);
summaryObj.services = await models.TicketService.find({ summaryObj.services = await models.TicketService.find({
where: {ticketFk: ticketFk}, where: {ticketFk: ticketFk},
include: [{relation: 'taxClass'}] include: [{relation: 'taxClass'}]
}); }, myOptions);
return summaryObj; return summaryObj;
}; };
async function getTicketData(Self, ticketFk) { async function getTicketData(Self, ticketFk, options) {
let filter = { const filter = {
include: [ include: [
{relation: 'warehouse', scope: {fields: ['name']}}, {relation: 'warehouse', scope: {fields: ['name']}},
{relation: 'agencyMode', scope: {fields: ['name']}}, {relation: 'agencyMode', scope: {fields: ['name']}},
{relation: 'zone', scope: {fields: ['name']}}, {relation: 'zone', scope: {fields: ['name']}},
{ {relation: 'client',
relation: 'client',
scope: { scope: {
fields: ['salesPersonFk', 'name', 'phone', 'mobile'], fields: ['salesPersonFk', 'name', 'phone', 'mobile'],
include: { include: {
@ -99,11 +107,11 @@ module.exports = Self => {
where: {id: ticketFk} where: {id: ticketFk}
}; };
return await Self.findOne(filter); return Self.findOne(filter, options);
} }
async function getRequests(Self, ticketFk) { async function getRequests(Self, ticketFk, options) {
let filter = { const filter = {
where: { where: {
ticketFk: ticketFk ticketFk: ticketFk
}, },
@ -127,6 +135,6 @@ module.exports = Self => {
} }
] ]
}; };
return await Self.app.models.TicketRequest.find(filter); return Self.app.models.TicketRequest.find(filter, options);
} }
}; };

View File

@ -5,25 +5,25 @@ module.exports = Self => {
description: 'Transfer sales to a new or a given ticket', description: 'Transfer sales to a new or a given ticket',
accepts: [{ accepts: [{
arg: 'id', arg: 'id',
type: 'Number', type: 'number',
required: true, required: true,
description: 'Origin ticket id', description: 'Origin ticket id',
http: {source: 'path'} http: {source: 'path'}
}, },
{ {
arg: 'ticketId', arg: 'ticketId',
type: 'Number', type: 'number',
description: 'Destination ticket id', description: 'Destination ticket id',
required: false required: false
}, },
{ {
arg: 'sales', arg: 'sales',
type: ['Object'], type: ['object'],
description: 'The sales to transfer', description: 'The sales to transfer',
required: true required: true
}], }],
returns: { returns: {
type: 'Object', type: 'object',
root: true root: true
}, },
http: { http: {
@ -32,28 +32,35 @@ module.exports = Self => {
} }
}); });
Self.transferSales = async(ctx, id, ticketId, sales) => { Self.transferSales = async(ctx, id, ticketId, sales, options) => {
let userId = ctx.req.accessToken.userId; const userId = ctx.req.accessToken.userId;
const models = Self.app.models; const models = Self.app.models;
const myOptions = {};
let tx;
const isEditable = await models.Ticket.isEditable(ctx, id); if (typeof options == 'object')
Object.assign(myOptions, options);
if (!myOptions.transaction) {
tx = await Self.beginTransaction({});
myOptions.transaction = tx;
}
try {
const isEditable = await models.Ticket.isEditable(ctx, id, myOptions);
if (!isEditable) if (!isEditable)
throw new UserError(`The sales of this ticket can't be modified`); throw new UserError(`The sales of this ticket can't be modified`);
if (ticketId) { if (ticketId) {
const isReceiverEditable = await models.Ticket.isEditable(ctx, ticketId); const isReceiverEditable = await models.Ticket.isEditable(ctx, ticketId, myOptions);
if (!isReceiverEditable) if (!isReceiverEditable)
throw new UserError(`The sales of the receiver ticket can't be modified`); throw new UserError(`The sales of the receiver ticket can't be modified`);
} }
let tx = await Self.beginTransaction({}); const originalTicket = await models.Ticket.findById(id, null, myOptions);
try {
const options = {transaction: tx};
const originalTicket = await models.Ticket.findById(id, null, options);
const originalSales = await models.Sale.find({ const originalSales = await models.Sale.find({
where: {ticketFk: id} where: {ticketFk: id}
}, options); }, myOptions);
if (!ticketId) { if (!ticketId) {
const ticket = await models.Ticket.findById(id); const ticket = await models.Ticket.findById(id);
@ -61,7 +68,7 @@ module.exports = Self => {
if (!canCreateTicket) if (!canCreateTicket)
throw new UserError(`You can't create a ticket for a inactive client`); throw new UserError(`You can't create a ticket for a inactive client`);
ticketId = await cloneTicket(originalTicket, options); ticketId = await cloneTicket(originalTicket, myOptions);
} }
const map = new Map(); const map = new Map();
@ -80,10 +87,10 @@ module.exports = Self => {
if (sale.quantity == originalSale.quantity) { if (sale.quantity == originalSale.quantity) {
await models.Sale.updateAll({ await models.Sale.updateAll({
id: sale.id id: sale.id
}, {ticketFk: ticketId}, options); }, {ticketFk: ticketId}, myOptions);
} else if (sale.quantity != originalSale.quantity) { } else if (sale.quantity != originalSale.quantity) {
await transferPartialSale( await transferPartialSale(
ticketId, originalSale, sale, options); ticketId, originalSale, sale, myOptions);
} }
// Log to original ticket // Log to original ticket
@ -105,7 +112,7 @@ module.exports = Self => {
concept: sale.concept, concept: sale.concept,
ticket: ticketId ticket: ticketId
} }
}, options); }, myOptions);
// Log to destination ticket // Log to destination ticket
await models.TicketLog.create({ await models.TicketLog.create({
@ -126,22 +133,22 @@ module.exports = Self => {
concept: sale.concept, concept: sale.concept,
ticket: ticketId ticket: ticketId
} }
}, options); }, myOptions);
} }
const isTicketEmpty = await models.Ticket.isEmpty(id, options); const isTicketEmpty = await models.Ticket.isEmpty(id, myOptions);
if (isTicketEmpty) { if (isTicketEmpty) {
await originalTicket.updateAttributes({ await originalTicket.updateAttributes({
isDeleted: true isDeleted: true
}, options); }, myOptions);
} }
await tx.commit(); if (tx) await tx.commit();
return {id: ticketId}; return {id: ticketId};
} catch (error) { } catch (e) {
await tx.rollback(); if (tx) await tx.rollback();
throw error; throw e;
} }
}; };
@ -163,7 +170,7 @@ module.exports = Self => {
// Update original sale // Update original sale
const rest = originalSale.quantity - sale.quantity; const rest = originalSale.quantity - sale.quantity;
const originalInstance = await models.Sale.findById(sale.id, options); const originalInstance = await models.Sale.findById(sale.id, null, options);
await originalInstance.updateAttribute('quantity', rest, options); await originalInstance.updateAttribute('quantity', rest, options);
// Clone sale with new quantity // Clone sale with new quantity

View File

@ -35,14 +35,21 @@ module.exports = Self => {
} }
}); });
Self.updateDiscount = async(ctx, id, salesIds, newDiscount) => { Self.updateDiscount = async(ctx, id, salesIds, newDiscount, options) => {
const $t = ctx.req.__; // $translate const $t = ctx.req.__; // $translate
const models = Self.app.models; const models = Self.app.models;
const tx = await Self.beginTransaction({}); const myOptions = {};
let tx;
if (typeof options == 'object')
Object.assign(myOptions, options);
if (!myOptions.transaction) {
tx = await Self.beginTransaction({});
myOptions.transaction = tx;
}
try { try {
const options = {transaction: tx};
const filter = { const filter = {
where: { where: {
id: {inq: salesIds} id: {inq: salesIds}
@ -61,7 +68,7 @@ module.exports = Self => {
} }
}; };
const sales = await models.Sale.find(filter, options); const sales = await models.Sale.find(filter, myOptions);
if (sales.length === 0) if (sales.length === 0)
throw new UserError('Please select at least one sale'); throw new UserError('Please select at least one sale');
@ -71,15 +78,15 @@ module.exports = Self => {
throw new UserError('All sales must belong to the same ticket'); throw new UserError('All sales must belong to the same ticket');
const userId = ctx.req.accessToken.userId; const userId = ctx.req.accessToken.userId;
const isLocked = await models.Ticket.isLocked(id); const isLocked = await models.Ticket.isLocked(id, myOptions);
const roles = await models.Account.getRoles(userId); const roles = await models.Account.getRoles(userId, myOptions);
const hasAllowedRoles = roles.filter(role => const hasAllowedRoles = roles.filter(role =>
role == 'salesPerson' || role == 'claimManager' role == 'salesPerson' || role == 'claimManager'
); );
const state = await Self.app.models.TicketState.findOne({ const state = await Self.app.models.TicketState.findOne({
where: {ticketFk: id} where: {ticketFk: id}
}); }, myOptions);
const alertLevel = state ? state.alertLevel : null; const alertLevel = state ? state.alertLevel : null;
if (isLocked || (!hasAllowedRoles && alertLevel > 0)) if (isLocked || (!hasAllowedRoles && alertLevel > 0))
@ -89,11 +96,11 @@ module.exports = Self => {
where: { where: {
workerFk: userId workerFk: userId
}, },
fields: 'amount'}, options); fields: 'amount'}, myOptions);
const componentCode = usesMana ? 'mana' : 'buyerDiscount'; const componentCode = usesMana ? 'mana' : 'buyerDiscount';
const discountComponent = await models.Component.findOne({ const discountComponent = await models.Component.findOne({
where: {code: componentCode}}, options); where: {code: componentCode}}, myOptions);
const componentId = discountComponent.id; const componentId = discountComponent.id;
const promises = []; const promises = [];
@ -105,9 +112,9 @@ module.exports = Self => {
const newComponent = models.SaleComponent.upsert({ const newComponent = models.SaleComponent.upsert({
saleFk: sale.id, saleFk: sale.id,
value: value, value: value,
componentFk: componentId}, options); componentFk: componentId}, myOptions);
const updatedSale = sale.updateAttribute('discount', newDiscount, options); const updatedSale = sale.updateAttribute('discount', newDiscount, myOptions);
promises.push(newComponent, updatedSale); promises.push(newComponent, updatedSale);
changesMade += `\r\n-${sale.itemFk}: ${sale.concept} (${sale.quantity}) ${oldDiscount}% ➔ *${newDiscount}%*`; changesMade += `\r\n-${sale.itemFk}: ${sale.concept} (${sale.quantity}) ${oldDiscount}% ➔ *${newDiscount}%*`;
@ -116,7 +123,7 @@ module.exports = Self => {
await Promise.all(promises); await Promise.all(promises);
const query = `call vn.manaSpellersRequery(?)`; const query = `call vn.manaSpellersRequery(?)`;
await Self.rawSql(query, [userId], options); await Self.rawSql(query, [userId], myOptions);
const ticket = await models.Ticket.findById(id, { const ticket = await models.Ticket.findById(id, {
include: { include: {
@ -130,7 +137,7 @@ module.exports = Self => {
} }
} }
} }
}, options); }, myOptions);
const salesPerson = ticket.client().salesPersonUser(); const salesPerson = ticket.client().salesPersonUser();
if (salesPerson) { if (salesPerson) {
@ -141,13 +148,13 @@ module.exports = Self => {
ticketUrl: `${origin}/#!/ticket/${id}/sale`, ticketUrl: `${origin}/#!/ticket/${id}/sale`,
changes: changesMade changes: changesMade
}); });
await models.Chat.sendCheckingPresence(ctx, salesPerson.id, message); await models.Chat.sendCheckingPresence(ctx, salesPerson.id, message, myOptions);
} }
await tx.commit(); if (tx) await tx.commit();
} catch (error) { } catch (e) {
await tx.rollback(); if (tx) await tx.rollback();
throw error; throw e;
} }
}; };
}; };

View File

@ -15,7 +15,7 @@ module.exports = Self => {
{ {
arg: 'data', arg: 'data',
description: 'Model instance data', description: 'Model instance data',
type: 'Object', type: 'object',
required: true, required: true,
http: {source: 'body'} http: {source: 'body'}
} }
@ -30,12 +30,30 @@ module.exports = Self => {
} }
}); });
Self.updateEditableTicket = async(ctx, id, data) => { Self.updateEditableTicket = async(ctx, id, data, options) => {
let ticketIsEditable = await Self.app.models.Ticket.isEditable(ctx, id); const myOptions = {};
let tx;
if (typeof options == 'object')
Object.assign(myOptions, options);
if (!myOptions.transaction) {
tx = await Self.beginTransaction({});
myOptions.transaction = tx;
}
try {
const ticketIsEditable = await Self.app.models.Ticket.isEditable(ctx, id, myOptions);
if (!ticketIsEditable) if (!ticketIsEditable)
throw new UserError('This ticket can not be modified'); throw new UserError('This ticket can not be modified');
let ticket = await Self.app.models.Ticket.findById(id); const ticket = await Self.app.models.Ticket.findById(id, null, myOptions);
await ticket.updateAttributes(data); await ticket.updateAttributes(data, myOptions);
if (tx) await tx.commit();
} catch (e) {
if (tx) await tx.rollback();
throw e;
}
}; };
}; };

View File

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

View File

@ -3,10 +3,8 @@ const app = require('vn-loopback/server/server');
describe('ticket model TicketTracking', () => { describe('ticket model TicketTracking', () => {
let ticketTrackingId; let ticketTrackingId;
afterAll(async done => { afterAll(async() => {
await app.models.TicketPackaging.destroyById(ticketTrackingId); await app.models.TicketPackaging.destroyById(ticketTrackingId);
done();
}); });
it('should save a ticketTraing as the quantity is greater than 0', async() => { it('should save a ticketTraing as the quantity is greater than 0', async() => {

Some files were not shown because too many files have changed in this diff Show More