2509 - Added forms to create new locations + e2e #412

Merged
carlosjr merged 6 commits from 2509-add_geolocation into dev 2020-10-19 18:57:18 +00:00
78 changed files with 1191 additions and 766 deletions
Showing only changes of commit cc99c1e980 - Show all commits

View File

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

View File

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

View File

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

View File

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

View File

@ -1208,11 +1208,11 @@ INSERT INTO `vn`.`annualAverageInvoiced`(`clientFk`, `invoiced`)
(104, 500),
(105, 5000);
INSERT INTO `vn`.`supplier`(`id`, `name`, `nickname`,`account`,`countryFk`,`nif`,`isFarmer`,`retAccount`,`commission`, `created`, `postcodeFk`, `isActive`, `street`, `city`, `provinceFk`, `postCode`, `payMethodFk`, `payDemFk`)
INSERT INTO `vn`.`supplier`(`id`, `name`, `nickname`,`account`,`countryFk`,`nif`,`isFarmer`,`retAccount`,`commission`, `created`, `postcodeFk`, `isActive`, `street`, `city`, `provinceFk`, `postCode`, `payMethodFk`, `payDemFk`, `phone`, `payDay`)
VALUES
(1, 'Plants SL', 'Plants nick', 4000000001, 1, 'A11111111', 0, NULL, 0, CURDATE(), 1111, 1, 'supplier address 1', 'PONTEVEDRA', 1, 15214, 1, 1),
(2, 'Flower King', 'The king', 4000000002, 1, 'B22222222', 0, NULL, 0, CURDATE(), 2222, 1, 'supplier address 2', 'LONDON', 2, 45671, 1, 2),
(442, 'Verdnatura Levante SL', 'Verdnatura', 4000000442, 1, 'C33333333', 0, NULL, 0, CURDATE(), 3333, 1, 'supplier address 3', 'SILLA', 1, 43022, 1, 2);
(1, 'Plants SL', 'Plants nick', 4000000001, 1, 'A11111111', 0, NULL, 0, CURDATE(), 1111, 1, 'supplier address 1', 'PONTEVEDRA', 1, 15214, 1, 1, 123456789, 15),
(2, 'Flower King', 'The king', 4000000002, 1, 'B22222222', 0, NULL, 0, CURDATE(), 2222, 1, 'supplier address 2', 'LONDON', 2, 45671, 1, 2, 987654321, 10),
(442, 'Verdnatura Levante SL', 'Verdnatura', 4000000442, 1, 'C33333333', 0, NULL, 0, CURDATE(), 3333, 1, 'supplier address 3', 'SILLA', 1, 43022, 1, 2, 987123654, 15);
INSERT INTO `cache`.`cache_calc`(`id`, `cache_id`, `cacheName`, `params`, `last_refresh`, `expires`, `created`, `connection_id`)
VALUES

View File

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

View File

@ -22,6 +22,9 @@
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
}
.icon-supplier:before {
content: "\e936";
}
.icon-latestBuys:before {
content: "\e95f";
}

View File

@ -61,7 +61,7 @@
<glyph unicode="&#xe933;" glyph-name="solclaim" d="M1024 917.333v-938.667h-938.667v68.267h234.667v51.2h38.4c8.533-4.267 17.067-4.267 29.867-4.267h298.667c42.667 0 76.8 34.133 76.8 76.8 0 0 0 0 0 0 29.867 12.8 46.933 38.4 46.933 72.533 0 0 0 0 0 0 29.867 12.8 46.933 38.4 46.933 72.533s-21.333 59.733-46.933 72.533c0 0 0 0 0 0 0 42.667-34.133 76.8-76.8 76.8h-106.667c21.333 21.333 29.867 55.467 17.067 89.6-12.8 25.6-38.4 42.667-68.267 42.667-12.8 0-21.333-4.267-34.133-8.533l-217.6-98.133v29.867h-238.933v396.8h362.667v-209.067h209.067v209.067h366.933zM0 89.6h281.6v51.2h89.6c4.267-4.267 12.8-4.267 17.067-4.267h298.667c21.333 0 34.133 12.8 34.133 34.133s-12.8 34.133-34.133 34.133h-136.533v12.8h183.467c21.333 0 34.133 12.8 34.133 29.867 0 21.333-12.8 29.867-34.133 29.867h-179.2v12.8h234.667c21.333 0 34.133 8.533 34.133 29.867s-12.8 29.867-34.133 29.867h-230.4v12.8h183.467c21.333 0 29.867 12.8 29.867 34.133s-12.8 34.133-34.133 34.133h-230.4l93.867 64c12.8 8.533 21.333 29.867 12.8 46.933s-29.867 25.6-51.2 17.067l-251.733-119.467c-4.267 0-4.267-4.267-8.533-4.267-4.267-4.267-8.533-8.533-12.8-12.8h-8.533v55.467h-281.6v-388.267z" />
<glyph unicode="&#xe934;" glyph-name="solunion" d="M759.467 870.4v-136.533h-601.6c0 0-128-341.333 106.667-341.333s469.333 0 469.333 0 34.133 0 34.133-34.133-8.533-98.133-8.533-98.133h-541.867c0 0-247.467 29.867-204.8 320 0 0 8.533 140.8 72.533 298.667 0 0 21.333-8.533 85.333-8.533h588.8zM853.333 25.6c64 0 85.333-8.533 85.333-8.533 64 153.6 72.533 298.667 72.533 298.667 42.667 290.133-204.8 320-204.8 320h-541.867c0 0-8.533-64-8.533-98.133s34.133-34.133 34.133-34.133 238.933 0 469.333 0 106.667-341.333 106.667-341.333h-601.6v-136.533h588.8z" />
<glyph unicode="&#xe935;" glyph-name="splur" d="M640 960l145.067-145.067-183.467-183.467 89.6-89.6 183.467 183.467 149.333-149.333v384h-384zM384 960h-384v-384l145.067 145.067 302.933-302.933v-482.133h128v537.6l-337.067 341.333 145.067 145.067z" />
<glyph unicode="&#xe936;" glyph-name="supplier" d="M1011.2 503.467c0 0 0 0 0 0s0 0 0 0c0 4.267 0 4.267 0 4.267s0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0-4.267 0c0 0 0 0 0 0s0 0-4.267 0c0 0 0 0 0 0s0 0 0 0 0 0 0 0h-145.067l4.267 4.267c4.267 4.267 8.533 8.533 8.533 17.067v409.6c0 0 0 0 0 0s0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0-4.267 0c0 0 0 0 0 0s0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0h-392.533c-4.267 0-8.533 0-12.8-4.267l-221.867-132.267c0 0 0 0 0 0s0 0 0 0 0 0-4.267-4.267c0 0 0 0 0 0s0 0 0-4.267c0 0 0 0 0 0s0 0 0-4.267c0 0 0 0 0 0s0 0 0-4.267c0 0 0 0 0 0v-153.6c-4.267 0-8.533 0-12.8-4.267l-196.267-153.6c0 0 0 0 0 0s0 0 0 0 0 0-4.267-4.267c0 0 0 0 0 0s0 0 0-4.267c0 0 0 0 0 0s0 0 0-4.267c0 0 0 0 0 0s0 0 0-4.267c0 0 0 0 0 0v-392.533c0-12.8 8.533-21.333 21.333-21.333h349.867v-85.333c0-12.8 8.533-21.333 21.333-21.333h392.533c0 0 4.267 0 4.267 0s0 0 0 0 0 0 4.267 0c0 0 0 0 0 0s0 0 0 0 0 0 0 0 0 0 0 0l196.267 153.6c4.267 4.267 8.533 8.533 8.533 17.067l-8.533 396.8c0 0 0 0 0 0zM968.533 302.933l-76.8-59.733v153.6l76.8 59.733v-153.6zM55.467 435.2h162.133v-162.133h-162.133v162.133zM776.533 917.333l-42.667-34.133c0 0 0 0-4.267 0h-153.6l38.4 34.133h162.133zM802.133 384l-17.067-12.8h-145.067c0 0 4.267 0 4.267 0s0 0 0 0 0 0 4.267 0c0 0 0 0 0 0s0 0 4.267 0c0 0 0 0 0 0s0 0 0 0l64 51.2h136.533l-51.2-38.4zM273.067 580.267h162.133v-162.133h-162.133v162.133zM665.6 439.467v153.6l55.467 42.667v-153.6c0 0 0 0 0 0l-55.467-42.667zM665.6 618.667v157.867l51.2 42.667v-157.867l-51.2-42.667zM622.933 601.6h-162.133v162.133h162.133v-162.133zM622.933 580.267v-162.133h-162.133v162.133h162.133zM742.4 499.2v153.6l76.8 59.733v-153.6l-76.8-59.733zM814.933 738.133l-76.8-59.733v157.867l76.8 59.733v-157.867zM704 861.867l-42.667-34.133-25.6-21.333h-145.067l64 55.467h149.333zM435.2 601.6h-162.133v162.133h162.133v-162.133zM238.933 375.467c4.267 0 8.533-4.267 12.8-4.267h145.067l-4.267-4.267c0 0 0 0 0 0s0 0 0 0 0 0-4.267-4.267c0 0 0 0 0 0s0 0 0-4.267c0 0 0 0 0 0s0 0 0-4.267c0 0 0 0 0 0s0 0 0-4.267c0 0 0 0 0 0v-76.8h-149.333v102.4zM426.667 328.533h162.133v-162.133h-162.133v162.133zM610.133 328.533h162.133v-162.133h-162.133v162.133zM772.267 145.067v-166.4h-162.133v162.133h162.133zM814.933 341.333l25.6 21.333 29.867 21.333v-153.6l-55.467-42.667v153.6zM925.867 482.133l-42.667-34.133h-136.533l42.667 34.133h136.533zM452.267 917.333h123.733l-38.4-34.133h-128l42.667 34.133zM384 861.867h132.267l-64-55.467h-136.533l68.267 55.467zM230.4 584.533v-25.6h-34.133l34.133 25.6zM166.4 533.333h64v-55.467h-132.267l68.267 55.467zM55.467 251.733h162.133v-162.133h-162.133v162.133zM238.933 89.6v162.133h140.8v-162.133h-140.8zM426.667 145.067h162.133v-166.4h-162.133v166.4zM814.933 157.867l55.467 42.667v-153.6c0 0 0 0 0 0l-55.467-42.667v153.6zM891.733 59.733v153.6l76.8 59.733v-153.6l-76.8-59.733z" />
<glyph unicode="&#xe936;" glyph-name="supplier" d="M608 755.2c0-113.108-90.259-204.8-201.6-204.8s-201.6 91.692-201.6 204.8c0 113.108 90.259 204.8 201.6 204.8s201.6-91.692 201.6-204.8zM534.4 342.4c-9.6 0-19.2 3.2-28.8 9.6-16 9.6-25.6 25.6-28.8 41.6l-12.8 54.4c-3.2 19.2 0 35.2 9.6 51.2-25.6 3.2-51.2 6.4-73.6 6.4-128-6.4-400-73.6-400-211.2v-144h582.4l-48 192zM832 192l-51.2-12.8-16 64-57.6-16 35.2-134.4c22.4-3.2 41.6-16 54.4-35.2l163.2 41.6-44.8 176-38.4-12.8-60.8-6.4 16-64zM758.4 256l51.2 12.8 60.8 16-38.4 153.6-38.4-6.4 16-54.4-41.6-9.6-16 54.4-89.6-22.4 38.4-156.8 57.6 12.8zM1014.4 108.8l-227.2-57.6c-12.8 19.2-35.2 35.2-60.8 38.4l-92.8 364.8c-3.2 16-19.2 22.4-35.2 19.2l-70.4-16 12.8-54.4 41.6 12.8 83.2-342.4c-19.2-16-25.6-35.2-25.6-57.6 0-41.6 35.2-76.8 76.8-76.8 38.4 0 70.4 25.6 73.6 60.8l227.2 57.6-3.2 51.2z" />
<glyph unicode="&#xe937;" glyph-name="tags" d="M729.6 960c-42.667 0-89.6 0-132.267 0-21.333 0-38.4-8.533-51.2-21.333-140.8-140.8-281.6-281.6-422.4-422.4-25.6-25.6-25.6-51.2 0-76.8 93.867-93.867 187.733-187.733 281.6-281.6 25.6-25.6 51.2-25.6 76.8 0 140.8 140.8 281.6 281.6 422.4 422.4 17.067 12.8 21.333 29.867 21.333 51.2 0 93.867 0 183.467 0 277.333 0 34.133-17.067 51.2-51.2 51.2-51.2 0-98.133 0-145.067 0zM682.667 763.733c0 25.6 17.067 46.933 42.667 46.933s46.933-21.333 46.933-46.933c0-25.6-21.333-46.933-46.933-46.933-21.333 0-42.667 21.333-42.667 46.933zM878.933 482.133c4.267-12.8 0-21.333-8.533-29.867-34.133-51.2-64-98.133-98.133-149.333-76.8-115.2-153.6-234.667-230.4-349.867-12.8-17.067-21.333-21.333-38.4-8.533-115.2 76.8-226.133 149.333-337.067 226.133-17.067 8.533-17.067 21.333-8.533 38.4 12.8 21.333 29.867 46.933 42.667 68.267 8.533 12.8 8.533 12.8 17.067 0 55.467-55.467 115.2-115.2 170.667-170.667 8.533-8.533 17.067-17.067 29.867-21.333 29.867-12.8 55.467-4.267 76.8 21.333 123.733 123.733 247.467 247.467 371.2 371.2 4.267 4.267 4.267 8.533 8.533 12.8 0-8.533 0-8.533 4.267-8.533z" />
<glyph unicode="&#xe938;" glyph-name="tax" d="M448 192c0 174.933 145.067 320 320 320 76.8 0 145.067-25.6 196.267-68.267v324.267c4.267 51.2-38.4 98.133-93.867 98.133h-204.8c-21.333 55.467-72.533 93.867-136.533 93.867s-115.2-38.4-136.533-98.133h-209.067c-55.467 0-98.133-42.667-98.133-93.867v-674.133c0-51.2 42.667-98.133 98.133-98.133h332.8c-42.667 55.467-68.267 123.733-68.267 196.267zM529.067 861.867c29.867 0 46.933-21.333 46.933-46.933 0-29.867-25.6-46.933-46.933-46.933-29.867 0-46.933 21.333-46.933 46.933-4.267 29.867 17.067 46.933 46.933 46.933zM708.267 247.467c-8.533 0-12.8 4.267-17.067 8.533s-8.533 8.533-8.533 17.067v17.067c0 8.533 0 12.8 4.267 17.067s8.533 8.533 17.067 8.533c8.533 0 12.8-4.267 17.067-8.533s4.267-12.8 4.267-17.067v-12.8c4.267-21.333-4.267-29.867-17.067-29.867zM870.4 132.267c4.267-4.267 4.267-12.8 4.267-17.067v-21.333c0-12.8-8.533-21.333-21.333-21.333-8.533 0-12.8 4.267-17.067 8.533s-8.533 12.8-8.533 17.067v17.067c0 8.533 4.267 12.8 8.533 17.067s8.533 8.533 17.067 8.533c8.533 0 12.8-4.267 17.067-8.533zM768 448c-140.8 0-256-115.2-256-256s115.2-256 256-256 256 115.2 256 256-115.2 256-256 256zM635.733 273.067v17.067c0 21.333 4.267 34.133 17.067 46.933s29.867 17.067 51.2 17.067c21.333 0 38.4-4.267 51.2-17.067s17.067-29.867 17.067-46.933v-17.067c0-21.333-4.267-34.133-17.067-46.933s-29.867-17.067-51.2-17.067c-21.333 0-38.4 4.267-51.2 17.067-8.533 12.8-17.067 29.867-17.067 46.933zM721.067 59.733l-34.133 17.067 153.6 243.2 34.133-17.067-153.6-243.2zM925.867 98.133c0-21.333-4.267-34.133-17.067-46.933s-29.867-17.067-51.2-17.067c-21.333 0-38.4 4.267-51.2 17.067s-21.333 25.6-21.333 46.933v17.067c0 21.333 4.267 34.133 17.067 46.933s29.867 17.067 51.2 17.067c21.333 0 38.4-4.267 51.2-17.067s17.067-29.867 17.067-46.933v-17.067z" />
<glyph unicode="&#xe939;" glyph-name="ticket" d="M200.533 311.467c12.8 38.4 25.6 76.8 38.4 115.2 8.533 25.6 17.067 55.467 29.867 81.067 29.867 81.067 55.467 166.4 85.333 247.467 21.333 55.467 38.4 110.933 59.733 166.4 4.267 12.8 8.533 21.333 12.8 34.133 0 4.267 4.267 4.267 8.533 4.267 59.733-12.8 115.2-21.333 174.933-34.133 81.067-17.067 157.867-34.133 238.933-46.933 55.467-12.8 110.933-21.333 170.667-34.133 4.267-4.267 4.267-4.267 4.267-12.8-29.867-89.6-59.733-179.2-89.6-264.533-21.333-64-42.667-128-64-187.733-25.6-68.267-46.933-140.8-76.8-209.067-17.067-51.2-38.4-98.133-59.733-145.067-12.8-25.6-25.6-51.2-46.933-68.267-17.067-17.067-34.133-21.333-59.733-12.8-59.733 17.067-93.867 59.733-106.667 119.467-4.267 25.6-8.533 51.2-8.533 76.8 0 12.8 0 25.6 0 38.4s-8.533 21.333-17.067 25.6c-76.8 29.867-153.6 64-234.667 93.867-25.6 0-42.667 4.267-59.733 12.8zM554.667 550.4c-17.067 0-29.867-4.267-29.867-17.067-4.267-12.8 4.267-25.6 17.067-29.867 59.733-21.333 123.733-42.667 183.467-59.733 12.8-4.267 25.6 0 29.867 8.533 8.533 17.067 4.267 29.867-12.8 38.4-46.933 17.067-98.133 34.133-145.067 46.933-17.067 4.267-34.133 8.533-42.667 12.8zM477.867 375.467c-4.267 0-8.533 0-12.8-4.267-8.533-4.267-12.8-12.8-12.8-21.333 0-12.8 8.533-21.333 21.333-25.6 59.733-21.333 119.467-38.4 183.467-59.733 17.067-4.267 29.867 0 34.133 12.8s-4.267 25.6-17.067 29.867c-42.667 12.8-85.333 29.867-132.267 42.667-25.6 12.8-46.933 21.333-64 25.6zM806.4 631.467c21.333 0 29.867 4.267 34.133 21.333 4.267 8.533-8.533 21.333-21.333 25.6-21.333 4.267-42.667 12.8-68.267 17.067-38.4 12.8-76.8 21.333-119.467 34.133-17.067 4.267-34.133-8.533-29.867-25.6 0-12.8 12.8-17.067 25.6-21.333 42.667-12.8 89.6-25.6 132.267-38.4 17.067-4.267 34.133-8.533 46.933-12.8zM516.267 746.667c0 12.8-12.8 25.6-25.6 25.6-17.067 0-25.6-8.533-25.6-21.333s12.8-25.6 29.867-25.6c12.8-4.267 21.333 4.267 21.333 21.333zM426.667 541.867c12.8 0 25.6 8.533 25.6 21.333s-12.8 25.6-25.6 25.6c-17.067 0-29.867-8.533-25.6-21.333-4.267-12.8 4.267-25.6 25.6-25.6zM354.133 422.4c-17.067 0-25.6-8.533-25.6-25.6s12.8-25.6 29.867-25.6c12.8 0 25.6 8.533 25.6 21.333-4.267 17.067-17.067 29.867-29.867 29.867zM4.267 341.333c25.6-12.8 55.467-21.333 81.067-34.133 59.733-25.6 119.467-46.933 174.933-72.533 51.2-21.333 102.4-42.667 157.867-64 8.533-4.267 17.067-8.533 25.6-12.8s12.8-8.533 12.8-17.067c0-42.667 4.267-89.6 21.333-128 8.533-17.067 17.067-38.4 25.6-55.467-12.8 4.267-29.867 8.533-42.667 17.067-46.933 17.067-93.867 38.4-145.067 55.467-42.667 17.067-85.333 38.4-128 55.467-29.867 12.8-59.733 25.6-89.6 38.4s-55.467 38.4-72.533 64c-21.333 42.667-25.6 85.333-25.6 132.267 0 4.267 4.267 12.8 4.267 21.333z" />
@ -102,7 +102,7 @@
<glyph unicode="&#xe95c;" glyph-name="anonymous" d="M230.4 605.867c12.8 46.933 29.867 93.867 46.933 140.8 8.533 34.133 21.333 64 29.867 98.133 4.267 12.8 8.533 12.8 21.333 12.8 38.4-12.8 72.533-21.333 110.933-25.6 12.8-4.267 29.867 0 46.933 0 34.133 4.267 68.267 8.533 102.4 12.8s72.533 12.8 106.667 17.067c4.267 0 8.533 0 12.8 0s8.533 0 12.8-8.533c12.8-46.933 29.867-93.867 42.667-136.533 12.8-38.4 25.6-76.8 34.133-115.2-192 4.267-379.733 4.267-567.467 4.267zM456.533 247.467c34.133 8.533 64 12.8 98.133 4.267 8.533 0 12.8 0 12.8 8.533 17.067 34.133 42.667 59.733 76.8 72.533 38.4 17.067 76.8 21.333 115.2 8.533 34.133-8.533 59.733-29.867 81.067-55.467 25.6-34.133 38.4-72.533 34.133-119.467-8.533-51.2-34.133-89.6-76.8-115.2-51.2-34.133-132.267-29.867-179.2 12.8-42.667 34.133-59.733 76.8-64 128 0 8.533-4.267 12.8-12.8 17.067-21.333 4.267-42.667 4.267-64 0-8.533-4.267-8.533-4.267-8.533-12.8 0-21.333-4.267-46.933-12.8-68.267-12.8-29.867-34.133-55.467-64-72.533-55.467-38.4-136.533-34.133-183.467 8.533-42.667 34.133-64 76.8-59.733 128 0 59.733 29.867 106.667 85.333 136.533s115.2 25.6 170.667-12.8c12.8-8.533 21.333-17.067 34.133-29.867 0-8.533 8.533-21.333 17.067-38.4zM307.2 302.933c-64 0-115.2-51.2-115.2-110.933 0-64 46.933-115.2 110.933-115.2s115.2 46.933 115.2 110.933c4.267 64-46.933 115.2-110.933 115.2zM712.533 302.933c-64 0-115.2-51.2-115.2-110.933 0-64 46.933-115.2 110.933-115.2 59.733 0 115.2 46.933 115.2 110.933 0 59.733-46.933 115.2-110.933 115.2zM1024 409.6c-341.333 0-682.667 0-1024 0 0 0 0 0 0 0 17.067 8.533 29.867 17.067 46.933 21.333 42.667 12.8 85.333 29.867 132.267 42.667 34.133 8.533 68.267 17.067 102.4 21.333 89.6 17.067 183.467 21.333 277.333 17.067 55.467-4.267 110.933-8.533 166.4-17.067 38.4-4.267 72.533-12.8 110.933-21.333s72.533-21.333 110.933-34.133c21.333-4.267 51.2-17.067 76.8-29.867 0 0 0 0 0 0z" />
<glyph unicode="&#xe95d;" glyph-name="zone" d="M243.2 448c-12.8 17.067-25.6 34.133-38.4 51.2-34.133 46.933-68.267 98.133-89.6 153.6-17.067 34.133-25.6 72.533-17.067 110.933 8.533 51.2 38.4 89.6 85.333 110.933 59.733 25.6 132.267 8.533 174.933-34.133 34.133-38.4 42.667-81.067 34.133-132.267-8.533-46.933-29.867-85.333-51.2-123.733-29.867-46.933-59.733-89.6-89.6-132.267-4.267 0-4.267 0-8.533-4.267zM247.467 823.467c-46.933 0-89.6-38.4-89.6-89.6 0-46.933 38.4-89.6 85.333-89.6s89.6 38.4 89.6 85.333c0 55.467-38.4 93.867-85.333 93.867zM490.667 379.733l-17.067 25.6 12.8 8.533-34.133 183.467c0 0 0 8.533-8.533 8.533l-42.667 4.267c0 0-68.267-110.933-157.867-217.6 4.267 4.267-93.867 110.933-132.267 187.733l-110.933-51.2c0 0-4.267 0-4.267-8.533l25.6-145.067 34.133-21.333-8.533-21.333-17.067 8.533 59.733-332.8 213.333 102.4 238.933-21.333-51.2 290.133zM149.333 285.867c-12.8 4.267-29.867 12.8-42.667 17.067 4.267 8.533 4.267 17.067 8.533 21.333 17.067 0 29.867-4.267 42.667-12.8-4.267-8.533-4.267-17.067-8.533-25.6zM256 268.8c-17.067 0-34.133 4.267-46.933 4.267 0 8.533 4.267 17.067 4.267 25.6 12.8 0 29.867-4.267 42.667-4.267 0-8.533 0-17.067 0-25.6zM315.733 277.333c-4.267 8.533-4.267 12.8-8.533 21.333 17.067 8.533 29.867 17.067 42.667 21.333 4.267-8.533 8.533-12.8 8.533-21.333-12.8-8.533-25.6-12.8-42.667-21.333zM405.333 328.533c-4.267 8.533-8.533 12.8-12.8 21.333 12.8 8.533 25.6 17.067 38.4 25.6 4.267-4.267 8.533-12.8 12.8-21.333-8.533-8.533-21.333-17.067-38.4-25.6zM972.8 460.8l-29.867 25.6 12.8 21.333 12.8-8.533-34.133 187.733c0 0 0 8.533-8.533 8.533l-226.133 17.067-209.067-93.867c0 0-8.533-4.267-4.267-12.8l29.867-170.667 21.333-12.8-17.067-17.067 55.467-307.2 213.333 102.4 234.667-21.333-51.2 281.6zM580.267 465.067c-4.267 4.267-8.533 12.8-12.8 17.067 12.8 12.8 21.333 21.333 29.867 34.133 4.267-4.267 12.8-12.8 17.067-17.067-12.8-8.533-25.6-21.333-34.133-34.133zM657.067 541.867c-4.267 4.267-8.533 12.8-12.8 21.333 12.8 8.533 25.6 17.067 38.4 25.6 8.533-8.533 12.8-17.067 12.8-21.333-12.8-8.533-25.6-17.067-38.4-25.6zM797.867 571.733c-12.8 4.267-25.6 4.267-42.667 4.267 0 8.533 0 17.067 0 25.6 17.067 0 34.133 0 51.2-4.267-4.267-8.533-4.267-17.067-8.533-25.6zM891.733 520.533c-12.8 8.533-25.6 17.067-38.4 25.6 4.267 8.533 8.533 12.8 12.8 21.333 12.8-8.533 25.6-17.067 38.4-25.6-4.267-8.533-8.533-12.8-12.8-21.333z" />
<glyph unicode="&#xe95e;" glyph-name="inventory" d="M273.067 226.133c4.267 0 8.533 4.267 8.533 8.533v85.333h98.133v-221.867h-217.6v221.867h98.133v-81.067c0-8.533 8.533-12.8 12.8-12.8zM512 226.133c4.267 0 8.533 4.267 8.533 8.533v85.333h98.133v-221.867h-217.6v221.867h98.133v-81.067c0-8.533 8.533-12.8 12.8-12.8zM750.933 226.133c4.267 0 8.533 4.267 8.533 8.533v85.333h98.133v-221.867h-217.6v221.867h98.133v-81.067c4.267-8.533 8.533-12.8 12.8-12.8zM644.267 780.8h98.133v-81.067c0-4.267 4.267-8.533 8.533-8.533s8.533 4.267 8.533 8.533v81.067h98.133v-221.867h-217.6v221.867h4.267zM401.067 780.8h98.133v-81.067c0-4.267 4.267-8.533 8.533-8.533s8.533 4.267 8.533 8.533v81.067h98.133v-221.867h-213.333v221.867zM162.133 780.8h98.133v-81.067c0-4.267 4.267-8.533 8.533-8.533s8.533 4.267 8.533 8.533v81.067h98.133v-221.867h-213.333v221.867zM153.6 537.6h780.8v-38.4h-844.8v38.4zM68.267-42.667h-42.667v981.333h42.667v-908.8zM89.6 38.4v38.4h844.8v-38.4zM998.4-42.667h-42.667v981.333h42.667z" />
<glyph unicode="&#xe95f;" glyph-name="latestBuys" d="M183.467 750.933h712.533v-38.4h-768v38.4zM89.6 64c8.533 0 12.8 0 21.333-4.267v900.267h-42.667v-900.267c8.533 4.267 12.8 4.267 21.333 4.267zM955.733 512v448h-42.667v-413.867c17.067-12.8 29.867-21.333 42.667-34.133zM145.067-8.533c0-30.633-24.833-55.467-55.467-55.467s-55.467 24.833-55.467 55.467c0 30.633 24.833 55.467 55.467 55.467s55.467-24.833 55.467-55.467zM418.133 426.667h-290.133v-38.4h273.067c4.267 17.067 8.533 29.867 17.067 38.4zM392.533 106.667h-264.533v-38.4h281.6c-8.533 12.8-12.8 25.6-17.067 38.4zM725.333 247.467c-12.8 0-21.333-8.533-21.333-21.333s8.533-21.333 21.333-21.333c12.8 0 21.333 8.533 21.333 21.333 0 8.533-12.8 21.333-21.333 21.333zM721.067 541.867c-166.4 0-298.667-136.533-298.667-302.933s132.267-302.933 298.667-302.933c166.4 0 298.667 136.533 298.667 302.933 0 170.667-132.267 302.933-298.667 302.933zM725.333 34.133c-98.133 0-174.933 72.533-187.733 162.133h-34.133l51.2 64 59.733-64h-38.4c8.533-68.267 72.533-123.733 149.333-123.733 81.067 0 149.333 64 149.333 145.067s-68.267 145.067-149.333 145.067c-68.267 0-128-46.933-145.067-110.933l-21.333 29.867-17.067-12.8c8.533 29.867 25.6 55.467 46.933 76.8l-25.6 21.333c-4.267 4.267-4.267 12.8 0 17.067l12.8 12.8c4.267 4.267 12.8 4.267 17.067 0l25.6-25.6c21.333 12.8 51.2 25.6 76.8 25.6v29.867h-8.533c-8.533 0-12.8 4.267-12.8 12.8v17.067c0 8.533 4.267 12.8 12.8 12.8h59.733c8.533 0 12.8-4.267 12.8-12.8v-17.067c0-8.533-4.267-12.8-12.8-12.8h-8.533v-21.333c29.867-4.267 55.467-12.8 81.067-29.867l34.133 29.867c4.267 4.267 12.8 4.267 17.067 0l12.8-12.8c4.267-4.267 4.267-12.8 0-17.067l-25.6-25.6c29.867-34.133 51.2-76.8 51.2-128 4.267-102.4-81.067-187.733-183.467-187.733zM772.267 226.133c0-25.6-21.333-46.933-46.933-46.933s-46.933 21.333-46.933 46.933c0 25.6 21.333 46.933 46.933 46.933 8.533 0 17.067-4.267 21.333-4.267l46.933 46.933 21.333-21.333-46.933-46.933c4.267-4.267 4.267-12.8 4.267-21.333z" />
<glyph unicode="&#xe95f;" glyph-name="latestBuy" d="M183.467 750.933h712.533v-38.4h-768v38.4zM89.6 64c8.533 0 12.8 0 21.333-4.267v900.267h-42.667v-900.267c8.533 4.267 12.8 4.267 21.333 4.267zM955.733 512v448h-42.667v-413.867c17.067-12.8 29.867-21.333 42.667-34.133zM145.067-8.533c0-30.633-24.833-55.467-55.467-55.467s-55.467 24.833-55.467 55.467c0 30.633 24.833 55.467 55.467 55.467s55.467-24.833 55.467-55.467zM418.133 426.667h-290.133v-38.4h273.067c4.267 17.067 8.533 29.867 17.067 38.4zM392.533 106.667h-264.533v-38.4h281.6c-8.533 12.8-12.8 25.6-17.067 38.4zM725.333 247.467c-12.8 0-21.333-8.533-21.333-21.333s8.533-21.333 21.333-21.333c12.8 0 21.333 8.533 21.333 21.333 0 8.533-12.8 21.333-21.333 21.333zM721.067 541.867c-166.4 0-298.667-136.533-298.667-302.933s132.267-302.933 298.667-302.933c166.4 0 298.667 136.533 298.667 302.933 0 170.667-132.267 302.933-298.667 302.933zM725.333 34.133c-98.133 0-174.933 72.533-187.733 162.133h-34.133l51.2 64 59.733-64h-38.4c8.533-68.267 72.533-123.733 149.333-123.733 81.067 0 149.333 64 149.333 145.067s-68.267 145.067-149.333 145.067c-68.267 0-128-46.933-145.067-110.933l-21.333 29.867-17.067-12.8c8.533 29.867 25.6 55.467 46.933 76.8l-25.6 21.333c-4.267 4.267-4.267 12.8 0 17.067l12.8 12.8c4.267 4.267 12.8 4.267 17.067 0l25.6-25.6c21.333 12.8 51.2 25.6 76.8 25.6v29.867h-8.533c-8.533 0-12.8 4.267-12.8 12.8v17.067c0 8.533 4.267 12.8 12.8 12.8h59.733c8.533 0 12.8-4.267 12.8-12.8v-17.067c0-8.533-4.267-12.8-12.8-12.8h-8.533v-21.333c29.867-4.267 55.467-12.8 81.067-29.867l34.133 29.867c4.267 4.267 12.8 4.267 17.067 0l12.8-12.8c4.267-4.267 4.267-12.8 0-17.067l-25.6-25.6c29.867-34.133 51.2-76.8 51.2-128 4.267-102.4-81.067-187.733-183.467-187.733zM772.267 226.133c0-25.6-21.333-46.933-46.933-46.933s-46.933 21.333-46.933 46.933c0 25.6 21.333 46.933 46.933 46.933 8.533 0 17.067-4.267 21.333-4.267l46.933 46.933 21.333-21.333-46.933-46.933c4.267-4.267 4.267-12.8 4.267-21.333z" />
<glyph unicode="&#xe968;" glyph-name="wiki" d="M793.6 733.867c0 0 4.267 0 4.267 0l76.8 12.8v-42.667c0-34.133-21.333-68.267-46.933-72.533 0 0-4.267 0-4.267 0l-76.8-12.8v42.667c0 34.133 21.333 64 46.933 72.533zM742.4 597.333l38.4 4.267c12.8 0 25.6-12.8 25.6-29.867v-21.333l-38.4-4.267c-12.8 0-25.6 12.8-25.6 29.867v21.333zM618.667 699.733l68.267 8.533c25.6 4.267 42.667-21.333 42.667-55.467v-38.4l-68.267-8.533c-25.6-4.267-42.667 21.333-42.667 55.467v38.4zM665.6 588.8c4.267 0 4.267 0 0 0l59.733 4.267v-29.867c0-25.6-17.067-46.933-34.133-55.467 0 0-4.267 0-4.267 0l-55.467-8.533v29.867c4.267 29.867 17.067 51.2 34.133 59.733zM443.733 648.533c0 0-4.267 0-4.267 0-119.467 85.333-273.067 46.933-277.333 46.933s-8.533 0-12.8 8.533c0 4.267 0 8.533 8.533 12.8 0 0 42.667 12.8 98.133 8.533 51.2 0 128-12.8 196.267-59.733 4.267-4.267 4.267-8.533 4.267-12.8-4.267-4.267-8.533-4.267-12.8-4.267zM443.733 512c0 0-4.267 0-4.267 0-119.467 85.333-273.067 46.933-277.333 46.933s-8.533 0-12.8 8.533c0 4.267 0 8.533 8.533 12.8 0 0 42.667 12.8 98.133 8.533 51.2 0 128-12.8 196.267-59.733 4.267-4.267 4.267-8.533 4.267-12.8-4.267 0-8.533-4.267-12.8-4.267zM443.733 379.733c0 0-4.267 0-4.267 0-119.467 85.333-273.067 46.933-277.333 46.933s-8.533 0-12.8 8.533c0 4.267 0 8.533 8.533 12.8 0 0 42.667 12.8 98.133 8.533 51.2 0 128-12.8 196.267-59.733 4.267-4.267 4.267-8.533 4.267-12.8-4.267 0-8.533-4.267-12.8-4.267zM443.733 247.467c0 0-4.267 0-4.267 0-119.467 85.333-273.067 46.933-277.333 46.933s-8.533 0-12.8 8.533c0 4.267 0 8.533 8.533 12.8 0 0 42.667 12.8 98.133 8.533 51.2 0 128-12.8 196.267-59.733 4.267-4.267 4.267-8.533 4.267-12.8-4.267 0-8.533-4.267-12.8-4.267zM588.8 379.733c-4.267 0-4.267 0-8.533 4.267s0 8.533 4.267 12.8c68.267 46.933 140.8 59.733 196.267 59.733s93.867-8.533 98.133-8.533c4.267 0 8.533-8.533 8.533-12.8s-8.533-8.533-12.8-8.533v0c0 0-153.6 38.4-277.333-46.933-4.267 4.267-4.267 0-8.533 0zM588.8 247.467c-4.267 0-4.267 0-8.533 4.267s0 8.533 4.267 12.8c68.267 46.933 140.8 59.733 196.267 59.733s93.867-8.533 98.133-8.533c4.267 0 8.533-8.533 8.533-12.8s-8.533-8.533-12.8-8.533v0c0 0-153.6 38.4-277.333-46.933-4.267 4.267-4.267 0-8.533 0zM985.6 738.133v64l-8.533 4.267c-4.267 0-81.067 29.867-179.2 29.867-106.667 0-200.533-34.133-277.333-98.133-76.8 64-170.667 98.133-277.333 98.133-102.4 0-174.933-29.867-179.2-29.867l-12.8-4.267v-59.733c-34.133-4.267-51.2-17.067-51.2-34.133v-614.4h452.267c17.067-12.8 38.4-21.333 64-21.333s46.933 8.533 64 21.333h443.733v614.4c0 17.067-17.067 25.6-38.4 29.867v0zM512 145.067c-38.4 17.067-166.4 64-298.667 64-51.2 0-98.133-8.533-136.533-21.333v597.333c21.333 8.533 85.333 25.6 162.133 25.6 98.133 0 183.467-29.867 256-89.6v-358.4l17.067 17.067v-234.667zM955.733 183.467c-42.667 17.067-89.6 25.6-140.8 25.6-128 0-251.733-51.2-290.133-64v238.933l17.067-17.067v349.867c68.267 59.733 153.6 89.6 256 89.6 76.8 0 136.533-17.067 162.133-25.6v-597.333z" />
<glyph unicode="&#xe96c;" glyph-name="attach" d="M960 866.133c-42.667 42.667-98.133 64-157.867 64s-115.2-21.333-157.867-64l-593.067-593.067c-34.133-34.133-55.467-85.333-51.2-136.533 0-42.667 17.067-81.067 46.933-110.933 34.133-38.4 81.067-59.733 132.267-59.733 46.933 0 93.867 17.067 128 51.2l541.867 546.133c25.6 25.6 42.667 64 42.667 98.133s-12.8 68.267-38.4 93.867c-25.6 25.6-59.733 38.4-98.133 38.4-34.133 0-72.533-17.067-98.133-42.667l-354.133-354.133c-4.267 0-4.267-4.267-4.267-12.8s4.267-12.8 8.533-17.067 25.6-8.533 34.133 0l354.133 354.133c12.8 17.067 38.4 25.6 59.733 25.6 25.6 0 51.2-12.8 68.267-34.133 8.533-12.8 17.067-25.6 17.067-42.667 4.267-25.6-4.267-55.467-25.6-72.533l-541.867-541.867c-25.6-25.6-55.467-38.4-93.867-38.4-34.133 0-68.267 12.8-93.867 38.4s-38.4 59.733-38.4 93.867c0 34.133 12.8 68.267 38.4 93.867l588.8 584.533c34.133 34.133 76.8 51.2 123.733 51.2s89.6-17.067 123.733-51.2c34.133-34.133 51.2-76.8 51.2-123.733s-17.067-89.6-51.2-123.733l-401.067-401.067c-4.267-4.267-8.533-12.8-8.533-17.067 0-8.533 4.267-12.8 8.533-17.067 8.533-8.533 25.6-8.533 34.133 0l401.067 401.067c89.6 89.6 89.6 230.4 4.267 320z" />
<glyph unicode="&#xe96d;" glyph-name="zone2" d="M98.133 17.067c-4.267 29.867-12.8 64-17.067 93.867-17.067 98.133-34.133 192-51.2 290.133-12.8 46.933-21.333 98.133-29.867 149.333 0 4.267 0 8.533 4.267 8.533 42.667 21.333 85.333 42.667 128 59.733 0 0 0 0 4.267 0 4.267-8.533 8.533-12.8 12.8-21.333-21.333-8.533-42.667-21.333-64-29.867-17.067-8.533-34.133-17.067-51.2-21.333-4.267 0-4.267-4.267-4.267-8.533 8.533-42.667 17.067-85.333 25.6-132.267 0-4.267 4.267-4.267 4.267-8.533 8.533-4.267 17.067-8.533 25.6-17.067 0-4.267-4.267-12.8-8.533-17.067-4.267 4.267-12.8 4.267-17.067 8.533 17.067-102.4 38.4-209.067 55.467-311.467 17.067 8.533 29.867 12.8 42.667 21.333 51.2 25.6 102.4 51.2 153.6 72.533 4.267 0 8.533 0 17.067 0 68.267-4.267 136.533-12.8 204.8-17.067 0 0 4.267 0 8.533 0-4.267 17.067-4.267 34.133-8.533 51.2-12.8 68.267-25.6 136.533-38.4 204.8 0 8.533-4.267 17.067-12.8 25.6-4.267 4.267-4.267 8.533-8.533 12.8 12.8 4.267 8.533 17.067 8.533 25.6-8.533 51.2-17.067 106.667-29.867 157.867 0 4.267 0 4.267-8.533 4.267-17.067 0-38.4 4.267-55.467 4.267 4.267 8.533 8.533 17.067 12.8 21.333 0 0 4.267 4.267 8.533 4.267 17.067 0 34.133-4.267 46.933-4.267 4.267 0 8.533 0 12.8 0 68.267 29.867 132.267 64 200.533 93.867 4.267 4.267 8.533 8.533 12.8 8.533 68.267-4.267 136.533-8.533 204.8-17.067 8.533 0 17.067 0 29.867-4.267 4.267 0 8.533-4.267 8.533-8.533 12.8-64 25.6-132.267 34.133-196.267 17.067-102.4 38.4-204.8 55.467-311.467 0-8.533 4.267-17.067 4.267-25.6-17.067 0-34.133 4.267-51.2 4.267-42.667 4.267-89.6 8.533-132.267 12.8-17.067 0-38.4 4.267-55.467 4.267-4.267 0-12.8 0-17.067-4.267-68.267-29.867-132.267-64-200.533-93.867 0 0-4.267 0-8.533 0-76.8 8.533-149.333 12.8-226.133 21.333-4.267 0-8.533 0-12.8 0-72.533-34.133-140.8-68.267-213.333-102.4 0-4.267 0-8.533-4.267-8.533zM989.867 217.6c0 4.267 0 4.267 0 8.533-8.533 34.133-12.8 72.533-21.333 106.667-8.533 46.933-17.067 89.6-25.6 136.533 0 8.533-4.267 12.8-8.533 17.067-8.533 4.267-12.8 12.8-21.333 17.067 4.267 8.533 8.533 12.8 12.8 17.067 4.267-4.267 8.533-4.267 12.8-8.533-4.267 12.8-4.267 21.333-4.267 34.133-8.533 46.933-17.067 93.867-25.6 145.067 0 4.267-4.267 8.533-8.533 8.533-68.267 4.267-136.533 12.8-209.067 17.067-4.267 0-8.533 0-12.8-4.267-64-29.867-123.733-59.733-187.733-85.333-4.267-4.267-8.533-4.267-4.267-12.8 4.267-29.867 12.8-64 17.067-93.867 4.267-21.333 8.533-42.667 12.8-59.733 12.8-4.267 12.8-12.8 21.333-21.333-12.8-4.267-12.8-12.8-12.8-25.6 12.8-68.267 25.6-132.267 38.4-200.533 4.267-25.6 8.533-51.2 12.8-76.8 4.267 0 4.267 0 8.533 4.267 59.733 29.867 119.467 59.733 183.467 89.6-4.267 4.267 0 4.267 4.267 4.267 51.2-4.267 106.667-8.533 157.867-12.8 21.333 0 38.4-4.267 59.733-4.267zM260.267 469.333c-12.8 17.067-25.6 34.133-38.4 46.933-29.867 46.933-59.733 93.867-85.333 145.067-12.8 29.867-21.333 64-17.067 102.4 8.533 51.2 34.133 85.333 81.067 102.4 55.467 21.333 123.733 8.533 162.133-34.133 34.133-34.133 42.667-76.8 34.133-123.733-8.533-42.667-25.6-76.8-46.933-115.2-25.6-42.667-55.467-81.067-85.333-123.733 0 4.267-4.267 0-4.267 0zM260.267 819.2c-46.933 0-81.067-34.133-81.067-81.067s38.4-81.067 81.067-81.067c46.933 0 81.067 38.4 85.333 81.067 0 42.667-38.4 81.067-85.333 81.067zM358.4 349.867c4.267-8.533 4.267-12.8 8.533-21.333-12.8-4.267-25.6-12.8-38.4-17.067-4.267 8.533-4.267 12.8-8.533 21.333 12.8 4.267 25.6 12.8 38.4 17.067zM226.133 302.933c0 8.533 4.267 17.067 4.267 21.333 12.8 0 25.6-4.267 38.4-4.267 0-8.533 0-12.8 0-25.6-12.8 8.533-29.867 8.533-42.667 8.533zM413.867 354.133c-4.267 8.533-8.533 12.8-12.8 21.333 12.8 8.533 21.333 17.067 34.133 25.6 4.267-4.267 8.533-12.8 12.8-17.067-12.8-12.8-21.333-21.333-34.133-29.867zM179.2 341.333c-4.267-8.533-4.267-12.8-8.533-21.333-12.8 4.267-25.6 8.533-38.4 17.067 4.267 8.533 4.267 12.8 8.533 21.333 12.8-8.533 25.6-12.8 38.4-17.067zM682.667 580.267c-12.8-8.533-21.333-17.067-34.133-21.333-4.267 4.267-8.533 12.8-12.8 17.067 12.8 8.533 25.6 17.067 38.4 25.6 4.267-8.533 4.267-17.067 8.533-21.333zM878.933 558.933c-4.267-8.533-8.533-12.8-12.8-21.333-12.8 8.533-25.6 17.067-34.133 21.333 4.267 8.533 8.533 12.8 8.533 21.333 12.8-8.533 25.6-17.067 38.4-21.333zM571.733 486.4c-4.267 4.267-8.533 12.8-12.8 17.067 8.533 8.533 21.333 21.333 29.867 29.867 4.267-4.267 8.533-12.8 12.8-17.067-8.533-8.533-17.067-21.333-29.867-29.867zM785.067 610.133c-4.267-8.533-4.267-17.067-8.533-21.333-12.8 0-25.6 4.267-38.4 4.267 0 8.533 0 12.8 0 21.333 17.067 0 29.867-4.267 46.933-4.267z" />

Before

Width:  |  Height:  |  Size: 107 KiB

After

Width:  |  Height:  |  Size: 107 KiB

View File

@ -19,5 +19,6 @@ export default function moduleImport(moduleName) {
case 'route' : return import('route/front');
case 'entry' : return import('entry/front');
case 'account' : return import('account/front');
case 'supplier' : return import('supplier/front');
}
}

View File

@ -45,6 +45,7 @@ Locator: Localizador
Invoices out: Facturas emitidas
Entries: Entradas
Users: Usuarios
Suppliers: Proveedores
# Common

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -76,8 +76,5 @@
},
"TaxType": {
"dataSource": "vn"
},
"Supplier": {
"dataSource": "vn"
}
}

View File

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

View File

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

View File

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

View File

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

View File

@ -0,0 +1,106 @@
const ParameterizedSQL = require('loopback-connector').ParameterizedSQL;
const buildFilter = require('vn-loopback/util/filter').buildFilter;
const mergeFilters = require('vn-loopback/util/filter').mergeFilters;
module.exports = Self => {
Self.remoteMethodCtx('filter', {
description: 'Find all instances of the model matched by filter from the data source.',
accessType: 'READ',
accepts: [
{
arg: 'filter',
type: 'Object',
description: 'Filter defining where, order, offset, and limit - must be a JSON-encoded string',
http: {source: 'query'}
}, {
arg: 'search',
type: 'String',
description: 'Searchs the supplier by id',
http: {source: 'query'}
}, {
arg: 'id',
type: 'Integer',
description: 'The supplier id',
http: {source: 'query'}
}, {
arg: 'nickname',
type: 'String',
description: 'The supplier name to filter',
http: {source: 'query'}
}, {
arg: 'provinceFk',
type: 'Number',
description: 'The province id to filter',
http: {source: 'query'}
}, {
arg: 'countryFk',
type: 'Number',
description: 'The country id to filter',
http: {source: 'query'}
}, {
arg: 'nif',
type: 'String',
description: 'The supplier nif to filter',
http: {source: 'query'}
}
],
returns: {
type: ['Object'],
root: true
},
http: {
path: `/filter`,
verb: 'GET'
}
});
Self.filter = async(ctx, filter) => {
let conn = Self.dataSource.connector;
let where = buildFilter(ctx.args, (param, value) => {
switch (param) {
case 'search':
return {'s.id': value};
case 'nickname':
param = `s.${param}`;
return {[param]: {like: `%${value}%`}};
case 'id':
case 'provinceFk':
case 'countryFk':
case 'nif':
param = `s.${param}`;
return {[param]: value};
}
});
filter = mergeFilters(ctx.args.filter, {where});
let stmts = [];
let stmt;
stmt = new ParameterizedSQL(
`SELECT
s.id,
s.name AS socialName,
s.nickname AS alias,
s.account,
s.provinceFk,
s.nif,
s.payDemFk,
s.payDay,
s.phone,
s.city,
pm.name AS payMethod,
pd.payDem AS payDem
FROM vn.supplier s
JOIN vn.payMethod pm ON pm.id = s.payMethodFk
JOIN vn.payDem pd ON pd.id = s.payDemFk`
);
stmt.merge(conn.makeSuffix(filter));
let itemsIndex = stmts.push(stmt) - 1;
let sql = ParameterizedSQL.join(stmts, ';');
let result = await conn.executeStmt(sql);
return itemsIndex === 0 ? result : result[itemsIndex];
};
};

View File

@ -0,0 +1,28 @@
const app = require('vn-loopback/server/server');
describe('Supplier filter()', () => {
it('should return the supplier matching "search"', async() => {
let ctx = {
args: {
search: 1
}
};
let result = await app.models.Supplier.filter(ctx);
expect(result.length).toEqual(1);
expect(result[0].id).toEqual(1);
});
it('should return the supplier matching the province', async() => {
let ctx = {
args: {
provinceFk: 1
}
};
let result = await app.models.Supplier.filter(ctx);
expect(result.length).toEqual(2);
});
});

View File

@ -0,0 +1,5 @@
{
"Supplier": {
"dataSource": "vn"
}
}

View File

@ -0,0 +1,3 @@
module.exports = Self => {
require('../methods/supplier/filter')(Self);
};

View File

@ -0,0 +1,5 @@
export * from './module';
import './main';
import './index/';
import './search-panel';

View File

@ -0,0 +1,61 @@
<vn-auto-search
model="model">
</vn-auto-search>
<vn-data-viewer
model="model"
class="vn-w-sm">
<vn-card>
<div class="vn-list separated">
<a
ng-repeat="supplier in model.data track by supplier.id"
ui-sref="supplier.card.summary(::{id: supplier.id})"
translate-attr="{title: 'View supplier'}"
class="vn-item search-result">
<vn-item-section>
<h6>{{::supplier.socialName}}</h6>
<vn-label-value
label="Id"
value="{{::supplier.id}}">
</vn-label-value>
<vn-label-value
label="Tax number"
value="{{::supplier.nif}}">
</vn-label-value>
<vn-label-value
label="Alias"
value="{{::supplier.alias}}">
</vn-label-value>
<vn-label-value
label="Pay method"
value="{{::supplier.payMethod}}">
</vn-label-value>
<vn-label-value
label="Payment deadline"
value="{{::supplier.payDem}}">
</vn-label-value>
<vn-label-value
label="Pay day"
value="{{::supplier.payDay}}">
</vn-label-value>
<vn-label-value
label="Account"
value="{{::supplier.account}}">
</vn-label-value>
</vn-item-section>
<vn-item-section side>
<vn-icon-button
ng-click="$ctrl.openSummary(supplier, $event)"
vn-tooltip="Preview"
icon="desktop_windows">
</vn-icon-button>
</vn-item-section>
</a>
</div>
</vn-card>
</vn-data-viewer>
<vn-popup vn-id="dialog-summary-client">
<vn-supplier-summary
supplier="$ctrl.supplierSelected">
</vn-supplier-summary>
</vn-popup>

View File

@ -0,0 +1,9 @@
import ngModule from '../module';
import Section from 'salix/components/section';
export default class Controller extends Section {}
ngModule.vnComponent('vnSupplierIndex', {
template: require('./index.html'),
controller: Controller
});

View File

@ -0,0 +1,5 @@
Payment deadline: Plazo de pago
Pay day: Dia de pago
Account: Cuenta
Pay method: Metodo de pago
Tax number: Nif

View File

@ -0,0 +1,18 @@
<vn-crud-model
vn-id="model"
url="Suppliers/filter"
limit="20"
auto-load="true">
</vn-crud-model>
<vn-portal slot="topbar">
<vn-searchbar
vn-focus
panel="vn-supplier-search-panel"
info="Search suppliers by id"
model="model">
</vn-searchbar>
</vn-portal>
<vn-portal slot="menu">
<vn-left-menu></vn-left-menu>
</vn-portal>
<ui-view></ui-view>

View File

@ -0,0 +1,9 @@
import ngModule from '../module';
import ModuleMain from 'salix/components/module-main';
export default class Supplier extends ModuleMain {}
ngModule.vnComponent('vnSupplier', {
controller: Supplier,
template: require('./index.html')
});

View File

@ -0,0 +1,3 @@
import {ng} from 'core/vendor';
export default ng.module('supplier', ['salix']);

View File

@ -0,0 +1,27 @@
{
"module": "supplier",
"name": "Suppliers",
"icon" : "icon-supplier",
"validations" : true,
"menus": {
"main": [
{"state": "supplier.index", "icon": "icon-supplier"}
],
"card": [
]
},
"routes": [
{
"url": "/supplier",
"state": "supplier",
"abstract": true,
"component": "vn-supplier",
"description": "Suppliers"
}, {
"url": "/index?q",
"state": "supplier.index",
"component": "vn-supplier-index",
"description": "Suppliers"
}
]
}

View File

@ -0,0 +1,46 @@
<div class="search-panel">
<form ng-submit="$ctrl.onSearch()">
<vn-horizontal>
<vn-textfield
vn-one
label="General search"
ng-model="filter.search"
info="Search suppliers by id"
vn-focus>
</vn-textfield>
</vn-horizontal>
<vn-horizontal>
<vn-textfield
vn-one
label="Alias"
ng-model="filter.nickname">
</vn-textfield>
<vn-textfield
vn-one
label="Tax number"
ng-model="filter.nif">
</vn-textfield>
</vn-horizontal>
<vn-horizontal>
<vn-autocomplete
vn-one
label="Province"
ng-model="filter.provinceFk"
url="Provinces"
show-field="name"
value-field="id">
</vn-autocomplete>
<vn-autocomplete
vn-one
label="Country"
ng-model="filter.countryFk"
url="countries"
show-field="country"
value-field="id">
</vn-autocomplete>
</vn-horizontal>
<vn-horizontal class="vn-mt-lg">
<vn-submit label="Search"></vn-submit>
</vn-horizontal>
</form>
</div>

View File

@ -0,0 +1,7 @@
import ngModule from '../module';
import SearchPanel from 'core/components/searchbar/search-panel';
ngModule.vnComponent('vnSupplierSearchPanel', {
template: require('./index.html'),
controller: SearchPanel
});

View File

@ -0,0 +1,3 @@
Province: Provincia
Country: País
Tax number: Nif

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -7,21 +7,29 @@ describe('sale updateDiscount()', () => {
let salesPersonMana;
beforeAll(async done => {
originalSale = await app.models.Sale.findById(originalSaleId);
let manaDiscount = await app.models.Component.findOne({where: {code: 'buyerDiscount'}});
componentId = manaDiscount.id;
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);
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 => {
await originalSale.save();
await app.models.SaleComponent.updateAll({componentFk: componentId, saleFk: originalSaleId}, {value: 0});
await salesPersonMana.save();
try {
await originalSale.save();
await app.models.SaleComponent.updateAll({componentFk: componentId, saleFk: originalSaleId}, {value: 0});
await salesPersonMana.save();
} catch (error) {
console.error(error);
}
done();
});

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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