Merge branch 'dev' of https://gitea.verdnatura.es/verdnatura/salix into dev
gitea/salix/dev This commit has test failures
Details
gitea/salix/dev This commit has test failures
Details
This commit is contained in:
commit
02569aeea4
|
@ -163,6 +163,10 @@ export default {
|
||||||
firstRiskLineBalance: 'vn-client-risk-index vn-tbody > vn-tr:nth-child(1) > vn-td:nth-child(8)'
|
firstRiskLineBalance: 'vn-client-risk-index vn-tbody > vn-tr:nth-child(1) > vn-td:nth-child(8)'
|
||||||
|
|
||||||
},
|
},
|
||||||
|
webPayment: {
|
||||||
|
confirmFirstPaymentButton: 'vn-client-web-payment vn-tr:nth-child(1) vn-icon-button[icon="done_all"]',
|
||||||
|
firstPaymentConfirmed: 'vn-client-web-payment vn-tr:nth-child(1) vn-icon[icon="check"]'
|
||||||
|
},
|
||||||
itemsIndex: {
|
itemsIndex: {
|
||||||
goBackToModuleIndexButton: `vn-ticket-descriptor a[href="#!/ticket/index"]`,
|
goBackToModuleIndexButton: `vn-ticket-descriptor a[href="#!/ticket/index"]`,
|
||||||
createItemButton: `${components.vnFloatButton}`,
|
createItemButton: `${components.vnFloatButton}`,
|
||||||
|
@ -269,7 +273,7 @@ export default {
|
||||||
sale: 'vn-ticket-summary [name="sales"] vn-table > div > vn-tbody > vn-tr',
|
sale: 'vn-ticket-summary [name="sales"] vn-table > div > vn-tbody > vn-tr',
|
||||||
firstSaleItemId: 'vn-ticket-summary [name="sales"] vn-table > div > vn-tbody > vn-tr > vn-td:nth-child(2) > span',
|
firstSaleItemId: 'vn-ticket-summary [name="sales"] vn-table > div > vn-tbody > vn-tr > vn-td:nth-child(2) > span',
|
||||||
popoverDiaryButton: 'vn-ticket-summary vn-item-descriptor-popover vn-item-descriptor vn-icon[icon="icon-transaction"]',
|
popoverDiaryButton: 'vn-ticket-summary vn-item-descriptor-popover vn-item-descriptor vn-icon[icon="icon-transaction"]',
|
||||||
firstSaleQuantity: 'vn-ticket-summary [name="sales"] vn-table > div > vn-tbody > vn-tr > vn-td:nth-child(4)',
|
firstSaleQuantity: 'vn-ticket-summary [name="sales"] vn-table > div > vn-tbody > vn-tr > vn-td:nth-child(3)',
|
||||||
firstSaleDiscount: 'vn-ticket-summary [name="sales"] vn-table > div > vn-tbody > vn-tr > vn-td:nth-child(6)'
|
firstSaleDiscount: 'vn-ticket-summary [name="sales"] vn-table > div > vn-tbody > vn-tr > vn-td:nth-child(6)'
|
||||||
},
|
},
|
||||||
ticketsIndex: {
|
ticketsIndex: {
|
||||||
|
@ -337,18 +341,18 @@ export default {
|
||||||
firstSaleQuantity: `vn-textfield[model="sale.quantity"]:nth-child(1) input`,
|
firstSaleQuantity: `vn-textfield[model="sale.quantity"]:nth-child(1) input`,
|
||||||
firstSaleQuantityClearInput: `vn-textfield[model="sale.quantity"] div.suffix > i`,
|
firstSaleQuantityClearInput: `vn-textfield[model="sale.quantity"] div.suffix > i`,
|
||||||
firstSaleID: 'vn-ticket-sale:nth-child(1) vn-td:nth-child(4) > span',
|
firstSaleID: 'vn-ticket-sale:nth-child(1) vn-td:nth-child(4) > span',
|
||||||
firstSalePrice: 'vn-ticket-sale:nth-child(1) vn-td:nth-child(7)',
|
firstSalePrice: 'vn-ticket-sale:nth-child(1) vn-tr:nth-child(1) > vn-td:nth-child(7) > span',
|
||||||
firstSalePriceInput: 'vn-ticket-sale:nth-child(1) > vn-vertical > vn-popover.edit.dialog-summary.ng-isolate-scope.vn-popover.shown > div > div.content > div > vn-textfield',
|
firstSalePriceInput: 'vn-ticket-sale:nth-child(1) > vn-vertical > vn-popover.edit.dialog-summary.ng-isolate-scope.vn-popover.shown > div > div.content > div > vn-textfield',
|
||||||
firstSaleDiscount: 'vn-ticket-sale:nth-child(1) vn-td:nth-child(8)',
|
firstSaleDiscount: 'vn-ticket-sale:nth-child(1) vn-td:nth-child(8) > span',
|
||||||
firstSaleDiscountInput: 'vn-ticket-sale:nth-child(1) vn-ticket-sale-edit-discount > div > vn-textfield > div > div > div.infix > input.ng-not-empty',
|
firstSaleDiscountInput: 'vn-ticket-sale:nth-child(1) vn-ticket-sale-edit-discount > div > vn-textfield > div > div > div.infix > input.ng-not-empty',
|
||||||
firstSaleImport: 'vn-ticket-sale:nth-child(1) vn-td:nth-child(9)',
|
firstSaleImport: 'vn-ticket-sale:nth-child(1) vn-td:nth-child(9)',
|
||||||
firstSaleReservedIcon: 'vn-ticket-sale vn-tr:nth-child(1) > vn-td:nth-child(2) > vn-icon:nth-child(3)',
|
firstSaleReservedIcon: 'vn-ticket-sale vn-tr:nth-child(1) > vn-td:nth-child(2) > vn-icon:nth-child(3)',
|
||||||
firstSaleColour: `vn-ticket-sale vn-tr:nth-child(1) vn-td:nth-child(5) section:nth-child(1)`,
|
firstSaleColour: `vn-ticket-sale vn-tr:nth-child(1) vn-td:nth-child(6) section:nth-child(1)`,
|
||||||
firstSaleLength: `vn-ticket-sale vn-tr:nth-child(1) vn-td:nth-child(5) section:nth-child(3)`,
|
firstSaleLength: `vn-ticket-sale vn-tr:nth-child(1) vn-td:nth-child(6) section:nth-child(3)`,
|
||||||
firstSaleCheckbox: `vn-ticket-sale vn-tr:nth-child(1) vn-check[field="sale.checked"] md-checkbox`,
|
firstSaleCheckbox: `vn-ticket-sale vn-tr:nth-child(1) vn-check[field="sale.checked"] md-checkbox`,
|
||||||
secondSaleClaimIcon: 'vn-ticket-sale > vn-vertical > vn-card > div > vn-vertical > vn-table > div > vn-tbody > vn-tr:nth-child(2) > vn-td:nth-child(2) > a > vn-icon',
|
secondSaleClaimIcon: 'vn-ticket-sale > vn-vertical > vn-card > div > vn-vertical > vn-table > div > vn-tbody > vn-tr:nth-child(2) > vn-td:nth-child(2) > a > vn-icon',
|
||||||
secondSaleColour: `vn-ticket-sale vn-tr:nth-child(2) vn-td:nth-child(5) section:nth-child(5)`,
|
secondSaleColour: `vn-ticket-sale vn-tr:nth-child(2) vn-td:nth-child(6) section:nth-child(5)`,
|
||||||
secondSalePrice: `vn-ticket-sale vn-tr:nth-child(2) vn-td:nth-child(7)`,
|
secondSalePrice: `vn-ticket-sale vn-tr:nth-child(2) vn-td:nth-child(7) > span`,
|
||||||
secondSaleDiscount: `vn-ticket-sale vn-tr:nth-child(2) vn-td:nth-child(8)`,
|
secondSaleDiscount: `vn-ticket-sale vn-tr:nth-child(2) vn-td:nth-child(8)`,
|
||||||
secondSaleImport: `vn-ticket-sale vn-tr:nth-child(2) vn-td:nth-child(9)`,
|
secondSaleImport: `vn-ticket-sale vn-tr:nth-child(2) vn-td:nth-child(9)`,
|
||||||
secondSaleText: `vn-table div > vn-tbody > vn-tr:nth-child(2)`,
|
secondSaleText: `vn-table div > vn-tbody > vn-tr:nth-child(2)`,
|
||||||
|
|
|
@ -0,0 +1,40 @@
|
||||||
|
import selectors from '../../helpers/selectors.js';
|
||||||
|
import createNightmare from '../../helpers/nightmare';
|
||||||
|
|
||||||
|
// #860 E2E client/client/src/web-payment/index.js missing fixtures for another client
|
||||||
|
xdescribe('Client web Payment', () => {
|
||||||
|
const nightmare = createNightmare();
|
||||||
|
|
||||||
|
describe('as employee', () => {
|
||||||
|
beforeAll(() => {
|
||||||
|
nightmare
|
||||||
|
.loginAndModule('employee', 'client')
|
||||||
|
.accessToSearchResult('Bruce Wayne')
|
||||||
|
.accessToSection('client.card.webPayment');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should not be able to confirm payments', async() => {
|
||||||
|
let exists = await nightmare
|
||||||
|
.exists(selectors.webPayment.confirmFirstPaymentButton);
|
||||||
|
|
||||||
|
expect(exists).toBeFalsy();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('as administrative', () => {
|
||||||
|
beforeAll(() => {
|
||||||
|
nightmare
|
||||||
|
.loginAndModule('administrative', 'client')
|
||||||
|
.accessToSearchResult('Bruce Wayne')
|
||||||
|
.accessToSection('client.card.webPayment');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should be able to confirm payments', async() => {
|
||||||
|
let exists = await nightmare
|
||||||
|
.waitToClick(selectors.webPayment.confirmFirstPaymentButton)
|
||||||
|
.exists(selectors.webPayment.firstPaymentConfirmed);
|
||||||
|
|
||||||
|
expect(exists).toBeTruthy();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
|
@ -0,0 +1,61 @@
|
||||||
|
import createNightmare from '../../helpers/nightmare';
|
||||||
|
import config from '../../helpers/config.js';
|
||||||
|
|
||||||
|
|
||||||
|
describe('Login path', () => {
|
||||||
|
const nightmare = createNightmare();
|
||||||
|
|
||||||
|
it('should receive an error when the username is incorrect', async() => {
|
||||||
|
const username = 'nobody';
|
||||||
|
const password = 'nightmare';
|
||||||
|
|
||||||
|
const result = await nightmare
|
||||||
|
.goto(`${config.url}/#!/login`)
|
||||||
|
.wait(`vn-login input[name=user]`)
|
||||||
|
.write(`vn-login input[name=user]`, username)
|
||||||
|
.write(`vn-login input[name=password]`, password)
|
||||||
|
.click(`vn-login input[type=submit]`)
|
||||||
|
.waitForLastSnackbar();
|
||||||
|
|
||||||
|
expect(result.length).toBeGreaterThan(0);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should receive an error when the username is blank', async() => {
|
||||||
|
const password = 'nightmare';
|
||||||
|
|
||||||
|
const result = await nightmare
|
||||||
|
.clearInput(`vn-login input[name=user]`)
|
||||||
|
.write(`vn-login input[name=password]`, password)
|
||||||
|
.click(`vn-login input[type=submit]`)
|
||||||
|
.waitForLastSnackbar();
|
||||||
|
|
||||||
|
expect(result.length).toBeGreaterThan(0);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should receive an error when the password is incorrect', async() => {
|
||||||
|
const username = 'employee';
|
||||||
|
const password = 'badpassword';
|
||||||
|
|
||||||
|
const result = await nightmare
|
||||||
|
.write(`vn-login input[name=user]`, username)
|
||||||
|
.write(`vn-login input[name=password]`, password)
|
||||||
|
.click(`vn-login input[type=submit]`)
|
||||||
|
.waitForLastSnackbar();
|
||||||
|
|
||||||
|
expect(result.length).toBeGreaterThan(0);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should log in', async() => {
|
||||||
|
const username = 'employee';
|
||||||
|
const password = 'nightmare';
|
||||||
|
|
||||||
|
const url = await nightmare
|
||||||
|
.write(`vn-login input[name=user]`, username)
|
||||||
|
.write(`vn-login input[name=password]`, password)
|
||||||
|
.click(`vn-login input[type=submit]`)
|
||||||
|
.wait('#logout')
|
||||||
|
.parsedUrl();
|
||||||
|
|
||||||
|
expect(url.hash).toEqual('#!/');
|
||||||
|
});
|
||||||
|
});
|
|
@ -538,16 +538,16 @@ xdescribe('Ticket Edit sale path', () => {
|
||||||
describe('when state is preparation and loged as Production', () => {
|
describe('when state is preparation and loged as Production', () => {
|
||||||
it(`should not be able to edit the sale price`, async() => {
|
it(`should not be able to edit the sale price`, async() => {
|
||||||
const result = await nightmare
|
const result = await nightmare
|
||||||
.waitToClick(selectors.ticketSales.firstSalePrice)
|
.wait(selectors.ticketSales.firstSaleID)
|
||||||
.exists(selectors.ticketSales.firstSalePriceInput);
|
.exists(selectors.ticketSales.firstSalePrice);
|
||||||
|
|
||||||
expect(result).toBeFalsy();
|
expect(result).toBeFalsy();
|
||||||
});
|
});
|
||||||
|
|
||||||
it(`should not be able to edit the sale discount`, async() => {
|
it(`should not be able to edit the sale discount`, async() => {
|
||||||
const result = await nightmare
|
const result = await nightmare
|
||||||
.waitToClick(selectors.ticketSales.firstSaleDiscount)
|
.waitToClick(selectors.ticketSales.firstSaleID)
|
||||||
.exists(selectors.ticketSales.firstSaleDiscountInput);
|
.exists(selectors.ticketSales.firstSaleDiscount);
|
||||||
|
|
||||||
expect(result).toBeFalsy();
|
expect(result).toBeFalsy();
|
||||||
});
|
});
|
||||||
|
|
|
@ -22,12 +22,12 @@ module.exports = function(Self) {
|
||||||
oldInstance = await fkToValue(oldInstanceFk, ctx);
|
oldInstance = await fkToValue(oldInstanceFk, ctx);
|
||||||
if (ctx.where && !ctx.currentInstance) {
|
if (ctx.where && !ctx.currentInstance) {
|
||||||
let fields = Object.keys(ctx.data);
|
let fields = Object.keys(ctx.data);
|
||||||
ctx.oldInstances = await Self.modelBuilder.models[ctx.Model.definition.name].find({where: ctx.where, fields: fields});
|
ctx.oldInstances = await ctx.Model.app.models[ctx.Model.definition.name].find({where: ctx.where, fields: fields});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (ctx.isNewInstance) {
|
if (ctx.isNewInstance)
|
||||||
newInstance = await fkToValue(ctx.instance.__data, ctx);
|
newInstance = await fkToValue(ctx.instance.__data, ctx);
|
||||||
}
|
|
||||||
ctx.hookState.oldInstance = oldInstance;
|
ctx.hookState.oldInstance = oldInstance;
|
||||||
ctx.hookState.newInstance = newInstance;
|
ctx.hookState.newInstance = newInstance;
|
||||||
});
|
});
|
||||||
|
@ -36,7 +36,7 @@ module.exports = function(Self) {
|
||||||
if (ctx.where) {
|
if (ctx.where) {
|
||||||
let affectedModel = ctx.Model.definition.name;
|
let affectedModel = ctx.Model.definition.name;
|
||||||
let definition = ctx.Model.definition;
|
let definition = ctx.Model.definition;
|
||||||
let deletedInstances = await Self.modelBuilder.models[affectedModel].find({where: ctx.where});
|
let deletedInstances = await ctx.Model.app.models[affectedModel].find({where: ctx.where});
|
||||||
let relation = definition.settings.log.relation;
|
let relation = definition.settings.log.relation;
|
||||||
|
|
||||||
if (relation) {
|
if (relation) {
|
||||||
|
@ -81,12 +81,11 @@ module.exports = function(Self) {
|
||||||
};
|
};
|
||||||
|
|
||||||
let transaction = {};
|
let transaction = {};
|
||||||
if (ctx.options && ctx.options.transaction) {
|
if (ctx.options && ctx.options.transaction)
|
||||||
transaction = ctx.options.transaction;
|
transaction = ctx.options.transaction;
|
||||||
}
|
|
||||||
|
|
||||||
let logModel = definition.settings.log.model;
|
let logModel = definition.settings.log.model;
|
||||||
await Self.modelBuilder.models[logModel].create(logRecord, transaction);
|
await ctx.Model.app.models[logModel].create(logRecord, transaction);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -147,25 +146,23 @@ module.exports = function(Self) {
|
||||||
if (changedModelValue && (!ctx.instance || !ctx.instance[changedModelValue])) {
|
if (changedModelValue && (!ctx.instance || !ctx.instance[changedModelValue])) {
|
||||||
var where = [];
|
var where = [];
|
||||||
changedModelId = [];
|
changedModelId = [];
|
||||||
let changedInstances = await Self.modelBuilder.models[definition.name].find({where: ctx.where, fields: ['id', changedModelValue]});
|
let changedInstances = await ctx.Model.app.models[definition.name].find({where: ctx.where, fields: ['id', changedModelValue]});
|
||||||
changedInstances.forEach(element => {
|
changedInstances.forEach(element => {
|
||||||
where.push(element[changedModelValue]);
|
where.push(element[changedModelValue]);
|
||||||
changedModelId.push(element.id);
|
changedModelId.push(element.id);
|
||||||
});
|
});
|
||||||
} else if (ctx.hookState.oldInstance) {
|
} else if (ctx.hookState.oldInstance)
|
||||||
where = ctx.instance[changedModelValue];
|
where = ctx.instance[changedModelValue];
|
||||||
}
|
|
||||||
|
|
||||||
// Set oldInstance, newInstance, userFk and action
|
// Set oldInstance, newInstance, userFk and action
|
||||||
let oldInstance = {};
|
let oldInstance = {};
|
||||||
if (ctx.hookState.oldInstance) {
|
if (ctx.hookState.oldInstance)
|
||||||
Object.assign(oldInstance, ctx.hookState.oldInstance);
|
Object.assign(oldInstance, ctx.hookState.oldInstance);
|
||||||
}
|
|
||||||
|
|
||||||
let newInstance = {};
|
let newInstance = {};
|
||||||
if (ctx.hookState.newInstance) {
|
if (ctx.hookState.newInstance)
|
||||||
Object.assign(newInstance, ctx.hookState.newInstance);
|
Object.assign(newInstance, ctx.hookState.newInstance);
|
||||||
}
|
|
||||||
|
|
||||||
let userFk;
|
let userFk;
|
||||||
if (loopBackContext)
|
if (loopBackContext)
|
||||||
|
@ -189,17 +186,16 @@ module.exports = function(Self) {
|
||||||
let logModel = definition.settings.log.model;
|
let logModel = definition.settings.log.model;
|
||||||
|
|
||||||
let transaction = {};
|
let transaction = {};
|
||||||
if (ctx.options && ctx.options.transaction) {
|
if (ctx.options && ctx.options.transaction)
|
||||||
transaction = ctx.options.transaction;
|
transaction = ctx.options.transaction;
|
||||||
}
|
|
||||||
|
|
||||||
await Self.modelBuilder.models[logModel].create(logsToSave, transaction);
|
await ctx.Model.app.models[logModel].create(logsToSave, transaction);
|
||||||
}
|
}
|
||||||
|
|
||||||
// this function retuns all the instances changed in case this is an updateAll
|
// this function retuns all the instances changed in case this is an updateAll
|
||||||
function setLogsToSave(changedInstances, changedInstancesIds, logRecord, ctx) {
|
function setLogsToSave(changedInstances, changedInstancesIds, logRecord, ctx) {
|
||||||
let promises = [];
|
let promises = [];
|
||||||
if (changedInstances && typeof changedInstances == "object") {
|
if (changedInstances && typeof changedInstances == 'object') {
|
||||||
for (let i = 0; i < changedInstances.length; i++) {
|
for (let i = 0; i < changedInstances.length; i++) {
|
||||||
logRecord.changedModelId = changedInstancesIds[i];
|
logRecord.changedModelId = changedInstancesIds[i];
|
||||||
logRecord.changedModelValue = changedInstances[i];
|
logRecord.changedModelValue = changedInstances[i];
|
||||||
|
@ -207,9 +203,9 @@ module.exports = function(Self) {
|
||||||
logRecord.oldInstance = ctx.oldInstances[i];
|
logRecord.oldInstance = ctx.oldInstances[i];
|
||||||
promises.push(JSON.parse(JSON.stringify(logRecord)));
|
promises.push(JSON.parse(JSON.stringify(logRecord)));
|
||||||
}
|
}
|
||||||
} else {
|
} else
|
||||||
return logRecord;
|
return logRecord;
|
||||||
}
|
|
||||||
return promises;
|
return promises;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -217,11 +213,11 @@ module.exports = function(Self) {
|
||||||
let oldInstance = ctx.hookState.oldInstance;
|
let oldInstance = ctx.hookState.oldInstance;
|
||||||
let newInstance = ctx.hookState.newInstance;
|
let newInstance = ctx.hookState.newInstance;
|
||||||
|
|
||||||
if (oldInstance && newInstance) {
|
if (oldInstance && newInstance)
|
||||||
return 'update';
|
return 'update';
|
||||||
} else if (!oldInstance && newInstance) {
|
else if (!oldInstance && newInstance)
|
||||||
return 'insert';
|
return 'insert';
|
||||||
}
|
|
||||||
return 'delete';
|
return 'delete';
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
|
@ -67,5 +67,6 @@
|
||||||
"You can't create a ticket for a inactive client": "No puedes crear un ticket para un cliente inactivo",
|
"You can't create a ticket for a inactive client": "No puedes crear un ticket para un cliente inactivo",
|
||||||
"Tag value cannot be blank": "El valor del tag no puede quedar en blanco",
|
"Tag value cannot be blank": "El valor del tag no puede quedar en blanco",
|
||||||
"ORDER_EMPTY": "Cesta vacía",
|
"ORDER_EMPTY": "Cesta vacía",
|
||||||
"You don't have enough privileges to do that": "No tienes permisos para cambiar esto"
|
"You don't have enough privileges to do that": "No tienes permisos para cambiar esto",
|
||||||
|
"You can't create a ticket for a client that has a debt": "No puedes crear un ticket para un client con deuda"
|
||||||
}
|
}
|
|
@ -40,7 +40,7 @@
|
||||||
<vn-table model="model">
|
<vn-table model="model">
|
||||||
<vn-thead>
|
<vn-thead>
|
||||||
<vn-tr>
|
<vn-tr>
|
||||||
<vn-th number>Id</vn-th>
|
<vn-th number>Item</vn-th>
|
||||||
<vn-th>Landed</vn-th>
|
<vn-th>Landed</vn-th>
|
||||||
<vn-th number>Quantity</vn-th>
|
<vn-th number>Quantity</vn-th>
|
||||||
<vn-th number>Claimed</vn-th>
|
<vn-th number>Claimed</vn-th>
|
||||||
|
@ -107,7 +107,8 @@
|
||||||
<vn-table model="model">
|
<vn-table model="model">
|
||||||
<vn-thead>
|
<vn-thead>
|
||||||
<vn-tr>
|
<vn-tr>
|
||||||
<vn-th number>Id</vn-th>
|
<vn-th number>Item</vn-th>
|
||||||
|
<vn-th number>Ticket</vn-th>
|
||||||
<vn-th>Destination</vn-th>
|
<vn-th>Destination</vn-th>
|
||||||
<vn-th number>Landed</vn-th>
|
<vn-th number>Landed</vn-th>
|
||||||
<vn-th number>Quantity</vn-th>
|
<vn-th number>Quantity</vn-th>
|
||||||
|
@ -126,7 +127,13 @@
|
||||||
{{action.sale.itemFk | zeroFill:6}}
|
{{action.sale.itemFk | zeroFill:6}}
|
||||||
</span>
|
</span>
|
||||||
</vn-td>
|
</vn-td>
|
||||||
<vn-td number>{{action.sale.id}}</vn-td>
|
<vn-td number>
|
||||||
|
<span
|
||||||
|
ng-click="$ctrl.showTicketDescriptor($event, action.sale.ticket.id)"
|
||||||
|
class="link">
|
||||||
|
{{action.sale.ticket.id | zeroFill:6}}
|
||||||
|
</span>
|
||||||
|
</vn-td>
|
||||||
<vn-td expand>{{action.claimBeggining.description}}</vn-td>
|
<vn-td expand>{{action.claimBeggining.description}}</vn-td>
|
||||||
<vn-td number>{{action.sale.ticket.landed | dateTime: 'dd/MM/yyyy'}}</vn-td>
|
<vn-td number>{{action.sale.ticket.landed | dateTime: 'dd/MM/yyyy'}}</vn-td>
|
||||||
<vn-td number>{{action.sale.quantity}}</vn-td>
|
<vn-td number>{{action.sale.quantity}}</vn-td>
|
||||||
|
@ -150,4 +157,7 @@
|
||||||
<vn-worker-descriptor-popover
|
<vn-worker-descriptor-popover
|
||||||
vn-id="workerDescriptor"
|
vn-id="workerDescriptor"
|
||||||
user-id="$ctrl.selectedWorker">
|
user-id="$ctrl.selectedWorker">
|
||||||
</vn-worker-descriptor-popover>
|
</vn-worker-descriptor-popover>
|
||||||
|
<vn-ticket-descriptor-popover
|
||||||
|
vn-id="ticketDescriptor">
|
||||||
|
</vn-ticket-descriptor-popover>
|
|
@ -33,12 +33,16 @@ class Controller {
|
||||||
}
|
}
|
||||||
|
|
||||||
showWorkerDescriptor(event, userId) {
|
showWorkerDescriptor(event, userId) {
|
||||||
event.preventDefault();
|
|
||||||
event.stopImmediatePropagation();
|
|
||||||
this.selectedWorker = userId;
|
this.selectedWorker = userId;
|
||||||
this.$.workerDescriptor.parent = event.target;
|
this.$.workerDescriptor.parent = event.target;
|
||||||
this.$.workerDescriptor.show();
|
this.$.workerDescriptor.show();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
showTicketDescriptor(event, ticketId) {
|
||||||
|
this.$.ticketDescriptor.ticketFk = ticketId;
|
||||||
|
this.$.ticketDescriptor.parent = event.target;
|
||||||
|
this.$.ticketDescriptor.show();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Controller.$inject = ['$scope', '$http'];
|
Controller.$inject = ['$scope', '$http'];
|
||||||
|
|
|
@ -1,12 +1,12 @@
|
||||||
const app = require('vn-loopback/server/server');
|
const app = require('vn-loopback/server/server');
|
||||||
|
|
||||||
describe('Client card', () => {
|
describe('Client get', () => {
|
||||||
it('should call the card() method to receive a formated card of Bruce Wayne', async() => {
|
it('should call the card() method to receive a formated card of Bruce Wayne', async() => {
|
||||||
let id = 101;
|
let id = 101;
|
||||||
let result = await app.models.Client.getCard(id);
|
let result = await app.models.Client.getCard(id);
|
||||||
|
|
||||||
expect(result.id).toEqual(101);
|
expect(result.id).toEqual(101);
|
||||||
expect(result.name).toEqual('Bruce Wayne');
|
expect(result.name).toEqual('Bruce Wayne');
|
||||||
expect(result.debt).toEqual(579.1);
|
expect(result.debt).toEqual(595.81);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
|
@ -17,7 +17,7 @@ describe('client summary()', () => {
|
||||||
it('should return a summary object containing debt', async() => {
|
it('should return a summary object containing debt', async() => {
|
||||||
let result = await app.models.Client.summary(101);
|
let result = await app.models.Client.summary(101);
|
||||||
|
|
||||||
expect(result.debt.debt).toEqual(579.1);
|
expect(result.debt.debt).toEqual(595.81);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should return a summary object containing averageInvoiced', async() => {
|
it('should return a summary object containing averageInvoiced', async() => {
|
||||||
|
|
|
@ -97,7 +97,7 @@
|
||||||
</a>
|
</a>
|
||||||
</vn-tbody>
|
</vn-tbody>
|
||||||
</vn-table>
|
</vn-table>
|
||||||
<vn-card margin-medium-v>
|
</vn-card>
|
||||||
<vn-pagination model="model"></vn-pagination>
|
<vn-pagination model="model"></vn-pagination>
|
||||||
</div>
|
</div>
|
||||||
<a ui-sref="item.create" vn-tooltip="New item" vn-bind="+" fixed-bottom-right>
|
<a ui-sref="item.create" vn-tooltip="New item" vn-bind="+" fixed-bottom-right>
|
||||||
|
|
|
@ -25,23 +25,17 @@ module.exports = Self => {
|
||||||
Self.filter = async filter => {
|
Self.filter = async filter => {
|
||||||
const stmt = new ParameterizedSQL(
|
const stmt = new ParameterizedSQL(
|
||||||
`SELECT
|
`SELECT
|
||||||
e.id,
|
e.id, e.ticketFk, e.isBox,
|
||||||
e.ticketFk,
|
i1.name namePackage, e.counter,
|
||||||
e.isBox,
|
e.checked, i2.name nameBox,
|
||||||
i1.name namePackage,
|
e.itemFk, u.nickname userNickname,
|
||||||
e.counter,
|
u.id userId, e.created, e.externalId
|
||||||
e.checked,
|
|
||||||
i2.name nameBox,
|
|
||||||
e.itemFk,
|
|
||||||
u.nickname userNickname,
|
|
||||||
u.id userId,
|
|
||||||
e.created
|
|
||||||
FROM
|
FROM
|
||||||
vn.expedition e
|
vn.expedition e
|
||||||
LEFT JOIN vn.item i2 ON i2.id = e.itemFk
|
LEFT JOIN vn.item i2 ON i2.id = e.itemFk
|
||||||
INNER JOIN vn.item i1 ON i1.id = e.isBox
|
INNER JOIN vn.item i1 ON i1.id = e.isBox
|
||||||
LEFT JOIN vn.worker w ON w.id = e.workerFk
|
LEFT JOIN vn.worker w ON w.id = e.workerFk
|
||||||
JOIN account.user u ON u.id = w.id
|
JOIN account.user u ON u.id = w.userFk
|
||||||
`);
|
`);
|
||||||
stmt.merge(Self.buildSuffix(filter, 'e'));
|
stmt.merge(Self.buildSuffix(filter, 'e'));
|
||||||
|
|
||||||
|
|
|
@ -3,21 +3,21 @@ const app = require('vn-loopback/server/server');
|
||||||
describe('ticket deleted()', () => {
|
describe('ticket deleted()', () => {
|
||||||
let ticket;
|
let ticket;
|
||||||
|
|
||||||
beforeAll(async () => {
|
beforeAll(async() => {
|
||||||
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);
|
||||||
});
|
});
|
||||||
|
|
||||||
afterAll(async () => {
|
afterAll(async() => {
|
||||||
await app.models.Ticket.destroyById(ticket.id);
|
await app.models.Ticket.destroyById(ticket.id);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should make sure the ticket is not deleted yet', async () => {
|
it('should make sure the ticket is not deleted yet', async() => {
|
||||||
expect(ticket.isDeleted).toEqual(false);
|
expect(ticket.isDeleted).toEqual(false);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should set a ticket to deleted and log the change on TicketState table', async () => {
|
it('should set a ticket to deleted and log the change on TicketState table', async() => {
|
||||||
let ctx = {req: {accessToken: {userId: 9}}};
|
let ctx = {req: {accessToken: {userId: 9}}};
|
||||||
let params = {id: ticket.id};
|
let params = {id: ticket.id};
|
||||||
await app.models.Ticket.deleted(ctx, params);
|
await app.models.Ticket.deleted(ctx, params);
|
||||||
|
@ -29,7 +29,7 @@ describe('ticket deleted()', () => {
|
||||||
expect(changedState.stateFk).toEqual(17);
|
expect(changedState.stateFk).toEqual(17);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should throw an error if the given ticket has a claim', async () => {
|
it('should throw an error if the given ticket has a claim', async() => {
|
||||||
let ctx = {req: {accessToken: {userId: 9}}};
|
let ctx = {req: {accessToken: {userId: 9}}};
|
||||||
let params = {id: 16};
|
let params = {id: 16};
|
||||||
let error;
|
let error;
|
||||||
|
|
|
@ -4,6 +4,6 @@ describe('ticket getTaxes()', () => {
|
||||||
it('should return the tax of a given ticket', async() => {
|
it('should return the tax of a given ticket', async() => {
|
||||||
let result = await app.models.Ticket.getTaxes(1);
|
let result = await app.models.Ticket.getTaxes(1);
|
||||||
|
|
||||||
expect(result[0].tax).toEqual(7.44);
|
expect(result[0].tax).toEqual(7.64);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
|
@ -4,7 +4,7 @@ describe('ticket getTotal()', () => {
|
||||||
it('should return the total of a ticket', async() => {
|
it('should return the total of a ticket', async() => {
|
||||||
let result = await app.models.Ticket.getTotal(1);
|
let result = await app.models.Ticket.getTotal(1);
|
||||||
|
|
||||||
expect(result).toEqual(155.89);
|
expect(result).toEqual(158.09);
|
||||||
});
|
});
|
||||||
|
|
||||||
it(`should return zero if the ticket doesn't have lines`, async() => {
|
it(`should return zero if the ticket doesn't have lines`, async() => {
|
||||||
|
|
|
@ -4,7 +4,7 @@ describe('ticket getVAT()', () => {
|
||||||
it('should call the getVAT method and return the response', async() => {
|
it('should call the getVAT method and return the response', async() => {
|
||||||
await app.models.Ticket.getVAT(1)
|
await app.models.Ticket.getVAT(1)
|
||||||
.then(response => {
|
.then(response => {
|
||||||
expect(response).toEqual(20.29);
|
expect(response).toEqual(20.49);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,15 @@
|
||||||
|
const app = require('vn-loopback/server/server');
|
||||||
|
|
||||||
|
describe('ticket subtotal()', () => {
|
||||||
|
it('should return the subtotal of a ticket', async() => {
|
||||||
|
let result = await app.models.Ticket.subtotal(1);
|
||||||
|
|
||||||
|
expect(result).toEqual(137.60);
|
||||||
|
});
|
||||||
|
|
||||||
|
it(`should return zero if the ticket doesn't have lines`, async() => {
|
||||||
|
let result = await app.models.Ticket.subtotal(13);
|
||||||
|
|
||||||
|
expect(result).toEqual(0.00);
|
||||||
|
});
|
||||||
|
});
|
|
@ -14,21 +14,21 @@ describe('ticket summary()', () => {
|
||||||
expect(result.sales.length).toEqual(4);
|
expect(result.sales.length).toEqual(4);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should return a summary object containing subTotal for 1 ticket', async() => {
|
it('should return a summary object containing subtotal for 1 ticket', async() => {
|
||||||
let result = await app.models.Ticket.summary(1);
|
let result = await app.models.Ticket.summary(1);
|
||||||
|
|
||||||
expect(Math.round(result.subTotal * 100) / 100).toEqual(135.60);
|
expect(Math.round(result.subtotal * 100) / 100).toEqual(137.60);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should return a summary object containing VAT for 1 ticket', async() => {
|
it('should return a summary object containing VAT for 1 ticket', async() => {
|
||||||
let result = await app.models.Ticket.summary(1);
|
let result = await app.models.Ticket.summary(1);
|
||||||
|
|
||||||
expect(Math.round(result.VAT * 100) / 100).toEqual(20.29);
|
expect(Math.round(result.vat * 100) / 100).toEqual(20.49);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should return a summary object containing total for 1 ticket', async() => {
|
it('should return a summary object containing total for 1 ticket', async() => {
|
||||||
let result = await app.models.Ticket.summary(1);
|
let result = await app.models.Ticket.summary(1);
|
||||||
let total = result.subTotal + result.VAT;
|
let total = result.subtotal + result.vat;
|
||||||
let expectedTotal = Math.round(total * 100) / 100;
|
let expectedTotal = Math.round(total * 100) / 100;
|
||||||
|
|
||||||
expect(result.total).toEqual(expectedTotal);
|
expect(result.total).toEqual(expectedTotal);
|
||||||
|
|
|
@ -0,0 +1,40 @@
|
||||||
|
module.exports = Self => {
|
||||||
|
Self.remoteMethod('subtotal', {
|
||||||
|
description: 'Returns the total of a ticket',
|
||||||
|
accessType: 'READ',
|
||||||
|
accepts: [{
|
||||||
|
arg: 'id',
|
||||||
|
type: 'number',
|
||||||
|
required: true,
|
||||||
|
description: 'ticket id',
|
||||||
|
http: {source: 'path'}
|
||||||
|
}],
|
||||||
|
returns: {
|
||||||
|
type: 'number',
|
||||||
|
root: true
|
||||||
|
},
|
||||||
|
http: {
|
||||||
|
path: `/:id/subtotal`,
|
||||||
|
verb: 'GET'
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
Self.subtotal = async ticketFk => {
|
||||||
|
const sale = Self.app.models.Sale;
|
||||||
|
const ticketSales = await sale.find({where: {ticketFk}});
|
||||||
|
const ticketService = Self.app.models.TicketService;
|
||||||
|
const ticketServices = await ticketService.find({where: {ticketFk}});
|
||||||
|
|
||||||
|
let subtotal = 0.00;
|
||||||
|
ticketSales.forEach(sale => {
|
||||||
|
subtotal += sale.price * sale.quantity * ((100 - sale.discount) / 100);
|
||||||
|
});
|
||||||
|
|
||||||
|
ticketServices.forEach(service => {
|
||||||
|
subtotal += service.price * service.quantity;
|
||||||
|
});
|
||||||
|
|
||||||
|
|
||||||
|
return Math.round(subtotal * 100) / 100;
|
||||||
|
};
|
||||||
|
};
|
|
@ -23,9 +23,9 @@ module.exports = Self => {
|
||||||
let models = Self.app.models;
|
let models = Self.app.models;
|
||||||
let summaryObj = await getTicketData(Self, ticketFk);
|
let summaryObj = await getTicketData(Self, ticketFk);
|
||||||
summaryObj.sales = await getSales(models.Sale, ticketFk);
|
summaryObj.sales = await getSales(models.Sale, ticketFk);
|
||||||
summaryObj.subTotal = getSubTotal(summaryObj.sales);
|
summaryObj.subtotal = await models.Ticket.subtotal(ticketFk);
|
||||||
summaryObj.VAT = await models.Ticket.getVAT(ticketFk);
|
summaryObj.vat = await models.Ticket.getVAT(ticketFk);
|
||||||
summaryObj.total = await models.Ticket.getTotal(ticketFk);
|
summaryObj.total = summaryObj.subtotal + summaryObj.vat;
|
||||||
summaryObj.packagings = await models.TicketPackaging.find({
|
summaryObj.packagings = await models.TicketPackaging.find({
|
||||||
where: {ticketFk: ticketFk},
|
where: {ticketFk: ticketFk},
|
||||||
include: [{relation: 'packaging',
|
include: [{relation: 'packaging',
|
||||||
|
@ -135,27 +135,18 @@ module.exports = Self => {
|
||||||
relation: 'user'
|
relation: 'user'
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
}, {
|
||||||
{
|
|
||||||
relation: 'atender',
|
relation: 'atender',
|
||||||
scope: {
|
scope: {
|
||||||
include: {
|
include: {
|
||||||
relation: 'user'
|
relation: 'user'
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}, {
|
||||||
|
relation: 'sale'
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
};
|
};
|
||||||
return await Self.app.models.TicketRequest.find(filter);
|
return await Self.app.models.TicketRequest.find(filter);
|
||||||
}
|
}
|
||||||
|
|
||||||
function getSubTotal(sales) {
|
|
||||||
let subTotal = 0.00;
|
|
||||||
|
|
||||||
sales.forEach(sale => {
|
|
||||||
subTotal += sale.quantity * sale.price;
|
|
||||||
});
|
|
||||||
|
|
||||||
return subTotal;
|
|
||||||
}
|
|
||||||
};
|
};
|
||||||
|
|
|
@ -5,6 +5,7 @@ module.exports = Self => {
|
||||||
require('../methods/ticket/summary')(Self);
|
require('../methods/ticket/summary')(Self);
|
||||||
require('../methods/ticket/getTotal')(Self);
|
require('../methods/ticket/getTotal')(Self);
|
||||||
require('../methods/ticket/getTaxes')(Self);
|
require('../methods/ticket/getTaxes')(Self);
|
||||||
|
require('../methods/ticket/subtotal')(Self);
|
||||||
require('../methods/ticket/componentUpdate')(Self);
|
require('../methods/ticket/componentUpdate')(Self);
|
||||||
require('../methods/ticket/new')(Self);
|
require('../methods/ticket/new')(Self);
|
||||||
require('../methods/ticket/isEditable')(Self);
|
require('../methods/ticket/isEditable')(Self);
|
||||||
|
|
|
@ -1,6 +1,9 @@
|
||||||
{
|
{
|
||||||
"name": "Ticket",
|
"name": "Ticket",
|
||||||
"base": "VnModel",
|
"base": "Loggable",
|
||||||
|
"log": {
|
||||||
|
"model":"TicketLog"
|
||||||
|
},
|
||||||
"options": {
|
"options": {
|
||||||
"mysql": {
|
"mysql": {
|
||||||
"table": "ticket"
|
"table": "ticket"
|
||||||
|
|
|
@ -13,6 +13,8 @@
|
||||||
<vn-thead>
|
<vn-thead>
|
||||||
<vn-tr>
|
<vn-tr>
|
||||||
<vn-th></vn-th>
|
<vn-th></vn-th>
|
||||||
|
<vn-th field="itemFk" number>Expedition</vn-th>
|
||||||
|
<vn-th field="itemFk" number>Envialia</vn-th>
|
||||||
<vn-th field="itemFk" number>Item</vn-th>
|
<vn-th field="itemFk" number>Item</vn-th>
|
||||||
<vn-th field="name">Name</vn-th>
|
<vn-th field="name">Name</vn-th>
|
||||||
<vn-th field="isBox">Package type</vn-th>
|
<vn-th field="isBox">Package type</vn-th>
|
||||||
|
@ -30,6 +32,8 @@
|
||||||
vn-tooltip="Delete expedition">
|
vn-tooltip="Delete expedition">
|
||||||
</vn-icon-button>
|
</vn-icon-button>
|
||||||
</vn-td>
|
</vn-td>
|
||||||
|
<vn-td number>{{expedition.id | zeroFill:6}}</vn-td>
|
||||||
|
<vn-td number>{{expedition.externalId | zeroFill:6}}</vn-td>
|
||||||
<vn-td number>
|
<vn-td number>
|
||||||
<span
|
<span
|
||||||
ng-class="{link: expedition.itemFk}"
|
ng-class="{link: expedition.itemFk}"
|
||||||
|
@ -41,7 +45,6 @@
|
||||||
<vn-td>{{::expedition.nameBox}}</vn-td>
|
<vn-td>{{::expedition.nameBox}}</vn-td>
|
||||||
<vn-td number>{{::expedition.counter}}</vn-td>
|
<vn-td number>{{::expedition.counter}}</vn-td>
|
||||||
<vn-td number>{{::expedition.checked}}</vn-td>
|
<vn-td number>{{::expedition.checked}}</vn-td>
|
||||||
<vn-td>{{::expedition.userNickname}}</vn-td>
|
|
||||||
<vn-td expand>
|
<vn-td expand>
|
||||||
<span
|
<span
|
||||||
class="link"
|
class="link"
|
||||||
|
|
|
@ -54,7 +54,7 @@
|
||||||
<vn-td number>
|
<vn-td number>
|
||||||
<span
|
<span
|
||||||
ng-show="::request.saleFk"
|
ng-show="::request.saleFk"
|
||||||
ng-click="$ctrl.showItemDescriptor($event, request.sale)"
|
ng-click="$ctrl.showItemDescriptor($event, request.sale.itemFk)"
|
||||||
class="link">
|
class="link">
|
||||||
{{request.saleFk | zeroFill:6}}
|
{{request.saleFk | zeroFill:6}}
|
||||||
</span>
|
</span>
|
||||||
|
|
|
@ -44,18 +44,18 @@ class Controller {
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
showItemDescriptor(event, sale) {
|
showItemDescriptor(event, itemFk) {
|
||||||
this.quicklinks = {
|
this.quicklinks = {
|
||||||
btnThree: {
|
btnThree: {
|
||||||
icon: 'icon-transaction',
|
icon: 'icon-transaction',
|
||||||
state: `item.card.diary({
|
state: `item.card.diary({
|
||||||
id: ${sale.itemFk},
|
id: ${itemFk},
|
||||||
ticketFk: ${this.$stateParams.id}
|
ticketFk: ${this.$stateParams.id}
|
||||||
})`,
|
})`,
|
||||||
tooltip: 'Item diary'
|
tooltip: 'Item diary'
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
this.$.itemDescriptor.itemFk = sale.itemFk;
|
this.$.itemDescriptor.itemFk = itemFk;
|
||||||
this.$.itemDescriptor.parent = event.target;
|
this.$.itemDescriptor.parent = event.target;
|
||||||
this.$.itemDescriptor.show();
|
this.$.itemDescriptor.show();
|
||||||
}
|
}
|
||||||
|
|
|
@ -46,7 +46,7 @@
|
||||||
</vn-button>
|
</vn-button>
|
||||||
</vn-tool-bar>
|
</vn-tool-bar>
|
||||||
<vn-one class="taxes" ng-if="$ctrl.sales.length > 0">
|
<vn-one class="taxes" ng-if="$ctrl.sales.length > 0">
|
||||||
<p><vn-label translate>Subtotal</vn-label> {{$ctrl.subTotal | currency: 'EUR':2}}</p>
|
<p><vn-label translate>Subtotal</vn-label> {{$ctrl.subtotal | currency: 'EUR':2}}</p>
|
||||||
<p><vn-label translate>VAT</vn-label> {{$ctrl.VAT | currency: 'EUR':2}}</p>
|
<p><vn-label translate>VAT</vn-label> {{$ctrl.VAT | currency: 'EUR':2}}</p>
|
||||||
<p><vn-label><strong>Total</strong></vn-label> <strong>{{$ctrl.total | currency: 'EUR':2}}</strong></p>
|
<p><vn-label><strong>Total</strong></vn-label> <strong>{{$ctrl.total | currency: 'EUR':2}}</strong></p>
|
||||||
</vn-one>
|
</vn-one>
|
||||||
|
@ -62,8 +62,8 @@
|
||||||
<vn-th shrink></vn-th>
|
<vn-th shrink></vn-th>
|
||||||
<vn-th shrink></vn-th>
|
<vn-th shrink></vn-th>
|
||||||
<vn-th number>Id</vn-th>
|
<vn-th number>Id</vn-th>
|
||||||
|
<vn-th>Quantity</vn-th>
|
||||||
<vn-th>Item</vn-th>
|
<vn-th>Item</vn-th>
|
||||||
<vn-th number>Quantity</vn-th>
|
|
||||||
<vn-th number>Price</vn-th>
|
<vn-th number>Price</vn-th>
|
||||||
<vn-th number>Disc</vn-th>
|
<vn-th number>Disc</vn-th>
|
||||||
<vn-th number>Amount</vn-th>
|
<vn-th number>Amount</vn-th>
|
||||||
|
@ -94,7 +94,7 @@
|
||||||
icon="icon-reserve"
|
icon="icon-reserve"
|
||||||
vn-tooltip="{{::$ctrl.$translate.instant('Reserved')}}"></vn-icon>
|
vn-tooltip="{{::$ctrl.$translate.instant('Reserved')}}"></vn-icon>
|
||||||
</vn-td>
|
</vn-td>
|
||||||
<vn-td shrink>
|
<vn-td shrink>
|
||||||
<img
|
<img
|
||||||
ng-src="{{::$ctrl.imagesPath}}/50x50/{{::sale.image}}"
|
ng-src="{{::$ctrl.imagesPath}}/50x50/{{::sale.image}}"
|
||||||
zoom-image="{{::$ctrl.imagesPath}}/1600x900/{{::sale.image}}"
|
zoom-image="{{::$ctrl.imagesPath}}/1600x900/{{::sale.image}}"
|
||||||
|
@ -107,13 +107,6 @@
|
||||||
{{::sale.itemFk | zeroFill:6}}
|
{{::sale.itemFk | zeroFill:6}}
|
||||||
</span>
|
</span>
|
||||||
</vn-td>
|
</vn-td>
|
||||||
<vn-td expand>
|
|
||||||
<vn-fetched-tags
|
|
||||||
max-length="6"
|
|
||||||
item="::sale.tags"
|
|
||||||
title="::sale.concept">
|
|
||||||
</vn-fetched-tags>
|
|
||||||
</vn-td>
|
|
||||||
<vn-td ng-if="!$ctrl.isEditable" number>{{sale.quantity}}</vn-td>
|
<vn-td ng-if="!$ctrl.isEditable" number>{{sale.quantity}}</vn-td>
|
||||||
<vn-td ng-if="$ctrl.isEditable" number>
|
<vn-td ng-if="$ctrl.isEditable" number>
|
||||||
<vn-textfield
|
<vn-textfield
|
||||||
|
@ -122,22 +115,29 @@
|
||||||
type="text">
|
type="text">
|
||||||
</vn-textfield>
|
</vn-textfield>
|
||||||
</vn-td>
|
</vn-td>
|
||||||
<vn-td number
|
<vn-td expand>
|
||||||
ng-if="$ctrl.isEditable"
|
<vn-fetched-tags
|
||||||
class="link"
|
max-length="6"
|
||||||
ng-click="$ctrl.showEditPricePopover($event, sale)"
|
item="::sale.tags"
|
||||||
vn-tooltip="Edit price">
|
title="::sale.concept">
|
||||||
{{sale.price | currency: 'EUR':2}}
|
</vn-fetched-tags>
|
||||||
|
</vn-td>
|
||||||
|
<vn-td number ng-if="$ctrl.isEditable">
|
||||||
|
<span class="link"
|
||||||
|
vn-tooltip="Edit price"
|
||||||
|
ng-click="$ctrl.showEditPricePopover($event, sale)">
|
||||||
|
{{sale.price | currency: 'EUR':2}}
|
||||||
|
</span>
|
||||||
</vn-td>
|
</vn-td>
|
||||||
<vn-td number ng-if="!$ctrl.isEditable">
|
<vn-td number ng-if="!$ctrl.isEditable">
|
||||||
{{sale.price | currency: 'EUR':2}}
|
{{sale.price | currency: 'EUR':2}}
|
||||||
</vn-td>
|
</vn-td>
|
||||||
<vn-td number
|
<vn-td number ng-if="$ctrl.isEditable">
|
||||||
ng-if="$ctrl.isEditable"
|
<span class="link"
|
||||||
class="link"
|
vn-tooltip="Edit discount"
|
||||||
ng-click="$ctrl.showEditPopover($event, sale)"
|
ng-click="$ctrl.showEditPopover($event, sale)">
|
||||||
vn-tooltip="Edit discount">
|
{{sale.discount}} %
|
||||||
{{sale.discount}} %
|
</span>
|
||||||
</vn-td>
|
</vn-td>
|
||||||
<vn-td number
|
<vn-td number
|
||||||
ng-if="!$ctrl.isEditable">
|
ng-if="!$ctrl.isEditable">
|
||||||
|
@ -158,7 +158,7 @@
|
||||||
<!-- Edit Price Popover -->
|
<!-- Edit Price Popover -->
|
||||||
<vn-popover
|
<vn-popover
|
||||||
class="edit dialog-summary"
|
class="edit dialog-summary"
|
||||||
vn-id="editPricePopover"
|
vn-id="edit-price-popover"
|
||||||
on-open="$ctrl.getManaSalespersonMana()">
|
on-open="$ctrl.getManaSalespersonMana()">
|
||||||
<vn-horizontal pad-medium class="header">
|
<vn-horizontal pad-medium class="header">
|
||||||
<h5>MANÁ: {{$ctrl.mana | currency: 'EUR':0}}</h5>
|
<h5>MANÁ: {{$ctrl.mana | currency: 'EUR':0}}</h5>
|
||||||
|
@ -185,7 +185,7 @@
|
||||||
<!-- Edit Popover -->
|
<!-- Edit Popover -->
|
||||||
<vn-popover
|
<vn-popover
|
||||||
class="edit dialog-summary"
|
class="edit dialog-summary"
|
||||||
vn-id="editPopover"
|
vn-id="edit-popover"
|
||||||
on-open="$ctrl.getManaSalespersonMana()">
|
on-open="$ctrl.getManaSalespersonMana()">
|
||||||
<vn-ticket-sale-edit-discount
|
<vn-ticket-sale-edit-discount
|
||||||
mana="$ctrl.mana"
|
mana="$ctrl.mana"
|
||||||
|
|
|
@ -36,9 +36,10 @@ class Controller {
|
||||||
}
|
}
|
||||||
|
|
||||||
loadSubTotal() {
|
loadSubTotal() {
|
||||||
this.subTotal = 0.0;
|
if (!this.$stateParams.id || !this.sales) return;
|
||||||
if (!this.sales) return;
|
this.$http.get(`/ticket/api/Tickets/${this.$stateParams.id}/subtotal`).then(res => {
|
||||||
this.subTotal = this.sales.reduce((sum, sale) => sum + this.getSaleTotal(sale), 0.0);
|
this.subtotal = res.data || 0.0;
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
getSaleTotal(sale) {
|
getSaleTotal(sale) {
|
||||||
|
@ -54,7 +55,7 @@ class Controller {
|
||||||
}
|
}
|
||||||
|
|
||||||
get total() {
|
get total() {
|
||||||
return this.subTotal + this.VAT;
|
return this.subtotal + this.VAT;
|
||||||
}
|
}
|
||||||
|
|
||||||
onMoreOpen() {
|
onMoreOpen() {
|
||||||
|
|
|
@ -41,6 +41,7 @@ describe('Ticket', () => {
|
||||||
$httpBackend = _$httpBackend_;
|
$httpBackend = _$httpBackend_;
|
||||||
$httpBackend.whenGET(/api\/Tickets\/1\/getSales.*/).respond(sales);
|
$httpBackend.whenGET(/api\/Tickets\/1\/getSales.*/).respond(sales);
|
||||||
$httpBackend.whenGET(`/ticket/api/Tickets/1/getVAT`).respond(200, 10.5);
|
$httpBackend.whenGET(`/ticket/api/Tickets/1/getVAT`).respond(200, 10.5);
|
||||||
|
$httpBackend.whenGET(`/ticket/api/Tickets/1/subtotal`).respond(200, 227.5);
|
||||||
|
|
||||||
$element = $compile('<vn-ticket-sale ticket="ticket"></vn-ticket-sale>')($scope);
|
$element = $compile('<vn-ticket-sale ticket="ticket"></vn-ticket-sale>')($scope);
|
||||||
controller = $element.controller('vnTicketSale');
|
controller = $element.controller('vnTicketSale');
|
||||||
|
@ -67,9 +68,9 @@ describe('Ticket', () => {
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
describe('total/VAT/subTotal properties', () => {
|
describe('total/VAT/subtotal properties', () => {
|
||||||
it('should fill total, VAT and subTotal', () => {
|
it('should fill total, VAT and subTotal', () => {
|
||||||
expect(controller.subTotal).toEqual(227.5);
|
expect(controller.subtotal).toEqual(227.5);
|
||||||
expect(controller.VAT).toEqual(10.5);
|
expect(controller.VAT).toEqual(10.5);
|
||||||
expect(controller.total).toEqual(238);
|
expect(controller.total).toEqual(238);
|
||||||
});
|
});
|
||||||
|
|
|
@ -46,8 +46,8 @@
|
||||||
</vn-label-value>
|
</vn-label-value>
|
||||||
</vn-one>
|
</vn-one>
|
||||||
<vn-one class="taxes">
|
<vn-one class="taxes">
|
||||||
<p><vn-label translate>Subtotal</vn-label> {{$ctrl.summary.subTotal | currency: 'EUR':2}}</p>
|
<p><vn-label translate>Subtotal</vn-label> {{$ctrl.summary.subtotal | currency: 'EUR':2}}</p>
|
||||||
<p><vn-label translate>VAT</vn-label> {{$ctrl.summary.VAT | currency: 'EUR':2}}</p>
|
<p><vn-label translate>VAT</vn-label> {{$ctrl.summary.vat | currency: 'EUR':2}}</p>
|
||||||
<p><vn-label><strong>Total</strong></vn-label> <strong>{{$ctrl.summary.total | currency: 'EUR':2}}</strong></p>
|
<p><vn-label><strong>Total</strong></vn-label> <strong>{{$ctrl.summary.total | currency: 'EUR':2}}</strong></p>
|
||||||
</vn-one>
|
</vn-one>
|
||||||
<vn-auto name="sales">
|
<vn-auto name="sales">
|
||||||
|
@ -57,8 +57,8 @@
|
||||||
<vn-tr>
|
<vn-tr>
|
||||||
<vn-th shrink></vn-th>
|
<vn-th shrink></vn-th>
|
||||||
<vn-th number>Item</vn-th>
|
<vn-th number>Item</vn-th>
|
||||||
<vn-th>Description</vn-th>
|
|
||||||
<vn-th number>Quantity</vn-th>
|
<vn-th number>Quantity</vn-th>
|
||||||
|
<vn-th>Description</vn-th>
|
||||||
<vn-th number>Price</vn-th>
|
<vn-th number>Price</vn-th>
|
||||||
<vn-th number>Discount</vn-th>
|
<vn-th number>Discount</vn-th>
|
||||||
<vn-th number>Amount</vn-th>
|
<vn-th number>Amount</vn-th>
|
||||||
|
@ -89,8 +89,8 @@
|
||||||
{{sale.itemFk | zeroFill:6}}
|
{{sale.itemFk | zeroFill:6}}
|
||||||
</span>
|
</span>
|
||||||
</vn-td>
|
</vn-td>
|
||||||
<vn-td expand><vn-fetched-tags max-length="6" item="sale.item" title="sale.concept"/></vn-td>
|
|
||||||
<vn-td number>{{::sale.quantity}}</vn-td>
|
<vn-td number>{{::sale.quantity}}</vn-td>
|
||||||
|
<vn-td expand><vn-fetched-tags max-length="6" item="sale.item" title="sale.concept"/></vn-td>
|
||||||
<vn-td number>{{::sale.price | currency: 'EUR':2}}</vn-td>
|
<vn-td number>{{::sale.price | currency: 'EUR':2}}</vn-td>
|
||||||
<vn-td number>{{::sale.discount}} %</vn-td>
|
<vn-td number>{{::sale.discount}} %</vn-td>
|
||||||
<vn-td number>{{::sale.quantity * sale.price | currency: 'EUR':2}}</vn-td>
|
<vn-td number>{{::sale.quantity * sale.price | currency: 'EUR':2}}</vn-td>
|
||||||
|
@ -123,8 +123,8 @@
|
||||||
<vn-thead>
|
<vn-thead>
|
||||||
<vn-tr>
|
<vn-tr>
|
||||||
<vn-th number>Id</vn-th>
|
<vn-th number>Id</vn-th>
|
||||||
<vn-th>Description</vn-th>
|
|
||||||
<vn-th number>Quantity</vn-th>
|
<vn-th number>Quantity</vn-th>
|
||||||
|
<vn-th>Description</vn-th>
|
||||||
<vn-th number>Price</vn-th>
|
<vn-th number>Price</vn-th>
|
||||||
<vn-th>Tax class</vn-th>
|
<vn-th>Tax class</vn-th>
|
||||||
</vn-tr>
|
</vn-tr>
|
||||||
|
@ -132,8 +132,8 @@
|
||||||
<vn-tbody>
|
<vn-tbody>
|
||||||
<vn-tr ng-repeat="service in $ctrl.summary.services">
|
<vn-tr ng-repeat="service in $ctrl.summary.services">
|
||||||
<vn-td number>{{::service.id}}</vn-td>
|
<vn-td number>{{::service.id}}</vn-td>
|
||||||
<vn-td expand>{{::service.description}}</vn-td>
|
|
||||||
<vn-td number>{{::service.quantity}}</vn-td>
|
<vn-td number>{{::service.quantity}}</vn-td>
|
||||||
|
<vn-td expand>{{::service.description}}</vn-td>
|
||||||
<vn-td number>{{::service.price}}</vn-td>
|
<vn-td number>{{::service.price}}</vn-td>
|
||||||
<vn-td>{{::service.taxClass.description}}</vn-td>
|
<vn-td>{{::service.taxClass.description}}</vn-td>
|
||||||
</vn-tr>
|
</vn-tr>
|
||||||
|
@ -151,7 +151,7 @@
|
||||||
<vn-th>Atender</vn-th>
|
<vn-th>Atender</vn-th>
|
||||||
<vn-th number>Quantity</vn-th>
|
<vn-th number>Quantity</vn-th>
|
||||||
<vn-th number>Price</vn-th>
|
<vn-th number>Price</vn-th>
|
||||||
<vn-th number>Sale id</vn-th>
|
<vn-th number>Item</vn-th>
|
||||||
<vn-th number>Ok</vn-th>
|
<vn-th number>Ok</vn-th>
|
||||||
</vn-tr>
|
</vn-tr>
|
||||||
</vn-thead>
|
</vn-thead>
|
||||||
|
@ -166,9 +166,9 @@
|
||||||
<vn-td number>
|
<vn-td number>
|
||||||
<span
|
<span
|
||||||
ng-show="::request.saleFk"
|
ng-show="::request.saleFk"
|
||||||
ng-click="$ctrl.showDescriptor($event, request.saleFk)"
|
ng-click="$ctrl.showDescriptor($event, request.sale.itemFk)"
|
||||||
class="link">
|
class="link">
|
||||||
{{request.saleFk | zeroFill:6}}
|
{{request.sale.itemFk | zeroFill:6}}
|
||||||
</span>
|
</span>
|
||||||
</vn-td>
|
</vn-td>
|
||||||
<vn-td number>
|
<vn-td number>
|
||||||
|
|
|
@ -0,0 +1,99 @@
|
||||||
|
USE `vn`;
|
||||||
|
DROP procedure IF EXISTS `ticketGetTax`;
|
||||||
|
|
||||||
|
DELIMITER $$
|
||||||
|
USE `vn`$$
|
||||||
|
CREATE DEFINER=`root`@`%` PROCEDURE `ticketGetTax`()
|
||||||
|
READS SQL DATA
|
||||||
|
BEGIN
|
||||||
|
/**
|
||||||
|
* Calcula la base imponible, el IVA y el recargo de equivalencia para
|
||||||
|
* un conjunto de tickets.
|
||||||
|
*
|
||||||
|
* @table tmp.ticket(ticketFk) Identificadores de los tickets a calcular
|
||||||
|
* @return tmp.ticketAmount
|
||||||
|
* @return tmp.ticketTax Impuesto desglosado para cada ticket.
|
||||||
|
|
||||||
|
*/
|
||||||
|
DROP TEMPORARY TABLE IF EXISTS tmp.addressCompany;
|
||||||
|
CREATE TEMPORARY TABLE tmp.addressCompany
|
||||||
|
(INDEX (addressFk, companyFk))
|
||||||
|
ENGINE = MEMORY
|
||||||
|
SELECT DISTINCT t.addressFk, t.companyFk
|
||||||
|
FROM tmp.ticket tmpTicket
|
||||||
|
JOIN ticket t ON t.id = tmpTicket.ticketFk;
|
||||||
|
|
||||||
|
CALL addressTaxArea ();
|
||||||
|
|
||||||
|
|
||||||
|
/** Solo se calcula la base imponible (taxableBase) y el impuesto se calculará posteriormente
|
||||||
|
* No se debería cambiar el sistema por problemas con los decimales
|
||||||
|
*/
|
||||||
|
|
||||||
|
DROP TEMPORARY TABLE IF EXISTS tmp.ticketTax;
|
||||||
|
CREATE TEMPORARY TABLE tmp.ticketTax
|
||||||
|
(INDEX (ticketFk))
|
||||||
|
ENGINE = MEMORY
|
||||||
|
SELECT tmpTicket.ticketFk,
|
||||||
|
bp.pgcFk,
|
||||||
|
SUM(s.quantity * s.price * (100 - s.discount)/100 ) AS taxableBase,
|
||||||
|
pgc.rate,
|
||||||
|
tc.code
|
||||||
|
FROM tmp.ticket tmpTicket
|
||||||
|
JOIN sale s ON s.ticketFk = tmpTicket.ticketFk
|
||||||
|
JOIN item i ON i.id = s.itemFk
|
||||||
|
JOIN ticket t ON t.id = tmpTicket.ticketFk
|
||||||
|
JOIN supplier su ON su.id = t.companyFk
|
||||||
|
JOIN tmp.addressTaxArea ata
|
||||||
|
ON ata.addressFk = t.addressFk AND ata.companyFk = t.companyFk
|
||||||
|
JOIN itemTaxCountry itc
|
||||||
|
ON itc.itemFk = i.id AND itc.countryFk = su.countryFk
|
||||||
|
JOIN bookingPlanner bp
|
||||||
|
ON bp.countryFk = su.countryFk
|
||||||
|
AND bp.taxAreaFk = ata.areaFk
|
||||||
|
AND bp.taxClassFk = itc.taxClassFk
|
||||||
|
JOIN pgc ON pgc.code = bp.pgcFk
|
||||||
|
JOIN taxClass tc ON tc.id = bp.taxClassFk
|
||||||
|
GROUP BY tmpTicket.ticketFk, pgc.code,pgc.rate
|
||||||
|
HAVING taxableBase != 0;
|
||||||
|
|
||||||
|
DROP TEMPORARY TABLE IF EXISTS tmp.ticketServiceTax;
|
||||||
|
CREATE TEMPORARY TABLE tmp.ticketServiceTax
|
||||||
|
(INDEX (ticketFk))
|
||||||
|
ENGINE = MEMORY
|
||||||
|
SELECT tt.ticketFk,
|
||||||
|
SUM(ts.quantity * ts.price) AS taxableBase,
|
||||||
|
pgc.rate,
|
||||||
|
tc.code
|
||||||
|
FROM tmp.ticketTax tt
|
||||||
|
JOIN ticketService ts ON ts.ticketFk = tt.ticketFk
|
||||||
|
JOIN ticket t ON t.id = tt.ticketFk
|
||||||
|
JOIN supplier su ON su.id = t.companyFk
|
||||||
|
JOIN tmp.addressTaxArea ata
|
||||||
|
ON ata.addressFk = t.addressFk AND ata.companyFk = t.companyFk
|
||||||
|
JOIN bookingPlanner bp
|
||||||
|
ON bp.countryFk = su.countryFk
|
||||||
|
AND bp.taxAreaFk = ata.areaFk
|
||||||
|
AND bp.taxClassFk = ts.taxClassFk
|
||||||
|
JOIN pgc ON pgc.code = bp.pgcFk AND pgc.rate = tt.rate
|
||||||
|
JOIN taxClass tc ON tc.id = bp.taxClassFk
|
||||||
|
GROUP BY tt.ticketFk, tt.code,tt.rate
|
||||||
|
HAVING taxableBase != 0;
|
||||||
|
|
||||||
|
UPDATE tmp.ticketTax tt
|
||||||
|
JOIN tmp.ticketServiceTax ts ON tt.ticketFk = ts.ticketFk AND tt.code = ts.code AND tt.rate = ts.rate
|
||||||
|
SET tt.taxableBase = tt.taxableBase + ts.taxableBase;
|
||||||
|
|
||||||
|
DROP TEMPORARY TABLE IF EXISTS tmp.ticketAmount;
|
||||||
|
CREATE TEMPORARY TABLE tmp.ticketAmount
|
||||||
|
(INDEX (ticketFk))
|
||||||
|
ENGINE = MEMORY
|
||||||
|
SELECT ticketFk, taxableBase, SUM(CAST(taxableBase * rate / 100 AS DECIMAL(10, 2))) tax,code
|
||||||
|
FROM tmp.ticketTax
|
||||||
|
GROUP BY ticketFk, code;
|
||||||
|
|
||||||
|
DROP TEMPORARY TABLE IF EXISTS tmp.addressCompany;
|
||||||
|
DROP TEMPORARY TABLE IF EXISTS tmp.addressTaxArea;
|
||||||
|
END$$
|
||||||
|
|
||||||
|
DELIMITER ;
|
|
@ -0,0 +1,34 @@
|
||||||
|
DROP PROCEDURE IF EXISTS vn.ticketGetTaxAdd;
|
||||||
|
|
||||||
|
DELIMITER $$
|
||||||
|
$$
|
||||||
|
CREATE DEFINER=`root`@`%` PROCEDURE `vn`.`ticketGetTaxAdd`(vTicketFk INT)
|
||||||
|
BEGIN
|
||||||
|
/**
|
||||||
|
* Añade un ticket a la tabla tmp.ticket para calcular
|
||||||
|
* el IVA y el recargo de equivalencia y devuelve el resultado.
|
||||||
|
*/
|
||||||
|
DROP TEMPORARY TABLE IF EXISTS tmp.ticket;
|
||||||
|
CREATE TEMPORARY TABLE tmp.ticket
|
||||||
|
ENGINE = MEMORY
|
||||||
|
SELECT vTicketFk ticketFk;
|
||||||
|
|
||||||
|
CALL vn.ticketGetTax();
|
||||||
|
|
||||||
|
SELECT
|
||||||
|
tt.ticketFk,
|
||||||
|
CAST(tt.taxableBase AS DECIMAL(10, 2)) AS taxableBase,
|
||||||
|
CAST(tt.rate * tt.taxableBase / 100 AS DECIMAL(10, 2)) AS tax,
|
||||||
|
pgc.*,
|
||||||
|
CAST(IF(pe.equFk IS NULL, taxableBase, 0) AS DECIMAL(10, 2)) AS Base,
|
||||||
|
pgc.rate / 100 as vatPercent
|
||||||
|
FROM tmp.ticketTax tt
|
||||||
|
JOIN vn.pgc ON pgc.code = tt.pgcFk
|
||||||
|
LEFT JOIN vn.pgcEqu pe ON pe.equFk = pgc.code;
|
||||||
|
|
||||||
|
DROP TEMPORARY TABLE tmp.ticket;
|
||||||
|
DROP TEMPORARY TABLE tmp.ticketTax;
|
||||||
|
DROP TEMPORARY TABLE tmp.ticketAmount;
|
||||||
|
|
||||||
|
END$$
|
||||||
|
DELIMITER ;
|
Loading…
Reference in New Issue