3092-module_transactions #740
|
@ -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.
|
||||||
```
|
```
|
||||||
|
|
|
@ -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();
|
||||||
|
|
|
@ -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();
|
||||||
|
|
|
@ -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();
|
||||||
|
|
|
@ -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)
|
||||||
|
|
|
@ -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)};
|
||||||
}
|
}
|
||||||
|
|
|
@ -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"]',
|
||||||
|
|
|
@ -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');
|
||||||
|
|
|
@ -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');
|
||||||
|
|
|
@ -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);
|
||||||
|
|
|
@ -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);
|
||||||
|
|
|
@ -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);
|
||||||
|
|
|
@ -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');
|
||||||
|
|
|
@ -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);
|
||||||
|
|
|
@ -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);
|
||||||
|
|
|
@ -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);
|
||||||
|
|
|
@ -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);
|
||||||
|
|
|
@ -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');
|
||||||
|
|
|
@ -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');
|
||||||
|
|
|
@ -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: {}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -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"
|
||||||
}
|
}
|
|
@ -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() => {
|
||||||
|
|
|
@ -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()', () => {
|
||||||
|
|
|
@ -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();
|
||||||
|
|
|
@ -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();
|
||||||
|
|
|
@ -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() => {
|
||||||
|
|
|
@ -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() => {
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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);
|
||||||
|
|
|
@ -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();
|
||||||
|
|
|
@ -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">
|
||||||
|
|
|
@ -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') {
|
||||||
|
|
|
@ -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() => {
|
||||||
|
|
|
@ -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() => {
|
||||||
|
|
|
@ -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() => {
|
||||||
|
|
|
@ -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() => {
|
||||||
|
|
|
@ -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() => {
|
||||||
|
|
|
@ -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() => {
|
||||||
|
|
|
@ -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', () => {
|
||||||
|
|
|
@ -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);
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
|
@ -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;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
|
@ -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);
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
|
@ -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);
|
|
||||||
|
|
||||||
expect(response[0].name).toBeDefined();
|
try {
|
||||||
expect(response[0].name).toEqual('Container ammo box 1m');
|
const options = {transaction: tx};
|
||||||
|
|
||||||
|
const filter = {where: {packagingFk: 1}};
|
||||||
|
const response = await models.Packaging.listPackaging(filter, options);
|
||||||
|
|
||||||
|
expect(response[0].name).toEqual('Container ammo box 1m');
|
||||||
|
|
||||||
|
await tx.rollback();
|
||||||
|
} catch (e) {
|
||||||
|
await tx.rollback();
|
||||||
|
throw e;
|
||||||
|
}
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
|
@ -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;
|
||||||
};
|
};
|
||||||
|
|
|
@ -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);
|
|
||||||
|
|
||||||
expect(result.length).toEqual(4);
|
try {
|
||||||
|
const options = {transaction: tx};
|
||||||
|
|
||||||
|
const filter = {where: {ticketFk: 1}};
|
||||||
|
const result = await models.SaleTracking.listSaleTracking(filter, options);
|
||||||
|
|
||||||
|
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);
|
|
||||||
|
|
||||||
expect(result.length).toEqual(0);
|
try {
|
||||||
|
const options = {transaction: tx};
|
||||||
|
|
||||||
|
const filter = {where: {ticketFk: 2}};
|
||||||
|
const result = await models.SaleTracking.listSaleTracking(filter, options);
|
||||||
|
|
||||||
|
expect(result.length).toEqual(0);
|
||||||
|
|
||||||
|
await tx.rollback();
|
||||||
|
} catch (e) {
|
||||||
|
await tx.rollback();
|
||||||
|
throw e;
|
||||||
|
}
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
|
@ -26,54 +26,73 @@ 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);
|
||||||
|
|
||||||
const ticket = await models.Ticket.findById(ticketId, {
|
if (!myOptions.transaction) {
|
||||||
include: {
|
tx = await Self.beginTransaction({});
|
||||||
relation: 'client',
|
myOptions.transaction = tx;
|
||||||
scope: {
|
}
|
||||||
include: {
|
|
||||||
relation: 'salesPersonUser',
|
try {
|
||||||
scope: {
|
const canEditSales = await models.Sale.canEdit(ctx, sales, myOptions);
|
||||||
fields: ['id', 'name']
|
|
||||||
|
const ticket = await models.Ticket.findById(ticketId, {
|
||||||
|
include: {
|
||||||
|
relation: 'client',
|
||||||
|
scope: {
|
||||||
|
include: {
|
||||||
|
relation: 'salesPersonUser',
|
||||||
|
scope: {
|
||||||
|
fields: ['id', 'name']
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}, myOptions);
|
||||||
|
|
||||||
|
const isTicketEditable = await models.Ticket.isEditable(ctx, ticketId, myOptions);
|
||||||
|
if (!isTicketEditable)
|
||||||
|
throw new UserError(`The sales of this ticket can't be modified`);
|
||||||
|
|
||||||
|
if (!canEditSales)
|
||||||
|
throw new UserError(`Sale(s) blocked, please contact production`);
|
||||||
|
|
||||||
|
const promises = [];
|
||||||
|
let deletions = '';
|
||||||
|
for (let sale of sales) {
|
||||||
|
const deletedSale = models.Sale.destroyById(sale.id, myOptions);
|
||||||
|
deletions += `\r\n-${sale.itemFk}: ${sale.concept} (${sale.quantity})`;
|
||||||
|
|
||||||
|
promises.push(deletedSale);
|
||||||
}
|
}
|
||||||
});
|
|
||||||
|
|
||||||
const isTicketEditable = await models.Ticket.isEditable(ctx, ticketId);
|
const salesPerson = ticket.client().salesPersonUser();
|
||||||
if (!isTicketEditable)
|
if (salesPerson) {
|
||||||
throw new UserError(`The sales of this ticket can't be modified`);
|
const origin = ctx.req.headers.origin;
|
||||||
|
|
||||||
if (!canEditSales)
|
const message = $t('Deleted sales from ticket', {
|
||||||
throw new UserError(`Sale(s) blocked, please contact production`);
|
ticketId: ticketId,
|
||||||
|
ticketUrl: `${origin}/#!/ticket/${ticketId}/sale`,
|
||||||
|
deletions: deletions
|
||||||
|
});
|
||||||
|
await models.Chat.sendCheckingPresence(ctx, salesPerson.id, message, myOptions);
|
||||||
|
}
|
||||||
|
|
||||||
const promises = [];
|
const deletedSales = await Promise.all(promises);
|
||||||
let deletions = '';
|
|
||||||
for (let sale of sales) {
|
|
||||||
const deletedSale = models.Sale.destroyById(sale.id);
|
|
||||||
deletions += `\r\n-${sale.itemFk}: ${sale.concept} (${sale.quantity})`;
|
|
||||||
|
|
||||||
promises.push(deletedSale);
|
if (tx) await tx.commit();
|
||||||
|
|
||||||
|
return deletedSales;
|
||||||
|
} catch (e) {
|
||||||
|
if (tx) await tx.rollback();
|
||||||
|
throw e;
|
||||||
}
|
}
|
||||||
|
|
||||||
const salesPerson = ticket.client().salesPersonUser();
|
|
||||||
if (salesPerson) {
|
|
||||||
const origin = ctx.req.headers.origin;
|
|
||||||
|
|
||||||
const message = $t('Deleted sales from ticket', {
|
|
||||||
ticketId: ticketId,
|
|
||||||
ticketUrl: `${origin}/#!/ticket/${ticketId}/sale`,
|
|
||||||
deletions: deletions
|
|
||||||
});
|
|
||||||
await models.Chat.sendCheckingPresence(ctx, salesPerson.id, message);
|
|
||||||
}
|
|
||||||
|
|
||||||
return Promise.all(promises);
|
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
|
@ -18,26 +18,32 @@ module.exports = Self => {
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
Self.getClaimableFromTicket = async ticketFk => {
|
Self.getClaimableFromTicket = async(ticketFk, options) => {
|
||||||
let query = `SELECT
|
const myOptions = {};
|
||||||
s.id AS saleFk,
|
|
||||||
t.id AS ticketFk,
|
|
||||||
t.landed,
|
|
||||||
s.concept,
|
|
||||||
s.itemFk,
|
|
||||||
s.quantity,
|
|
||||||
s.price,
|
|
||||||
s.discount,
|
|
||||||
t.nickname
|
|
||||||
FROM vn.ticket t
|
|
||||||
INNER JOIN vn.sale s ON s.ticketFk = t.id
|
|
||||||
LEFT JOIN vn.claimBeginning cb ON cb.saleFk = s.id
|
|
||||||
|
|
||||||
WHERE (t.landed) >= TIMESTAMPADD(DAY, -7, CURDATE())
|
if (typeof options == 'object')
|
||||||
AND t.id = ? AND cb.id IS NULL
|
Object.assign(myOptions, options);
|
||||||
ORDER BY t.landed DESC, t.id DESC`;
|
|
||||||
|
|
||||||
let claimableSales = await Self.rawSql(query, [ticketFk]);
|
const query = `
|
||||||
|
SELECT
|
||||||
|
s.id AS saleFk,
|
||||||
|
t.id AS ticketFk,
|
||||||
|
t.landed,
|
||||||
|
s.concept,
|
||||||
|
s.itemFk,
|
||||||
|
s.quantity,
|
||||||
|
s.price,
|
||||||
|
s.discount,
|
||||||
|
t.nickname
|
||||||
|
FROM vn.ticket t
|
||||||
|
INNER JOIN vn.sale s ON s.ticketFk = t.id
|
||||||
|
LEFT JOIN vn.claimBeginning cb ON cb.saleFk = s.id
|
||||||
|
|
||||||
|
WHERE (t.landed) >= TIMESTAMPADD(DAY, -7, CURDATE())
|
||||||
|
AND t.id = ? AND cb.id IS NULL
|
||||||
|
ORDER BY t.landed DESC, t.id DESC`;
|
||||||
|
|
||||||
|
const claimableSales = await Self.rawSql(query, [ticketFk], myOptions);
|
||||||
|
|
||||||
return claimableSales;
|
return claimableSales;
|
||||||
};
|
};
|
||||||
|
|
|
@ -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 (!isEditable)
|
if (!myOptions.transaction) {
|
||||||
throw new UserError(`The sales of this ticket can't be modified`);
|
tx = await Self.beginTransaction({});
|
||||||
|
myOptions.transaction = tx;
|
||||||
|
}
|
||||||
|
|
||||||
const canEditSale = await models.Sale.canEdit(ctx, [id]);
|
try {
|
||||||
|
const sale = await Self.findById(id, null, myOptions);
|
||||||
|
const isEditable = await models.Ticket.isEditable(ctx, sale.ticketFk, myOptions);
|
||||||
|
|
||||||
if (!canEditSale)
|
if (!isEditable)
|
||||||
throw new UserError(`Sale(s) blocked, please contact production`);
|
throw new UserError(`The sales of this ticket can't be modified`);
|
||||||
|
|
||||||
return Self.rawSql('CALL vn.sale_calculateComponent(?, null)', [id]);
|
const canEditSale = await models.Sale.canEdit(ctx, [id], myOptions);
|
||||||
|
|
||||||
|
if (!canEditSale)
|
||||||
|
throw new UserError(`Sale(s) blocked, please contact production`);
|
||||||
|
|
||||||
|
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;
|
||||||
|
}
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
|
@ -34,63 +34,80 @@ 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')
|
||||||
if (!isTicketEditable)
|
Object.assign(myOptions, options);
|
||||||
throw new UserError(`The sales of this ticket can't be modified`);
|
|
||||||
|
|
||||||
const canEditSale = await models.Sale.canEdit(ctx, sales);
|
if (!myOptions.transaction) {
|
||||||
|
tx = await Self.beginTransaction({});
|
||||||
if (!canEditSale)
|
myOptions.transaction = tx;
|
||||||
throw new UserError(`Sale(s) blocked, please contact production`);
|
|
||||||
|
|
||||||
let changesMade = '';
|
|
||||||
const promises = [];
|
|
||||||
|
|
||||||
for (let sale of sales) {
|
|
||||||
if (sale.reserved != reserved) {
|
|
||||||
const oldState = sale.reserved ? 'reserved' : 'regular';
|
|
||||||
const newState = reserved ? 'reserved' : 'regular';
|
|
||||||
|
|
||||||
const reservedSale = models.Sale.update({id: sale.id}, {reserved: reserved});
|
|
||||||
|
|
||||||
promises.push(reservedSale);
|
|
||||||
|
|
||||||
changesMade += `\r\n-${sale.itemFk}: ${sale.concept} (${sale.quantity}) ${$t('State')}: ${$t(oldState)} ➔ *${$t(newState)}*`;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
const result = await Promise.all(promises);
|
try {
|
||||||
|
const isTicketEditable = await models.Ticket.isEditable(ctx, ticketId, myOptions);
|
||||||
|
if (!isTicketEditable)
|
||||||
|
throw new UserError(`The sales of this ticket can't be modified`);
|
||||||
|
|
||||||
const ticket = await models.Ticket.findById(ticketId, {
|
const canEditSale = await models.Sale.canEdit(ctx, sales, myOptions);
|
||||||
include: {
|
|
||||||
relation: 'client',
|
if (!canEditSale)
|
||||||
scope: {
|
throw new UserError(`Sale(s) blocked, please contact production`);
|
||||||
include: {
|
|
||||||
relation: 'salesPersonUser',
|
let changesMade = '';
|
||||||
scope: {
|
const promises = [];
|
||||||
fields: ['id', 'name']
|
|
||||||
|
for (let sale of sales) {
|
||||||
|
if (sale.reserved != reserved) {
|
||||||
|
const oldState = sale.reserved ? 'reserved' : 'regular';
|
||||||
|
const newState = reserved ? 'reserved' : 'regular';
|
||||||
|
|
||||||
|
const reservedSale = models.Sale.updateAll({id: sale.id}, {reserved: reserved}, myOptions);
|
||||||
|
|
||||||
|
promises.push(reservedSale);
|
||||||
|
|
||||||
|
changesMade += `\r\n-${sale.itemFk}: ${sale.concept} (${sale.quantity}) ${$t('State')}: ${$t(oldState)} ➔ *${$t(newState)}*`;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const result = await Promise.all(promises);
|
||||||
|
|
||||||
|
const ticket = await models.Ticket.findById(ticketId, {
|
||||||
|
include: {
|
||||||
|
relation: 'client',
|
||||||
|
scope: {
|
||||||
|
include: {
|
||||||
|
relation: 'salesPersonUser',
|
||||||
|
scope: {
|
||||||
|
fields: ['id', 'name']
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}, myOptions);
|
||||||
|
|
||||||
|
const salesPerson = ticket.client().salesPersonUser();
|
||||||
|
if (salesPerson) {
|
||||||
|
const origin = ctx.req.headers.origin;
|
||||||
|
|
||||||
|
const message = $t('Changed sale reserved state', {
|
||||||
|
ticketId: ticketId,
|
||||||
|
ticketUrl: `${origin}/#!/ticket/${ticketId}/sale`,
|
||||||
|
changes: changesMade
|
||||||
|
});
|
||||||
|
await models.Chat.sendCheckingPresence(ctx, salesPerson.id, message, myOptions);
|
||||||
}
|
}
|
||||||
});
|
|
||||||
|
|
||||||
const salesPerson = ticket.client().salesPersonUser();
|
if (tx) await tx.commit();
|
||||||
if (salesPerson) {
|
|
||||||
const origin = ctx.req.headers.origin;
|
|
||||||
|
|
||||||
const message = $t('Changed sale reserved state', {
|
return result;
|
||||||
ticketId: ticketId,
|
} catch (e) {
|
||||||
ticketUrl: `${origin}/#!/ticket/${ticketId}/sale`,
|
if (tx) await tx.rollback();
|
||||||
changes: changesMade
|
throw e;
|
||||||
});
|
|
||||||
await models.Chat.sendCheckingPresence(ctx, salesPerson.id, message);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return result;
|
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
|
@ -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 productionUserID = 49;
|
const tx = await models.Sale.beginTransaction({});
|
||||||
let ctx = {req: {accessToken: {userId: productionUserID}}};
|
|
||||||
|
|
||||||
const sales = [{id: 3}];
|
try {
|
||||||
|
const options = {transaction: tx};
|
||||||
|
|
||||||
const result = await app.models.Sale.canEdit(ctx, sales);
|
const productionUserID = 49;
|
||||||
|
const ctx = {req: {accessToken: {userId: productionUserID}}};
|
||||||
|
|
||||||
expect(result).toEqual(true);
|
const sales = [{id: 3}];
|
||||||
|
|
||||||
|
const result = await models.Sale.canEdit(ctx, sales, options);
|
||||||
|
|
||||||
|
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 salesPersonUserID = 18;
|
const tx = await models.Sale.beginTransaction({});
|
||||||
let ctx = {req: {accessToken: {userId: salesPersonUserID}}};
|
|
||||||
|
|
||||||
const sales = [{id: 10}];
|
try {
|
||||||
|
const options = {transaction: tx};
|
||||||
|
|
||||||
const result = await app.models.Sale.canEdit(ctx, sales);
|
const salesPersonUserID = 18;
|
||||||
|
const ctx = {req: {accessToken: {userId: salesPersonUserID}}};
|
||||||
|
|
||||||
expect(result).toEqual(true);
|
const sales = [{id: 10}];
|
||||||
|
|
||||||
|
const result = await models.Sale.canEdit(ctx, sales, options);
|
||||||
|
|
||||||
|
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 salesPersonUserID = 18;
|
const tx = await models.Sale.beginTransaction({});
|
||||||
let ctx = {req: {accessToken: {userId: salesPersonUserID}}};
|
|
||||||
|
|
||||||
const sales = [{id: 3}];
|
try {
|
||||||
|
const options = {transaction: tx};
|
||||||
|
|
||||||
const result = await app.models.Sale.canEdit(ctx, sales);
|
const salesPersonUserID = 18;
|
||||||
|
const ctx = {req: {accessToken: {userId: salesPersonUserID}}};
|
||||||
|
|
||||||
expect(result).toEqual(false);
|
const sales = [{id: 3}];
|
||||||
|
|
||||||
|
const result = await models.Sale.canEdit(ctx, sales, options);
|
||||||
|
|
||||||
|
expect(result).toEqual(false);
|
||||||
|
|
||||||
|
await tx.rollback();
|
||||||
|
} catch (e) {
|
||||||
|
await tx.rollback();
|
||||||
|
throw e;
|
||||||
|
}
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
|
@ -1,38 +1,29 @@
|
||||||
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({});
|
||||||
req: {
|
|
||||||
accessToken: {userId: 9},
|
|
||||||
headers: {origin: 'localhost:5000'},
|
|
||||||
__: () => {}
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
let error;
|
let error;
|
||||||
|
|
||||||
const sales = [{id: 1, instance: 0}, {id: 2, instance: 1}];
|
|
||||||
const ticketId = 2;
|
|
||||||
|
|
||||||
try {
|
try {
|
||||||
await app.models.Sale.deleteSales(ctx, sales, ticketId);
|
const options = {transaction: tx};
|
||||||
|
|
||||||
|
const ctx = {
|
||||||
|
req: {
|
||||||
|
accessToken: {userId: 9},
|
||||||
|
headers: {origin: 'localhost:5000'},
|
||||||
|
__: () => {}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const sales = [{id: 1, instance: 0}, {id: 2, instance: 1}];
|
||||||
|
const ticketId = 2;
|
||||||
|
|
||||||
|
await models.Sale.deleteSales(ctx, sales, ticketId, options);
|
||||||
|
|
||||||
|
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({});
|
||||||
req: {
|
|
||||||
accessToken: {userId: 9},
|
|
||||||
headers: {origin: 'localhost:5000'},
|
|
||||||
__: () => {}
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
const sales = [{id: newSale.id, instance: 0}];
|
try {
|
||||||
const ticketId = 16;
|
const options = {transaction: tx};
|
||||||
|
|
||||||
let res = await app.models.Sale.deleteSales(ctx, sales, ticketId);
|
const ctx = {
|
||||||
|
req: {
|
||||||
|
accessToken: {userId: 9},
|
||||||
|
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);
|
||||||
|
|
||||||
expect(res).toEqual([{count: 1}]);
|
const sales = [{id: newSale.id, instance: 0}];
|
||||||
|
const ticketId = 16;
|
||||||
|
|
||||||
|
const deletions = await models.Sale.deleteSales(ctx, sales, ticketId, options);
|
||||||
|
|
||||||
|
expect(deletions).toEqual([{count: 1}]);
|
||||||
|
|
||||||
|
await tx.rollback();
|
||||||
|
} catch (e) {
|
||||||
|
await tx.rollback();
|
||||||
|
throw e;
|
||||||
|
}
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
|
@ -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({});
|
||||||
|
|
||||||
expect(claimableFromTicket[0].concept).toBe('Ranged weapon longbow 2m');
|
try {
|
||||||
expect(claimableFromTicket.length).toBe(3);
|
const options = {transaction: tx};
|
||||||
|
|
||||||
|
const claimableFromTicket = await models.Sale.getClaimableFromTicket(16, options);
|
||||||
|
|
||||||
|
expect(claimableFromTicket[0].concept).toBe('Ranged weapon longbow 2m');
|
||||||
|
expect(claimableFromTicket.length).toBe(3);
|
||||||
|
|
||||||
|
await tx.rollback();
|
||||||
|
} catch (e) {
|
||||||
|
await tx.rollback();
|
||||||
|
throw e;
|
||||||
|
}
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
|
@ -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 ctx = {req: {accessToken: {userId: 9}}};
|
const tx = await models.Sale.beginTransaction({});
|
||||||
const response = await app.models.Sale.recalculatePrice(ctx, saleId);
|
|
||||||
|
|
||||||
expect(response.affectedRows).toBeDefined();
|
try {
|
||||||
|
const options = {transaction: tx};
|
||||||
|
|
||||||
|
const ctx = {req: {accessToken: {userId: 9}}};
|
||||||
|
const response = await models.Sale.recalculatePrice(ctx, saleId, options);
|
||||||
|
|
||||||
|
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 ctx = {req: {accessToken: {userId: 9}}};
|
const tx = await models.Sale.beginTransaction({});
|
||||||
const immutableSaleId = 1;
|
|
||||||
await app.models.Sale.recalculatePrice(ctx, immutableSaleId)
|
|
||||||
.catch(response => {
|
|
||||||
expect(response).toEqual(new Error(`The sales of this ticket can't be modified`));
|
|
||||||
error = response;
|
|
||||||
});
|
|
||||||
|
|
||||||
expect(error).toBeDefined();
|
let error;
|
||||||
|
try {
|
||||||
|
const options = {transaction: tx};
|
||||||
|
|
||||||
|
const ctx = {req: {accessToken: {userId: 9}}};
|
||||||
|
const immutableSaleId = 1;
|
||||||
|
await models.Sale.recalculatePrice(ctx, immutableSaleId, 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`));
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
|
@ -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,51 +9,54 @@ 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;
|
||||||
const ticketId = 2;
|
try {
|
||||||
const sales = [{id: 5}];
|
const options = {transaction: tx};
|
||||||
const reserved = false;
|
|
||||||
|
|
||||||
await app.models.Sale.reserve(ctx, ticketId, sales, reserved)
|
const ticketId = 2;
|
||||||
.catch(response => {
|
const sales = [{id: 5}];
|
||||||
expect(response).toEqual(new Error(`The sales of this ticket can't be modified`));
|
const reserved = false;
|
||||||
error = response;
|
|
||||||
});
|
|
||||||
|
|
||||||
expect(error).toBeDefined();
|
await models.Sale.reserve(ctx, ticketId, sales, reserved, 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 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({});
|
||||||
|
|
||||||
expect(originalTicketSales[0].reserved).toEqual(false);
|
try {
|
||||||
expect(originalTicketSales[1].reserved).toEqual(false);
|
const options = {transaction: tx};
|
||||||
|
|
||||||
const ticketId = 11;
|
originalTicketSales = await models.Ticket.getSales(11, options);
|
||||||
const sales = [{id: 7}, {id: 8}];
|
|
||||||
const reserved = true;
|
|
||||||
|
|
||||||
await app.models.Sale.reserve(ctx, ticketId, sales, reserved);
|
expect(originalTicketSales[0].reserved).toEqual(false);
|
||||||
|
expect(originalTicketSales[1].reserved).toEqual(false);
|
||||||
|
|
||||||
const reservedTicketSales = await app.models.Ticket.getSales(ticketId);
|
const ticketId = 11;
|
||||||
|
const sales = [{id: 7}, {id: 8}];
|
||||||
|
const reserved = true;
|
||||||
|
|
||||||
expect(reservedTicketSales[0].reserved).toEqual(true);
|
await models.Sale.reserve(ctx, ticketId, sales, reserved, options);
|
||||||
expect(reservedTicketSales[1].reserved).toEqual(true);
|
|
||||||
|
const reservedTicketSales = await models.Ticket.getSales(ticketId, options);
|
||||||
|
|
||||||
|
expect(reservedTicketSales[0].reserved).toEqual(true);
|
||||||
|
expect(reservedTicketSales[1].reserved).toEqual(true);
|
||||||
|
|
||||||
|
await tx.rollback();
|
||||||
|
} catch (e) {
|
||||||
|
await tx.rollback();
|
||||||
|
throw e;
|
||||||
|
}
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
|
@ -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 newConcept = 'I am the new concept';
|
const tx = await models.Sale.beginTransaction({});
|
||||||
|
|
||||||
let response = await app.models.Sale.updateConcept(ctx, saleId, newConcept);
|
try {
|
||||||
|
const options = {transaction: tx};
|
||||||
|
|
||||||
expect(response.concept).toEqual(newConcept);
|
const newConcept = 'I am the new concept';
|
||||||
|
|
||||||
|
let response = await models.Sale.updateConcept(ctx, saleId, newConcept, options);
|
||||||
|
|
||||||
|
expect(response.concept).toEqual(newConcept);
|
||||||
|
|
||||||
|
await tx.rollback();
|
||||||
|
} catch (e) {
|
||||||
|
await tx.rollback();
|
||||||
|
throw e;
|
||||||
|
}
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
|
@ -1,104 +1,100 @@
|
||||||
const app = require('vn-loopback/server/server');
|
const models = require('vn-loopback/server/server').models;
|
||||||
|
|
||||||
describe('sale updatePrice()', () => {
|
describe('sale updatePrice()', () => {
|
||||||
let originalSale;
|
const ctx = {
|
||||||
let originalSalesPersonMana;
|
req: {
|
||||||
let createdSaleComponent;
|
accessToken: {userId: 18},
|
||||||
let saleId = 7;
|
headers: {origin: 'localhost:5000'},
|
||||||
let manaComponentId;
|
__: () => {}
|
||||||
|
}
|
||||||
beforeAll(async done => {
|
};
|
||||||
let component = await app.models.Component.findOne({where: {code: 'mana'}});
|
const saleId = 7;
|
||||||
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() => {
|
it('should throw an error if the ticket is not editable', async() => {
|
||||||
const ctx = {
|
const tx = await models.Sale.beginTransaction({});
|
||||||
req: {
|
|
||||||
accessToken: {userId: 18},
|
|
||||||
headers: {origin: 'localhost:5000'},
|
|
||||||
__: () => {}
|
|
||||||
}
|
|
||||||
};
|
|
||||||
let immutableSaleId = 1;
|
|
||||||
let price = 5;
|
|
||||||
|
|
||||||
await app.models.Sale.updatePrice(ctx, immutableSaleId, price)
|
try {
|
||||||
.catch(response => {
|
const options = {transaction: tx};
|
||||||
expect(response).toEqual(new Error(`The sales of this ticket can't be modified`));
|
|
||||||
error = response;
|
|
||||||
});
|
|
||||||
|
|
||||||
expect(error).toBeDefined();
|
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);
|
|
||||||
|
|
||||||
expect(updatedSale.price).toEqual(0);
|
await models.Sale.updatePrice(ctx, saleId, price, options);
|
||||||
|
const updatedSale = await models.Sale.findById(saleId, null, options);
|
||||||
|
|
||||||
// restores
|
expect(updatedSale.price).toEqual(0);
|
||||||
await originalSale.updateAttributes(originalSale);
|
|
||||||
await app.models.SaleComponent.updateAll({componentFk: manaComponentId, saleFk: saleId}, {value: 0});
|
await tx.rollback();
|
||||||
await originalSalesPersonMana.updateAttributes(originalSalesPersonMana);
|
} catch (e) {
|
||||||
|
await tx.rollback();
|
||||||
|
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);
|
|
||||||
|
|
||||||
expect(updatedSale.price).toEqual(8);
|
await models.Sale.updatePrice(ctx, saleId, price, options);
|
||||||
|
const updatedSale = await models.Sale.findById(saleId, null, options);
|
||||||
|
|
||||||
// restores
|
expect(updatedSale.price).toEqual(8);
|
||||||
await originalSale.updateAttributes(originalSale);
|
|
||||||
await app.models.SaleComponent.updateAll({componentFk: manaComponentId, saleFk: saleId}, {value: 0});
|
await tx.rollback();
|
||||||
await originalSalesPersonMana.updateAttributes(originalSalesPersonMana);
|
} catch (e) {
|
||||||
|
await tx.rollback();
|
||||||
|
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}});
|
|
||||||
|
|
||||||
expect(updatedSale.price).toBe(price);
|
const price = 5.4;
|
||||||
expect(createdSaleComponent.value).toEqual(-2.04);
|
const originalSalesPersonMana = await models.WorkerMana.findById(18, null, options);
|
||||||
|
const manaComponent = await models.Component.findOne({where: {code: 'mana'}}, options);
|
||||||
|
|
||||||
let updatedSalesPersonMana = await app.models.WorkerMana.findById(18);
|
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(updatedSalesPersonMana.amount).not.toEqual(originalSalesPersonMana.amount);
|
expect(updatedSale.price).toBe(price);
|
||||||
|
expect(createdSaleComponent.value).toEqual(-2.04);
|
||||||
|
|
||||||
// restores
|
const updatedSalesPersonMana = await models.WorkerMana.findById(18, null, options);
|
||||||
await originalSale.updateAttributes(originalSale);
|
|
||||||
await app.models.SaleComponent.updateAll({componentFk: manaComponentId, saleFk: saleId}, {value: 0});
|
expect(updatedSalesPersonMana.amount).not.toEqual(originalSalesPersonMana.amount);
|
||||||
await originalSalesPersonMana.updateAttributes(originalSalesPersonMana);
|
|
||||||
|
await tx.rollback();
|
||||||
|
} catch (e) {
|
||||||
|
await tx.rollback();
|
||||||
|
throw e;
|
||||||
|
}
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
|
@ -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;
|
||||||
|
}
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
|
@ -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 (!canEditSale)
|
if (!myOptions.transaction) {
|
||||||
throw new UserError(`Sale(s) blocked, please contact production`);
|
tx = await Self.beginTransaction({});
|
||||||
|
myOptions.transaction = tx;
|
||||||
|
}
|
||||||
|
|
||||||
return await currentLine.updateAttributes({concept: newConcept});
|
try {
|
||||||
|
const currentLine = await models.Sale.findById(id, null, myOptions);
|
||||||
|
|
||||||
|
const canEditSale = await models.Sale.canEdit(ctx, [id], myOptions);
|
||||||
|
|
||||||
|
if (!canEditSale)
|
||||||
|
throw new UserError(`Sale(s) blocked, please contact production`);
|
||||||
|
|
||||||
|
const line = await currentLine.updateAttributes({concept: newConcept}, myOptions);
|
||||||
|
|
||||||
|
if (tx) await tx.commit();
|
||||||
|
|
||||||
|
return line;
|
||||||
|
} catch (e) {
|
||||||
|
if (tx) await tx.rollback();
|
||||||
|
throw e;
|
||||||
|
}
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
|
@ -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;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
|
@ -26,60 +26,77 @@ 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 (!canEditSale)
|
if (!myOptions.transaction) {
|
||||||
throw new UserError(`Sale(s) blocked, please contact production`);
|
tx = await Self.beginTransaction({});
|
||||||
|
myOptions.transaction = tx;
|
||||||
|
}
|
||||||
|
|
||||||
if (isNaN(newQuantity))
|
try {
|
||||||
throw new UserError(`The value should be a number`);
|
const canEditSale = await models.Sale.canEdit(ctx, [id], myOptions);
|
||||||
|
|
||||||
const filter = {
|
if (!canEditSale)
|
||||||
include: {
|
throw new UserError(`Sale(s) blocked, please contact production`);
|
||||||
relation: 'ticket',
|
|
||||||
scope: {
|
if (isNaN(newQuantity))
|
||||||
include: {
|
throw new UserError(`The value should be a number`);
|
||||||
relation: 'client',
|
|
||||||
scope: {
|
const filter = {
|
||||||
include: {
|
include: {
|
||||||
relation: 'salesPersonUser',
|
relation: 'ticket',
|
||||||
scope: {
|
scope: {
|
||||||
fields: ['id', 'name']
|
include: {
|
||||||
|
relation: 'client',
|
||||||
|
scope: {
|
||||||
|
include: {
|
||||||
|
relation: 'salesPersonUser',
|
||||||
|
scope: {
|
||||||
|
fields: ['id', 'name']
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const sale = await models.Sale.findById(id, filter, myOptions);
|
||||||
|
|
||||||
|
if (newQuantity > sale.quantity)
|
||||||
|
throw new UserError('The new quantity should be smaller than the old one');
|
||||||
|
|
||||||
|
const oldQuantity = sale.quantity;
|
||||||
|
const result = await sale.updateAttributes({quantity: newQuantity}, myOptions);
|
||||||
|
|
||||||
|
const salesPerson = sale.ticket().client().salesPersonUser();
|
||||||
|
if (salesPerson) {
|
||||||
|
const origin = ctx.req.headers.origin;
|
||||||
|
const message = $t('Changed sale quantity', {
|
||||||
|
ticketId: sale.ticket().id,
|
||||||
|
itemId: sale.itemFk,
|
||||||
|
concept: sale.concept,
|
||||||
|
oldQuantity: oldQuantity,
|
||||||
|
newQuantity: newQuantity,
|
||||||
|
ticketUrl: `${origin}/#!/ticket/${sale.ticket().id}/sale`,
|
||||||
|
itemUrl: `${origin}/#!/item/${sale.itemFk}/summary`
|
||||||
|
});
|
||||||
|
await models.Chat.sendCheckingPresence(ctx, salesPerson.id, message, myOptions);
|
||||||
}
|
}
|
||||||
};
|
|
||||||
|
|
||||||
const sale = await models.Sale.findById(id, filter);
|
if (tx) await tx.commit();
|
||||||
|
|
||||||
if (newQuantity > sale.quantity)
|
return result;
|
||||||
throw new UserError('The new quantity should be smaller than the old one');
|
} catch (error) {
|
||||||
|
if (tx) await tx.rollback();
|
||||||
const oldQuantity = sale.quantity;
|
throw error;
|
||||||
const result = await sale.updateAttributes({quantity: newQuantity});
|
|
||||||
|
|
||||||
const salesPerson = sale.ticket().client().salesPersonUser();
|
|
||||||
if (salesPerson) {
|
|
||||||
const origin = ctx.req.headers.origin;
|
|
||||||
const message = $t('Changed sale quantity', {
|
|
||||||
ticketId: sale.ticket().id,
|
|
||||||
itemId: sale.itemFk,
|
|
||||||
concept: sale.concept,
|
|
||||||
oldQuantity: oldQuantity,
|
|
||||||
newQuantity: newQuantity,
|
|
||||||
ticketUrl: `${origin}/#!/ticket/${sale.ticket().id}/sale`,
|
|
||||||
itemUrl: `${origin}/#!/item/${sale.itemFk}/summary`
|
|
||||||
});
|
|
||||||
await models.Chat.sendCheckingPresence(ctx, salesPerson.id, message);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return result;
|
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
|
@ -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;
|
||||||
|
|
|
@ -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;
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
|
@ -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 productionRole = 49;
|
const tx = await models.State.beginTransaction({});
|
||||||
const ctx = {req: {accessToken: {userId: productionRole}}};
|
|
||||||
|
|
||||||
let result = await app.models.State.editableStates(ctx, filter);
|
try {
|
||||||
let deliveredState = result.some(state => state.code == 'DELIVERED');
|
const options = {transaction: tx};
|
||||||
|
|
||||||
expect(deliveredState).toBeTruthy();
|
const productionRole = 49;
|
||||||
|
const ctx = {req: {accessToken: {userId: productionRole}}};
|
||||||
|
|
||||||
|
const editableStates = await models.State.editableStates(ctx, filter, options);
|
||||||
|
|
||||||
|
const deliveredState = editableStates.some(state => state.code == 'DELIVERED');
|
||||||
|
|
||||||
|
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 productionRole = 18;
|
const tx = await models.State.beginTransaction({});
|
||||||
const ctx = {req: {accessToken: {userId: productionRole}}};
|
|
||||||
let result = await app.models.State.editableStates(ctx, filter);
|
|
||||||
let deliveredState = result.some(state => state.code == 'DELIVERED');
|
|
||||||
let pickerDesignedState = result.some(state => state.code == 'PICKER_DESIGNED');
|
|
||||||
|
|
||||||
expect(deliveredState).toBeFalsy();
|
try {
|
||||||
expect(pickerDesignedState).toBeTruthy();
|
const options = {transaction: tx};
|
||||||
|
|
||||||
|
const productionRole = 18;
|
||||||
|
const ctx = {req: {accessToken: {userId: productionRole}}};
|
||||||
|
|
||||||
|
const editableStates = await models.State.editableStates(ctx, filter, options);
|
||||||
|
|
||||||
|
const deliveredState = editableStates.some(state => state.code == 'DELIVERED');
|
||||||
|
|
||||||
|
const pickerDesignedState = editableStates.some(state => state.code == 'PICKER_DESIGNED');
|
||||||
|
|
||||||
|
expect(deliveredState).toBeFalsy();
|
||||||
|
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 employeeRole = 1;
|
const tx = await models.State.beginTransaction({});
|
||||||
const ctx = {req: {accessToken: {userId: employeeRole}}};
|
|
||||||
let result = await app.models.State.editableStates(ctx, filter);
|
|
||||||
let pickerDesignedState = result.some(state => state.code == 'PICKER_DESIGNED');
|
|
||||||
|
|
||||||
expect(pickerDesignedState).toBeTruthy();
|
try {
|
||||||
|
const options = {transaction: tx};
|
||||||
|
|
||||||
|
const employeeRole = 1;
|
||||||
|
const ctx = {req: {accessToken: {userId: employeeRole}}};
|
||||||
|
|
||||||
|
const editableStates = await models.State.editableStates(ctx, filter, options);
|
||||||
|
|
||||||
|
const pickerDesignedState = editableStates.some(state => state.code == 'PICKER_DESIGNED');
|
||||||
|
|
||||||
|
expect(pickerDesignedState).toBeTruthy();
|
||||||
|
|
||||||
|
await tx.rollback();
|
||||||
|
} catch (e) {
|
||||||
|
await tx.rollback();
|
||||||
|
throw e;
|
||||||
|
}
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
|
@ -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 salesPersonRole = 18;
|
const tx = await models.State.beginTransaction({});
|
||||||
const onDeliveryState = 13;
|
|
||||||
let ctx = {req: {accessToken: {userId: salesPersonRole}}};
|
|
||||||
let result = await app.models.State.isEditable(ctx, onDeliveryState);
|
|
||||||
|
|
||||||
expect(result).toBe(false);
|
try {
|
||||||
|
const options = {transaction: tx};
|
||||||
|
|
||||||
|
const salesPersonRole = 18;
|
||||||
|
const onDeliveryState = 13;
|
||||||
|
const ctx = {req: {accessToken: {userId: salesPersonRole}}};
|
||||||
|
const result = await models.State.isEditable(ctx, onDeliveryState, options);
|
||||||
|
|
||||||
|
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 salesPersonRole = 18;
|
const tx = await models.State.beginTransaction({});
|
||||||
const asignedState = 20;
|
|
||||||
let ctx = {req: {accessToken: {userId: salesPersonRole}}};
|
|
||||||
let result = await app.models.State.isEditable(ctx, asignedState);
|
|
||||||
|
|
||||||
expect(result).toBe(true);
|
try {
|
||||||
|
const options = {transaction: tx};
|
||||||
|
|
||||||
|
const salesPersonRole = 18;
|
||||||
|
const asignedState = 20;
|
||||||
|
const ctx = {req: {accessToken: {userId: salesPersonRole}}};
|
||||||
|
const result = await models.State.isEditable(ctx, asignedState, options);
|
||||||
|
|
||||||
|
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 employeeRole = 1;
|
const tx = await models.State.beginTransaction({});
|
||||||
const fixingState = 1;
|
|
||||||
let ctx = {req: {accessToken: {userId: employeeRole}}};
|
|
||||||
let result = await app.models.State.isEditable(ctx, fixingState);
|
|
||||||
|
|
||||||
expect(result).toBe(true);
|
try {
|
||||||
|
const options = {transaction: tx};
|
||||||
|
|
||||||
|
const employeeRole = 1;
|
||||||
|
const fixingState = 1;
|
||||||
|
const ctx = {req: {accessToken: {userId: employeeRole}}};
|
||||||
|
const result = await models.State.isEditable(ctx, fixingState, options);
|
||||||
|
|
||||||
|
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 employeeRole = 1;
|
const tx = await models.State.beginTransaction({});
|
||||||
const asignedState = 13;
|
|
||||||
let ctx = {req: {accessToken: {userId: employeeRole}}};
|
|
||||||
let result = await app.models.State.isEditable(ctx, asignedState);
|
|
||||||
|
|
||||||
expect(result).toBe(false);
|
try {
|
||||||
|
const options = {transaction: tx};
|
||||||
|
|
||||||
|
const employeeRole = 1;
|
||||||
|
const asignedState = 13;
|
||||||
|
const ctx = {req: {accessToken: {userId: employeeRole}}};
|
||||||
|
const result = await models.State.isEditable(ctx, asignedState, options);
|
||||||
|
|
||||||
|
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 productionRole = 49;
|
const tx = await models.State.beginTransaction({});
|
||||||
const onDeliveryState = 13;
|
|
||||||
let ctx = {req: {accessToken: {userId: productionRole}}};
|
|
||||||
let result = await app.models.State.isEditable(ctx, onDeliveryState);
|
|
||||||
|
|
||||||
expect(result).toBe(true);
|
try {
|
||||||
|
const options = {transaction: tx};
|
||||||
|
|
||||||
|
const productionRole = 49;
|
||||||
|
const onDeliveryState = 13;
|
||||||
|
const ctx = {req: {accessToken: {userId: productionRole}}};
|
||||||
|
const result = await models.State.isEditable(ctx, onDeliveryState, options);
|
||||||
|
|
||||||
|
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 salesPersonRole = 18;
|
const tx = await models.State.beginTransaction({});
|
||||||
const printedState = 4;
|
|
||||||
const okState = 3;
|
|
||||||
const ctx = {req: {accessToken: {userId: salesPersonRole}}};
|
|
||||||
|
|
||||||
let canEditCurrent = await app.models.State.isEditable(ctx, printedState);
|
try {
|
||||||
let canAsignNew = await app.models.State.isEditable(ctx, okState);
|
const options = {transaction: tx};
|
||||||
let result = canEditCurrent && canAsignNew;
|
|
||||||
|
|
||||||
expect(result).toBe(true);
|
const salesPersonRole = 18;
|
||||||
|
const printedState = 4;
|
||||||
|
const okState = 3;
|
||||||
|
const ctx = {req: {accessToken: {userId: salesPersonRole}}};
|
||||||
|
|
||||||
|
const canEditCurrent = await models.State.isEditable(ctx, printedState, options);
|
||||||
|
const canAsignNew = await models.State.isEditable(ctx, okState, options);
|
||||||
|
const result = canEditCurrent && canAsignNew;
|
||||||
|
|
||||||
|
expect(result).toBe(true);
|
||||||
|
|
||||||
|
await tx.rollback();
|
||||||
|
} catch (e) {
|
||||||
|
await tx.rollback();
|
||||||
|
throw e;
|
||||||
|
}
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
|
@ -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;
|
||||||
|
}
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -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 {
|
||||||
error = e;
|
const options = {transaction: tx};
|
||||||
}).finally(() => {
|
|
||||||
expect(error.message).toEqual(`You don't have enough privileges`);
|
|
||||||
});
|
|
||||||
|
|
||||||
expect(error).toBeDefined();
|
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;
|
||||||
|
}
|
||||||
|
|
||||||
|
expect(error.message).toEqual(`You don't have enough privileges`);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
|
@ -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;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
|
@ -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')
|
||||||
isOk: false,
|
Object.assign(myOptions, options);
|
||||||
attenderFk: worker.id,
|
|
||||||
response: ctx.args.observation,
|
|
||||||
};
|
|
||||||
|
|
||||||
let request = await Self.app.models.TicketRequest.findById(ctx.args.id);
|
if (!myOptions.transaction) {
|
||||||
return request.updateAttributes(params);
|
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,
|
||||||
|
attenderFk: worker.id,
|
||||||
|
response: ctx.args.observation,
|
||||||
|
};
|
||||||
|
|
||||||
|
const request = await Self.app.models.TicketRequest.findById(ctx.args.id, null, myOptions);
|
||||||
|
await request.updateAttributes(params, myOptions);
|
||||||
|
|
||||||
|
if (tx) await tx.commit();
|
||||||
|
|
||||||
|
return request;
|
||||||
|
} catch (e) {
|
||||||
|
if (tx) await tx.rollback();
|
||||||
|
throw e;
|
||||||
|
}
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
|
@ -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;
|
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
|
@ -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,74 +12,89 @@ 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 requestId = 5;
|
const tx = await models.TicketRequest.beginTransaction({});
|
||||||
const itemId = 4;
|
|
||||||
const quantity = 99999;
|
|
||||||
|
|
||||||
ctx.args = {
|
|
||||||
itemFk: itemId,
|
|
||||||
id: requestId,
|
|
||||||
quantity: quantity
|
|
||||||
};
|
|
||||||
|
|
||||||
let error;
|
let error;
|
||||||
|
|
||||||
try {
|
try {
|
||||||
await app.models.TicketRequest.confirm(ctx);
|
const options = {transaction: tx};
|
||||||
} catch (err) {
|
|
||||||
error = err;
|
const requestId = 5;
|
||||||
|
const itemId = 4;
|
||||||
|
const quantity = 99999;
|
||||||
|
|
||||||
|
ctx.args = {
|
||||||
|
itemFk: itemId,
|
||||||
|
id: requestId,
|
||||||
|
quantity: quantity
|
||||||
|
};
|
||||||
|
|
||||||
|
await models.TicketRequest.confirm(ctx, options);
|
||||||
|
|
||||||
|
await tx.rollback();
|
||||||
|
} catch (e) {
|
||||||
|
await tx.rollback();
|
||||||
|
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 requestId = 4;
|
const tx = await models.TicketRequest.beginTransaction({});
|
||||||
const itemId = 1;
|
|
||||||
const quantity = 10;
|
|
||||||
|
|
||||||
ctx.args = {
|
|
||||||
itemFk: itemId,
|
|
||||||
id: requestId,
|
|
||||||
quantity: quantity
|
|
||||||
};
|
|
||||||
|
|
||||||
const request = await app.models.TicketRequest.findById(requestId);
|
|
||||||
|
|
||||||
expect(request.saleFk).toBeNull();
|
|
||||||
|
|
||||||
await request.updateAttributes({saleFk: 2});
|
|
||||||
|
|
||||||
ctx.args = {
|
|
||||||
itemFk: itemId,
|
|
||||||
id: requestId,
|
|
||||||
quantity: quantity
|
|
||||||
};
|
|
||||||
|
|
||||||
let error;
|
let error;
|
||||||
|
|
||||||
try {
|
try {
|
||||||
await app.models.TicketRequest.confirm(ctx);
|
const options = {transaction: tx};
|
||||||
} catch (err) {
|
|
||||||
error = err;
|
const requestId = 4;
|
||||||
|
const itemId = 1;
|
||||||
|
const quantity = 10;
|
||||||
|
|
||||||
|
ctx.args = {
|
||||||
|
itemFk: itemId,
|
||||||
|
id: requestId,
|
||||||
|
quantity: quantity
|
||||||
|
};
|
||||||
|
|
||||||
|
const request = await models.TicketRequest.findById(requestId, null, options);
|
||||||
|
|
||||||
|
expect(request.saleFk).toBeNull();
|
||||||
|
|
||||||
|
await request.updateAttributes({saleFk: 2}, options);
|
||||||
|
|
||||||
|
ctx.args = {
|
||||||
|
itemFk: itemId,
|
||||||
|
id: requestId,
|
||||||
|
quantity: quantity
|
||||||
|
};
|
||||||
|
|
||||||
|
await models.TicketRequest.confirm(ctx, options);
|
||||||
|
|
||||||
|
await tx.rollback();
|
||||||
|
} catch (e) {
|
||||||
|
await tx.rollback();
|
||||||
|
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});
|
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
|
@ -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;
|
||||||
|
}
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
|
@ -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() => {
|
||||||
ctx.args = {};
|
const tx = await models.TicketRequest.beginTransaction({});
|
||||||
|
|
||||||
const result = await app.models.TicketRequest.filter(ctx);
|
try {
|
||||||
|
const options = {transaction: tx};
|
||||||
|
|
||||||
expect(result.length).toEqual(3);
|
ctx.args = {};
|
||||||
|
|
||||||
|
const result = await models.TicketRequest.filter(ctx, options);
|
||||||
|
|
||||||
|
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() => {
|
||||||
ctx.args = {search: 11};
|
const tx = await models.TicketRequest.beginTransaction({});
|
||||||
|
|
||||||
const result = await app.models.TicketRequest.filter(ctx);
|
try {
|
||||||
const requestId = result[0].id;
|
const options = {transaction: tx};
|
||||||
|
|
||||||
expect(requestId).toEqual(4);
|
ctx.args = {search: 11};
|
||||||
|
|
||||||
|
const result = await models.TicketRequest.filter(ctx, options);
|
||||||
|
const requestId = result[0].id;
|
||||||
|
|
||||||
|
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() => {
|
||||||
ctx.args = {search: 'NY roofs'};
|
const tx = await models.TicketRequest.beginTransaction({});
|
||||||
|
|
||||||
const result = await app.models.TicketRequest.filter(ctx);
|
try {
|
||||||
const requestId = result[0].id;
|
const options = {transaction: tx};
|
||||||
|
|
||||||
expect(requestId).toEqual(4);
|
ctx.args = {search: 'NY roofs'};
|
||||||
|
|
||||||
|
const result = await models.TicketRequest.filter(ctx, options);
|
||||||
|
const requestId = result[0].id;
|
||||||
|
|
||||||
|
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() => {
|
||||||
ctx.args = {ticketFk: 11};
|
const tx = await models.TicketRequest.beginTransaction({});
|
||||||
const result = await app.models.TicketRequest.filter(ctx);
|
|
||||||
const requestId = result[0].id;
|
|
||||||
|
|
||||||
expect(requestId).toEqual(4);
|
try {
|
||||||
|
const options = {transaction: tx};
|
||||||
|
|
||||||
|
ctx.args = {ticketFk: 11};
|
||||||
|
const result = await models.TicketRequest.filter(ctx, options);
|
||||||
|
const requestId = result[0].id;
|
||||||
|
|
||||||
|
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() => {
|
||||||
ctx.args = {attenderFk: 35};
|
const tx = await models.TicketRequest.beginTransaction({});
|
||||||
|
|
||||||
const result = await app.models.TicketRequest.filter(ctx);
|
try {
|
||||||
const requestId = result[0].id;
|
const options = {transaction: tx};
|
||||||
|
|
||||||
expect(requestId).toEqual(3);
|
ctx.args = {attenderFk: 35};
|
||||||
|
|
||||||
|
const result = await models.TicketRequest.filter(ctx, options);
|
||||||
|
const requestId = result[0].id;
|
||||||
|
|
||||||
|
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() => {
|
||||||
ctx.args = {isOk: null};
|
const tx = await models.TicketRequest.beginTransaction({});
|
||||||
|
|
||||||
const result = await app.models.TicketRequest.filter(ctx);
|
try {
|
||||||
const requestId = result[0].id;
|
const options = {transaction: tx};
|
||||||
|
|
||||||
expect(requestId).toEqual(3);
|
ctx.args = {isOk: null};
|
||||||
|
|
||||||
|
const result = await models.TicketRequest.filter(ctx, options);
|
||||||
|
const requestId = result[0].id;
|
||||||
|
|
||||||
|
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() => {
|
||||||
ctx.args = {clientFk: 1102};
|
const tx = await models.TicketRequest.beginTransaction({});
|
||||||
|
|
||||||
const result = await app.models.TicketRequest.filter(ctx);
|
try {
|
||||||
const requestId = result[0].id;
|
const options = {transaction: tx};
|
||||||
|
|
||||||
expect(requestId).toEqual(4);
|
ctx.args = {clientFk: 1102};
|
||||||
|
|
||||||
|
const result = await models.TicketRequest.filter(ctx, options);
|
||||||
|
const requestId = result[0].id;
|
||||||
|
|
||||||
|
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() => {
|
||||||
ctx.args = {warehouse: 1};
|
const tx = await models.TicketRequest.beginTransaction({});
|
||||||
|
|
||||||
const result = await app.models.TicketRequest.filter(ctx, {order: 'id'});
|
try {
|
||||||
const requestId = result[0].id;
|
const options = {transaction: tx};
|
||||||
|
|
||||||
expect(requestId).toEqual(3);
|
ctx.args = {warehouse: 1};
|
||||||
|
|
||||||
|
const result = await models.TicketRequest.filter(ctx, {order: 'id'}, options);
|
||||||
|
const requestId = result[0].id;
|
||||||
|
|
||||||
|
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() => {
|
||||||
ctx.args = {salesPersonFk: 18};
|
const tx = await models.TicketRequest.beginTransaction({});
|
||||||
|
|
||||||
const result = await app.models.TicketRequest.filter(ctx);
|
try {
|
||||||
const requestId = result[0].id;
|
const options = {transaction: tx};
|
||||||
|
|
||||||
expect(requestId).toEqual(3);
|
ctx.args = {salesPersonFk: 18};
|
||||||
|
|
||||||
|
const result = await models.TicketRequest.filter(ctx, options);
|
||||||
|
const requestId = result[0].id;
|
||||||
|
|
||||||
|
expect(requestId).toEqual(3);
|
||||||
|
|
||||||
|
await tx.rollback();
|
||||||
|
} catch (e) {
|
||||||
|
await tx.rollback();
|
||||||
|
throw e;
|
||||||
|
}
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
|
@ -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 (!params.stateFk && !params.code)
|
if (typeof options == 'object')
|
||||||
throw new UserError('State cannot be blank');
|
Object.assign(myOptions, options);
|
||||||
|
|
||||||
if (params.code) {
|
if (!myOptions.transaction) {
|
||||||
let state = await models.State.findOne({where: {code: params.code}, fields: ['id']});
|
tx = await Self.beginTransaction({});
|
||||||
params.stateFk = state.id;
|
myOptions.transaction = tx;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!params.workerFk) {
|
try {
|
||||||
let worker = await models.Worker.findOne({where: {userFk: userId}});
|
const userId = ctx.req.accessToken.userId;
|
||||||
params.workerFk = worker.id;
|
|
||||||
|
if (!params.stateFk && !params.code)
|
||||||
|
throw new UserError('State cannot be blank');
|
||||||
|
|
||||||
|
if (params.code) {
|
||||||
|
const state = await models.State.findOne({
|
||||||
|
where: {code: params.code},
|
||||||
|
fields: ['id']
|
||||||
|
}, myOptions);
|
||||||
|
|
||||||
|
params.stateFk = state.id;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!params.workerFk) {
|
||||||
|
const worker = await models.Worker.findOne({
|
||||||
|
where: {userFk: userId}
|
||||||
|
}, myOptions);
|
||||||
|
|
||||||
|
params.workerFk = worker.id;
|
||||||
|
}
|
||||||
|
|
||||||
|
const ticketState = await models.TicketState.findById(params.ticketFk, {
|
||||||
|
fields: ['stateFk']
|
||||||
|
}, myOptions);
|
||||||
|
|
||||||
|
let oldStateAllowed;
|
||||||
|
if (ticketState)
|
||||||
|
oldStateAllowed = await models.State.isEditable(ctx, ticketState.stateFk, myOptions);
|
||||||
|
const newStateAllowed = await models.State.isEditable(ctx, params.stateFk, myOptions);
|
||||||
|
|
||||||
|
const isAllowed = (!ticketState || oldStateAllowed == true) && newStateAllowed == true;
|
||||||
|
|
||||||
|
if (!isAllowed)
|
||||||
|
throw new UserError(`You don't have enough privileges`, 'ACCESS_DENIED');
|
||||||
|
|
||||||
|
const ticketTracking = await models.TicketTracking.create(params, myOptions);
|
||||||
|
|
||||||
|
if (tx) await tx.commit();
|
||||||
|
|
||||||
|
return ticketTracking;
|
||||||
|
} catch (e) {
|
||||||
|
if (tx) await tx.rollback();
|
||||||
|
throw e;
|
||||||
}
|
}
|
||||||
|
|
||||||
let ticketState = await models.TicketState.findById(
|
|
||||||
params.ticketFk,
|
|
||||||
{fields: ['stateFk']}
|
|
||||||
);
|
|
||||||
|
|
||||||
let oldStateAllowed;
|
|
||||||
if (ticketState)
|
|
||||||
oldStateAllowed = await models.State.isEditable(ctx, ticketState.stateFk);
|
|
||||||
let newStateAllowed = await models.State.isEditable(ctx, params.stateFk);
|
|
||||||
|
|
||||||
let isAllowed = (!ticketState || oldStateAllowed == true) && newStateAllowed == true;
|
|
||||||
|
|
||||||
if (!isAllowed)
|
|
||||||
throw new UserError(`You don't have enough privileges`, 'ACCESS_DENIED');
|
|
||||||
|
|
||||||
return models.TicketTracking.create(params);
|
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
|
@ -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')
|
||||||
where: {
|
Object.assign(myOptions, options);
|
||||||
code: 'delivered'
|
|
||||||
},
|
|
||||||
fields: ['id', 'name', 'alertLevel', 'code']
|
|
||||||
});
|
|
||||||
|
|
||||||
let worker = await models.Worker.findOne({where: {userFk: userId}});
|
if (!myOptions.transaction) {
|
||||||
|
tx = await Self.beginTransaction({});
|
||||||
let promises = [];
|
myOptions.transaction = tx;
|
||||||
for (let id of ticketIds) {
|
|
||||||
let promise = models.TicketTracking.changeState(ctx, {
|
|
||||||
stateFk: state.id,
|
|
||||||
workerFk: worker.id,
|
|
||||||
ticketFk: id
|
|
||||||
});
|
|
||||||
promises.push(promise);
|
|
||||||
}
|
}
|
||||||
await Promise.all(promises);
|
|
||||||
|
|
||||||
return state;
|
try {
|
||||||
|
const state = await models.State.findOne({
|
||||||
|
where: {
|
||||||
|
code: 'delivered'
|
||||||
|
},
|
||||||
|
fields: ['id', 'name', 'alertLevel', 'code']
|
||||||
|
}, myOptions);
|
||||||
|
|
||||||
|
const worker = await models.Worker.findOne({where: {userFk: userId}}, myOptions);
|
||||||
|
|
||||||
|
const promises = [];
|
||||||
|
for (const id of ticketIds) {
|
||||||
|
const promise = models.TicketTracking.changeState(ctx, {
|
||||||
|
stateFk: state.id,
|
||||||
|
workerFk: worker.id,
|
||||||
|
ticketFk: id
|
||||||
|
}, myOptions);
|
||||||
|
promises.push(promise);
|
||||||
|
}
|
||||||
|
await Promise.all(promises);
|
||||||
|
|
||||||
|
if (tx) await tx.commit();
|
||||||
|
|
||||||
|
return state;
|
||||||
|
} catch (e) {
|
||||||
|
if (tx) await tx.rollback();
|
||||||
|
throw e;
|
||||||
|
}
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
|
@ -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};
|
||||||
|
|
||||||
expect(ticketTracking.__data.ticketFk).toBe(params.ticketFk);
|
const ticket = await models.Ticket.create(sampleTicket, options);
|
||||||
expect(ticketTracking.__data.stateFk).toBe(params.stateFk);
|
|
||||||
expect(ticketTracking.__data.workerFk).toBe(49);
|
|
||||||
expect(ticketTracking.__data.id).toBeDefined();
|
|
||||||
|
|
||||||
// restores
|
activeCtx.accessToken.userId = productionId;
|
||||||
await app.models.TicketTracking.destroyById(ticketTracking.__data.id);
|
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.stateFk).toBe(params.stateFk);
|
||||||
|
expect(ticketTracking.__data.workerFk).toBe(49);
|
||||||
|
expect(ticketTracking.__data.id).toBeDefined();
|
||||||
|
|
||||||
|
await tx.rollback();
|
||||||
|
} 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};
|
|
||||||
let res = await app.models.TicketTracking.changeState(ctx, params);
|
|
||||||
|
|
||||||
expect(res.__data.ticketFk).toBe(params.ticketFk);
|
try {
|
||||||
expect(res.__data.stateFk).toBe(params.stateFk);
|
const options = {transaction: tx};
|
||||||
expect(res.__data.workerFk).toBe(params.workerFk);
|
|
||||||
expect(res.__data.workerFk).toBe(1);
|
const ticket = await models.Ticket.create(sampleTicket, options);
|
||||||
expect(res.__data.id).toBeDefined();
|
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.stateFk).toBe(params.stateFk);
|
||||||
|
expect(res.__data.workerFk).toBe(params.workerFk);
|
||||||
|
expect(res.__data.workerFk).toBe(1);
|
||||||
|
expect(res.__data.id).toBeDefined();
|
||||||
|
|
||||||
|
await tx.rollback();
|
||||||
|
} catch (e) {
|
||||||
|
await tx.rollback();
|
||||||
|
throw e;
|
||||||
|
}
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
|
@ -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);
|
const delivered = await models.State.findOne({where: {code: 'delivered'}, fields: ['id']}, options);
|
||||||
|
|
||||||
|
const params = [ticketOne.id, ticketTwo.id];
|
||||||
|
const state = await models.TicketTracking.setDelivered(ctx, params, options);
|
||||||
|
|
||||||
|
expect(state.id).toEqual(delivered.id);
|
||||||
|
|
||||||
|
await tx.rollback();
|
||||||
|
} catch (e) {
|
||||||
|
await tx.rollback();
|
||||||
|
throw e;
|
||||||
}
|
}
|
||||||
|
|
||||||
done();
|
|
||||||
});
|
|
||||||
|
|
||||||
afterAll(async done => {
|
|
||||||
try {
|
|
||||||
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);
|
|
||||||
|
|
||||||
// restores
|
|
||||||
await app.models.TicketTracking.destroyById(state.id);
|
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
|
@ -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];
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
|
@ -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 filter = {order: 't.id ASC'};
|
const tx = await models.TicketWeekly.beginTransaction({});
|
||||||
const ctx = {req: {accessToken: {userId: authUserId}}, args: {filter: filter}};
|
|
||||||
const result = await app.models.TicketWeekly.filter(ctx);
|
|
||||||
|
|
||||||
const firstRow = result[0];
|
try {
|
||||||
|
const options = {transaction: tx};
|
||||||
|
|
||||||
expect(firstRow.ticketFk).toEqual(1);
|
const filter = {order: 't.id ASC'};
|
||||||
expect(result.length).toEqual(5);
|
|
||||||
|
const ctx = {req: {accessToken: {userId: authUserId}}, args: {filter: filter}};
|
||||||
|
|
||||||
|
const result = await models.TicketWeekly.filter(ctx, null, options);
|
||||||
|
|
||||||
|
const firstRow = result[0];
|
||||||
|
|
||||||
|
expect(firstRow.ticketFk).toEqual(1);
|
||||||
|
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 ctx = {req: {accessToken: {userId: authUserId}}, args: {search: 2}};
|
const tx = await models.TicketWeekly.beginTransaction({});
|
||||||
const filter = {};
|
|
||||||
const result = await app.models.TicketWeekly.filter(ctx, filter);
|
|
||||||
const firstRow = result[0];
|
|
||||||
|
|
||||||
expect(firstRow.ticketFk).toEqual(2);
|
try {
|
||||||
|
const options = {transaction: tx};
|
||||||
|
|
||||||
|
const ctx = {req: {accessToken: {userId: authUserId}}, args: {search: 2}};
|
||||||
|
|
||||||
|
const result = await models.TicketWeekly.filter(ctx, null, options);
|
||||||
|
const firstRow = result[0];
|
||||||
|
|
||||||
|
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 ctx = {req: {accessToken: {userId: authUserId}}, args: {search: 'bruce'}};
|
const tx = await models.TicketWeekly.beginTransaction({});
|
||||||
const filter = {};
|
|
||||||
const result = await app.models.TicketWeekly.filter(ctx, filter);
|
|
||||||
const firstRow = result[0];
|
|
||||||
|
|
||||||
expect(firstRow.clientName).toEqual('Bruce Wayne');
|
try {
|
||||||
|
const options = {transaction: tx};
|
||||||
|
|
||||||
|
const ctx = {req: {accessToken: {userId: authUserId}}, args: {search: 'bruce'}};
|
||||||
|
|
||||||
|
const result = await models.TicketWeekly.filter(ctx, null, options);
|
||||||
|
const firstRow = result[0];
|
||||||
|
|
||||||
|
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 ctx = {req: {accessToken: {userId: authUserId}}, args: {search: 1101}};
|
const tx = await models.TicketWeekly.beginTransaction({});
|
||||||
const filter = {};
|
|
||||||
const result = await app.models.TicketWeekly.filter(ctx, filter);
|
|
||||||
const firstRow = result[0];
|
|
||||||
|
|
||||||
expect(firstRow.clientFk).toEqual(1101);
|
try {
|
||||||
expect(firstRow.clientName).toEqual('Bruce Wayne');
|
const options = {transaction: tx};
|
||||||
|
|
||||||
|
const ctx = {req: {accessToken: {userId: authUserId}}, args: {search: 1101}};
|
||||||
|
|
||||||
|
const result = await models.TicketWeekly.filter(ctx, null, options);
|
||||||
|
const firstRow = result[0];
|
||||||
|
|
||||||
|
expect(firstRow.clientFk).toEqual(1101);
|
||||||
|
expect(firstRow.clientName).toEqual('Bruce Wayne');
|
||||||
|
|
||||||
|
await tx.rollback();
|
||||||
|
} catch (e) {
|
||||||
|
await tx.rollback();
|
||||||
|
throw e;
|
||||||
|
}
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
|
@ -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},
|
||||||
|
|
|
@ -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;
|
||||||
|
|
|
@ -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 (!isEditable)
|
if (typeof options == 'object')
|
||||||
throw new UserError(`The current ticket can't be modified`);
|
Object.assign(myOptions, options);
|
||||||
|
|
||||||
return Self.rawSql('CALL vn.ticket_recalcComponents(?, NULL)', [id]);
|
if (!myOptions.transaction) {
|
||||||
|
tx = await Self.beginTransaction({});
|
||||||
|
myOptions.transaction = tx;
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
const isEditable = await Self.isEditable(ctx, id, myOptions);
|
||||||
|
|
||||||
|
if (!isEditable)
|
||||||
|
throw new UserError(`The current ticket can't be modified`);
|
||||||
|
|
||||||
|
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;
|
||||||
|
}
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
|
@ -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'}
|
||||||
|
|
|
@ -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,28 +30,46 @@ 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);
|
||||||
originFk: id,
|
const logRecord = {
|
||||||
userFk: userId,
|
originFk: id,
|
||||||
action: 'insert',
|
userFk: userId,
|
||||||
changedModel: 'sms',
|
action: 'insert',
|
||||||
newInstance: {
|
changedModel: 'sms',
|
||||||
destinationFk: id,
|
newInstance: {
|
||||||
destination: destination,
|
destinationFk: id,
|
||||||
message: message,
|
destination: destination,
|
||||||
statusCode: sms.statusCode,
|
message: message,
|
||||||
status: sms.status
|
statusCode: sms.statusCode,
|
||||||
}
|
status: sms.status
|
||||||
};
|
}
|
||||||
|
};
|
||||||
|
|
||||||
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;
|
||||||
|
|
||||||
return sms;
|
if (tx) await tx.commit();
|
||||||
|
|
||||||
|
return sms;
|
||||||
|
} catch (e) {
|
||||||
|
if (tx) await tx.rollback();
|
||||||
|
throw e;
|
||||||
|
}
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
|
@ -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,113 +21,133 @@ 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 (!isEditable)
|
if (typeof options == 'object')
|
||||||
throw new UserError(`The sales of this ticket can't be modified`);
|
Object.assign(myOptions, options);
|
||||||
|
|
||||||
// Check if has sales with shelving
|
if (!myOptions.transaction) {
|
||||||
const isSalesAssistant = await models.Account.hasRole(userId, 'salesAssistant');
|
tx = await Self.beginTransaction({});
|
||||||
const sales = await models.Sale.find({
|
myOptions.transaction = tx;
|
||||||
include: {relation: 'itemShelvingSale'},
|
|
||||||
where: {ticketFk: id}
|
|
||||||
});
|
|
||||||
const hasItemShelvingSales = sales.some(sale => {
|
|
||||||
return sale.itemShelvingSale();
|
|
||||||
});
|
|
||||||
|
|
||||||
if (hasItemShelvingSales && !isSalesAssistant)
|
|
||||||
throw new UserError(`You cannot delete a ticket that part of it is being prepared`);
|
|
||||||
|
|
||||||
// Check for existing claim
|
|
||||||
const claimOfATicket = await models.Claim.findOne({where: {ticketFk: id}});
|
|
||||||
if (claimOfATicket)
|
|
||||||
throw new UserError('You must delete the claim id %d first', 'DELETE_CLAIM_FIRST', claimOfATicket.id);
|
|
||||||
|
|
||||||
// Check for existing purchase requests
|
|
||||||
const hasPurchaseRequests = await models.TicketRequest.count({
|
|
||||||
ticketFk: id,
|
|
||||||
isOk: true
|
|
||||||
});
|
|
||||||
|
|
||||||
if (hasPurchaseRequests)
|
|
||||||
throw new UserError('You must delete all the buy requests first');
|
|
||||||
|
|
||||||
// removes item shelvings
|
|
||||||
if (hasItemShelvingSales && isSalesAssistant) {
|
|
||||||
const promises = [];
|
|
||||||
for (let sale of sales) {
|
|
||||||
if (sale.itemShelvingSale()) {
|
|
||||||
const itemShelvingSale = sale.itemShelvingSale();
|
|
||||||
const destroyedShelving = models.ItemShelvingSale.destroyById(itemShelvingSale.id);
|
|
||||||
promises.push(destroyedShelving);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
await Promise.all(promises);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Remove ticket greuges
|
try {
|
||||||
const ticketGreuges = await models.Greuge.find({where: {ticketFk: id}});
|
const userId = ctx.req.accessToken.userId;
|
||||||
const ownGreuges = ticketGreuges.every(greuge => {
|
const isEditable = await Self.isEditable(ctx, id, myOptions);
|
||||||
return greuge.ticketFk == id;
|
|
||||||
});
|
|
||||||
if (ownGreuges) {
|
|
||||||
for (const greuge of ticketGreuges) {
|
|
||||||
const instance = await models.Greuge.findById(greuge.id);
|
|
||||||
|
|
||||||
await instance.destroy();
|
if (!isEditable)
|
||||||
}
|
throw new UserError(`The sales of this ticket can't be modified`);
|
||||||
}
|
|
||||||
|
|
||||||
const ticket = await models.Ticket.findById(id, {
|
// Check if has sales with shelving
|
||||||
include: [{
|
const isSalesAssistant = await models.Account.hasRole(userId, 'salesAssistant', myOptions);
|
||||||
relation: 'client',
|
const sales = await models.Sale.find({
|
||||||
scope: {
|
include: {relation: 'itemShelvingSale'},
|
||||||
fields: ['id', 'salesPersonFk'],
|
where: {ticketFk: id}
|
||||||
include: {
|
}, myOptions);
|
||||||
relation: 'salesPersonUser',
|
const hasItemShelvingSales = sales.some(sale => {
|
||||||
scope: {
|
return sale.itemShelvingSale();
|
||||||
fields: ['id', 'name']
|
});
|
||||||
}
|
|
||||||
|
if (hasItemShelvingSales && !isSalesAssistant)
|
||||||
|
throw new UserError(`You cannot delete a ticket that part of it is being prepared`);
|
||||||
|
|
||||||
|
// Check for existing claim
|
||||||
|
const claimOfATicket = await models.Claim.findOne({where: {ticketFk: id}}, myOptions);
|
||||||
|
if (claimOfATicket)
|
||||||
|
throw new UserError('You must delete the claim id %d first', 'DELETE_CLAIM_FIRST', claimOfATicket.id);
|
||||||
|
|
||||||
|
// Check for existing purchase requests
|
||||||
|
const hasPurchaseRequests = await models.TicketRequest.count({
|
||||||
|
ticketFk: id,
|
||||||
|
isOk: true
|
||||||
|
}, myOptions);
|
||||||
|
|
||||||
|
if (hasPurchaseRequests)
|
||||||
|
throw new UserError('You must delete all the buy requests first');
|
||||||
|
|
||||||
|
// removes item shelvings
|
||||||
|
if (hasItemShelvingSales && isSalesAssistant) {
|
||||||
|
const promises = [];
|
||||||
|
for (let sale of sales) {
|
||||||
|
if (sale.itemShelvingSale()) {
|
||||||
|
const itemShelvingSale = sale.itemShelvingSale();
|
||||||
|
const destroyedShelving = models.ItemShelvingSale.destroyById(itemShelvingSale.id, myOptions);
|
||||||
|
promises.push(destroyedShelving);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}, {
|
await Promise.all(promises);
|
||||||
relation: 'ship'
|
}
|
||||||
}, {
|
|
||||||
relation: 'stowaway'
|
|
||||||
}]
|
|
||||||
});
|
|
||||||
|
|
||||||
// Change state to "fixing" if contains an stowaway and remove the link between them
|
// Remove ticket greuges
|
||||||
let otherTicketId;
|
const ticketGreuges = await models.Greuge.find({where: {ticketFk: id}}, myOptions);
|
||||||
if (ticket.stowaway())
|
const ownGreuges = ticketGreuges.every(greuge => {
|
||||||
otherTicketId = ticket.stowaway().shipFk;
|
return greuge.ticketFk == id;
|
||||||
else if (ticket.ship())
|
|
||||||
otherTicketId = ticket.ship().id;
|
|
||||||
|
|
||||||
if (otherTicketId) {
|
|
||||||
await models.Ticket.deleteStowaway(ctx, otherTicketId);
|
|
||||||
await models.TicketTracking.changeState(ctx, {
|
|
||||||
ticketFk: otherTicketId,
|
|
||||||
code: 'FIXING'
|
|
||||||
});
|
});
|
||||||
}
|
if (ownGreuges) {
|
||||||
|
for (const greuge of ticketGreuges) {
|
||||||
|
const instance = await models.Greuge.findById(greuge.id, null, myOptions);
|
||||||
|
|
||||||
// Send notification to salesPerson
|
await instance.destroy(myOptions);
|
||||||
const salesPersonUser = ticket.client().salesPersonUser();
|
}
|
||||||
if (salesPersonUser) {
|
}
|
||||||
const origin = ctx.req.headers.origin;
|
|
||||||
const message = $t(`I have deleted the ticket id`, {
|
|
||||||
id: id,
|
|
||||||
url: `${origin}/#!/ticket/${id}/summary`
|
|
||||||
});
|
|
||||||
await models.Chat.send(ctx, `@${salesPersonUser.name}`, message);
|
|
||||||
}
|
|
||||||
|
|
||||||
return ticket.updateAttribute('isDeleted', true);
|
const ticket = await models.Ticket.findById(id, {
|
||||||
|
include: [{
|
||||||
|
relation: 'client',
|
||||||
|
scope: {
|
||||||
|
fields: ['id', 'salesPersonFk'],
|
||||||
|
include: {
|
||||||
|
relation: 'salesPersonUser',
|
||||||
|
scope: {
|
||||||
|
fields: ['id', 'name']
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}, {
|
||||||
|
relation: 'ship'
|
||||||
|
}, {
|
||||||
|
relation: 'stowaway'
|
||||||
|
}]
|
||||||
|
}, myOptions);
|
||||||
|
|
||||||
|
// Change state to "fixing" if contains an stowaway and remove the link between them
|
||||||
|
let otherTicketId;
|
||||||
|
if (ticket.stowaway())
|
||||||
|
otherTicketId = ticket.stowaway().shipFk;
|
||||||
|
else if (ticket.ship())
|
||||||
|
otherTicketId = ticket.ship().id;
|
||||||
|
|
||||||
|
if (otherTicketId) {
|
||||||
|
await models.Ticket.deleteStowaway(ctx, otherTicketId, myOptions);
|
||||||
|
await models.TicketTracking.changeState(ctx, {
|
||||||
|
ticketFk: otherTicketId,
|
||||||
|
code: 'FIXING'
|
||||||
|
}, myOptions);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Send notification to salesPerson
|
||||||
|
const salesPersonUser = ticket.client().salesPersonUser();
|
||||||
|
if (salesPersonUser) {
|
||||||
|
const origin = ctx.req.headers.origin;
|
||||||
|
const message = $t(`I have deleted the ticket id`, {
|
||||||
|
id: id,
|
||||||
|
url: `${origin}/#!/ticket/${id}/summary`
|
||||||
|
});
|
||||||
|
await models.Chat.send(ctx, `@${salesPersonUser.name}`, message);
|
||||||
|
}
|
||||||
|
|
||||||
|
const updatedTicket = await ticket.updateAttribute('isDeleted', true, myOptions);
|
||||||
|
|
||||||
|
if (tx) await tx.commit();
|
||||||
|
|
||||||
|
return updatedTicket;
|
||||||
|
} catch (e) {
|
||||||
|
if (tx) await tx.rollback();
|
||||||
|
throw e;
|
||||||
|
}
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
|
@ -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() => {
|
||||||
|
|
|
@ -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() => {
|
||||||
|
|
|
@ -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 ctx = {req: {accessToken: {userId: 9}}};
|
const tx = await models.Ticket.beginTransaction({});
|
||||||
const response = await app.models.Ticket.recalculateComponents(ctx, ticketId);
|
|
||||||
|
|
||||||
expect(response.affectedRows).toBeDefined();
|
try {
|
||||||
|
const options = {transaction: tx};
|
||||||
|
|
||||||
|
const ctx = {req: {accessToken: {userId: 9}}};
|
||||||
|
const response = await models.Ticket.recalculateComponents(ctx, ticketId, options);
|
||||||
|
|
||||||
|
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 ctx = {req: {accessToken: {userId: 9}}};
|
const tx = await models.Ticket.beginTransaction({});
|
||||||
const immutableTicketId = 1;
|
|
||||||
await app.models.Ticket.recalculateComponents(ctx, immutableTicketId)
|
|
||||||
.catch(response => {
|
|
||||||
expect(response).toEqual(new Error(`The current ticket can't be modified`));
|
|
||||||
error = response;
|
|
||||||
});
|
|
||||||
|
|
||||||
expect(error).toBeDefined();
|
let error;
|
||||||
|
try {
|
||||||
|
const options = {transaction: tx};
|
||||||
|
|
||||||
|
const ctx = {req: {accessToken: {userId: 9}}};
|
||||||
|
const immutableTicketId = 1;
|
||||||
|
await models.Ticket.recalculateComponents(ctx, immutableTicketId, options);
|
||||||
|
|
||||||
|
await tx.rollback();
|
||||||
|
} catch (e) {
|
||||||
|
await tx.rollback();
|
||||||
|
error = e;
|
||||||
|
}
|
||||||
|
|
||||||
|
expect(error).toEqual(new Error(`The current ticket can't be modified`));
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
|
@ -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() => {
|
||||||
spyOn(soap, 'createClientAsync').and.returnValue('a so fake client');
|
const tx = await models.Ticket.beginTransaction({});
|
||||||
let ctx = {req: {accessToken: {userId: 9}}};
|
|
||||||
let id = 11;
|
|
||||||
let destination = 222222222;
|
|
||||||
let message = 'this is the message created in a test';
|
|
||||||
|
|
||||||
let sms = await app.models.Ticket.sendSms(ctx, id, destination, message);
|
try {
|
||||||
|
const options = {transaction: tx};
|
||||||
|
|
||||||
logId = sms.logId;
|
spyOn(soap, 'createClientAsync').and.returnValue('a so fake client');
|
||||||
|
const ctx = {req: {accessToken: {userId: 9}}};
|
||||||
|
const id = 11;
|
||||||
|
const destination = 222222222;
|
||||||
|
const message = 'this is the message created in a test';
|
||||||
|
|
||||||
let createdLog = await app.models.TicketLog.findById(logId);
|
const sms = await models.Ticket.sendSms(ctx, id, destination, message, options);
|
||||||
let json = JSON.parse(JSON.stringify(createdLog.newInstance));
|
|
||||||
|
|
||||||
expect(json.message).toEqual(message);
|
const createdLog = await models.TicketLog.findById(sms.logId, null, options);
|
||||||
|
const json = JSON.parse(JSON.stringify(createdLog.newInstance));
|
||||||
|
|
||||||
|
expect(json.message).toEqual(message);
|
||||||
|
|
||||||
|
await tx.rollback();
|
||||||
|
} catch (e) {
|
||||||
|
await tx.rollback();
|
||||||
|
throw e;
|
||||||
|
}
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
|
@ -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,95 +9,110 @@ 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 ctx = {req: activeCtx};
|
const tx = await models.Ticket.beginTransaction({});
|
||||||
const ticketId = 16;
|
|
||||||
let error;
|
|
||||||
|
|
||||||
|
let error;
|
||||||
try {
|
try {
|
||||||
await app.models.Ticket.setDeleted(ctx, ticketId);
|
const options = {transaction: tx};
|
||||||
|
|
||||||
|
const ctx = {req: activeCtx};
|
||||||
|
const ticketId = 16;
|
||||||
|
|
||||||
|
await models.Ticket.setDeleted(ctx, ticketId, options);
|
||||||
|
|
||||||
|
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() => {
|
||||||
spyOn(LoopBackContext, 'getCurrentContext').and.returnValue({
|
const tx = await models.Ticket.beginTransaction({});
|
||||||
active: activeCtx
|
|
||||||
});
|
|
||||||
const ctx = {
|
|
||||||
req: {
|
|
||||||
accessToken: {userId: employeeUser},
|
|
||||||
headers: {
|
|
||||||
origin: 'http://localhost:5000'
|
|
||||||
},
|
|
||||||
__: () => {}
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
let sampleTicket = await models.Ticket.findById(12);
|
try {
|
||||||
let sampleStowaway = await models.Ticket.findById(13);
|
const options = {transaction: tx};
|
||||||
|
|
||||||
sampleTicket.id = undefined;
|
spyOn(LoopBackContext, 'getCurrentContext').and.returnValue({
|
||||||
let shipTicket = await models.Ticket.create(sampleTicket);
|
active: activeCtx
|
||||||
|
});
|
||||||
|
const ctx = {
|
||||||
|
req: {
|
||||||
|
accessToken: {userId: employeeUser},
|
||||||
|
headers: {
|
||||||
|
origin: 'http://localhost:5000'
|
||||||
|
},
|
||||||
|
__: () => {}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
sampleStowaway.id = undefined;
|
const sampleTicket = await models.Ticket.findById(12);
|
||||||
let stowawayTicket = await models.Ticket.create(sampleStowaway);
|
const sampleStowaway = await models.Ticket.findById(13);
|
||||||
|
|
||||||
await models.Stowaway.rawSql(`
|
sampleTicket.id = undefined;
|
||||||
INSERT INTO vn.stowaway(id, shipFk)
|
const shipTicket = await models.Ticket.create(sampleTicket, options);
|
||||||
VALUES (?, ?)`, [stowawayTicket.id, shipTicket.id]);
|
|
||||||
|
|
||||||
const boardingState = await models.State.findOne({
|
sampleStowaway.id = undefined;
|
||||||
where: {
|
const stowawayTicket = await models.Ticket.create(sampleStowaway, options);
|
||||||
code: 'BOARDING'
|
|
||||||
}
|
|
||||||
});
|
|
||||||
await models.TicketTracking.create({
|
|
||||||
ticketFk: stowawayTicket.id,
|
|
||||||
stateFk: boardingState.id,
|
|
||||||
workerFk: ctx.req.accessToken.userId
|
|
||||||
});
|
|
||||||
|
|
||||||
const okState = await models.State.findOne({
|
await models.Stowaway.rawSql(`
|
||||||
where: {
|
INSERT INTO vn.stowaway(id, shipFk)
|
||||||
code: 'OK'
|
VALUES (?, ?)`, [stowawayTicket.id, shipTicket.id], options);
|
||||||
}
|
|
||||||
});
|
|
||||||
await models.TicketTracking.create({
|
|
||||||
ticketFk: shipTicket.id,
|
|
||||||
stateFk: okState.id,
|
|
||||||
workerFk: ctx.req.accessToken.userId
|
|
||||||
});
|
|
||||||
|
|
||||||
let stowawayTicketState = await models.TicketState.findOne({
|
const boardingState = await models.State.findOne({
|
||||||
where: {
|
where: {
|
||||||
ticketFk: stowawayTicket.id
|
code: 'BOARDING'
|
||||||
}
|
}
|
||||||
});
|
}, options);
|
||||||
|
|
||||||
let stowaway = await models.Stowaway.findById(shipTicket.id);
|
await models.TicketTracking.create({
|
||||||
|
ticketFk: stowawayTicket.id,
|
||||||
|
stateFk: boardingState.id,
|
||||||
|
workerFk: ctx.req.accessToken.userId
|
||||||
|
}, options);
|
||||||
|
|
||||||
expect(stowaway).toBeDefined();
|
const okState = await models.State.findOne({
|
||||||
expect(stowawayTicketState.code).toEqual('BOARDING');
|
where: {
|
||||||
|
code: 'OK'
|
||||||
|
}
|
||||||
|
}, options);
|
||||||
|
|
||||||
await models.Ticket.setDeleted(ctx, shipTicket.id);
|
await models.TicketTracking.create({
|
||||||
|
ticketFk: shipTicket.id,
|
||||||
|
stateFk: okState.id,
|
||||||
|
workerFk: ctx.req.accessToken.userId
|
||||||
|
}, options);
|
||||||
|
|
||||||
stowawayTicketState = await models.TicketState.findOne({
|
let stowawayTicketState = await models.TicketState.findOne({
|
||||||
where: {
|
where: {
|
||||||
ticketFk: stowawayTicket.id
|
ticketFk: stowawayTicket.id
|
||||||
}
|
}
|
||||||
});
|
}, options);
|
||||||
|
|
||||||
stowaway = await models.Stowaway.findById(shipTicket.id);
|
let stowaway = await models.Stowaway.findById(shipTicket.id, null, options);
|
||||||
|
|
||||||
expect(stowaway).toBeNull();
|
expect(stowaway).toBeDefined();
|
||||||
expect(stowawayTicketState.code).toEqual('FIXING');
|
expect(stowawayTicketState.code).toEqual('BOARDING');
|
||||||
|
|
||||||
// restores
|
await models.Ticket.setDeleted(ctx, shipTicket.id, options);
|
||||||
await models.Ticket.destroyById(shipTicket.id);
|
|
||||||
await models.Ticket.destroyById(stowawayTicket.id);
|
stowawayTicketState = await models.TicketState.findOne({
|
||||||
|
where: {
|
||||||
|
ticketFk: stowawayTicket.id
|
||||||
|
}
|
||||||
|
}, options);
|
||||||
|
|
||||||
|
stowaway = await models.Stowaway.findById(shipTicket.id, null, options);
|
||||||
|
|
||||||
|
expect(stowaway).toBeNull();
|
||||||
|
expect(stowawayTicketState.code).toEqual('FIXING');
|
||||||
|
|
||||||
|
await tx.rollback();
|
||||||
|
} catch (e) {
|
||||||
|
await tx.rollback();
|
||||||
|
throw e;
|
||||||
|
}
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
|
@ -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({});
|
||||||
|
|
||||||
expect(result.id).toEqual(1);
|
try {
|
||||||
expect(result.nickname).toEqual('Bat cave');
|
const options = {transaction: tx};
|
||||||
|
|
||||||
|
const result = await models.Ticket.summary(1, options);
|
||||||
|
|
||||||
|
expect(result.id).toEqual(1);
|
||||||
|
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({});
|
||||||
|
|
||||||
expect(result.sales.length).toEqual(4);
|
try {
|
||||||
|
const options = {transaction: tx};
|
||||||
|
|
||||||
|
const result = await models.Ticket.summary(1, options);
|
||||||
|
|
||||||
|
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({});
|
||||||
|
|
||||||
expect(result.totalWithoutVat).toEqual(jasmine.any(Number));
|
try {
|
||||||
|
const options = {transaction: tx};
|
||||||
|
|
||||||
|
const result = await models.Ticket.summary(1, options);
|
||||||
|
|
||||||
|
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({});
|
||||||
|
|
||||||
expect(result.totalWithVat).toEqual(jasmine.any(Number));
|
try {
|
||||||
|
const options = {transaction: tx};
|
||||||
|
|
||||||
|
const result = await models.Ticket.summary(1, options);
|
||||||
|
|
||||||
|
expect(result.totalWithVat).toEqual(jasmine.any(Number));
|
||||||
|
|
||||||
|
await tx.rollback();
|
||||||
|
} catch (e) {
|
||||||
|
await tx.rollback();
|
||||||
|
throw e;
|
||||||
|
}
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
|
@ -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,160 +8,167 @@ 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 => {
|
it('should throw an error as the ticket is not editable', async() => {
|
||||||
if (createdTicketsIds.length) {
|
const tx = await models.Ticket.beginTransaction({});
|
||||||
try {
|
|
||||||
createdTicketsIds.forEach(async createdTicketId => {
|
let error;
|
||||||
await app.models.Ticket.destroyById(createdTicketId);
|
try {
|
||||||
});
|
const options = {transaction: tx};
|
||||||
} catch (error) {
|
|
||||||
console.error(error);
|
const currentTicketId = 1;
|
||||||
}
|
const receiverTicketId = undefined;
|
||||||
|
const sales = [];
|
||||||
|
|
||||||
|
await models.Ticket.transferSales(ctx, currentTicketId, receiverTicketId, sales, options);
|
||||||
|
|
||||||
|
await tx.rollback();
|
||||||
|
} catch (e) {
|
||||||
|
await tx.rollback();
|
||||||
|
error = e;
|
||||||
}
|
}
|
||||||
|
|
||||||
done();
|
expect(error.message).toEqual(`The sales of this ticket can't be modified`);
|
||||||
});
|
|
||||||
|
|
||||||
it('should throw an error as the ticket is not editable', async() => {
|
|
||||||
let error;
|
|
||||||
|
|
||||||
const currentTicketId = 1;
|
|
||||||
const receiverTicketId = undefined;
|
|
||||||
const sales = [];
|
|
||||||
|
|
||||||
await app.models.Ticket.transferSales(ctx, currentTicketId, receiverTicketId, sales)
|
|
||||||
.catch(response => {
|
|
||||||
expect(response.message).toEqual(`The sales of this ticket can't be modified`);
|
|
||||||
error = response;
|
|
||||||
});
|
|
||||||
|
|
||||||
expect(error).toBeDefined();
|
|
||||||
});
|
});
|
||||||
|
|
||||||
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 formerTicketId = 11;
|
const tx = await models.Ticket.beginTransaction({});
|
||||||
let createdTicketId = undefined;
|
|
||||||
|
|
||||||
let formerTicketSales = await app.models.Ticket.getSales(formerTicketId);
|
try {
|
||||||
|
const options = {transaction: tx};
|
||||||
|
|
||||||
expect(formerTicketSales.length).toEqual(2);
|
const formerTicketId = 11;
|
||||||
|
let createdTicketId = undefined;
|
||||||
|
|
||||||
let createdTicket = await app.models.Ticket.transferSales(
|
let formerTicketSales = await models.Ticket.getSales(formerTicketId, options);
|
||||||
ctx, formerTicketId, createdTicketId, formerTicketSales);
|
|
||||||
|
|
||||||
createdTicketId = createdTicket.id;
|
expect(formerTicketSales.length).toEqual(2);
|
||||||
createdTicketsIds.push(createdTicketId);
|
|
||||||
|
|
||||||
formerTicketSales = await app.models.Ticket.getSales(formerTicketId);
|
let createdTicket = await models.Ticket.transferSales(
|
||||||
createdTicketSales = await app.models.Ticket.getSales(createdTicketId);
|
ctx, formerTicketId, createdTicketId, formerTicketSales, options);
|
||||||
|
|
||||||
expect(formerTicketSales.length).toEqual(0);
|
createdTicketId = createdTicket.id;
|
||||||
expect(createdTicketSales.length).toEqual(2);
|
|
||||||
|
|
||||||
await app.models.Ticket.transferSales(
|
formerTicketSales = await models.Ticket.getSales(formerTicketId, options);
|
||||||
ctx, createdTicketId, formerTicketId, createdTicketSales);
|
createdTicketSales = await models.Ticket.getSales(createdTicketId, options);
|
||||||
|
|
||||||
formerTicketSales = await app.models.Ticket.getSales(formerTicketId);
|
expect(formerTicketSales.length).toEqual(0);
|
||||||
createdTicketSales = await app.models.Ticket.getSales(createdTicketId);
|
expect(createdTicketSales.length).toEqual(2);
|
||||||
|
|
||||||
createdTicket = await app.models.Ticket.findById(createdTicketId);
|
await models.Ticket.transferSales(
|
||||||
|
ctx, createdTicketId, formerTicketId, createdTicketSales, options);
|
||||||
|
|
||||||
expect(createdTicket.isDeleted).toBeTruthy();
|
formerTicketSales = await models.Ticket.getSales(formerTicketId, options);
|
||||||
expect(formerTicketSales.length).toEqual(2);
|
createdTicketSales = await models.Ticket.getSales(createdTicketId, options);
|
||||||
expect(createdTicketSales.length).toEqual(0);
|
|
||||||
|
createdTicket = await models.Ticket.findById(createdTicketId, null, options);
|
||||||
|
|
||||||
|
expect(createdTicket.isDeleted).toBeTruthy();
|
||||||
|
expect(formerTicketSales.length).toEqual(2);
|
||||||
|
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 currentTicketId = currentTicket.id;
|
const currentTicket = await models.Ticket.findById(11, null, options);
|
||||||
const receiverTicketId = undefined;
|
const currentTicketSales = await models.Ticket.getSales(currentTicket.id, options);
|
||||||
|
|
||||||
currentTicketSales[0].quantity = 99;
|
const currentTicketId = currentTicket.id;
|
||||||
|
const receiverTicketId = undefined;
|
||||||
|
|
||||||
await app.models.Ticket.transferSales(
|
currentTicketSales[0].quantity = 99;
|
||||||
ctx, currentTicketId, receiverTicketId, currentTicketSales)
|
|
||||||
.catch(response => {
|
|
||||||
expect(response.message).toEqual(`Invalid quantity`);
|
|
||||||
error = response;
|
|
||||||
});
|
|
||||||
|
|
||||||
expect(error).toBeDefined();
|
await models.Ticket.transferSales(
|
||||||
|
ctx, currentTicketId, receiverTicketId, currentTicketSales, options);
|
||||||
|
|
||||||
|
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 formerTicketId = 11;
|
const tx = await models.Ticket.beginTransaction({});
|
||||||
let createdTicketId = undefined;
|
|
||||||
|
|
||||||
let formerTicketSales = await app.models.Ticket.getSales(formerTicketId);
|
try {
|
||||||
|
const options = {transaction: tx};
|
||||||
|
|
||||||
const partialSaleId = formerTicketSales[0].id;
|
const formerTicketId = 11;
|
||||||
const completeSaleId = formerTicketSales[1].id;
|
let createdTicketId = undefined;
|
||||||
let partialSaleTotalQuantity = formerTicketSales[0].quantity;
|
|
||||||
|
|
||||||
expect(partialSaleTotalQuantity).toEqual(15);
|
let formerTicketSales = await models.Ticket.getSales(formerTicketId, options);
|
||||||
|
|
||||||
formerTicketSales[0].quantity = 1;
|
const completeSaleId = formerTicketSales[1].id;
|
||||||
|
let partialSaleTotalQuantity = formerTicketSales[0].quantity;
|
||||||
|
|
||||||
let createdTicket = await app.models.Ticket.transferSales(
|
expect(partialSaleTotalQuantity).toEqual(15);
|
||||||
ctx, formerTicketId, createdTicketId, formerTicketSales);
|
|
||||||
|
|
||||||
createdTicketId = createdTicket.id;
|
formerTicketSales[0].quantity = 1;
|
||||||
createdTicketsIds.push(createdTicket.id);
|
|
||||||
|
|
||||||
formerTicketSales = await app.models.Ticket.getSales(formerTicketId);
|
let createdTicket = await models.Ticket.transferSales(
|
||||||
createdTicketSales = await app.models.Ticket.getSales(createdTicketId);
|
ctx, formerTicketId, createdTicketId, formerTicketSales, options);
|
||||||
|
|
||||||
const [createdPartialSale] = createdTicketSales.filter(sale => {
|
createdTicketId = createdTicket.id;
|
||||||
return sale.id != completeSaleId;
|
|
||||||
});
|
|
||||||
|
|
||||||
expect(formerTicketSales.length).toEqual(1);
|
formerTicketSales = await models.Ticket.getSales(formerTicketId, options);
|
||||||
expect(formerTicketSales[0].quantity).toEqual(partialSaleTotalQuantity - 1);
|
createdTicketSales = await models.Ticket.getSales(createdTicketId, options);
|
||||||
expect(createdTicketSales.length).toEqual(2);
|
|
||||||
expect(createdPartialSale.quantity).toEqual(1);
|
|
||||||
|
|
||||||
let saleToRestore = await app.models.Sale.findById(partialSaleId);
|
const [createdPartialSale] = createdTicketSales.filter(sale => {
|
||||||
await saleToRestore.updateAttribute('quantity', partialSaleTotalQuantity);
|
return sale.id != completeSaleId;
|
||||||
|
});
|
||||||
|
|
||||||
let saleToReturnToTicket = await app.models.Sale.findById(completeSaleId);
|
expect(formerTicketSales.length).toEqual(1);
|
||||||
await saleToReturnToTicket.updateAttribute('ticketFk', formerTicketId);
|
expect(formerTicketSales[0].quantity).toEqual(partialSaleTotalQuantity - 1);
|
||||||
|
expect(createdTicketSales.length).toEqual(2);
|
||||||
|
expect(createdPartialSale.quantity).toEqual(1);
|
||||||
|
|
||||||
formerTicketSales = await app.models.Ticket.getSales(formerTicketId);
|
await tx.rollback();
|
||||||
|
} catch (e) {
|
||||||
const [returningPartialSale] = formerTicketSales.filter(sale => {
|
await tx.rollback();
|
||||||
return sale.id == partialSaleId;
|
throw e;
|
||||||
});
|
}
|
||||||
|
|
||||||
expect(returningPartialSale.quantity).toEqual(partialSaleTotalQuantity);
|
|
||||||
expect(formerTicketSales.length).toEqual(2);
|
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
|
@ -1,156 +1,171 @@
|
||||||
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 ctx = {
|
const tx = await models.Ticket.beginTransaction({});
|
||||||
req: {
|
|
||||||
accessToken: {userId: 9},
|
|
||||||
headers: {origin: 'localhost:5000'},
|
|
||||||
__: () => {}
|
|
||||||
}
|
|
||||||
};
|
|
||||||
let error;
|
|
||||||
const ticketId = 11;
|
|
||||||
const sales = [];
|
|
||||||
const newDiscount = 10;
|
|
||||||
|
|
||||||
|
let error;
|
||||||
try {
|
try {
|
||||||
await app.models.Ticket.updateDiscount(ctx, ticketId, sales, newDiscount);
|
const options = {transaction: tx};
|
||||||
} catch (err) {
|
|
||||||
error = err;
|
const ctx = {
|
||||||
|
req: {
|
||||||
|
accessToken: {userId: 9},
|
||||||
|
headers: {origin: 'localhost:5000'},
|
||||||
|
__: () => {}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
const ticketId = 11;
|
||||||
|
const sales = [];
|
||||||
|
const newDiscount = 10;
|
||||||
|
|
||||||
|
await models.Ticket.updateDiscount(ctx, ticketId, sales, newDiscount, options);
|
||||||
|
|
||||||
|
await tx.rollback();
|
||||||
|
} 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 ctx = {
|
const tx = await models.Ticket.beginTransaction({});
|
||||||
req: {
|
|
||||||
accessToken: {userId: 9},
|
|
||||||
headers: {origin: 'localhost:5000'},
|
|
||||||
__: () => {}
|
|
||||||
}
|
|
||||||
};
|
|
||||||
let error;
|
|
||||||
const ticketId = 11;
|
|
||||||
const sales = [1, 14];
|
|
||||||
const newDiscount = 10;
|
|
||||||
|
|
||||||
|
let error;
|
||||||
try {
|
try {
|
||||||
await app.models.Ticket.updateDiscount(ctx, ticketId, sales, newDiscount);
|
const options = {transaction: tx};
|
||||||
} catch (err) {
|
|
||||||
error = err;
|
const ctx = {
|
||||||
|
req: {
|
||||||
|
accessToken: {userId: 9},
|
||||||
|
headers: {origin: 'localhost:5000'},
|
||||||
|
__: () => {}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
const ticketId = 11;
|
||||||
|
const sales = [1, 14];
|
||||||
|
const newDiscount = 10;
|
||||||
|
|
||||||
|
await models.Ticket.updateDiscount(ctx, ticketId, sales, newDiscount, options);
|
||||||
|
|
||||||
|
await tx.rollback();
|
||||||
|
} 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 ctx = {
|
const tx = await models.Ticket.beginTransaction({});
|
||||||
req: {
|
|
||||||
accessToken: {userId: 9},
|
|
||||||
headers: {origin: 'localhost:5000'},
|
|
||||||
__: () => {}
|
|
||||||
}
|
|
||||||
};
|
|
||||||
let error;
|
|
||||||
const ticketId = 1;
|
|
||||||
const sales = [1];
|
|
||||||
const newDiscount = 100;
|
|
||||||
|
|
||||||
|
let error;
|
||||||
try {
|
try {
|
||||||
await app.models.Ticket.updateDiscount(ctx, ticketId, sales, newDiscount);
|
const options = {transaction: tx};
|
||||||
} catch (err) {
|
|
||||||
error = err;
|
const ctx = {
|
||||||
|
req: {
|
||||||
|
accessToken: {userId: 9},
|
||||||
|
headers: {origin: 'localhost:5000'},
|
||||||
|
__: () => {}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
const ticketId = 1;
|
||||||
|
const sales = [1];
|
||||||
|
const newDiscount = 100;
|
||||||
|
|
||||||
|
await models.Ticket.updateDiscount(ctx, ticketId, sales, newDiscount, options);
|
||||||
|
|
||||||
|
await tx.rollback();
|
||||||
|
} 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 ctx = {
|
const tx = await models.Ticket.beginTransaction({});
|
||||||
req: {
|
|
||||||
accessToken: {userId: 18},
|
|
||||||
headers: {origin: 'localhost:5000'},
|
|
||||||
__: () => {}
|
|
||||||
}
|
|
||||||
};
|
|
||||||
const ticketId = 11;
|
|
||||||
const sales = [originalSaleId];
|
|
||||||
const newDiscount = 100;
|
|
||||||
let manaDiscount = await app.models.Component.findOne({where: {code: 'mana'}});
|
|
||||||
componentId = manaDiscount.id;
|
|
||||||
|
|
||||||
await app.models.Ticket.updateDiscount(ctx, ticketId, sales, newDiscount);
|
try {
|
||||||
|
const options = {transaction: tx};
|
||||||
|
|
||||||
let updatedSale = await app.models.Sale.findById(originalSaleId);
|
const ctx = {
|
||||||
let createdSaleComponent = await app.models.SaleComponent.findOne({
|
req: {
|
||||||
where: {
|
accessToken: {userId: 18},
|
||||||
componentFk: componentId,
|
headers: {origin: 'localhost:5000'},
|
||||||
saleFk: originalSaleId
|
__: () => {}
|
||||||
}
|
}
|
||||||
});
|
};
|
||||||
|
const ticketId = 11;
|
||||||
|
const sales = [originalSaleId];
|
||||||
|
const newDiscount = 100;
|
||||||
|
const manaDiscount = await models.Component.findOne({where: {code: 'mana'}}, options);
|
||||||
|
const componentId = manaDiscount.id;
|
||||||
|
|
||||||
expect(createdSaleComponent.componentFk).toEqual(componentId);
|
await models.Ticket.updateDiscount(ctx, ticketId, sales, newDiscount, options);
|
||||||
expect(updatedSale.discount).toEqual(100);
|
|
||||||
|
const updatedSale = await models.Sale.findById(originalSaleId, null, options);
|
||||||
|
const createdSaleComponent = await models.SaleComponent.findOne({
|
||||||
|
where: {
|
||||||
|
componentFk: componentId,
|
||||||
|
saleFk: originalSaleId
|
||||||
|
}
|
||||||
|
}, options);
|
||||||
|
|
||||||
|
expect(createdSaleComponent.componentFk).toEqual(componentId);
|
||||||
|
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 ctx = {
|
const tx = await models.Ticket.beginTransaction({});
|
||||||
req: {
|
|
||||||
accessToken: {userId: 9},
|
|
||||||
headers: {origin: 'localhost:5000'},
|
|
||||||
__: () => {}
|
|
||||||
}
|
|
||||||
};
|
|
||||||
const ticketId = 11;
|
|
||||||
const sales = [originalSaleId];
|
|
||||||
const newDiscount = 100;
|
|
||||||
|
|
||||||
await app.models.Ticket.updateDiscount(ctx, ticketId, sales, newDiscount);
|
try {
|
||||||
|
const options = {transaction: tx};
|
||||||
|
|
||||||
let updatedSale = await app.models.Sale.findById(originalSaleId);
|
const ctx = {
|
||||||
let createdSaleComponent = await app.models.SaleComponent.findOne({
|
req: {
|
||||||
where: {
|
accessToken: {userId: 9},
|
||||||
componentFk: componentId,
|
headers: {origin: 'localhost:5000'},
|
||||||
saleFk: originalSaleId
|
__: () => {}
|
||||||
}
|
}
|
||||||
});
|
};
|
||||||
|
const ticketId = 11;
|
||||||
|
const sales = [originalSaleId];
|
||||||
|
const newDiscount = 100;
|
||||||
|
|
||||||
expect(createdSaleComponent.componentFk).toEqual(componentId);
|
await models.Ticket.updateDiscount(ctx, ticketId, sales, newDiscount, options);
|
||||||
expect(updatedSale.discount).toEqual(100);
|
|
||||||
|
const updatedSale = await models.Sale.findById(originalSaleId, null, options);
|
||||||
|
const manaDiscount = await models.Component.findOne({where: {code: 'buyerDiscount'}}, options);
|
||||||
|
const componentId = manaDiscount.id;
|
||||||
|
|
||||||
|
const createdSaleComponent = await models.SaleComponent.findOne({
|
||||||
|
where: {
|
||||||
|
componentFk: componentId,
|
||||||
|
saleFk: originalSaleId
|
||||||
|
}
|
||||||
|
}, options);
|
||||||
|
|
||||||
|
expect(createdSaleComponent.componentFk).toEqual(componentId);
|
||||||
|
expect(updatedSale.discount).toEqual(100);
|
||||||
|
|
||||||
|
await tx.rollback();
|
||||||
|
} catch (e) {
|
||||||
|
await tx.rollback();
|
||||||
|
throw e;
|
||||||
|
}
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
|
@ -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() => {
|
||||||
|
const tx = await models.Ticket.beginTransaction({});
|
||||||
|
|
||||||
let error;
|
let error;
|
||||||
|
try {
|
||||||
|
const options = {transaction: tx};
|
||||||
|
|
||||||
await app.models.Ticket.updateEditableTicket(ctx, invalidTicketId, data).catch(e => {
|
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).toBeDefined();
|
expect(error.message).toEqual('This ticket can not be modified');
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should edit the ticket address', async() => {
|
it('should edit the ticket address', async() => {
|
||||||
spyOn(LoopBackContext, 'getCurrentContext').and.returnValue({
|
const tx = await models.Ticket.beginTransaction({});
|
||||||
active: activeCtx
|
|
||||||
});
|
|
||||||
await app.models.Ticket.updateEditableTicket(ctx, validTicketId, data);
|
|
||||||
|
|
||||||
let updatedTicket = await app.models.Ticket.findById(validTicketId);
|
try {
|
||||||
|
const options = {transaction: tx};
|
||||||
|
|
||||||
expect(updatedTicket.addressFk).toEqual(1);
|
spyOn(LoopBackContext, 'getCurrentContext').and.returnValue({
|
||||||
|
active: activeCtx
|
||||||
|
});
|
||||||
|
await models.Ticket.updateEditableTicket(ctx, validTicketId, data, options);
|
||||||
|
|
||||||
|
const updatedTicket = await models.Ticket.findById(validTicketId, null, options);
|
||||||
|
|
||||||
|
expect(updatedTicket.addressFk).toEqual(1);
|
||||||
|
|
||||||
|
await tx.rollback();
|
||||||
|
} catch (e) {
|
||||||
|
await tx.rollback();
|
||||||
|
throw e;
|
||||||
|
}
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
|
@ -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 {
|
||||||
error = e;
|
const options = {transaction: tx};
|
||||||
}).finally(() => {
|
|
||||||
expect(error.message).toEqual(`You don't have enough privileges`);
|
|
||||||
});
|
|
||||||
|
|
||||||
expect(error).toBeDefined();
|
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;
|
||||||
|
}
|
||||||
|
|
||||||
|
expect(error.message).toEqual(`You don't have enough privileges`);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
|
@ -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);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
|
@ -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')
|
||||||
if (!isEditable)
|
Object.assign(myOptions, options);
|
||||||
throw new UserError(`The sales of this ticket can't be modified`);
|
|
||||||
|
|
||||||
if (ticketId) {
|
if (!myOptions.transaction) {
|
||||||
const isReceiverEditable = await models.Ticket.isEditable(ctx, ticketId);
|
tx = await Self.beginTransaction({});
|
||||||
if (!isReceiverEditable)
|
myOptions.transaction = tx;
|
||||||
throw new UserError(`The sales of the receiver ticket can't be modified`);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
let tx = await Self.beginTransaction({});
|
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const options = {transaction: tx};
|
const isEditable = await models.Ticket.isEditable(ctx, id, myOptions);
|
||||||
const originalTicket = await models.Ticket.findById(id, null, options);
|
if (!isEditable)
|
||||||
|
throw new UserError(`The sales of this ticket can't be modified`);
|
||||||
|
|
||||||
|
if (ticketId) {
|
||||||
|
const isReceiverEditable = await models.Ticket.isEditable(ctx, ticketId, myOptions);
|
||||||
|
if (!isReceiverEditable)
|
||||||
|
throw new UserError(`The sales of the receiver ticket can't be modified`);
|
||||||
|
}
|
||||||
|
|
||||||
|
const originalTicket = await models.Ticket.findById(id, null, myOptions);
|
||||||
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
|
||||||
|
|
|
@ -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;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
|
@ -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 = {};
|
||||||
if (!ticketIsEditable)
|
let tx;
|
||||||
throw new UserError('This ticket can not be modified');
|
|
||||||
|
|
||||||
let ticket = await Self.app.models.Ticket.findById(id);
|
if (typeof options == 'object')
|
||||||
await ticket.updateAttributes(data);
|
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)
|
||||||
|
throw new UserError('This ticket can not be modified');
|
||||||
|
|
||||||
|
const ticket = await Self.app.models.Ticket.findById(id, null, myOptions);
|
||||||
|
await ticket.updateAttributes(data, myOptions);
|
||||||
|
|
||||||
|
if (tx) await tx.commit();
|
||||||
|
} catch (e) {
|
||||||
|
if (tx) await tx.rollback();
|
||||||
|
throw e;
|
||||||
|
}
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
|
@ -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;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
|
@ -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
Loading…
Reference in New Issue