Merge branch 'dev' of https://gitea.verdnatura.es/verdnatura/salix into 2539-supplier_log

This commit is contained in:
Bernat Exposito 2020-10-29 13:40:21 +01:00
commit 792b689b58
69 changed files with 910 additions and 106 deletions

View File

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

View File

@ -0,0 +1,3 @@
UPDATE `vn`.`claimState` SET `roleFk` = '72' WHERE (`id` = '3');
UPDATE `vn`.`claimState` SET `roleFk` = '72' WHERE (`id` = '4');
UPDATE `vn`.`claimState` SET `roleFk` = '72' WHERE (`id` = '5');

File diff suppressed because one or more lines are too long

View File

@ -1213,6 +1213,11 @@ INSERT INTO `vn`.`supplier`(`id`, `name`, `nickname`,`account`,`countryFk`,`nif`
(1, 'Plants SL', 'Plants nick', 4100000001, 1, 'A11111111', 0, 0, CURDATE(), 1, 'supplier address 1', 'PONTEVEDRA', 1, 15214, 1, 1, 15, NULL, NULL), (1, 'Plants SL', 'Plants nick', 4100000001, 1, 'A11111111', 0, 0, CURDATE(), 1, 'supplier address 1', 'PONTEVEDRA', 1, 15214, 1, 1, 15, NULL, NULL),
(2, 'Farmer King', 'The farmer', 4000020002, 1, 'B22222222', 1, 0, CURDATE(), 1, 'supplier address 2', 'SILLA', 2, 43022, 1, 2, 10, 93, 8), (2, 'Farmer King', 'The farmer', 4000020002, 1, 'B22222222', 1, 0, CURDATE(), 1, 'supplier address 2', 'SILLA', 2, 43022, 1, 2, 10, 93, 8),
(442, 'Verdnatura Levante SL', 'Verdnatura', 5115000442, 1, 'C33333333', 0, 0, CURDATE(), 1, 'supplier address 3', 'SILLA', 1, 43022, 1, 2, 15, NULL, NULL); (442, 'Verdnatura Levante SL', 'Verdnatura', 5115000442, 1, 'C33333333', 0, 0, CURDATE(), 1, 'supplier address 3', 'SILLA', 1, 43022, 1, 2, 15, NULL, NULL);
INSERT INTO `vn`.`supplier`(`id`, `name`, `nickname`,`account`,`countryFk`,`nif`,`isFarmer`,`retAccount`,`commission`, `created`, `postcodeFk`, `isActive`, `street`, `city`, `provinceFk`, `postCode`, `payMethodFk`, `payDemFk`, `payDay`)
VALUES
(1, 'Plants SL', 'Plants nick', 4000000001, 1, '06089160W', 0, NULL, 0, CURDATE(), 1111, 1, 'supplier address 1', 'PONTEVEDRA', 1, 15214, 1, 1, 15),
(2, 'Flower King', 'The king', 4000000002, 1, 'B22222222', 0, NULL, 0, CURDATE(), 2222, 1, 'supplier address 2', 'LONDON', 2, 45671, 1, 2, 10),
(442, 'Verdnatura Levante SL', 'Verdnatura', 4000000442, 1, 'C33333333', 0, NULL, 0, CURDATE(), 3333, 1, 'supplier address 3', 'SILLA', 1, 43022, 1, 2, 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
@ -1534,9 +1539,9 @@ INSERT INTO `vn`.`claimState`(`id`, `code`, `description`, `roleFk`, `priority`)
VALUES VALUES
( 1, 'pending', 'Pendiente', 1, 1), ( 1, 'pending', 'Pendiente', 1, 1),
( 2, 'managed', 'Gestionado', 1, 5), ( 2, 'managed', 'Gestionado', 1, 5),
( 3, 'resolved', 'Resuelto', 21, 7), ( 3, 'resolved', 'Resuelto', 72, 7),
( 4, 'canceled', 'Anulado', 1, 6), ( 4, 'canceled', 'Anulado', 72, 6),
( 5, 'disputed', 'Cuestionado', 21, 3), ( 5, 'disputed', 'Cuestionado', 72, 3),
( 6, 'mana', 'Mana', 1, 4), ( 6, 'mana', 'Mana', 1, 4),
( 7, 'inProgress', 'En Curso', 1, 2); ( 7, 'inProgress', 'En Curso', 1, 2);

View File

@ -901,5 +901,16 @@ export default {
newEntryTravel: 'vn-entry-create vn-autocomplete[ng-model="$ctrl.entry.travelFk"]', newEntryTravel: 'vn-entry-create vn-autocomplete[ng-model="$ctrl.entry.travelFk"]',
newEntryCompany: 'vn-entry-create vn-autocomplete[ng-model="$ctrl.entry.companyFk"]', newEntryCompany: 'vn-entry-create vn-autocomplete[ng-model="$ctrl.entry.companyFk"]',
saveNewEntry: 'vn-entry-create button[type="submit"]' saveNewEntry: 'vn-entry-create button[type="submit"]'
},
supplierSummary: {
header: 'vn-supplier-summary > vn-card > h5',
basicDataId: 'vn-supplier-summary vn-label-value[label="Id"]',
fiscalAddressTaxNumber: 'vn-supplier-summary vn-label-value[label="Tax number"]',
billingDataPayMethod: 'vn-supplier-summary vn-label-value[label="Pay method"]'
},
supplierDescriptor: {
alias: 'vn-supplier-descriptor vn-label-value[label="Alias"]',
clientButton: 'vn-supplier-descriptor vn-icon[icon="person"]',
entriesButton: 'vn-supplier-descriptor vn-icon[icon="icon-entry"]',
} }
}; };

View File

@ -14,8 +14,8 @@ describe('Claim edit basic data path', () => {
await browser.close(); await browser.close();
}); });
it(`should log in as salesAssistant then reach basic data of the target claim`, async() => { it(`should log in as claimManager then reach basic data of the target claim`, async() => {
await page.loginAndModule('salesAssistant', 'claim'); await page.loginAndModule('claimManager', 'claim');
await page.accessToSearchResult('1'); await page.accessToSearchResult('1');
await page.accessToSection('claim.card.basicData'); await page.accessToSection('claim.card.basicData');
}); });
@ -30,7 +30,7 @@ describe('Claim edit basic data path', () => {
expect(message.type).toBe('success'); expect(message.type).toBe('success');
}); });
it(`should have been redirected to the next section of claims as the role is salesAssistant`, async() => { it(`should have been redirected to the next section of claims as the role is claimManager`, async() => {
await page.waitForState('claim.card.detail'); await page.waitForState('claim.card.detail');
}); });

View File

@ -8,7 +8,7 @@ describe('Claim development', () => {
beforeAll(async() => { beforeAll(async() => {
browser = await getBrowser(); browser = await getBrowser();
page = browser.page; page = browser.page;
await page.loginAndModule('salesAssistant', 'claim'); await page.loginAndModule('claimManager', 'claim');
await page.accessToSearchResult('1'); await page.accessToSearchResult('1');
await page.accessToSection('claim.card.development'); await page.accessToSection('claim.card.development');
}); });
@ -31,7 +31,7 @@ describe('Claim development', () => {
expect(message.type).toBe('success'); expect(message.type).toBe('success');
}); });
it(`should redirect to the next section of claims as the role is salesAssistant`, async() => { it(`should redirect to the next section of claims as the role is claimManager`, async() => {
await page.waitForState('claim.card.action'); await page.waitForState('claim.card.action');
}); });

View File

@ -8,7 +8,7 @@ describe('Claim action path', () => {
beforeAll(async() => { beforeAll(async() => {
browser = await getBrowser(); browser = await getBrowser();
page = browser.page; page = browser.page;
await page.loginAndModule('administrative', 'claim'); await page.loginAndModule('claimManager', 'claim');
await page.accessToSearchResult('2'); await page.accessToSearchResult('2');
await page.accessToSection('claim.card.action'); await page.accessToSection('claim.card.action');
}); });

View File

@ -26,8 +26,8 @@ describe('claim Descriptor path', () => {
await page.waitForSelector(selectors.claimDescriptor.moreMenuDeleteClaim, {hidden: true}); await page.waitForSelector(selectors.claimDescriptor.moreMenuDeleteClaim, {hidden: true});
}); });
it(`should log in as salesAssistant and navigate to the target claim`, async() => { it(`should log in as claimManager and navigate to the target claim`, async() => {
await page.loginAndModule('salesAssistant', 'claim'); await page.loginAndModule('claimManager', 'claim');
await page.accessToSearchResult(claimId); await page.accessToSearchResult(claimId);
await page.waitForState('claim.card.summary'); await page.waitForState('claim.card.summary');
}); });

View File

@ -0,0 +1,84 @@
import selectors from '../../helpers/selectors.js';
import getBrowser from '../../helpers/puppeteer';
describe('Supplier descriptor path', () => {
let browser;
let page;
beforeAll(async() => {
browser = await getBrowser();
page = browser.page;
await page.loginAndModule('administrative', 'supplier');
await page.accessToSearchResult('1');
});
afterAll(async() => {
await browser.close();
});
// summary
it('should reach the second entry summary section', async() => {
await page.waitForState('supplier.card.summary');
});
it(`should confirm there's data on the summary header`, async() => {
const result = await page.waitToGetProperty(selectors.supplierSummary.header, 'innerText');
expect(result).toContain('Plants SL - 1');
});
it(`should confirm there's data on the summary basic data`, async() => {
const result = await page.waitToGetProperty(selectors.supplierSummary.basicDataId, 'innerText');
expect(result).toContain('Id 1');
});
it(`should confirm there's data on the summary fiscal address`, async() => {
const result = await page.waitToGetProperty(selectors.supplierSummary.fiscalAddressTaxNumber, 'innerText');
expect(result).toContain('Tax number 06089160W');
});
it(`should confirm there's data on the summary fiscal pay method`, async() => {
const result = await page.waitToGetProperty(selectors.supplierSummary.billingDataPayMethod, 'innerText');
expect(result).toContain('Pay method PayMethod one');
});
// descriptor
it(`should confirm there's data on the descriptor`, async() => {
const result = await page.waitToGetProperty(selectors.supplierDescriptor.alias, 'innerText');
expect(result).toContain('Plants nick');
});
it(`should navigate to the supplier's client summary using the icon client button`, async() => {
await page.waitToClick(selectors.supplierDescriptor.clientButton);
await page.waitForState('client.card.summary');
});
it(`should navigate back to the supplier`, async() => {
await page.waitToClick(selectors.globalItems.homeButton);
await page.waitForState('home');
await page.selectModule('supplier');
await page.accessToSearchResult('1');
await page.waitForState('supplier.card.summary');
});
it(`should navigate to the supplier's entries`, async() => {
await page.waitToClick(selectors.supplierDescriptor.entriesButton);
await page.waitForState('entry.index');
});
it(`should navigate back to suppliers but a different one this time`, async() => {
await page.waitToClick(selectors.globalItems.homeButton);
await page.waitForState('home');
await page.selectModule('supplier');
await page.accessToSearchResult('2');
await page.waitForState('supplier.card.summary');
});
it(`should check the client button isn't present since this supplier should not be a client`, async() => {
await page.waitForSelector(selectors.supplierDescriptor.clientButton, {hidden: true});
});
});

View File

@ -162,6 +162,9 @@ function e2eSingleRun() {
`${__dirname}/e2e/paths/08*/*[sS]pec.js`, `${__dirname}/e2e/paths/08*/*[sS]pec.js`,
`${__dirname}/e2e/paths/09*/*[sS]pec.js`, `${__dirname}/e2e/paths/09*/*[sS]pec.js`,
`${__dirname}/e2e/paths/10*/*[sS]pec.js`, `${__dirname}/e2e/paths/10*/*[sS]pec.js`,
`${__dirname}/e2e/paths/11*/*[sS]pec.js`,
`${__dirname}/e2e/paths/12*/*[sS]pec.js`,
`${__dirname}/e2e/paths/13*/*[sS]pec.js`,
`${__dirname}/e2e/paths/**/*[sS]pec.js` `${__dirname}/e2e/paths/**/*[sS]pec.js`
]; ];

View File

@ -73,6 +73,7 @@
"I have deleted the ticket id": "I have deleted the ticket id [{{id}}]({{{url}}})", "I have deleted the ticket id": "I have deleted the ticket id [{{id}}]({{{url}}})",
"I have restored the ticket id": "I have restored the ticket id [{{id}}]({{{url}}})", "I have restored the ticket id": "I have restored the ticket id [{{id}}]({{{url}}})",
"Changed this data from the ticket": "I have changed the data from the ticket [{{ticketId}}]({{{ticketUrl}}}): {{{changes}}}", "Changed this data from the ticket": "I have changed the data from the ticket [{{ticketId}}]({{{ticketUrl}}}): {{{changes}}}",
"The grade must be similar to the last one": "The grade must be similar to the last one",
"agencyModeFk": "Agency", "agencyModeFk": "Agency",
"clientFk": "Client", "clientFk": "Client",
"zoneFk": "Zone", "zoneFk": "Zone",

View File

@ -2,13 +2,13 @@ const app = require('vn-loopback/server/server');
const LoopBackContext = require('loopback-context'); const LoopBackContext = require('loopback-context');
describe('claimBeginning', () => { describe('claimBeginning', () => {
const salesAssistantId = 21; const claimManagerId = 72;
let ticket; let ticket;
let refundTicketSales; let refundTicketSales;
let salesInsertedInClaimEnd; let salesInsertedInClaimEnd;
const activeCtx = { const activeCtx = {
accessToken: {userId: salesAssistantId}, accessToken: {userId: claimManagerId},
}; };
const ctx = {req: activeCtx}; const ctx = {req: activeCtx};

View File

@ -1,12 +1,12 @@
module.exports = Self => { module.exports = Self => {
Self.remoteMethod('getSummary', { Self.remoteMethod('getSummary', {
description: 'Updates the item taxes', description: 'Return the claim summary',
accessType: 'READ', accessType: 'READ',
accepts: [{ accepts: [{
arg: 'id', arg: 'id',
type: 'number', type: 'number',
required: true, required: true,
description: 'The item id', description: 'The claim id',
http: {source: 'path'} http: {source: 'path'}
}], }],
returns: { returns: {

View File

@ -21,10 +21,8 @@ module.exports = Self => {
Self.isEditable = async(ctx, id) => { Self.isEditable = async(ctx, id) => {
const userId = ctx.req.accessToken.userId; const userId = ctx.req.accessToken.userId;
const isClaimManager = await Self.app.models.Account.hasRole(userId, 'claimManager');
const isSalesAssistant = await Self.app.models.Account.hasRole(userId, 'salesAssistant'); const claim = await Self.app.models.Claim.findById(id, {
let claim = await Self.app.models.Claim.findById(id, {
fields: ['claimStateFk'], fields: ['claimStateFk'],
include: [{ include: [{
relation: 'claimState' relation: 'claimState'
@ -33,7 +31,7 @@ module.exports = Self => {
const isClaimResolved = claim && claim.claimState().code == 'resolved'; const isClaimResolved = claim && claim.claimState().code == 'resolved';
if (!claim || (isClaimResolved && !isSalesAssistant)) if (!claim || (isClaimResolved && !isClaimManager))
return false; return false;
return true; return true;

View File

@ -2,9 +2,9 @@ const app = require('vn-loopback/server/server');
describe('claim isEditable()', () => { describe('claim isEditable()', () => {
const salesPerdonId = 18; const salesPerdonId = 18;
const salesAssistantId = 21; const claimManagerId = 72;
it('should return false if the given claim does not exist', async() => { it('should return false if the given claim does not exist', async() => {
let ctx = {req: {accessToken: {userId: salesAssistantId}}}; let ctx = {req: {accessToken: {userId: claimManagerId}}};
let result = await app.models.Claim.isEditable(ctx, 99999); let result = await app.models.Claim.isEditable(ctx, 99999);
expect(result).toEqual(false); expect(result).toEqual(false);
@ -17,14 +17,14 @@ describe('claim isEditable()', () => {
expect(result).toEqual(false); expect(result).toEqual(false);
}); });
it('should be able to edit a resolved claim for a salesAssistant', async() => { it('should be able to edit a resolved claim for a claimManager', async() => {
let ctx = {req: {accessToken: {userId: salesAssistantId}}}; let ctx = {req: {accessToken: {userId: claimManagerId}}};
let result = await app.models.Claim.isEditable(ctx, 4); let result = await app.models.Claim.isEditable(ctx, 4);
expect(result).toEqual(true); expect(result).toEqual(true);
}); });
it('should be able to edit a claim for a salesAssistant', async() => { it('should be able to edit a claim for a claimManager', async() => {
let ctx = {req: {accessToken: {userId: salesPerdonId}}}; let ctx = {req: {accessToken: {userId: salesPerdonId}}};
let result = await app.models.Claim.isEditable(ctx, 1); let result = await app.models.Claim.isEditable(ctx, 1);

View File

@ -42,21 +42,21 @@ describe('Update Claim', () => {
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); let newClaim = await app.models.Claim.create(originalData);
const correctState = 4; const canceledState = 4;
const salesPersonId = 18; const claimManagerId = 72;
const ctx = { const ctx = {
req: { req: {
accessToken: { accessToken: {
userId: salesPersonId userId: claimManagerId
} }
}, },
args: { args: {
observation: 'valid observation', observation: 'valid observation',
claimStateFk: correctState, claimStateFk: canceledState,
hasToPickUp: false hasToPickUp: false
} }
}; };
await app.models.Claim.updateClaim(ctx, newClaim.id,); await app.models.Claim.updateClaim(ctx, newClaim.id);
let updatedClaim = await app.models.Claim.findById(newClaim.id); let updatedClaim = await app.models.Claim.findById(newClaim.id);
@ -66,15 +66,15 @@ describe('Update Claim', () => {
await app.models.Claim.destroyById(newClaim.id); await app.models.Claim.destroyById(newClaim.id);
}); });
it('should change some sensible fields as salesAssistant', async() => { it('should change some sensible fields as claimManager', async() => {
let newClaim = await app.models.Claim.create(originalData); 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();
const salesAssistantId = 21; const claimManagerId = 72;
const ctx = { const ctx = {
req: { req: {
accessToken: {userId: salesAssistantId}, accessToken: {userId: claimManagerId},
headers: {origin: 'http://localhost'} headers: {origin: 'http://localhost'}
}, },
args: { args: {

View File

@ -60,9 +60,9 @@ module.exports = Self => {
if (args.claimStateFk) { if (args.claimStateFk) {
const canUpdate = await canChangeState(ctx, claim.claimStateFk); const canUpdate = await canChangeState(ctx, claim.claimStateFk);
const hasRights = await canChangeState(ctx, args.claimStateFk); const hasRights = await canChangeState(ctx, args.claimStateFk);
const isSalesAssistant = await models.Account.hasRole(userId, 'salesAssistant'); const isClaimManager = await models.Account.hasRole(userId, 'claimManager');
if (!canUpdate || !hasRights || changedHasToPickUp && !isSalesAssistant) if (!canUpdate || !hasRights || changedHasToPickUp && !isClaimManager)
throw new UserError(`You don't have enough privileges to change that field`); throw new UserError(`You don't have enough privileges to change that field`);
} }
delete args.ctx; delete args.ctx;

View File

@ -56,7 +56,8 @@
class="vn-mr-md" class="vn-mr-md"
label="Pick up" label="Pick up"
ng-model="$ctrl.claim.hasToPickUp" ng-model="$ctrl.claim.hasToPickUp"
vn-acl="salesAssistant"> vn-acl="claimManager"
info="When checked will notify to the salesPerson">
</vn-check> </vn-check>
</vn-horizontal> </vn-horizontal>
</vn-card> </vn-card>

View File

@ -5,7 +5,7 @@ import './style.scss';
class Controller extends Section { class Controller extends Section {
onSubmit() { onSubmit() {
this.$.watcher.submit().then(() => { this.$.watcher.submit().then(() => {
if (this.aclService.hasAny(['salesAssistant'])) if (this.aclService.hasAny(['claimManager']))
this.$state.go('claim.card.detail'); this.$state.go('claim.card.detail');
}); });
} }

View File

@ -5,3 +5,4 @@ Responsability: Responsabilidad
Company: Empresa Company: Empresa
Sales/Client: Comercial/Cliente Sales/Client: Comercial/Cliente
Pick up: Recoger Pick up: Recoger
When checked will notify a pickup to the salesPerson: Cuando se marque enviará una notificación de recogida al comercial

View File

@ -13,7 +13,7 @@
Send Pickup order Send Pickup order
</vn-item> </vn-item>
<vn-item <vn-item
vn-acl="salesAssistant" vn-acl="claimManager"
vn-acl-action="remove" vn-acl-action="remove"
ng-click="confirmDeleteClaim.show()" ng-click="confirmDeleteClaim.show()"
name="deleteClaim" name="deleteClaim"

View File

@ -77,7 +77,7 @@ class Controller extends Section {
this.$.model.refresh(); this.$.model.refresh();
this.vnApp.showSuccess(this.$t('Data saved!')); this.vnApp.showSuccess(this.$t('Data saved!'));
if (this.aclService.hasAny(['salesAssistant'])) if (this.aclService.hasAny(['claimManager']))
this.$state.go('claim.card.development'); this.$state.go('claim.card.development');
}); });
} }
@ -133,7 +133,7 @@ class Controller extends Section {
showEditPopover(event, saleClaimed) { showEditPopover(event, saleClaimed) {
if (this.isEditable) { if (this.isEditable) {
if (!this.aclService.hasAny(['salesAssistant'])) if (!this.aclService.hasAny(['claimManager']))
return this.vnApp.showError(this.$t('Insuficient permisos')); return this.vnApp.showError(this.$t('Insuficient permisos'));
this.saleClaimed = saleClaimed; this.saleClaimed = saleClaimed;

View File

@ -9,7 +9,7 @@ class Controller extends Section {
this.$.watcher.notifySaved(); this.$.watcher.notifySaved();
this.$.watcher.updateOriginalData(); this.$.watcher.updateOriginalData();
if (this.aclService.hasAny(['salesAssistant'])) if (this.aclService.hasAny(['claimManager']))
this.$state.go('claim.card.action'); this.$state.go('claim.card.action');
}); });
} }

View File

@ -70,7 +70,7 @@
"params": { "params": {
"claim": "$ctrl.claim" "claim": "$ctrl.claim"
}, },
"acl": ["salesAssistant"] "acl": ["claimManager"]
}, { }, {
"url": "/action", "url": "/action",
"state": "claim.card.action", "state": "claim.card.action",
@ -79,7 +79,7 @@
"params": { "params": {
"claim": "$ctrl.claim" "claim": "$ctrl.claim"
}, },
"acl": ["salesAssistant"] "acl": ["claimManager"]
}, { }, {
"url": "/photos", "url": "/photos",
"state": "claim.card.photos", "state": "claim.card.photos",

View File

@ -42,7 +42,7 @@
max="5" max="5"
min="1" min="1"
step="1" step="1"
vn-acl="salesAssistant"> vn-acl="claimManager">
</vn-range> </vn-range>
</vn-one> </vn-one>
<vn-auto> <vn-auto>

View File

@ -51,7 +51,7 @@ describe('Client', () => {
} }
}; };
const serializedParams = $httpParamSerializer({filter}); const serializedParams = $httpParamSerializer({filter});
$httpBackend.expect('GET', `ClientRisks?${serializedParams}`,).respond([{amount: 20}]); $httpBackend.expect('GET', `ClientRisks?${serializedParams}`).respond([{amount: 20}]);
controller.getAmountPaid(); controller.getAmountPaid();
$httpBackend.flush(); $httpBackend.flush();
@ -65,7 +65,7 @@ describe('Client', () => {
controller.$params = {id: 101}; controller.$params = {id: 101};
$httpBackend.expect('POST', `Receipts`,).respond({id: 1}); $httpBackend.expect('POST', `Receipts`).respond({id: 1});
controller.responseHandler('accept'); controller.responseHandler('accept');
$httpBackend.flush(); $httpBackend.flush();

View File

@ -7,7 +7,7 @@ export default class Client extends ModuleMain {
case 'search': case 'search':
return /^\d+$/.test(value) return /^\d+$/.test(value)
? {id: value} ? {id: value}
: {name: {like: `%${value}%`}}; : {or: [{name: {like: `%${value}%`}}, {socialName: {like: `%${value}%`}}]};
case 'phone': case 'phone':
return { return {
or: [ or: [

View File

@ -55,8 +55,8 @@
</span> </span>
</vn-td> </vn-td>
<vn-td number> <vn-td number>
<span ng-class="::{link: sale.isTicket}" <span class="link"
ng-click="$ctrl.showTicketDescriptor($event, sale)" ng-click="$ctrl.showDescriptor($event, sale)"
name="origin"> name="origin">
{{::sale.origin | dashIfEmpty}} {{::sale.origin | dashIfEmpty}}
</span> </span>
@ -94,3 +94,7 @@
<vn-client-descriptor-popover <vn-client-descriptor-popover
vn-id="clientDescriptor"> vn-id="clientDescriptor">
</vn-client-descriptor-popover> </vn-client-descriptor-popover>
<vn-entry-descriptor-popover
vn-id="entryDescriptor">
</vn-entry-descriptor-popover>

View File

@ -58,10 +58,12 @@ class Controller extends Section {
this.$anchorScroll(); this.$anchorScroll();
} }
showTicketDescriptor(event, sale) { showDescriptor(event, sale) {
if (!sale.isTicket) return; let descriptor = 'entryDescriptor';
if (sale.isTicket)
descriptor = 'ticketDescriptor';
this.$.ticketDescriptor.show(event.target, sale.origin); this.$[descriptor].show(event.target, sale.origin);
} }
} }

View File

@ -60,6 +60,34 @@ describe('Item', () => {
expect(controller.$anchorScroll).toHaveBeenCalledWith(); expect(controller.$anchorScroll).toHaveBeenCalledWith();
}); });
}); });
describe('showDescriptor ()', () => {
it('should call to the entryDescriptor show() method', () => {
controller.$.entryDescriptor = {};
controller.$.entryDescriptor.show = jest.fn();
const $event = new Event('click');
const target = document.createElement('div');
target.dispatchEvent($event);
const data = {id: 1, origin: 1};
controller.showDescriptor($event, data);
expect(controller.$.entryDescriptor.show).toHaveBeenCalledWith($event.target, data.origin);
});
it('should call to the ticketDescriptor show() method', () => {
controller.$.ticketDescriptor = {};
controller.$.ticketDescriptor.show = jest.fn();
const $event = new Event('click');
const target = document.createElement('div');
target.dispatchEvent($event);
const data = {id: 1, origin: 1, isTicket: true};
controller.showDescriptor($event, data);
expect(controller.$.ticketDescriptor.show).toHaveBeenCalledWith($event.target, data.origin);
});
});
}); });
}); });

View File

@ -3,7 +3,7 @@
"name": "Items", "name": "Items",
"icon": "icon-item", "icon": "icon-item",
"validations" : true, "validations" : true,
"dependencies": ["worker", "client", "ticket"], "dependencies": ["worker", "client", "ticket", "entry"],
"menus": { "menus": {
"main": [ "main": [
{"state": "item.index", "icon": "icon-item"}, {"state": "item.index", "icon": "icon-item"},

View File

@ -18,7 +18,7 @@ describe('Component vnOrderIndex', () => {
beforeEach(ngModule('order')); beforeEach(ngModule('order'));
beforeEach(inject(($componentController, _$window_,) => { beforeEach(inject(($componentController, _$window_) => {
$window = _$window_; $window = _$window_;
const $element = angular.element('<vn-order-index></vn-order-index>'); const $element = angular.element('<vn-order-index></vn-order-index>');
controller = $componentController('vnOrderIndex', {$element}); controller = $componentController('vnOrderIndex', {$element});

View File

@ -60,7 +60,11 @@ module.exports = Self => {
let where = buildFilter(ctx.args, (param, value) => { let where = buildFilter(ctx.args, (param, value) => {
switch (param) { switch (param) {
case 'search': case 'search':
return {'s.id': value}; return {or: [
{'s.id': value},
{'s.name': {like: `%${value}%`}},
{'s.nickname': {like: `%${value}%`}}
]};
case 'nickname': case 'nickname':
param = `s.${param}`; param = `s.${param}`;
return {[param]: {like: `%${value}%`}}; return {[param]: {like: `%${value}%`}};

View File

@ -0,0 +1,74 @@
module.exports = Self => {
Self.remoteMethod('getSummary', {
description: 'Returns the supplier summary',
accessType: 'READ',
accepts: {
arg: 'id',
type: 'number',
required: true,
description: 'The supplier id',
http: {source: 'path'}
},
returns: {
type: 'object',
root: true
},
http: {
path: `/:id/getSummary`,
verb: 'GET'
}
});
Self.getSummary = async id => {
let filter = {
where: {id: id},
fields: [
'id',
'name',
'nickname',
'isOfficial',
'isActive',
'note',
'nif',
'street',
'city',
'postCode',
'provinceFk',
'countryFk',
'payMethodFk',
'payDemFk',
'payDay',
'account',
'isFarmer',
],
include: [
{
relation: 'province',
scope: {
fields: ['id', 'name']
}
},
{
relation: 'country',
scope: {
fields: ['id', 'country', 'code']
}
},
{
relation: 'payMethod',
scope: {
fields: ['id', 'name']
}
},
{
relation: 'payDem',
scope: {
fields: ['id', 'payDem']
}
}
]
};
let supplier = await Self.app.models.Supplier.findOne(filter);
return supplier;
};
};

View File

@ -0,0 +1,28 @@
const app = require('vn-loopback/server/server');
describe('Supplier getSummary()', () => {
it('should return a summary object containing data from one supplier', async() => {
const supplier = await app.models.Supplier.getSummary(1);
expect(supplier.id).toEqual(1);
expect(supplier.name).toEqual('Plants SL');
expect(supplier.nif).toEqual('06089160W');
expect(supplier.account).toEqual(4000000001);
expect(supplier.payDay).toEqual(15);
});
it(`should return a summary object containing it's supplier country relation`, async() => {
const supplier = await app.models.Supplier.getSummary(1);
const country = supplier.country();
expect(country.id).toEqual(1);
expect(country.code).toEqual('ES');
});
it(`should return a summary object containing it's billing data relation`, async() => {
const supplier = await app.models.Supplier.getSummary(1);
const payMethod = supplier.payMethod();
expect(payMethod.name).toEqual('PayMethod one');
});
});

View File

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

View File

@ -0,0 +1,19 @@
{
"name": "PayDem",
"base": "VnModel",
"options": {
"mysql": {
"table": "payDem"
}
},
"properties": {
"id": {
"type": "Number",
"id": true,
"description": "Identifier"
},
"payDem": {
"type": "Number"
}
}
}

View File

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

View File

@ -48,6 +48,12 @@
"isActive": { "isActive": {
"type": "Boolean" "type": "Boolean"
}, },
"isOfficial": {
"type": "Boolean"
},
"note": {
"type": "String"
},
"street": { "street": {
"type": "String" "type": "String"
}, },
@ -66,10 +72,41 @@
"payDemFk": { "payDemFk": {
"type": "Number" "type": "Number"
}, },
"payDay": {
"type": "Number"
},
"nickname": { "nickname": {
"type": "String" "type": "String"
} }
}, },
"relations": {
"payMethod": {
"type": "belongsTo",
"model": "PayMethod",
"foreignKey": "payMethodFk"
},
"payDem": {
"type": "belongsTo",
"model": "PayDem",
"foreignKey": "payDemFk"
},
"province": {
"type": "belongsTo",
"model": "Province",
"foreignKey": "provinceFk"
},
"country": {
"type": "belongsTo",
"model": "Country",
"foreignKey": "countryFk"
},
"client": {
"type": "belongsTo",
"model": "Client",
"foreignKey": "nif",
"primaryKey": "fi"
}
},
"acls": [ "acls": [
{ {
"accessType": "READ", "accessType": "READ",

View File

@ -0,0 +1,5 @@
<vn-portal slot="menu">
<vn-supplier-descriptor supplier="$ctrl.supplier"></vn-entry-descriptor>
<vn-left-menu source="card"></vn-left-menu>
</vn-portal>
<ui-view></ui-view>

View File

@ -0,0 +1,48 @@
import ngModule from '../module';
import ModuleCard from 'salix/components/module-card';
class Controller extends ModuleCard {
reload() {
let filter = {
include: [
{
relation: 'province',
scope: {
fields: ['id', 'name']
}
},
{
relation: 'country',
scope: {
fields: ['id', 'name', 'code']
}
},
{
relation: 'payMethod',
scope: {
fields: ['id', 'name']
}
},
{
relation: 'payDem',
scope: {
fields: ['id', 'payDem']
}
},
{
relation: 'client',
scope: {
fields: ['id', 'fi']
}
}
]
};
this.$http.get(`Suppliers/${this.$params.id}`, {filter})
.then(response => this.supplier = response.data);
}
}
ngModule.vnComponent('vnSupplierCard', {
template: require('./index.html'),
controller: Controller
});

View File

@ -0,0 +1,57 @@
<vn-descriptor-content
module="supplier"
description="$ctrl.supplier.name">
<slot-body>
<div class="attributes">
<vn-label-value label="Tax number"
value="{{$ctrl.supplier.nif}}">
</vn-label-value>
<vn-label-value label="Alias"
value="{{$ctrl.supplier.nickname}}">
</vn-label-value>
<vn-label-value label="Pay method"
value="{{$ctrl.supplier.payMethod.name}}">
</vn-label-value>
<vn-label-value label="Payment deadline"
value="{{$ctrl.supplier.payDem.payDem}}">
</vn-label-value>
<vn-label-value label="Pay day"
value="{{$ctrl.supplier.payDay}}">
</vn-label-value>
<vn-label-value label="Account"
value="{{$ctrl.supplier.account}}">
</vn-label-value>
</div>
<div class="icons">
<vn-icon
vn-tooltip="Inactive supplier"
icon="icon-disabled"
ng-class="{bright: $ctrl.supplier.isActive == false}">
</vn-icon>
<vn-icon
vn-tooltip="Official supplier"
icon="icon-net"
ng-class="{bright: $ctrl.supplier.isOfficial == false}">
</vn-icon>
</div>
<div class="quicklinks">
<div ng-transclude="btnOne">
<vn-quick-link
tooltip="All entries with current supplier"
state="['entry.index', {q: $ctrl.entryFilter}]"
icon="icon-entry">
</vn-quick-link>
</div>
<div ng-transclude="btnTwo">
<vn-quick-link
ng-if="$ctrl.supplier.client.fi"
tooltip="Go to client"
state="['client.card.summary', {id: $ctrl.supplier.client.id}]"
icon="person">
</vn-quick-link>
</div>
<div ng-transclude="btnThree">
</div>
</div>
</slot-body>
</vn-descriptor-content>

View File

@ -0,0 +1,79 @@
import ngModule from '../module';
import Descriptor from 'salix/components/descriptor';
class Controller extends Descriptor {
get supplier() {
return this.entity;
}
set supplier(value) {
this.entity = value;
}
get entryFilter() {
if (!this.supplier) return null;
const date = new Date();
date.setHours(0, 0, 0, 0);
const from = new Date(date.getTime());
from.setDate(from.getDate() - 10);
const to = new Date(date.getTime());
to.setDate(to.getDate() + 10);
return JSON.stringify({
supplierFk: this.supplier.id,
from,
to
});
}
loadData() {
const filter = {
fields: [
'id',
'name',
'nickname',
'nif',
'payMethodFk',
'payDemFk',
'payDay',
'isActive',
'isOfficial',
'account'
],
include: [
{
relation: 'payMethod',
scope: {
fields: ['id', 'name']
}
},
{
relation: 'payDem',
scope: {
fields: ['id', 'payDem']
}
},
{
relation: 'client',
scope: {
fields: ['id', 'fi']
}
}
]
};
return this.getData(`Suppliers/${this.supplier.id}`, {filter})
.then(res => this.supplier = res.data);
}
}
ngModule.vnComponent('vnSupplierDescriptor', {
template: require('./index.html'),
controller: Controller,
bindings: {
supplier: '<'
}
});

View File

@ -0,0 +1,64 @@
import './index.js';
describe('Supplier Component vnSupplierDescriptor', () => {
let $httpBackend;
let controller;
let $httpParamSerializer;
const supplier = {id: 1};
beforeEach(ngModule('supplier'));
beforeEach(inject(($componentController, _$httpBackend_, _$httpParamSerializer_) => {
$httpBackend = _$httpBackend_;
$httpParamSerializer = _$httpParamSerializer_;
controller = $componentController('vnSupplierDescriptor', {$element: null}, {supplier});
}));
describe('loadData()', () => {
it('should perform ask for the supplier', () => {
const filter = {
fields: [
'id',
'name',
'nickname',
'nif',
'payMethodFk',
'payDemFk',
'payDay',
'isActive',
'isOfficial',
'account'
],
include: [
{
relation: 'payMethod',
scope: {
fields: ['id', 'name']
}
},
{
relation: 'payDem',
scope: {
fields: ['id', 'payDem']
}
},
{
relation: 'client',
scope: {
fields: ['id', 'fi']
}
}
]
};
const serializedParams = $httpParamSerializer({filter});
let query = `Suppliers/${controller.supplier.id}?${serializedParams}`;
jest.spyOn(controller, 'getData');
$httpBackend.expect('GET', query).respond({id: 1});
controller.loadData();
$httpBackend.flush();
expect(controller.getData).toHaveBeenCalledTimes(1);
});
});
});

View File

@ -0,0 +1,5 @@
Tax number: NIF / CIF
All entries with current supplier: Todas las entradas con el proveedor actual
Go to client: Ir al cliente
Official supplier: Proveedor oficial
Inactive supplier: Proveedor inactivo

View File

@ -4,3 +4,6 @@ import './main';
import './index/'; import './index/';
import './search-panel'; import './search-panel';
import './log'; import './log';
import './summary';
import './card';
import './descriptor';

View File

@ -8,7 +8,7 @@
<vn-searchbar <vn-searchbar
vn-focus vn-focus
panel="vn-supplier-search-panel" panel="vn-supplier-search-panel"
info="Search suppliers by id" info="Search suppliers by id, name or alias"
model="model"> model="model">
</vn-searchbar> </vn-searchbar>
</vn-portal> </vn-portal>

View File

@ -2,6 +2,7 @@
"module": "supplier", "module": "supplier",
"name": "Suppliers", "name": "Suppliers",
"icon" : "icon-supplier", "icon" : "icon-supplier",
"dependencies": ["client", "item"],
"validations" : true, "validations" : true,
"menus": { "menus": {
"main": [ "main": [
@ -27,6 +28,18 @@
"state": "supplier.card.log", "state": "supplier.card.log",
"component": "vn-supplier-log", "component": "vn-supplier-log",
"description": "Log" "description": "Log"
"url": "/:id",
"state": "supplier.card",
"abstract": true,
"component": "vn-supplier-card"
}, {
"url": "/summary",
"state": "supplier.card.summary",
"component": "vn-supplier-summary",
"description": "Summary",
"params": {
"supplier": "$ctrl.supplier"
}
} }
] ]
} }

View File

@ -5,7 +5,7 @@
vn-one vn-one
label="General search" label="General search"
ng-model="filter.search" ng-model="filter.search"
info="Search suppliers by id" info="Search suppliers by id, name or alias"
vn-focus> vn-focus>
</vn-textfield> </vn-textfield>
</vn-horizontal> </vn-horizontal>

View File

@ -1,3 +1,4 @@
Province: Provincia Province: Provincia
Country: País Country: País
Tax number: Nif Tax number: NIF / CIF
Search suppliers by id, name or alias: Busca proveedores por id, nombre o alias

View File

@ -0,0 +1,74 @@
<vn-card class="summary">
<h5>{{$ctrl.summary.name}} - {{$ctrl.summary.id}}</h5>
<vn-horizontal>
<vn-one>
<h4 translate>Basic data</h4>
<vn-label-value label="Id"
value="{{$ctrl.summary.id}}">
</vn-label-value>
<vn-label-value label="Alias"
value="{{$ctrl.summary.nickname}}">
</vn-label-value>
<vn-check
label="Is official"
ng-model="$ctrl.summary.isOfficial"
disabled="true">
</vn-check>
<vn-check
label="Is active"
ng-model="$ctrl.summary.isActive"
disabled="true">
</vn-check>
<vn-label-value label="Notes"
value="{{$ctrl.summary.note}}">
</vn-label-value>
</vn-one>
<vn-one>
<h4 translate>Fiscal address</h4>
<vn-label-value label="Social name"
value="{{$ctrl.summary.name}}">
</vn-label-value>
<vn-label-value label="Tax number"
value="{{$ctrl.summary.nif}}">
</vn-label-value>
<vn-label-value label="Street" ellipsize="false"
value="{{$ctrl.summary.street}}">
</vn-label-value>
<vn-label-value label="City"
value="{{$ctrl.summary.city}}">
</vn-label-value>
<vn-label-value label="Postcode"
value="{{$ctrl.summary.postCode}}">
</vn-label-value>
<vn-label-value label="Province"
value="{{$ctrl.summary.province.name}}">
</vn-label-value>
<vn-label-value label="Country"
value="{{$ctrl.summary.country.country}}">
</vn-label-value>
</vn-one>
<vn-one>
<h4 translate>Billing data</h4>
<vn-label-value label="Pay method"
value="{{$ctrl.summary.payMethod.name}}">
</vn-label-value>
<vn-label-value label="Payment deadline"
value="{{$ctrl.summary.payDem.payDem}}">
</vn-label-value>
<vn-label-value label="Pay day"
value="{{$ctrl.summary.payDay}}">
</vn-label-value>
<vn-label-value label="Account"
value="{{$ctrl.summary.account}}">
</vn-label-value>
<vn-check
label="Is Farmer"
ng-model="$ctrl.summary.isFarmer"
disabled="true">
</vn-check>
</vn-one>
</vn-horizontal>
</vn-card>
<vn-worker-descriptor-popover
vn-id="workerDescriptor">
</vn-worker-descriptor-popover>

View File

@ -0,0 +1,26 @@
import ngModule from '../module';
import Section from 'salix/components/section';
import './style.scss';
class Controller extends Section {
$onChanges() {
if (!this.supplier)
return;
this.getSummary();
}
getSummary() {
return this.$http.get(`Suppliers/${this.supplier.id}/getSummary`).then(response => {
this.summary = response.data;
});
}
}
ngModule.vnComponent('vnSupplierSummary', {
template: require('./index.html'),
controller: Controller,
bindings: {
supplier: '<'
}
});

View File

@ -0,0 +1,32 @@
import './index';
describe('Supplier', () => {
describe('Component vnSupplierSummary', () => {
let controller;
let $httpBackend;
let $scope;
beforeEach(ngModule('supplier'));
beforeEach(inject(($componentController, _$httpBackend_, $rootScope) => {
$httpBackend = _$httpBackend_;
$scope = $rootScope.$new();
const $element = angular.element('<vn-supplier-summary></vn-supplier-summary>');
controller = $componentController('vnSupplierSummary', {$element, $scope});
}));
describe('getSummary()', () => {
it('should perform a get asking for the supplier data', () => {
controller.supplier = {id: 1};
const query = `Suppliers/${controller.supplier.id}/getSummary`;
$httpBackend.expectGET(query).respond({id: 1});
controller.getSummary();
$httpBackend.flush();
expect(controller.summary).toEqual({id: 1});
});
});
});
});

View File

@ -0,0 +1,5 @@
Is official: Es oficial
Country: País
Tax number: NIF / CIF
Search suppliers by id, name or alias: Busca proveedores por id, nombre o alias
Is Farmer: Es agrícola

View File

@ -0,0 +1,7 @@
@import "variables";
vn-client-summary {
.alert span {
color: $color-alert
}
}

View File

@ -48,6 +48,12 @@
}, },
"zoneFk": { "zoneFk": {
"type": "Number" "type": "Number"
},
"zonePrice": {
"type": "Number"
},
"zoneBonus": {
"type": "Number"
} }
}, },
"relations": { "relations": {

View File

@ -15,6 +15,9 @@ class Controller extends ModuleCard {
relation: 'warehouse', relation: 'warehouse',
scope: {fields: ['name']} scope: {fields: ['name']}
}, },
{
relation: 'zone',
},
{ {
relation: 'invoiceOut', relation: 'invoiceOut',
scope: {fields: ['id']} scope: {fields: ['id']}

View File

@ -75,12 +75,36 @@
</div> </div>
</section> </section>
</div> </div>
<div class="totalBox align-left">
<h6 class="align-center" translate>Zone breakdown</h6>
<div> <vn-label translate>Price</vn-label> {{$ctrl.ticket.zonePrice | currency: 'EUR': 2}} </div>
<div> <vn-label translate>Bonus</vn-label> {{$ctrl.ticket.zoneBonus | currency: 'EUR': 2}} </div>
<div> <vn-label translate>Zone</vn-label>
<span
title="{{$ctrl.ticket.zone.name}}"
vn-click-stop="zoneDescriptor.show($event, $ctrl.ticket.zone.id)"
class="link">
{{$ctrl.ticket.zone.name | dashIfEmpty}}
</span>
</div>
<div ng-show="$ctrl.ticket.zone.isVolumetric">
<vn-label translate>Volume</vn-label> {{$ctrl.ticketVolume}}
</div>
<div ng-show="!$ctrl.ticket.zone.isVolumetric">
<vn-label translate>Packages</vn-label> {{$ctrl.ticket.packages}}
</div>
</div>
<div class="totalBox align-left"> <div class="totalBox align-left">
<h6 class="align-center" translate>Theorical cost</h6> <h6 class="align-center" translate>Theorical cost</h6>
<div> <vn-label translate>Price</vn-label> {{$ctrl.theoricalCost | currency: 'EUR': 2}} </div> <div class="total"> <vn-label translate>Price total</vn-label> {{$ctrl.theoricalCost | currency: 'EUR': 2}} </div>
</div> </div>
</vn-side-menu> </vn-side-menu>
<vn-item-descriptor-popover <vn-item-descriptor-popover
vn-id="descriptor" vn-id="descriptor"
warehouse-fk="$ctrl.ticket.warehouseFk"> warehouse-fk="$ctrl.ticket.warehouseFk">
</vn-item-descriptor-popover> </vn-item-descriptor-popover>
<vn-zone-descriptor-popover
vn-id="zoneDescriptor">
</vn-zone-descriptor-popover>

View File

@ -37,9 +37,10 @@ class Controller extends Section {
this._ticket = value; this._ticket = value;
if (!value) return; if (!value) return;
this.getTheoricalCost(); this.getTheoricalCost();
this.getComponentsSum(); this.getComponentsSum();
if (this.ticket.zone.isVolumetric)
this.getTicketVolume();
} }
base() { base() {
@ -76,6 +77,13 @@ class Controller extends Section {
this.$http.get(`Tickets/${this.ticket.id}/getComponentsSum`) this.$http.get(`Tickets/${this.ticket.id}/getComponentsSum`)
.then(res => this.componentsList = res.data); .then(res => this.componentsList = res.data);
} }
getTicketVolume() {
if (!this.ticket) return;
this.$http.get(`Tickets/${this.ticket.id}/getVolume`)
.then(res => this.ticketVolume = res.data[0].volume);
}
} }
ngModule.vnComponent('vnTicketComponents', { ngModule.vnComponent('vnTicketComponents', {

View File

@ -89,7 +89,10 @@ describe('ticket', () => {
jest.spyOn(controller, 'getComponentsSum'); jest.spyOn(controller, 'getComponentsSum');
controller._ticket = undefined; controller._ticket = undefined;
controller.ticket = { controller.ticket = {
id: 7 id: 7,
zone: {
isVolumetric: false
}
}; };
expect(controller.ticket).toBeDefined(); expect(controller.ticket).toBeDefined();

View File

@ -1,2 +1,6 @@
Theorical cost: Porte teorico Theorical cost: Porte teorico
Total without VAT: Total sin IVA Total without VAT: Total sin IVA
Bonus: Bonificación
Price: Precio
Price total: Precio total
Zone breakdown: Desglose zona

View File

@ -14,6 +14,7 @@
</vn-multi-check> </vn-multi-check>
</vn-th> </vn-th>
<vn-th></vn-th> <vn-th></vn-th>
<vn-th></vn-th>
<vn-th field="id" number>Id</vn-th> <vn-th field="id" number>Id</vn-th>
<vn-th field="salesPersonFk" class="expendable">Salesperson</vn-th> <vn-th field="salesPersonFk" class="expendable">Salesperson</vn-th>
<vn-th field="shipped">Date</vn-th> <vn-th field="shipped">Date</vn-th>
@ -40,27 +41,36 @@
</vn-td> </vn-td>
<vn-td shrink> <vn-td shrink>
<vn-icon <vn-icon
ng-show="ticket.hasTicketRequest" ng-show="ticket.isTaxDataChecked"
translate-attr="{title: 'Verified data'}"
class="bright"
icon="check">
</vn-icon>
</vn-td>
<vn-td shrink>
<vn-icon
ng-show="ticket.hasTicketRequest"
translate-attr="{title: 'Purchase request'}"
class="bright" class="bright"
vn-tooltip="Purchase request"
icon="icon-100"> icon="icon-100">
</vn-icon> </vn-icon>
<vn-icon <vn-icon
ng-show="ticket.isAvailable === 0" ng-show="ticket.isAvailable === 0"
translate-attr="{title: 'Not available'}"
class="bright" class="bright"
vn-tooltip="Not available" vn-tooltip="Not available"
icon="icon-unavailable"> icon="icon-unavailable">
</vn-icon> </vn-icon>
<vn-icon <vn-icon
ng-show="ticket.isFreezed" ng-show="ticket.isFreezed"
translate-attr="{title: 'Client frozen'}"
class="bright" class="bright"
vn-tooltip="Client frozen"
icon="icon-frozen"> icon="icon-frozen">
</vn-icon> </vn-icon>
<vn-icon <vn-icon
ng-show="ticket.risk" ng-show="ticket.risk"
title="{{::$ctrl.$t('Risk')}}: {{ticket.risk}}"
class="bright" class="bright"
vn-tooltip="{{::$ctrl.$t('Risk')}}: {{ticket.risk}}"
icon="icon-risk"> icon="icon-risk">
</vn-icon> </vn-icon>
</vn-td> </vn-td>

View File

@ -28,8 +28,12 @@
<vn-label-value label="Agency" <vn-label-value label="Agency"
value="{{$ctrl.summary.agencyMode.name}}"> value="{{$ctrl.summary.agencyMode.name}}">
</vn-label-value> </vn-label-value>
<vn-label-value label="Zone" <vn-label-value label="Zone">
value="{{$ctrl.summary.zone.name}}"> <span
ng-click="zoneDescriptor.show($event, $ctrl.summary.zoneFk)"
class="link">
{{$ctrl.summary.zone.name}}
</span>
</vn-label-value> </vn-label-value>
<vn-label-value label="Warehouse" <vn-label-value label="Warehouse"
value="{{$ctrl.summary.warehouse.name}}"> value="{{$ctrl.summary.warehouse.name}}">
@ -247,3 +251,6 @@
<vn-worker-descriptor-popover <vn-worker-descriptor-popover
vn-id="workerDescriptor"> vn-id="workerDescriptor">
</vn-worker-descriptor-popover> </vn-worker-descriptor-popover>
<vn-zone-descriptor-popover
vn-id="zoneDescriptor">
</vn-zone-descriptor-popover>

View File

@ -3,7 +3,7 @@
"name": "Travels", "name": "Travels",
"icon": "local_airport", "icon": "local_airport",
"validations": true, "validations": true,
"dependencies": ["worker"], "dependencies": ["worker", "entry"],
"menus": { "menus": {
"main": [ "main": [
{"state": "travel.index", "icon": "local_airport"} {"state": "travel.index", "icon": "local_airport"}

View File

@ -41,7 +41,7 @@
value="{{$ctrl.travelData.ref}}"> value="{{$ctrl.travelData.ref}}">
</vn-label-value> </vn-label-value>
<vn-label-value <vn-label-value
label="m3" label="m³"
value="{{$ctrl.travelData.m3}}"> value="{{$ctrl.travelData.m3}}">
</vn-label-value> </vn-label-value>
<vn-label-value <vn-label-value
@ -63,7 +63,7 @@
<vn-th shrink>Package</vn-th> <vn-th shrink>Package</vn-th>
<vn-th shrink>CC</vn-th> <vn-th shrink>CC</vn-th>
<vn-th shrink>Pallet</vn-th> <vn-th shrink>Pallet</vn-th>
<vn-th shrink>m3</vn-th> <vn-th shrink>m³</vn-th>
<vn-th shrink></vn-th> <vn-th shrink></vn-th>
</vn-tr> </vn-tr>
</vn-thead> </vn-thead>
@ -75,7 +75,12 @@
disabled="true"> disabled="true">
</vn-check> </vn-check>
</vn-td> </vn-td>
<vn-td shrink>{{entry.id}} </vn-td> <vn-td shrink>
<span class="link"
vn-click-stop="entryDescriptor.show($event, entry.id)">
{{entry.id}}
</span>
</vn-td>
<vn-td expand>{{entry.supplierName}}</vn-td> <vn-td expand>{{entry.supplierName}}</vn-td>
<vn-td shrink>{{entry.ref}}</vn-td> <vn-td shrink>{{entry.ref}}</vn-td>
<vn-td shrink>{{entry.hb}}</vn-td> <vn-td shrink>{{entry.hb}}</vn-td>
@ -142,3 +147,6 @@
</vn-auto> </vn-auto>
</vn-horizontal> </vn-horizontal>
</vn-card> </vn-card>
<vn-entry-descriptor-popover
vn-id="entryDescriptor">
</vn-entry-descriptor-popover>

View File

@ -9,7 +9,7 @@ Received: Recibida
Agency: Agencia Agency: Agencia
Entries: Entradas Entries: Entradas
Confirmed: Confirmada Confirmed: Confirmada
Entry Id: Entrada Id Entry Id: Id entrada
Supplier: Proveedor Supplier: Proveedor
Pallet: Pallet Pallet: Pallet
Freight: Porte Freight: Porte

View File

@ -73,7 +73,7 @@
value="{{::row.bonus | currency:'EUR':2}}"> value="{{::row.bonus | currency:'EUR':2}}">
</vn-label-value> </vn-label-value>
<vn-label-value <vn-label-value
label="Max m3" label="Max m³"
value="{{::row.m3Max}}"> value="{{::row.m3Max}}">
</vn-label-value> </vn-label-value>
</vn-item-section> </vn-item-section>
@ -166,7 +166,7 @@
</vn-input-number> </vn-input-number>
</vn-horizontal> </vn-horizontal>
<vn-input-number <vn-input-number
label="Max m3" label="Max m³"
ng-model="$ctrl.selected.m3Max" ng-model="$ctrl.selected.m3Max"
min="0" min="0"
step="0.01"> step="0.01">