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('account login()', () => {
describe('when credentials are correct', () => { describe('when credentials are correct', () => {
it('should return the token', async() => { it('should return the token', async() => {
let response = await app.models.Account.login('employee', 'nightmare'); let login = await app.models.Account.login('salesAssistant', 'nightmare');
let accessToken = await app.models.AccessToken.findById(login.token);
let ctx = {req: {accessToken: accessToken}};
expect(response.token).toBeDefined(); expect(login.token).toBeDefined();
await app.models.Account.logout(ctx);
}); });
it('should return the token if the user doesnt exist but the client does', async() => { it('should return the token if the user doesnt exist but the client does', async() => {
let response = await app.models.Account.login('PetterParker', 'nightmare'); let login = await app.models.Account.login('PetterParker', 'nightmare');
let accessToken = await app.models.AccessToken.findById(login.token);
let ctx = {req: {accessToken: accessToken}};
expect(response.token).toBeDefined(); expect(login.token).toBeDefined();
await app.models.Account.logout(ctx);
}); });
}); });

View File

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

View File

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

View File

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

View File

@ -1208,11 +1208,11 @@ INSERT INTO `vn`.`annualAverageInvoiced`(`clientFk`, `invoiced`)
(104, 500), (104, 500),
(105, 5000); (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 VALUES
(1, 'Plants SL', 'Plants nick', 4000000001, 1, 'A11111111', 0, NULL, 0, CURDATE(), 1111, 1, 'supplier address 1', 'PONTEVEDRA', 1, 15214, 1, 1), (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), (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); (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`) INSERT INTO `cache`.`cache_calc`(`id`, `cache_id`, `cacheName`, `params`, `last_refresh`, `expires`, `created`, `connection_id`)
VALUES VALUES

View File

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

View File

@ -22,6 +22,9 @@
-webkit-font-smoothing: antialiased; -webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale; -moz-osx-font-smoothing: grayscale;
} }
.icon-supplier:before {
content: "\e936";
}
.icon-latestBuys:before { .icon-latestBuys:before {
content: "\e95f"; 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="&#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="&#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="&#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="&#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="&#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" /> <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="&#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="&#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="&#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="&#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="&#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" /> <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 'route' : return import('route/front');
case 'entry' : return import('entry/front'); case 'entry' : return import('entry/front');
case 'account' : return import('account/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 Invoices out: Facturas emitidas
Entries: Entradas Entries: Entradas
Users: Usuarios Users: Usuarios
Suppliers: Proveedores
# Common # Common

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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