Merge branch 'dev' into 3752-claim_action
gitea/salix/pipeline/head This commit looks good Details

This commit is contained in:
Carlos Jimenez Ruiz 2022-05-10 11:36:35 +00:00
commit 0e65f382f9
21 changed files with 180 additions and 31 deletions

View File

@ -0,0 +1 @@
Delete file

View File

@ -111,7 +111,7 @@ describe('Client lock verified data path', () => {
await page.waitToClick(selectors.clientFiscalData.saveButton); await page.waitToClick(selectors.clientFiscalData.saveButton);
const message = await page.waitForSnackbar(); const message = await page.waitForSnackbar();
expect(message.text).toContain(`You can't make changes on a client with verified data`); expect(message.text).toContain(`Not enough privileges to edit a client with verified data`);
}); });
}); });
@ -123,19 +123,19 @@ describe('Client lock verified data path', () => {
await page.accessToSection('client.card.fiscalData'); await page.accessToSection('client.card.fiscalData');
}, 20000); }, 20000);
it('should confirm verified data button is enabled for salesAssistant', async() => { it('should confirm verified data button is disabled for salesAssistant', async() => {
const isDisabled = await page.isDisabled(selectors.clientFiscalData.verifiedDataCheckbox); const isDisabled = await page.isDisabled(selectors.clientFiscalData.verifiedDataCheckbox);
expect(isDisabled).toBeFalsy(); expect(isDisabled).toBeTrue();
}); });
it('should now edit the social name', async() => { it('should return error when edit the social name', async() => {
await page.clearInput(selectors.clientFiscalData.socialName); await page.clearInput(selectors.clientFiscalData.socialName);
await page.write(selectors.clientFiscalData.socialName, 'new social name edition'); await page.write(selectors.clientFiscalData.socialName, 'new social name edition');
await page.waitToClick(selectors.clientFiscalData.saveButton); await page.waitToClick(selectors.clientFiscalData.saveButton);
const message = await page.waitForSnackbar(); const message = await page.waitForSnackbar();
expect(message.text).toContain('Data saved!'); expect(message.text).toContain(`Not enough privileges to edit a client with verified data`);
}); });
it('should now confirm the social name have been edited once and for all', async() => { it('should now confirm the social name have been edited once and for all', async() => {

View File

@ -12,7 +12,6 @@
"That payment method requires an IBAN": "That payment method requires an IBAN", "That payment method requires an IBAN": "That payment method requires an IBAN",
"That payment method requires a BIC": "That payment method requires a BIC", "That payment method requires a BIC": "That payment method requires a BIC",
"The default consignee can not be unchecked": "The default consignee can not be unchecked", "The default consignee can not be unchecked": "The default consignee can not be unchecked",
"You can't make changes on a client with verified data": "You can't make changes on a client with verified data",
"Enter an integer different to zero": "Enter an integer different to zero", "Enter an integer different to zero": "Enter an integer different to zero",
"Package cannot be blank": "Package cannot be blank", "Package cannot be blank": "Package cannot be blank",
"The new quantity should be smaller than the old one": "The new quantity should be smaller than the old one", "The new quantity should be smaller than the old one": "The new quantity should be smaller than the old one",
@ -123,5 +122,6 @@
"The type of business must be filled in basic data": "The type of business must be filled in basic data", "The type of business must be filled in basic data": "The type of business must be filled in basic data",
"The worker has hours recorded that day": "The worker has hours recorded that day", "The worker has hours recorded that day": "The worker has hours recorded that day",
"isWithoutNegatives": "isWithoutNegatives", "isWithoutNegatives": "isWithoutNegatives",
"routeFk": "routeFk" "routeFk": "routeFk",
"Not enough privileges to edit a client with verified data": "Not enough privileges to edit a client with verified data"
} }

View File

@ -50,7 +50,7 @@
"You don't have enough privileges to change that field": "No tienes permisos para cambiar ese campo", "You don't have enough privileges to change that field": "No tienes permisos para cambiar ese campo",
"Warehouse cannot be blank": "El almacén no puede quedar en blanco", "Warehouse cannot be blank": "El almacén no puede quedar en blanco",
"Agency cannot be blank": "La agencia no puede quedar en blanco", "Agency cannot be blank": "La agencia no puede quedar en blanco",
"You can't make changes on a client with verified data": "No puedes hacer cambios en un cliente con datos comprobados", "Not enough privileges to edit a client with verified data": "No tienes permisos para hacer cambios en un cliente con datos comprobados",
"This address doesn't exist": "Este consignatario no existe", "This address doesn't exist": "Este consignatario no existe",
"You must delete the claim id %d first": "Antes debes borrar la reclamación %d", "You must delete the claim id %d first": "Antes debes borrar la reclamación %d",
"You don't have enough privileges": "No tienes suficientes permisos", "You don't have enough privileges": "No tienes suficientes permisos",

View File

@ -1,6 +1,22 @@
const models = require('vn-loopback/server/server').models; const models = require('vn-loopback/server/server').models;
const LoopBackContext = require('loopback-context');
describe('Client addressesPropagateRe', () => { describe('Client addressesPropagateRe', () => {
beforeAll(async() => {
const activeCtx = {
accessToken: {userId: 9},
http: {
req: {
headers: {origin: 'http://localhost'}
}
}
};
spyOn(LoopBackContext, 'getCurrentContext').and.returnValue({
active: activeCtx
});
});
it('should propagate the isEqualizated on both addresses of Mr Wayne and set hasToInvoiceByAddress to false', async() => { it('should propagate the isEqualizated on both addresses of Mr Wayne and set hasToInvoiceByAddress to false', async() => {
const tx = await models.Client.beginTransaction({}); const tx = await models.Client.beginTransaction({});

View File

@ -1,4 +1,5 @@
const models = require('vn-loopback/server/server').models; const models = require('vn-loopback/server/server').models;
const LoopBackContext = require('loopback-context');
describe('Address createAddress', () => { describe('Address createAddress', () => {
const clientFk = 1101; const clientFk = 1101;
@ -6,6 +7,21 @@ describe('Address createAddress', () => {
const incotermsFk = 'FAS'; const incotermsFk = 'FAS';
const customAgentOneId = 1; const customAgentOneId = 1;
beforeAll(async() => {
const activeCtx = {
accessToken: {userId: 9},
http: {
req: {
headers: {origin: 'http://localhost'}
}
}
};
spyOn(LoopBackContext, 'getCurrentContext').and.returnValue({
active: activeCtx
});
});
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 tx = await models.Client.beginTransaction({}); const tx = await models.Client.beginTransaction({});

View File

@ -1,4 +1,5 @@
const models = require('vn-loopback/server/server').models; const models = require('vn-loopback/server/server').models;
const LoopBackContext = require('loopback-context');
describe('Client Create', () => { describe('Client Create', () => {
const newAccount = { const newAccount = {
@ -12,6 +13,21 @@ describe('Client Create', () => {
businessTypeFk: 'florist' businessTypeFk: 'florist'
}; };
beforeAll(async() => {
const activeCtx = {
accessToken: {userId: 9},
http: {
req: {
headers: {origin: 'http://localhost'}
}
}
};
spyOn(LoopBackContext, 'getCurrentContext').and.returnValue({
active: activeCtx
});
});
it(`should not find Deadpool as he's not created yet`, async() => { it(`should not find Deadpool as he's not created yet`, async() => {
const tx = await models.Client.beginTransaction({}); const tx = await models.Client.beginTransaction({});

View File

@ -1,4 +1,5 @@
const models = require('vn-loopback/server/server').models; const models = require('vn-loopback/server/server').models;
const LoopBackContext = require('loopback-context');
describe('Address updateAddress', () => { describe('Address updateAddress', () => {
const clientId = 1101; const clientId = 1101;
@ -13,6 +14,21 @@ describe('Address updateAddress', () => {
} }
}; };
beforeAll(async() => {
const activeCtx = {
accessToken: {userId: 9},
http: {
req: {
headers: {origin: 'http://localhost'}
}
}
};
spyOn(LoopBackContext, 'getCurrentContext').and.returnValue({
active: activeCtx
});
});
it('should throw the non uee member error if no incoterms is defined', async() => { it('should throw the non uee member error if no incoterms is defined', async() => {
const tx = await models.Client.beginTransaction({}); const tx = await models.Client.beginTransaction({});
@ -93,6 +109,7 @@ describe('Address updateAddress', () => {
try { try {
const options = {transaction: tx}; const options = {transaction: tx};
ctx.req.accessToken.userId = employeeId;
ctx.args = { ctx.args = {
isLogifloraAllowed: true isLogifloraAllowed: true
}; };

View File

@ -1,10 +1,25 @@
const models = require('vn-loopback/server/server').models; const models = require('vn-loopback/server/server').models;
const LoopBackContext = require('loopback-context');
describe('Client updateFiscalData', () => { describe('Client updateFiscalData', () => {
const clientId = 1101; const clientId = 1101;
const employeeId = 1; const employeeId = 1;
const salesAssistantId = 21; const salesAssistantId = 21;
const administrativeId = 5; const administrativeId = 5;
const activeCtx = {
accessToken: {userId: employeeId},
http: {
req: {
headers: {origin: 'http://localhost'}
}
}
};
beforeEach(() => {
spyOn(LoopBackContext, 'getCurrentContext').and.returnValue({
active: activeCtx
});
});
it('should return an error if the user is not salesAssistant and the isTaxDataChecked value is true', async() => { it('should return an error if the user is not salesAssistant and the isTaxDataChecked value is true', async() => {
const tx = await models.Client.beginTransaction({}); const tx = await models.Client.beginTransaction({});
@ -24,7 +39,7 @@ describe('Client updateFiscalData', () => {
error = e; error = e;
} }
expect(error.message).toEqual(`You can't make changes on a client with verified data`); expect(error.message).toEqual(`Not enough privileges to edit a client with verified data`);
}); });
it('should return an error if the salesAssistant did not fill the sage data before checking verified data', async() => { it('should return an error if the salesAssistant did not fill the sage data before checking verified data', async() => {

View File

@ -125,11 +125,11 @@ module.exports = Self => {
} }
try { try {
const isSalesAssistant = await models.Account.hasRole(userId, 'salesAssistant', myOptions); const isAdministrative = await models.Account.hasRole(userId, 'administrative', myOptions);
const client = await models.Client.findById(clientId, null, myOptions); const client = await models.Client.findById(clientId, null, myOptions);
if (!isSalesAssistant && client.isTaxDataChecked) if (!isAdministrative && client.isTaxDataChecked)
throw new UserError(`You can't make changes on a client with verified data`); throw new UserError(`Not enough privileges to edit a client with verified data`);
// Sage data validation // Sage data validation
const taxDataChecked = args.isTaxDataChecked; const taxDataChecked = args.isTaxDataChecked;

View File

@ -224,6 +224,34 @@ module.exports = Self => {
throw new UserError(`The type of business must be filled in basic data`); throw new UserError(`The type of business must be filled in basic data`);
}); });
Self.observe('before save', async ctx => {
const changes = ctx.data || ctx.instance;
const orgData = ctx.currentInstance;
const models = Self.app.models;
const loopBackContext = LoopBackContext.getCurrentContext();
const userId = loopBackContext.active.accessToken.userId;
const isAdministrative = await models.Account.hasRole(userId, 'administrative', ctx.options);
const isSalesAssistant = await models.Account.hasRole(userId, 'salesAssistant', ctx.options);
const hasChanges = orgData && changes;
const isTaxDataChecked = hasChanges && (changes.isTaxDataChecked || orgData.isTaxDataChecked);
const isTaxDataCheckedChanged = hasChanges && orgData.isTaxDataChecked != isTaxDataChecked;
const sageTaxType = hasChanges && (changes.sageTaxTypeFk || orgData.sageTaxTypeFk);
const sageTaxTypeChanged = hasChanges && orgData.sageTaxTypeFk != sageTaxType;
const sageTransactionType = hasChanges && (changes.sageTransactionTypeFk || orgData.sageTransactionTypeFk);
const sageTransactionTypeChanged = hasChanges && orgData.sageTransactionTypeFk != sageTransactionType;
const cantEditVerifiedData = isTaxDataCheckedChanged && !isAdministrative;
const cantChangeSageData = (sageTaxTypeChanged || sageTransactionTypeChanged) && !isSalesAssistant;
if (cantEditVerifiedData || cantChangeSageData)
throw new UserError(`You don't have enough privileges`);
});
Self.observe('before save', async function(ctx) { Self.observe('before save', async function(ctx) {
const changes = ctx.data || ctx.instance; const changes = ctx.data || ctx.instance;
const orgData = ctx.currentInstance; const orgData = ctx.currentInstance;
@ -237,10 +265,10 @@ module.exports = Self => {
const socialNameChanged = hasChanges const socialNameChanged = hasChanges
&& orgData.socialName != socialName; && orgData.socialName != socialName;
const dataCheckedChanged = hasChanges const isTaxDataCheckedChanged = hasChanges
&& orgData.isTaxDataChecked != isTaxDataChecked; && orgData.isTaxDataChecked != isTaxDataChecked;
if ((socialNameChanged || dataCheckedChanged) && !isAlpha(socialName)) if ((socialNameChanged || isTaxDataCheckedChanged) && !isAlpha(socialName))
throw new UserError(`The social name has an invalid format`); throw new UserError(`The social name has an invalid format`);
if (changes.salesPerson === null) { if (changes.salesPerson === null) {

View File

@ -1,20 +1,36 @@
const app = require('vn-loopback/server/server'); const models = require('vn-loopback/server/server').models;
const LoopBackContext = require('loopback-context');
describe('loopback model address', () => { describe('loopback model address', () => {
let createdAddressId; let createdAddressId;
const clientId = 1101; const clientId = 1101;
afterAll(async() => { const activeCtx = {
let client = await app.models.Client.findById(clientId); accessToken: {userId: 9},
http: {
req: {
headers: {origin: 'http://localhost'}
}
}
};
await app.models.Address.destroyById(createdAddressId); beforeEach(() => {
spyOn(LoopBackContext, 'getCurrentContext').and.returnValue({
active: activeCtx
});
});
afterAll(async() => {
const client = await models.Client.findById(clientId);
await models.Address.destroyById(createdAddressId);
await client.updateAttribute('isEqualizated', false); await client.updateAttribute('isEqualizated', false);
}); });
describe('observe()', () => { describe('observe()', () => {
it('should throw an error when deactivating a consignee if its the default address', async() => { it('should throw an error when deactivating a consignee if its the default address', async() => {
let error; let error;
let address = await app.models.Address.findById(1); const address = await models.Address.findById(1);
await address.updateAttribute('isActive', false) await address.updateAttribute('isActive', false)
.catch(e => { .catch(e => {
@ -27,13 +43,13 @@ describe('loopback model address', () => {
}); });
it('should set isEqualizated to true of a given Client to trigger any new address to have it', async() => { it('should set isEqualizated to true of a given Client to trigger any new address to have it', async() => {
let client = await app.models.Client.findById(clientId); const client = await models.Client.findById(clientId);
expect(client.isEqualizated).toBeFalsy(); expect(client.isEqualizated).toBeFalsy();
await client.updateAttribute('isEqualizated', true); await client.updateAttribute('isEqualizated', true);
let newAddress = await app.models.Address.create({ const newAddress = await models.Address.create({
clientFk: clientId, clientFk: clientId,
agencyModeFk: 5, agencyModeFk: 5,
city: 'here', city: 'here',

View File

@ -33,7 +33,7 @@
label="Social name" label="Social name"
ng-model="$ctrl.client.socialName" ng-model="$ctrl.client.socialName"
rule rule
info="You can use letters and spaces" info="Only letters, numbers and spaces can be used"
required="true"> required="true">
</vn-textfield> </vn-textfield>
<vn-textfield <vn-textfield
@ -182,7 +182,7 @@
vn-one vn-one
label="Verified data" label="Verified data"
ng-model="$ctrl.client.isTaxDataChecked" ng-model="$ctrl.client.isTaxDataChecked"
vn-acl="salesAssistant"> vn-acl="administrative">
</vn-check> </vn-check>
</vn-horizontal> </vn-horizontal>
</vn-card> </vn-card>

View File

@ -3,7 +3,7 @@ You changed the equalization tax: Has cambiado el recargo de equivalencia
Do you want to spread the change?: ¿Deseas propagar el cambio a sus consignatarios? Do you want to spread the change?: ¿Deseas propagar el cambio a sus consignatarios?
Frozen: Congelado Frozen: Congelado
In order to invoice, this field is not consulted, but the consignee's ET. When modifying this field if the invoice by address option is not checked, the change will be automatically propagated to all addresses, otherwise the user will be asked if he wants to propagate it or not.: Para facturar no se consulta este campo, sino el RE de consignatario. Al modificar este campo si no esta marcada la casilla Facturar por consignatario, se propagará automáticamente el cambio a todos los consignatarios, en caso contrario preguntará al usuario si quiere o no propagar. In order to invoice, this field is not consulted, but the consignee's ET. When modifying this field if the invoice by address option is not checked, the change will be automatically propagated to all addresses, otherwise the user will be asked if he wants to propagate it or not.: Para facturar no se consulta este campo, sino el RE de consignatario. Al modificar este campo si no esta marcada la casilla Facturar por consignatario, se propagará automáticamente el cambio a todos los consignatarios, en caso contrario preguntará al usuario si quiere o no propagar.
You can use letters and spaces: Se pueden utilizar letras y espacios Only letters, numbers and spaces can be used: Sólo se pueden usar letras, numeros y espacios
Found a client with this data: Se ha encontrado un cliente con estos datos Found a client with this data: Se ha encontrado un cliente con estos datos
Found a client with this phone or email: El cliente con id <a href="#!/client/{{clientId}}/summary" target="_blank">{{clientId}}</a> ya tiene este teléfono o email. <br/> ¿Quieres continuar? Found a client with this phone or email: El cliente con id <a href="#!/client/{{clientId}}/summary" target="_blank">{{clientId}}</a> ya tiene este teléfono o email. <br/> ¿Quieres continuar?
Sage tax type: Tipo de impuesto Sage Sage tax type: Tipo de impuesto Sage

View File

@ -1,6 +1,6 @@
<vn-crud-model <vn-crud-model
vn-id="buysModel" vn-id="buysModel"
url="Entries/{{$ctrl.$params.id}}/getBuys" url="Entries/{{$ctrl.entry.id}}/getBuys"
limit="5" limit="5"
data="buys" data="buys"
auto-load="true"> auto-load="true">

View File

@ -4,6 +4,9 @@ import Summary from 'salix/components/summary';
class Controller extends Summary { class Controller extends Summary {
get entry() { get entry() {
if (!this._entry)
return this.$params;
return this._entry; return this._entry;
} }

View File

@ -144,6 +144,7 @@ module.exports = Self => {
ii.isBooked, ii.isBooked,
ii.supplierRef, ii.supplierRef,
ii.docFk AS dmsFk, ii.docFk AS dmsFk,
dm.file,
ii.supplierFk, ii.supplierFk,
ii.expenceFkDeductible deductibleExpenseFk, ii.expenceFkDeductible deductibleExpenseFk,
s.name AS supplierName, s.name AS supplierName,
@ -156,7 +157,8 @@ module.exports = Self => {
LEFT JOIN duaInvoiceIn dii ON dii.invoiceInFk = ii.id LEFT JOIN duaInvoiceIn dii ON dii.invoiceInFk = ii.id
LEFT JOIN dua d ON d.id = dii.duaFk LEFT JOIN dua d ON d.id = dii.duaFk
LEFT JOIN awb ON awb.id = d.awbFk LEFT JOIN awb ON awb.id = d.awbFk
LEFT JOIN company co ON co.id = ii.companyFk` LEFT JOIN company co ON co.id = ii.companyFk
LEFT JOIN dms dm ON dm.id = ii.docFk`
); );
const sqlWhere = conn.makeWhere(filter.where); const sqlWhere = conn.makeWhere(filter.where);

View File

@ -13,7 +13,7 @@
<vn-th field="supplierRef">Supplier ref.</vn-th> <vn-th field="supplierRef">Supplier ref.</vn-th>
<vn-th field="serialNumber">Serial number</vn-th> <vn-th field="serialNumber">Serial number</vn-th>
<vn-th field="serial">Serial</vn-th> <vn-th field="serial">Serial</vn-th>
<vn-th field="account">Account</vn-th> <vn-th field="dmsFk">File</vn-th>
<vn-th field="issued" expand>Issued</vn-th> <vn-th field="issued" expand>Issued</vn-th>
<vn-th field="isBooked" center>Is booked</vn-th> <vn-th field="isBooked" center>Is booked</vn-th>
<vn-th field="awbCode" vn-tooltip="Air Waybill">AWB</vn-th> <vn-th field="awbCode" vn-tooltip="Air Waybill">AWB</vn-th>
@ -27,7 +27,7 @@
class="clickable vn-tr search-result" class="clickable vn-tr search-result"
ui-sref="invoiceIn.card.summary({id: {{::invoiceIn.id}}})"> ui-sref="invoiceIn.card.summary({id: {{::invoiceIn.id}}})">
<vn-td>{{::invoiceIn.id}}</vn-td> <vn-td>{{::invoiceIn.id}}</vn-td>
<vn-td> <vn-td expand>
<span <span
class="link" class="link"
vn-click-stop="supplierDescriptor.show($event, invoiceIn.supplierFk)"> vn-click-stop="supplierDescriptor.show($event, invoiceIn.supplierFk)">
@ -36,8 +36,13 @@
</vn-td> </vn-td>
<vn-td>{{::invoiceIn.supplierRef | dashIfEmpty}}</vn-td> <vn-td>{{::invoiceIn.supplierRef | dashIfEmpty}}</vn-td>
<vn-td>{{::invoiceIn.serialNumber}}</vn-td> <vn-td>{{::invoiceIn.serialNumber}}</vn-td>
<vn-td>{{::invoiceIn.serial}}</vn-td> <vn-td>{{::invoiceIn.serial}}</vn-td>
<vn-td>{{::invoiceIn.account}}</vn-td> <vn-td>
<span title="{{'Download file' | translate}}" class="link"
ng-click="$ctrl.downloadFile(invoiceIn.dmsFk)">
{{::invoiceIn.file}}
</span>
</vn-td>
<vn-td expand>{{::invoiceIn.issued | date:'dd/MM/yyyy' | dashIfEmpty}}</vn-td> <vn-td expand>{{::invoiceIn.issued | date:'dd/MM/yyyy' | dashIfEmpty}}</vn-td>
<vn-td center> <vn-td center>
<vn-check disabled="true" <vn-check disabled="true"

View File

@ -2,6 +2,11 @@ import ngModule from '../module';
import Section from 'salix/components/section'; import Section from 'salix/components/section';
export default class Controller extends Section { export default class Controller extends Section {
constructor($element, $, vnFile) {
super($element, $, vnFile);
this.vnFile = vnFile;
}
exprBuilder(param, value) { exprBuilder(param, value) {
switch (param) { switch (param) {
case 'issued': case 'issued':
@ -39,8 +44,14 @@ export default class Controller extends Section {
this.selectedInvoiceIn = invoiceIn; this.selectedInvoiceIn = invoiceIn;
this.$.summary.show(); this.$.summary.show();
} }
downloadFile(dmsId) {
this.vnFile.download(`api/dms/${dmsId}/downloadFile`);
}
} }
Controller.$inject = ['$element', '$scope', 'vnFile'];
ngModule.vnComponent('vnInvoiceInIndex', { ngModule.vnComponent('vnInvoiceInIndex', {
template: require('./index.html'), template: require('./index.html'),
controller: Controller controller: Controller

View File

@ -93,6 +93,9 @@ describe('InvoiceOut createManualInvoice()', () => {
it('should throw an error for a non-invoiceable client', async() => { it('should throw an error for a non-invoiceable client', async() => {
spyOn(models.InvoiceOut, 'createPdf').and.returnValue(new Promise(resolve => resolve(true))); spyOn(models.InvoiceOut, 'createPdf').and.returnValue(new Promise(resolve => resolve(true)));
spyOn(LoopBackContext, 'getCurrentContext').and.returnValue({
active: activeCtx
});
const tx = await models.InvoiceOut.beginTransaction({}); const tx = await models.InvoiceOut.beginTransaction({});
const options = {transaction: tx}; const options = {transaction: tx};

View File

@ -38,7 +38,7 @@
vn-focus vn-focus
label="Social name" label="Social name"
ng-model="$ctrl.supplier.name" ng-model="$ctrl.supplier.name"
info="You can use letters and spaces" info="Only letters, numbers and spaces can be used"
required="true" required="true"
rule> rule>
</vn-textfield> </vn-textfield>