Merge branch 'dev' into 3625-ticket_log_routeFk
gitea/salix/pipeline/head This commit looks good
Details
gitea/salix/pipeline/head This commit looks good
Details
This commit is contained in:
commit
728a5de311
|
@ -9,35 +9,35 @@ module.exports = Self => {
|
||||||
accepts: [
|
accepts: [
|
||||||
{
|
{
|
||||||
arg: 'warehouseId',
|
arg: 'warehouseId',
|
||||||
type: 'Number',
|
type: 'number',
|
||||||
description: 'The warehouse id',
|
description: 'The warehouse id',
|
||||||
required: true
|
required: true
|
||||||
}, {
|
}, {
|
||||||
arg: 'companyId',
|
arg: 'companyId',
|
||||||
type: 'Number',
|
type: 'number',
|
||||||
description: 'The company id',
|
description: 'The company id',
|
||||||
required: true
|
required: true
|
||||||
}, {
|
}, {
|
||||||
arg: 'dmsTypeId',
|
arg: 'dmsTypeId',
|
||||||
type: 'Number',
|
type: 'number',
|
||||||
description: 'The dms type id',
|
description: 'The dms type id',
|
||||||
required: true
|
required: true
|
||||||
}, {
|
}, {
|
||||||
arg: 'reference',
|
arg: 'reference',
|
||||||
type: 'String',
|
type: 'string',
|
||||||
required: true
|
required: true
|
||||||
}, {
|
}, {
|
||||||
arg: 'description',
|
arg: 'description',
|
||||||
type: 'String',
|
type: 'string',
|
||||||
required: true
|
required: true
|
||||||
}, {
|
}, {
|
||||||
arg: 'hasFile',
|
arg: 'hasFile',
|
||||||
type: 'Boolean',
|
type: 'boolean',
|
||||||
description: 'True if has an attached file',
|
description: 'True if has an attached file',
|
||||||
required: true
|
required: true
|
||||||
}],
|
}],
|
||||||
returns: {
|
returns: {
|
||||||
type: 'Object',
|
type: 'object',
|
||||||
root: true
|
root: true
|
||||||
},
|
},
|
||||||
http: {
|
http: {
|
||||||
|
|
|
@ -18,9 +18,6 @@
|
||||||
},
|
},
|
||||||
"expired": {
|
"expired": {
|
||||||
"type": "date"
|
"type": "date"
|
||||||
},
|
|
||||||
"isOfficial": {
|
|
||||||
"type": "boolean"
|
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,2 @@
|
||||||
|
INSERT INTO salix.ACL (id, model, property, accessType, permission, principalType, principalId)
|
||||||
|
VALUES(301, 'Agency', '*', 'READ', 'ALLOW', 'ROLE', 'employee');
|
|
@ -0,0 +1,2 @@
|
||||||
|
INSERT INTO salix.ACL (model,property,accessType,principalId)
|
||||||
|
VALUES ('AgencyTerm','*','*','administrative');
|
|
@ -0,0 +1,10 @@
|
||||||
|
CREATE TABLE `vn`.`agencyTermConfig` (
|
||||||
|
`expenceFk` varchar(10) DEFAULT NULL,
|
||||||
|
`vatAccountSupported` varchar(15) DEFAULT NULL,
|
||||||
|
`vatPercentage` decimal(28,10) DEFAULT NULL,
|
||||||
|
`transaction` varchar(50) DEFAULT NULL
|
||||||
|
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
|
||||||
|
|
||||||
|
INSERT INTO `vn`.`agencyTermConfig`
|
||||||
|
(`expenceFk`, `vatAccountSupported`, `vatPercentage`, `transaction`)
|
||||||
|
VALUES('6240000000', '4721000015', 21.0000000000, 'Adquisiciones intracomunitarias de servicios');
|
|
@ -0,0 +1,3 @@
|
||||||
|
UPDATE `account`.`user`
|
||||||
|
SET `role` = 57
|
||||||
|
WHERE id IN (2294, 4365, 7294);
|
|
@ -167,9 +167,11 @@ INSERT INTO `vn`.`accountingType`(`id`, `description`, `receiptDescription`,`cod
|
||||||
|
|
||||||
INSERT INTO `vn`.`bank`(`id`, `bank`, `account`, `cash`, `entityFk`, `isActive`, `currencyFk`)
|
INSERT INTO `vn`.`bank`(`id`, `bank`, `account`, `cash`, `entityFk`, `isActive`, `currencyFk`)
|
||||||
VALUES
|
VALUES
|
||||||
(1, 'Pay on receipt', '5720000001', 3, 0, 1, 1),
|
(1, 'Pay on receipt', '5720000001', 3, 0, 1, 1),
|
||||||
(2, 'Cash', '5700000001', 2, 0, 1, 1),
|
(2, 'Cash', '5700000001', 2, 0, 1, 1),
|
||||||
(3, 'Compensation', '4000000000', 8, 0, 1, 1);
|
(3, 'Compensation', '4000000000', 8, 0, 1, 1),
|
||||||
|
(3117, 'Caixa Rural d''Algemesi', '5720000000', 8, 3117, 1, 1);
|
||||||
|
|
||||||
|
|
||||||
INSERT INTO `vn`.`deliveryMethod`(`id`, `code`, `description`)
|
INSERT INTO `vn`.`deliveryMethod`(`id`, `code`, `description`)
|
||||||
VALUES
|
VALUES
|
||||||
|
@ -522,6 +524,8 @@ INSERT INTO `vn`.`expence`(`id`, `taxTypeFk`, `name`, `isWithheld`)
|
||||||
(4751000000, 1, 'Retenciones', 1),
|
(4751000000, 1, 'Retenciones', 1),
|
||||||
(4751000000, 6, 'Retencion', 0),
|
(4751000000, 6, 'Retencion', 0),
|
||||||
(6210000567, 0, 'Alquiler VNH', 0),
|
(6210000567, 0, 'Alquiler VNH', 0),
|
||||||
|
(6240000000, 1, 'Transportes de ventas rutas', 0),
|
||||||
|
(6240000000, 4, 'Transportes de ventas rutas', 0),
|
||||||
(7001000000, 1, 'Mercaderia', 0),
|
(7001000000, 1, 'Mercaderia', 0),
|
||||||
(7050000000, 1, 'Prestacion de servicios', 1);
|
(7050000000, 1, 'Prestacion de servicios', 1);
|
||||||
|
|
||||||
|
@ -2453,6 +2457,28 @@ INSERT INTO `bs`.`defaulter` (`clientFk`, `amount`, `created`, `defaulterSinced`
|
||||||
(1107, 500, CURDATE(), CURDATE()),
|
(1107, 500, CURDATE(), CURDATE()),
|
||||||
(1109, 500, CURDATE(), CURDATE());
|
(1109, 500, CURDATE(), CURDATE());
|
||||||
|
|
||||||
|
INSERT INTO `vn`.`agencyTerm` (`agencyFk`, `minimumPackages`, `kmPrice`, `packagePrice`, `routePrice`, `minimumKm`, `minimumM3`, `m3Price`)
|
||||||
|
VALUES
|
||||||
|
(1, 0, 0.00, 0.00, NULL, 0, 0.00, 0),
|
||||||
|
(3, 0, 0.00, 3.05, NULL, 0, 0.00, 0),
|
||||||
|
(2, 60, 0.00, 0.00, NULL, 0, 5.00, 33);
|
||||||
|
|
||||||
|
UPDATE `vn`.`agency`
|
||||||
|
SET `supplierFk`=1
|
||||||
|
WHERE `id`=1;
|
||||||
|
UPDATE `vn`.`agency`
|
||||||
|
SET `supplierFk`=1
|
||||||
|
WHERE `id`=2;
|
||||||
|
UPDATE `vn`.`agency`
|
||||||
|
SET `supplierFk`=2
|
||||||
|
WHERE `id`=3;
|
||||||
|
|
||||||
|
UPDATE `vn`.`route`
|
||||||
|
SET `invoiceInFk`=1
|
||||||
|
WHERE `id`=1;
|
||||||
|
UPDATE `vn`.`route`
|
||||||
|
SET `invoiceInFk`=2
|
||||||
|
WHERE `id`=2;
|
||||||
INSERT INTO `bs`.`salesPerson` (`workerFk`, `year`, `month`, `portfolioWeight`)
|
INSERT INTO `bs`.`salesPerson` (`workerFk`, `year`, `month`, `portfolioWeight`)
|
||||||
VALUES
|
VALUES
|
||||||
(18, YEAR(CURDATE()), MONTH(CURDATE()), 807.23),
|
(18, YEAR(CURDATE()), MONTH(CURDATE()), 807.23),
|
||||||
|
|
|
@ -9,7 +9,7 @@ describe('Worker calendar path', () => {
|
||||||
browser = await getBrowser();
|
browser = await getBrowser();
|
||||||
page = browser.page;
|
page = browser.page;
|
||||||
await page.loginAndModule('hr', 'worker');
|
await page.loginAndModule('hr', 'worker');
|
||||||
await page.accessToSearchResult('Hank Pym');
|
await page.accessToSearchResult('Charles Xavier');
|
||||||
await page.accessToSection('worker.card.calendar');
|
await page.accessToSection('worker.card.calendar');
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -18,12 +18,6 @@ describe('Worker calendar path', () => {
|
||||||
});
|
});
|
||||||
|
|
||||||
describe('as hr', () => {
|
describe('as hr', () => {
|
||||||
it('should check 5 total holidays have been used so far before testing anything', async() => {
|
|
||||||
const result = await page.waitToGetProperty(selectors.workerCalendar.totalHolidaysUsed, 'innerText');
|
|
||||||
|
|
||||||
expect(result).toContain(' 5 ');
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should set two days as holidays on the calendar and check the total holidays increased by 1.5', async() => {
|
it('should set two days as holidays on the calendar and check the total holidays increased by 1.5', async() => {
|
||||||
await page.waitToClick(selectors.workerCalendar.holidays);
|
await page.waitToClick(selectors.workerCalendar.holidays);
|
||||||
await page.waitForTimeout(reasonableTimeBetweenClicks);
|
await page.waitForTimeout(reasonableTimeBetweenClicks);
|
||||||
|
@ -56,14 +50,14 @@ describe('Worker calendar path', () => {
|
||||||
|
|
||||||
const result = await page.waitToGetProperty(selectors.workerCalendar.totalHolidaysUsed, 'innerText');
|
const result = await page.waitToGetProperty(selectors.workerCalendar.totalHolidaysUsed, 'innerText');
|
||||||
|
|
||||||
expect(result).toContain(' 6.5 ');
|
expect(result).toContain(' 1.5 ');
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
describe(`as salesBoss`, () => {
|
describe(`as salesBoss`, () => {
|
||||||
it(`should log in and get to Hank's calendar`, async() => {
|
it(`should log in and get to Charles Xavier's calendar`, async() => {
|
||||||
await page.loginAndModule('salesBoss', 'worker');
|
await page.loginAndModule('salesBoss', 'worker');
|
||||||
await page.accessToSearchResult('Hank Pym');
|
await page.accessToSearchResult('Charles Xavier');
|
||||||
await page.accessToSection('worker.card.calendar');
|
await page.accessToSection('worker.card.calendar');
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -101,14 +95,14 @@ describe('Worker calendar path', () => {
|
||||||
it('should check the total holidays used are back to what it was', async() => {
|
it('should check the total holidays used are back to what it was', async() => {
|
||||||
const result = await page.waitToGetProperty(selectors.workerCalendar.totalHolidaysUsed, 'innerText');
|
const result = await page.waitToGetProperty(selectors.workerCalendar.totalHolidaysUsed, 'innerText');
|
||||||
|
|
||||||
expect(result).toContain(' 5 ');
|
expect(result).toContain(' 0 ');
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
describe(`as Hank`, () => {
|
describe(`as Charles Xavier`, () => {
|
||||||
it(`should log in and get to his calendar`, async() => {
|
it(`should log in and get to his calendar`, async() => {
|
||||||
await page.loginAndModule('HankPym', 'worker');
|
await page.loginAndModule('CharlesXavier', 'worker');
|
||||||
await page.accessToSearchResult('Hank Pym');
|
await page.accessToSearchResult('Charles Xavier');
|
||||||
await page.accessToSection('worker.card.calendar');
|
await page.accessToSection('worker.card.calendar');
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -122,7 +116,7 @@ describe('Worker calendar path', () => {
|
||||||
it('should check the total holidays used are now the initial ones', async() => {
|
it('should check the total holidays used are now the initial ones', async() => {
|
||||||
const result = await page.waitToGetProperty(selectors.workerCalendar.totalHolidaysUsed, 'innerText');
|
const result = await page.waitToGetProperty(selectors.workerCalendar.totalHolidaysUsed, 'innerText');
|
||||||
|
|
||||||
expect(result).toContain(' 5 ');
|
expect(result).toContain(' 0 ');
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should use the year selector to go to the previous year', async() => {
|
it('should use the year selector to go to the previous year', async() => {
|
||||||
|
|
|
@ -219,5 +219,6 @@
|
||||||
"You can not modify is pay method checked": "No se puede modificar el campo método de pago validado",
|
"You can not modify is pay method checked": "No se puede modificar el campo método de pago validado",
|
||||||
"Can't transfer claimed sales": "No puedes transferir lineas reclamadas",
|
"Can't transfer claimed sales": "No puedes transferir lineas reclamadas",
|
||||||
"You don't have privileges to create pay back": "No tienes permisos para crear un abono",
|
"You don't have privileges to create pay back": "No tienes permisos para crear un abono",
|
||||||
"The item is required": "El artículo es requerido"
|
"The item is required": "El artículo es requerido",
|
||||||
|
"reference duplicated": "Referencia duplicada"
|
||||||
}
|
}
|
|
@ -79,7 +79,6 @@
|
||||||
</vn-autocomplete>
|
</vn-autocomplete>
|
||||||
<vn-input-number
|
<vn-input-number
|
||||||
vn-one
|
vn-one
|
||||||
min="0"
|
|
||||||
step="0.1"
|
step="0.1"
|
||||||
label="Commission"
|
label="Commission"
|
||||||
ng-model="$ctrl.entry.commission"
|
ng-model="$ctrl.entry.commission"
|
||||||
|
|
|
@ -7,7 +7,7 @@
|
||||||
"menus": {
|
"menus": {
|
||||||
"main": [
|
"main": [
|
||||||
{"state": "entry.index", "icon": "icon-entry"},
|
{"state": "entry.index", "icon": "icon-entry"},
|
||||||
{"state": "entry.latestBuys", "icon": "icon-lastBuy"}
|
{"state": "entry.latestBuys", "icon": "contact_support"}
|
||||||
],
|
],
|
||||||
"card": [
|
"card": [
|
||||||
{"state": "entry.card.basicData", "icon": "settings"},
|
{"state": "entry.card.basicData", "icon": "settings"},
|
||||||
|
|
|
@ -0,0 +1,103 @@
|
||||||
|
module.exports = Self => {
|
||||||
|
Self.remoteMethod('createInvoiceIn', {
|
||||||
|
description: 'Creates an invoiceIn from one or more agency terms',
|
||||||
|
accessType: 'WRITE',
|
||||||
|
accepts: [{
|
||||||
|
arg: 'rows',
|
||||||
|
type: ['object'],
|
||||||
|
required: true,
|
||||||
|
description: `The rows from which the invoiceIn will be created`,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
arg: 'dms',
|
||||||
|
type: ['object'],
|
||||||
|
required: true,
|
||||||
|
description: 'The dms file attached'
|
||||||
|
}],
|
||||||
|
returns: {
|
||||||
|
type: 'object',
|
||||||
|
root: true
|
||||||
|
},
|
||||||
|
http: {
|
||||||
|
path: `/createInvoiceIn`,
|
||||||
|
verb: 'POST'
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
Self.createInvoiceIn = async(rows, dms, options) => {
|
||||||
|
const models = Self.app.models;
|
||||||
|
|
||||||
|
let tx;
|
||||||
|
const myOptions = {};
|
||||||
|
|
||||||
|
if (typeof options == 'object')
|
||||||
|
Object.assign(myOptions, options);
|
||||||
|
|
||||||
|
if (!myOptions.transaction) {
|
||||||
|
tx = await Self.beginTransaction({});
|
||||||
|
myOptions.transaction = tx;
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
const [firstRow] = rows;
|
||||||
|
const [firstDms] = dms;
|
||||||
|
|
||||||
|
const reference = await models.Dms.findById([firstDms.id], null, myOptions);
|
||||||
|
|
||||||
|
const newInvoiceIn = await models.InvoiceIn.create({
|
||||||
|
supplierFk: firstRow.supplierFk,
|
||||||
|
supplierRef: reference.reference,
|
||||||
|
issued: firstRow.created,
|
||||||
|
booked: firstRow.created,
|
||||||
|
operated: firstRow.created,
|
||||||
|
bookEntried: firstRow.created,
|
||||||
|
dmsFk: firstDms.id,
|
||||||
|
}, myOptions);
|
||||||
|
|
||||||
|
const expence = await models.AgencyTermConfig.findOne(null, myOptions);
|
||||||
|
|
||||||
|
const [taxTypeSage] = await Self.rawSql(`
|
||||||
|
SELECT IFNULL(s.taxTypeSageFk, CodigoIva) value
|
||||||
|
FROM vn.supplier s
|
||||||
|
JOIN sage.TiposIva ti ON TRUE
|
||||||
|
JOIN vn.agencyTermConfig atg
|
||||||
|
WHERE s.id = ?
|
||||||
|
AND ti.CuentaIvaSoportado = atg.vatAccountSupported
|
||||||
|
AND ti.PorcentajeIva = atg.vatPercentage
|
||||||
|
`, [firstRow.supplierFk], myOptions);
|
||||||
|
|
||||||
|
const [transactionTypeSage] = await Self.rawSql(`
|
||||||
|
SELECT IFNULL(s.transactionTypeSageFk, tt.CodigoTransaccion) value
|
||||||
|
FROM vn.supplier s
|
||||||
|
JOIN sage.TiposTransacciones tt ON TRUE
|
||||||
|
JOIN vn.agencyTermConfig atg
|
||||||
|
WHERE s.id = ?
|
||||||
|
AND tt.Transaccion = atg.transaction
|
||||||
|
`, [firstRow.supplierFk], myOptions);
|
||||||
|
|
||||||
|
await models.InvoiceInTax.create({
|
||||||
|
invoiceInFk: newInvoiceIn.id,
|
||||||
|
taxableBase: firstRow.totalPrice,
|
||||||
|
expenseFk: expence.expenceFk,
|
||||||
|
taxTypeSageFk: taxTypeSage.value,
|
||||||
|
transactionTypeSageFk: transactionTypeSage.value
|
||||||
|
}, myOptions);
|
||||||
|
|
||||||
|
await Self.rawSql(`CALL invoiceInDueDay_calculate(?)`, [newInvoiceIn.id], myOptions);
|
||||||
|
|
||||||
|
for (let agencyTerm of rows) {
|
||||||
|
const route = await models.Route.findById(agencyTerm.routeFk, null, myOptions);
|
||||||
|
await Self.rawSql(`
|
||||||
|
UPDATE vn.route SET invoiceInFk = ? WHERE id = ?
|
||||||
|
`, [newInvoiceIn.id, route.id], myOptions);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (tx) await tx.commit();
|
||||||
|
|
||||||
|
return newInvoiceIn;
|
||||||
|
} catch (e) {
|
||||||
|
if (tx) await tx.rollback();
|
||||||
|
throw e;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
};
|
|
@ -0,0 +1,77 @@
|
||||||
|
const ParameterizedSQL = require('loopback-connector').ParameterizedSQL;
|
||||||
|
|
||||||
|
module.exports = Self => {
|
||||||
|
Self.remoteMethodCtx('filter', {
|
||||||
|
description: 'Find all instances of the model matched by filter from the data source.',
|
||||||
|
accessType: 'READ',
|
||||||
|
accepts: [
|
||||||
|
{
|
||||||
|
arg: 'filter',
|
||||||
|
type: 'object',
|
||||||
|
description: 'Filter defining where, order, offset, and limit - must be a JSON-encoded string',
|
||||||
|
http: {source: 'query'}
|
||||||
|
}
|
||||||
|
],
|
||||||
|
returns: {
|
||||||
|
type: ['object'],
|
||||||
|
root: true
|
||||||
|
},
|
||||||
|
http: {
|
||||||
|
path: `/filter`,
|
||||||
|
verb: 'GET'
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
Self.filter = async(ctx, filter, options) => {
|
||||||
|
const conn = Self.dataSource.connector;
|
||||||
|
const myOptions = {};
|
||||||
|
|
||||||
|
if (typeof options == 'object')
|
||||||
|
Object.assign(myOptions, options);
|
||||||
|
|
||||||
|
const stmts = [];
|
||||||
|
const stmt = new ParameterizedSQL(
|
||||||
|
`SELECT *
|
||||||
|
FROM (
|
||||||
|
SELECT r.id routeFk,
|
||||||
|
r.created,
|
||||||
|
r.agencyModeFk,
|
||||||
|
am.name agencyModeName,
|
||||||
|
am.agencyFk,
|
||||||
|
a.name agencyAgreement,
|
||||||
|
SUM(t.packages) packages,
|
||||||
|
r.m3,
|
||||||
|
r.kmEnd - r.kmStart kmTotal,
|
||||||
|
CAST(IFNULL(ate.routePrice,
|
||||||
|
(ate.kmPrice * (GREATEST(r.kmEnd - r.kmStart , ate.minimumKm))
|
||||||
|
+ GREATEST(r.m3 , ate.minimumM3) * ate.m3Price)
|
||||||
|
+ ate.packagePrice * SUM(t.packages) )
|
||||||
|
AS DECIMAL(10,2)) price,
|
||||||
|
r.invoiceInFk,
|
||||||
|
a.supplierFk,
|
||||||
|
s.name supplierName
|
||||||
|
FROM vn.route r
|
||||||
|
LEFT JOIN vn.agencyMode am ON r.agencyModeFk = am.id
|
||||||
|
LEFT JOIN vn.agency a ON am.agencyFk = a.id
|
||||||
|
LEFT JOIN vn.ticket t ON t.routeFk = r.id
|
||||||
|
LEFT JOIN vn.agencyTerm ate ON ate.agencyFk = a.id
|
||||||
|
LEFT JOIN vn.supplier s ON s.id = a.supplierFk
|
||||||
|
WHERE r.created > DATE_ADD(CURDATE(), INTERVAL -2 MONTH) AND a.supplierFk IS NOT NULL
|
||||||
|
GROUP BY r.id
|
||||||
|
) a`
|
||||||
|
);
|
||||||
|
|
||||||
|
stmt.merge(conn.makeSuffix(filter));
|
||||||
|
|
||||||
|
const agencyTerm = stmts.push(stmt) - 1;
|
||||||
|
const sql = ParameterizedSQL.join(stmts, ';');
|
||||||
|
const result = await conn.executeStmt(sql, myOptions);
|
||||||
|
|
||||||
|
const models = Self.app.models;
|
||||||
|
for (let agencyTerm of result)
|
||||||
|
agencyTerm.route = await models.Route.findById(agencyTerm.routeFk);
|
||||||
|
|
||||||
|
return agencyTerm === 0 ? result : result[agencyTerm];
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
|
@ -0,0 +1,48 @@
|
||||||
|
const models = require('vn-loopback/server/server').models;
|
||||||
|
|
||||||
|
// Include after #3638 export database
|
||||||
|
xdescribe('AgencyTerm createInvoiceIn()', () => {
|
||||||
|
const rows = [
|
||||||
|
{
|
||||||
|
routeFk: 2,
|
||||||
|
supplierFk: 1,
|
||||||
|
created: '2022-03-02T23:00:00.000Z',
|
||||||
|
totalPrice: 165
|
||||||
|
}
|
||||||
|
];
|
||||||
|
const dms = [
|
||||||
|
{
|
||||||
|
id: 6
|
||||||
|
}
|
||||||
|
];
|
||||||
|
|
||||||
|
it('should make an invoiceIn', async() => {
|
||||||
|
const tx = await models.AgencyTerm.beginTransaction({});
|
||||||
|
const options = {transaction: tx};
|
||||||
|
|
||||||
|
try {
|
||||||
|
const invoiceInId = 10;
|
||||||
|
const invoiceInDueDayId = 11;
|
||||||
|
const invoiceInTaxId = 12;
|
||||||
|
|
||||||
|
const oldInvoiceIn = await models.InvoiceIn.findById(invoiceInId, null, options);
|
||||||
|
const oldInvoiceInDueDay = await models.InvoiceInDueDay.findById(invoiceInDueDayId, null, options);
|
||||||
|
const oldInvoiceInTax = await models.InvoiceInTax.findById(invoiceInTaxId, null, options);
|
||||||
|
|
||||||
|
await models.AgencyTerm.createInvoiceIn(rows, dms, options);
|
||||||
|
|
||||||
|
const [newInvoiceIn] = await models.InvoiceIn.rawSql('SELECT MAX(id) id FROM invoiceIn', null, options);
|
||||||
|
const [newInvoiceInDueDay] = await models.InvoiceInDueDay.rawSql('SELECT MAX(id) id FROM invoiceInDueDay', null, options);
|
||||||
|
const [newInvoiceInTax] = await models.InvoiceInTax.rawSql('SELECT MAX(id) id FROM invoiceInTax', null, options);
|
||||||
|
|
||||||
|
expect(newInvoiceIn.id).toBeGreaterThan(oldInvoiceIn.id);
|
||||||
|
expect(newInvoiceInDueDay.id).toBeGreaterThan(oldInvoiceInDueDay.id);
|
||||||
|
expect(newInvoiceInTax.id).toBeGreaterThan(oldInvoiceInTax.id);
|
||||||
|
|
||||||
|
await tx.rollback();
|
||||||
|
} catch (e) {
|
||||||
|
await tx.rollback();
|
||||||
|
throw e;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
|
@ -0,0 +1,26 @@
|
||||||
|
const models = require('vn-loopback/server/server').models;
|
||||||
|
|
||||||
|
describe('AgencyTerm filter()', () => {
|
||||||
|
const authUserId = 9;
|
||||||
|
|
||||||
|
it('should return all the tickets matching the filter', async() => {
|
||||||
|
const tx = await models.AgencyTerm.beginTransaction({});
|
||||||
|
|
||||||
|
try {
|
||||||
|
const options = {transaction: tx};
|
||||||
|
const filter = {};
|
||||||
|
const ctx = {req: {accessToken: {userId: authUserId}}};
|
||||||
|
|
||||||
|
const agencyTerms = await models.AgencyTerm.filter(ctx, filter, options);
|
||||||
|
const firstAgencyTerm = agencyTerms[0];
|
||||||
|
|
||||||
|
expect(firstAgencyTerm.routeFk).toEqual(1);
|
||||||
|
expect(agencyTerms.length).toEqual(3);
|
||||||
|
|
||||||
|
await tx.rollback();
|
||||||
|
} catch (e) {
|
||||||
|
await tx.rollback();
|
||||||
|
throw e;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
|
@ -1,4 +1,10 @@
|
||||||
{
|
{
|
||||||
|
"AgencyTerm": {
|
||||||
|
"dataSource": "vn"
|
||||||
|
},
|
||||||
|
"AgencyTermConfig": {
|
||||||
|
"dataSource": "vn"
|
||||||
|
},
|
||||||
"Route": {
|
"Route": {
|
||||||
"dataSource": "vn"
|
"dataSource": "vn"
|
||||||
},
|
},
|
||||||
|
|
|
@ -0,0 +1,24 @@
|
||||||
|
{
|
||||||
|
"name": "AgencyTermConfig",
|
||||||
|
"base": "VnModel",
|
||||||
|
"options": {
|
||||||
|
"mysql": {
|
||||||
|
"table": "agencyTermConfig"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"properties": {
|
||||||
|
"expenceFk": {
|
||||||
|
"type": "string",
|
||||||
|
"id": true
|
||||||
|
},
|
||||||
|
"vatAccountSupported": {
|
||||||
|
"type": "string"
|
||||||
|
},
|
||||||
|
"vatPercentage": {
|
||||||
|
"type": "number"
|
||||||
|
},
|
||||||
|
"transaction": {
|
||||||
|
"type": "string"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,4 @@
|
||||||
|
module.exports = Self => {
|
||||||
|
require('../methods/agency-term/filter')(Self);
|
||||||
|
require('../methods/agency-term/createInvoiceIn')(Self);
|
||||||
|
};
|
|
@ -0,0 +1,37 @@
|
||||||
|
{
|
||||||
|
"name": "AgencyTerm",
|
||||||
|
"base": "VnModel",
|
||||||
|
"options": {
|
||||||
|
"mysql": {
|
||||||
|
"table": "agencyTerm"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"properties": {
|
||||||
|
"agencyFk": {
|
||||||
|
"type": "number",
|
||||||
|
"id": true,
|
||||||
|
"description": "Identifier"
|
||||||
|
},
|
||||||
|
"minimumPackages": {
|
||||||
|
"type": "number"
|
||||||
|
},
|
||||||
|
"kmPrice": {
|
||||||
|
"type": "number"
|
||||||
|
},
|
||||||
|
"packagePrice": {
|
||||||
|
"type": "number"
|
||||||
|
},
|
||||||
|
"routePrice": {
|
||||||
|
"type": "number"
|
||||||
|
},
|
||||||
|
"minimumKm": {
|
||||||
|
"type": "number"
|
||||||
|
},
|
||||||
|
"minimumM3": {
|
||||||
|
"type": "number"
|
||||||
|
},
|
||||||
|
"m3Price": {
|
||||||
|
"type": "number"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,108 @@
|
||||||
|
<mg-ajax path="dms/upload" options="vnPost"></mg-ajax>
|
||||||
|
<vn-watcher
|
||||||
|
vn-id="watcher"
|
||||||
|
data="$ctrl.dms">
|
||||||
|
</vn-watcher>
|
||||||
|
<vn-crud-model
|
||||||
|
auto-load="true"
|
||||||
|
url="Companies"
|
||||||
|
data="companies"
|
||||||
|
order="code">
|
||||||
|
</vn-crud-model>
|
||||||
|
<vn-crud-model
|
||||||
|
auto-load="true"
|
||||||
|
url="Warehouses"
|
||||||
|
data="warehouses"
|
||||||
|
order="name">
|
||||||
|
</vn-crud-model>
|
||||||
|
<vn-crud-model
|
||||||
|
auto-load="true"
|
||||||
|
url="DmsTypes"
|
||||||
|
data="dmsTypes"
|
||||||
|
order="name">
|
||||||
|
</vn-crud-model>
|
||||||
|
<form
|
||||||
|
name="form"
|
||||||
|
ng-submit="$ctrl.onSubmit()"
|
||||||
|
class="vn-ma-md"
|
||||||
|
enctype="multipart/form-data">
|
||||||
|
<div class="vn-w-md">
|
||||||
|
<vn-card class="vn-pa-lg">
|
||||||
|
<vn-horizontal>
|
||||||
|
<vn-textfield
|
||||||
|
vn-one
|
||||||
|
vn-focus
|
||||||
|
label="Reference"
|
||||||
|
ng-model="$ctrl.dms.reference"
|
||||||
|
rule>
|
||||||
|
</vn-textfield>
|
||||||
|
<vn-autocomplete vn-one
|
||||||
|
label="Company"
|
||||||
|
ng-model="$ctrl.dms.companyId"
|
||||||
|
data="companies"
|
||||||
|
show-field="code"
|
||||||
|
value-field="id">
|
||||||
|
</vn-autocomplete>
|
||||||
|
</vn-horizontal>
|
||||||
|
<vn-horizontal>
|
||||||
|
<vn-autocomplete vn-one
|
||||||
|
label="Warehouse"
|
||||||
|
ng-model="$ctrl.dms.warehouseId"
|
||||||
|
data="warehouses"
|
||||||
|
show-field="name"
|
||||||
|
value-field="id">
|
||||||
|
</vn-autocomplete>
|
||||||
|
<vn-autocomplete vn-one
|
||||||
|
label="Type"
|
||||||
|
ng-model="$ctrl.dms.dmsTypeId"
|
||||||
|
data="dmsTypes"
|
||||||
|
show-field="name"
|
||||||
|
value-field="id">
|
||||||
|
</vn-autocomplete>
|
||||||
|
</vn-horizontal>
|
||||||
|
<vn-horizontal>
|
||||||
|
<vn-textarea
|
||||||
|
vn-one
|
||||||
|
label="Description"
|
||||||
|
ng-model="$ctrl.dms.description"
|
||||||
|
rule>
|
||||||
|
</vn-textarea>
|
||||||
|
</vn-horizontal>
|
||||||
|
<vn-horizontal>
|
||||||
|
<vn-input-file
|
||||||
|
vn-one
|
||||||
|
label="File"
|
||||||
|
ng-model="$ctrl.dms.files"
|
||||||
|
on-change="$ctrl.onFileChange($files)"
|
||||||
|
accept="{{$ctrl.allowedContentTypes}}"
|
||||||
|
required="true"
|
||||||
|
multiple="true">
|
||||||
|
<append>
|
||||||
|
<vn-icon vn-none
|
||||||
|
color-marginal
|
||||||
|
title="{{$ctrl.contentTypesInfo}}"
|
||||||
|
icon="info">
|
||||||
|
</vn-icon>
|
||||||
|
</append>
|
||||||
|
</vn-input-file>
|
||||||
|
</vn-horizontal>
|
||||||
|
<vn-vertical>
|
||||||
|
<vn-check
|
||||||
|
label="Generate identifier for original file"
|
||||||
|
ng-model="$ctrl.dms.hasFile">
|
||||||
|
</vn-check>
|
||||||
|
</vn-vertical>
|
||||||
|
</vn-card>
|
||||||
|
<vn-button-bar>
|
||||||
|
<vn-submit
|
||||||
|
disabled="!watcher.dataChanged()"
|
||||||
|
label="Upload">
|
||||||
|
</vn-submit>
|
||||||
|
<vn-button
|
||||||
|
class="cancel"
|
||||||
|
label="Cancel"
|
||||||
|
ui-sref="client.card.dms.index">
|
||||||
|
</vn-button>
|
||||||
|
</vn-button-bar>
|
||||||
|
</div>
|
||||||
|
</form>
|
|
@ -0,0 +1,120 @@
|
||||||
|
import ngModule from '../../module';
|
||||||
|
import Section from 'salix/components/section';
|
||||||
|
import './style.scss';
|
||||||
|
import UserError from 'core/lib/user-error';
|
||||||
|
|
||||||
|
class Controller extends Section {
|
||||||
|
constructor($element, $) {
|
||||||
|
super($element, $);
|
||||||
|
this.dms = {
|
||||||
|
files: [],
|
||||||
|
hasFile: false,
|
||||||
|
hasFileAttached: false
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
get route() {
|
||||||
|
return this._route;
|
||||||
|
}
|
||||||
|
|
||||||
|
set route(value) {
|
||||||
|
this._route = value;
|
||||||
|
|
||||||
|
this.setDefaultParams();
|
||||||
|
this.getAllowedContentTypes();
|
||||||
|
}
|
||||||
|
|
||||||
|
$onChanges() {
|
||||||
|
if (this.$params && this.$params.q)
|
||||||
|
this.params = JSON.parse(this.$params.q);
|
||||||
|
}
|
||||||
|
|
||||||
|
getAllowedContentTypes() {
|
||||||
|
this.$http.get('DmsContainers/allowedContentTypes').then(res => {
|
||||||
|
const contentTypes = res.data.join(', ');
|
||||||
|
this.allowedContentTypes = contentTypes;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
get contentTypesInfo() {
|
||||||
|
return this.$t('ContentTypesInfo', {
|
||||||
|
allowedContentTypes: this.allowedContentTypes
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
setDefaultParams() {
|
||||||
|
const params = {filter: {
|
||||||
|
where: {code: 'invoiceIn'}
|
||||||
|
}};
|
||||||
|
this.$http.get('DmsTypes/findOne', {params}).then(res => {
|
||||||
|
const dmsType = res.data && res.data;
|
||||||
|
const companyId = this.vnConfig.companyFk;
|
||||||
|
const warehouseId = this.vnConfig.warehouseFk;
|
||||||
|
const defaultParams = {
|
||||||
|
warehouseId: warehouseId,
|
||||||
|
companyId: companyId,
|
||||||
|
dmsTypeId: dmsType.id,
|
||||||
|
description: this.params.supplierName
|
||||||
|
};
|
||||||
|
|
||||||
|
this.dms = Object.assign(this.dms, defaultParams);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
onSubmit() {
|
||||||
|
if (this.dms.files.length > 1) throw new UserError('You cannot attach more than one document');
|
||||||
|
const query = `dms/uploadFile`;
|
||||||
|
const options = {
|
||||||
|
method: 'POST',
|
||||||
|
url: query,
|
||||||
|
params: this.dms,
|
||||||
|
headers: {
|
||||||
|
'Content-Type': undefined
|
||||||
|
},
|
||||||
|
transformRequest: files => {
|
||||||
|
const formData = new FormData();
|
||||||
|
formData.append(files[0].name, files[0]);
|
||||||
|
return formData;
|
||||||
|
},
|
||||||
|
data: this.dms.files
|
||||||
|
};
|
||||||
|
this.$http(options).then(res => {
|
||||||
|
if (res) {
|
||||||
|
const addedDms = res.data;
|
||||||
|
this.$.watcher.updateOriginalData();
|
||||||
|
|
||||||
|
const params = {
|
||||||
|
rows: this.params.rows,
|
||||||
|
dms: addedDms
|
||||||
|
};
|
||||||
|
|
||||||
|
this.$http.post('AgencyTerms/createInvoiceIn', params)
|
||||||
|
.then(() => {
|
||||||
|
this.$state.go('route.agencyTerm.index');
|
||||||
|
this.vnApp.showSuccess(this.$t('Data saved!'));
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
onFileChange(files) {
|
||||||
|
let hasFileAttached = false;
|
||||||
|
|
||||||
|
if (files.length > 0)
|
||||||
|
hasFileAttached = true;
|
||||||
|
|
||||||
|
this.$.$applyAsync(() => {
|
||||||
|
this.dms.hasFileAttached = hasFileAttached;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Controller.$inject = ['$element', '$scope'];
|
||||||
|
|
||||||
|
ngModule.vnComponent('vnAgencyTermCreateInvoiceIn', {
|
||||||
|
template: require('./index.html'),
|
||||||
|
controller: Controller,
|
||||||
|
bindings: {
|
||||||
|
route: '<'
|
||||||
|
}
|
||||||
|
});
|
|
@ -0,0 +1,107 @@
|
||||||
|
import './index';
|
||||||
|
import watcher from 'core/mocks/watcher.js';
|
||||||
|
|
||||||
|
describe('AgencyTerm', () => {
|
||||||
|
describe('Component vnAgencyTermCreateInvoiceIn', () => {
|
||||||
|
let controller;
|
||||||
|
let $scope;
|
||||||
|
let $httpBackend;
|
||||||
|
let $httpParamSerializer;
|
||||||
|
|
||||||
|
beforeEach(ngModule('route'));
|
||||||
|
|
||||||
|
beforeEach(inject(($componentController, $rootScope, _$httpBackend_, _$httpParamSerializer_) => {
|
||||||
|
$scope = $rootScope.$new();
|
||||||
|
$httpBackend = _$httpBackend_;
|
||||||
|
$httpParamSerializer = _$httpParamSerializer_;
|
||||||
|
const $element = angular.element('<vn-agency-term-create-invoice-in></vn-agency-term-create-invoice-in>');
|
||||||
|
controller = $componentController('vnAgencyTermCreateInvoiceIn', {$element});
|
||||||
|
controller._route = {
|
||||||
|
id: 1
|
||||||
|
};
|
||||||
|
}));
|
||||||
|
|
||||||
|
describe('$onChanges()', () => {
|
||||||
|
it('should update the params data when $params.q is defined', () => {
|
||||||
|
controller.$params = {q: '{"supplierName": "Plants SL","rows": null}'};
|
||||||
|
|
||||||
|
const params = {q: '{"supplierName": "Plants SL", "rows": null}'};
|
||||||
|
const json = JSON.parse(params.q);
|
||||||
|
|
||||||
|
controller.$onChanges();
|
||||||
|
|
||||||
|
expect(controller.params).toEqual(json);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('route() setter', () => {
|
||||||
|
it('should set the ticket data and then call setDefaultParams() and getAllowedContentTypes()', () => {
|
||||||
|
jest.spyOn(controller, 'setDefaultParams');
|
||||||
|
jest.spyOn(controller, 'getAllowedContentTypes');
|
||||||
|
controller.route = {
|
||||||
|
id: 1
|
||||||
|
};
|
||||||
|
|
||||||
|
expect(controller.route).toBeDefined();
|
||||||
|
expect(controller.setDefaultParams).toHaveBeenCalledWith();
|
||||||
|
expect(controller.getAllowedContentTypes).toHaveBeenCalledWith();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('getAllowedContentTypes()', () => {
|
||||||
|
it('should make an HTTP GET request to get the allowed content types', () => {
|
||||||
|
const expectedResponse = ['image/png', 'image/jpg'];
|
||||||
|
$httpBackend.expect('GET', `DmsContainers/allowedContentTypes`).respond(expectedResponse);
|
||||||
|
controller.getAllowedContentTypes();
|
||||||
|
$httpBackend.flush();
|
||||||
|
|
||||||
|
expect(controller.allowedContentTypes).toBeDefined();
|
||||||
|
expect(controller.allowedContentTypes).toEqual('image/png, image/jpg');
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('setDefaultParams()', () => {
|
||||||
|
it('should perform a GET query and define the dms property on controller', () => {
|
||||||
|
const params = {filter: {
|
||||||
|
where: {code: 'invoiceIn'}
|
||||||
|
}};
|
||||||
|
const serializedParams = $httpParamSerializer(params);
|
||||||
|
$httpBackend.expect('GET', `DmsTypes/findOne?${serializedParams}`).respond({id: 1, code: 'invoiceIn'});
|
||||||
|
controller.params = {supplierName: 'Plants SL'};
|
||||||
|
controller.setDefaultParams();
|
||||||
|
$httpBackend.flush();
|
||||||
|
|
||||||
|
expect(controller.dms).toBeDefined();
|
||||||
|
expect(controller.dms.dmsTypeId).toEqual(1);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('onSubmit()', () => {
|
||||||
|
it('should make an HTTP POST request to save the form data', () => {
|
||||||
|
controller.$.watcher = watcher;
|
||||||
|
|
||||||
|
jest.spyOn(controller.$.watcher, 'updateOriginalData');
|
||||||
|
const files = [{id: 1, name: 'MyFile'}];
|
||||||
|
controller.dms = {files};
|
||||||
|
const serializedParams = $httpParamSerializer(controller.dms);
|
||||||
|
const query = `dms/uploadFile?${serializedParams}`;
|
||||||
|
controller.params = {rows: null};
|
||||||
|
|
||||||
|
$httpBackend.expect('POST', query).respond({});
|
||||||
|
$httpBackend.expect('POST', 'AgencyTerms/createInvoiceIn').respond({});
|
||||||
|
controller.onSubmit();
|
||||||
|
$httpBackend.flush();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('onFileChange()', () => {
|
||||||
|
it('should set dms hasFileAttached property to true if has any files', () => {
|
||||||
|
const files = [{id: 1, name: 'MyFile'}];
|
||||||
|
controller.onFileChange(files);
|
||||||
|
$scope.$apply();
|
||||||
|
|
||||||
|
expect(controller.dms.hasFileAttached).toBeTruthy();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
|
@ -0,0 +1,7 @@
|
||||||
|
vn-ticket-request {
|
||||||
|
.vn-textfield {
|
||||||
|
margin: 0!important;
|
||||||
|
max-width: 100px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -0,0 +1,143 @@
|
||||||
|
<vn-crud-model
|
||||||
|
vn-id="model"
|
||||||
|
url="AgencyTerms/filter"
|
||||||
|
filter="::$ctrl.filter"
|
||||||
|
data="agencyTerms"
|
||||||
|
auto-load="true">
|
||||||
|
</vn-crud-model>
|
||||||
|
<vn-card>
|
||||||
|
<smart-table
|
||||||
|
model="model"
|
||||||
|
options="$ctrl.smartTableOptions"
|
||||||
|
expr-builder="$ctrl.exprBuilder(param, value)">
|
||||||
|
<slot-actions>
|
||||||
|
<div>
|
||||||
|
<div class="totalBox" style="text-align: center;">
|
||||||
|
<h6 translate>Total</h6>
|
||||||
|
<vn-label-value
|
||||||
|
label="Price"
|
||||||
|
value="{{$ctrl.totalPrice | currency: 'EUR': 2}}">
|
||||||
|
</vn-label-value>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</slot-actions>
|
||||||
|
<slot-table>
|
||||||
|
<table>
|
||||||
|
<thead>
|
||||||
|
<tr>
|
||||||
|
<th shrink>
|
||||||
|
<vn-multi-check
|
||||||
|
model="model">
|
||||||
|
</vn-multi-check>
|
||||||
|
</th>
|
||||||
|
<th shrink field="routeFk">
|
||||||
|
<span translate>Id</span>
|
||||||
|
</th>
|
||||||
|
<th field="created">
|
||||||
|
<span translate>Date</span>
|
||||||
|
</th>
|
||||||
|
<th field="agencyFk">
|
||||||
|
<span translate>Agency route</span>
|
||||||
|
</th>
|
||||||
|
<th field="agencyAgreement">
|
||||||
|
<span translate>Agency Agreement</span>
|
||||||
|
</th>
|
||||||
|
<th field="packages">
|
||||||
|
<span translate>Packages</span>
|
||||||
|
</th>
|
||||||
|
<th field="m3">
|
||||||
|
<span translate>M3</span>
|
||||||
|
</th>
|
||||||
|
<th field="kmTotal">
|
||||||
|
<span translate>Km</span>
|
||||||
|
</th>
|
||||||
|
<th field="price">
|
||||||
|
<span translate>Price</span>
|
||||||
|
</th>
|
||||||
|
<th field="invoiceInFk">
|
||||||
|
<span translate>Received</span>
|
||||||
|
</th>
|
||||||
|
<th field="supplierFk">
|
||||||
|
<span translate>Autonomous</span>
|
||||||
|
</th>
|
||||||
|
<th></th>
|
||||||
|
</tr>
|
||||||
|
</thead>
|
||||||
|
<tbody>
|
||||||
|
<tr ng-repeat="agencyTerm in agencyTerms">
|
||||||
|
<td shrink>
|
||||||
|
<vn-check
|
||||||
|
ng-model="agencyTerm.checked"
|
||||||
|
vn-click-stop>
|
||||||
|
</vn-check>
|
||||||
|
</td>
|
||||||
|
<td>
|
||||||
|
<span
|
||||||
|
title="{{::agencyTerm.id}}"
|
||||||
|
vn-click-stop="routeDescriptor.show($event, agencyTerm.routeFk)"
|
||||||
|
class="link">
|
||||||
|
{{::agencyTerm.routeFk}}
|
||||||
|
</span>
|
||||||
|
</td>
|
||||||
|
<td shrink-date>{{::agencyTerm.created | date:'dd/MM/yyyy'}}</td>
|
||||||
|
<td>{{::agencyTerm.agencyModeName | dashIfEmpty}}</td>
|
||||||
|
<td>{{::agencyTerm.agencyAgreement | dashIfEmpty}}</td>
|
||||||
|
<td>{{::agencyTerm.packages | dashIfEmpty}}</td>
|
||||||
|
<td>{{::agencyTerm.m3 | dashIfEmpty}}</td>
|
||||||
|
<td>{{::agencyTerm.kmTotal | dashIfEmpty}}</td>
|
||||||
|
<td>{{::agencyTerm.price | dashIfEmpty}}</td>
|
||||||
|
<td>
|
||||||
|
<span
|
||||||
|
vn-click-stop="invoiceInDescriptor.show($event, agencyTerm.invoiceInFk)"
|
||||||
|
class="link">
|
||||||
|
{{::agencyTerm.invoiceInFk}}
|
||||||
|
</span>
|
||||||
|
</td>
|
||||||
|
<td>
|
||||||
|
<span
|
||||||
|
class="link"
|
||||||
|
vn-click-stop="supplierDescriptor.show($event, agencyTerm.supplierFk)">
|
||||||
|
{{::agencyTerm.supplierName}}
|
||||||
|
</span>
|
||||||
|
</td>
|
||||||
|
<td>
|
||||||
|
<vn-icon-button
|
||||||
|
vn-click-stop="$ctrl.preview(agencyTerm.route)"
|
||||||
|
vn-tooltip="Preview"
|
||||||
|
icon="preview">
|
||||||
|
</vn-icon-button>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
</slot-table>
|
||||||
|
</smart-table>
|
||||||
|
</vn-card>
|
||||||
|
|
||||||
|
<vn-popup vn-id="summary">
|
||||||
|
<vn-route-summary
|
||||||
|
route="$ctrl.routeSelected">
|
||||||
|
</vn-route-summary>
|
||||||
|
</vn-popup>
|
||||||
|
|
||||||
|
<vn-route-descriptor-popover
|
||||||
|
vn-id="routeDescriptor">
|
||||||
|
</vn-route-descriptor-popover>
|
||||||
|
<vn-supplier-descriptor-popover
|
||||||
|
vn-id="supplierDescriptor">
|
||||||
|
</vn-supplier-descriptor-popover>
|
||||||
|
<vn-invoice-in-descriptor-popover
|
||||||
|
vn-id="invoiceInDescriptor">
|
||||||
|
</vn-invoice-in-descriptor-popover>
|
||||||
|
|
||||||
|
<div fixed-bottom-right>
|
||||||
|
<vn-vertical style="align-items: center;">
|
||||||
|
<vn-button class="round sm vn-mb-sm"
|
||||||
|
icon="icon-invoice-in-create"
|
||||||
|
ng-show="$ctrl.totalChecked > 0"
|
||||||
|
ng-click="$ctrl.createInvoiceIn()"
|
||||||
|
vn-tooltip="Create invoiceIn"
|
||||||
|
tooltip-position="left">
|
||||||
|
</vn-button>
|
||||||
|
</vn-vertical>
|
||||||
|
</div>
|
|
@ -0,0 +1,122 @@
|
||||||
|
import ngModule from '../../module';
|
||||||
|
import Section from 'salix/components/section';
|
||||||
|
import './style.scss';
|
||||||
|
|
||||||
|
class Controller extends Section {
|
||||||
|
constructor($element, $) {
|
||||||
|
super($element, $);
|
||||||
|
|
||||||
|
this.smartTableOptions = {
|
||||||
|
activeButtons: {
|
||||||
|
search: true
|
||||||
|
},
|
||||||
|
columns: [
|
||||||
|
{
|
||||||
|
field: 'agencyFk',
|
||||||
|
autocomplete: {
|
||||||
|
url: 'AgencyModes',
|
||||||
|
showField: 'name',
|
||||||
|
valueField: 'name'
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
field: 'agencyAgreement',
|
||||||
|
autocomplete: {
|
||||||
|
url: 'Agencies',
|
||||||
|
showField: 'name',
|
||||||
|
valueField: 'name'
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
field: 'supplierFk',
|
||||||
|
autocomplete: {
|
||||||
|
url: 'Suppliers',
|
||||||
|
showField: 'name',
|
||||||
|
valueField: 'name',
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
exprBuilder(param, value) {
|
||||||
|
switch (param) {
|
||||||
|
case 'agencyFk':
|
||||||
|
return {'a.agencyModeName': value};
|
||||||
|
case 'supplierFk':
|
||||||
|
return {'a.supplierName': value};
|
||||||
|
case 'routeFk':
|
||||||
|
return {'a.routeFk': value};
|
||||||
|
case 'created':
|
||||||
|
case 'agencyAgreement':
|
||||||
|
case 'packages':
|
||||||
|
case 'm3':
|
||||||
|
case 'kmTotal':
|
||||||
|
case 'price':
|
||||||
|
case 'invoiceInFk':
|
||||||
|
return {[`a.${param}`]: value};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
get checked() {
|
||||||
|
const agencyTerms = this.$.model.data || [];
|
||||||
|
const checkedAgencyTerms = [];
|
||||||
|
for (let agencyTerm of agencyTerms) {
|
||||||
|
if (agencyTerm.checked)
|
||||||
|
checkedAgencyTerms.push(agencyTerm);
|
||||||
|
}
|
||||||
|
|
||||||
|
return checkedAgencyTerms;
|
||||||
|
}
|
||||||
|
|
||||||
|
get totalChecked() {
|
||||||
|
return this.checked.length;
|
||||||
|
}
|
||||||
|
|
||||||
|
get totalPrice() {
|
||||||
|
let totalPrice = 0;
|
||||||
|
|
||||||
|
if (this.checked.length > 0) {
|
||||||
|
for (let agencyTerm of this.checked)
|
||||||
|
totalPrice += agencyTerm.price;
|
||||||
|
|
||||||
|
return totalPrice;
|
||||||
|
}
|
||||||
|
|
||||||
|
return totalPrice;
|
||||||
|
}
|
||||||
|
|
||||||
|
preview(route) {
|
||||||
|
this.routeSelected = route;
|
||||||
|
this.$.summary.show();
|
||||||
|
}
|
||||||
|
|
||||||
|
createInvoiceIn() {
|
||||||
|
const rowsToCreateInvoiceIn = [];
|
||||||
|
const supplierFk = this.checked[0].supplierFk;
|
||||||
|
|
||||||
|
for (let agencyTerm of this.checked) {
|
||||||
|
let hasSameSupplier = supplierFk == agencyTerm.supplierFk;
|
||||||
|
if (hasSameSupplier) {
|
||||||
|
rowsToCreateInvoiceIn.push({
|
||||||
|
routeFk: agencyTerm.routeFk,
|
||||||
|
supplierFk: agencyTerm.supplierFk,
|
||||||
|
created: agencyTerm.created,
|
||||||
|
totalPrice: this.totalPrice});
|
||||||
|
} else {
|
||||||
|
this.vnApp.showError(this.$t('Two autonomous cannot be counted at the same time'));
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
const params = JSON.stringify({
|
||||||
|
supplierName: this.checked[0].supplierName,
|
||||||
|
rows: rowsToCreateInvoiceIn
|
||||||
|
});
|
||||||
|
this.$state.go('route.agencyTerm.createInvoiceIn', {q: params});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
ngModule.vnComponent('vnAgencyTermIndex', {
|
||||||
|
template: require('./index.html'),
|
||||||
|
controller: Controller
|
||||||
|
});
|
|
@ -0,0 +1,94 @@
|
||||||
|
import './index.js';
|
||||||
|
import crudModel from 'core/mocks/crud-model';
|
||||||
|
|
||||||
|
describe('AgencyTerm', () => {
|
||||||
|
describe('Component vnAgencyTermIndex', () => {
|
||||||
|
let controller;
|
||||||
|
let $window;
|
||||||
|
|
||||||
|
beforeEach(ngModule('route'));
|
||||||
|
|
||||||
|
beforeEach(inject(($componentController, _$window_) => {
|
||||||
|
$window = _$window_;
|
||||||
|
const $element = angular.element('<vn-agency-term-index></vn-agency-term-index>');
|
||||||
|
controller = $componentController('vnAgencyTermIndex', {$element});
|
||||||
|
controller.$.model = crudModel;
|
||||||
|
controller.$.model.data = [
|
||||||
|
{supplierFk: 1, totalPrice: null},
|
||||||
|
{supplierFk: 1},
|
||||||
|
{supplierFk: 2}
|
||||||
|
];
|
||||||
|
}));
|
||||||
|
|
||||||
|
describe('checked() getter', () => {
|
||||||
|
it('should return the checked lines', () => {
|
||||||
|
const data = controller.$.model.data;
|
||||||
|
data[0].checked = true;
|
||||||
|
|
||||||
|
const checkedRows = controller.checked;
|
||||||
|
|
||||||
|
const firstCheckedRow = checkedRows[0];
|
||||||
|
|
||||||
|
expect(firstCheckedRow.supplierFk).toEqual(1);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('totalCheked() getter', () => {
|
||||||
|
it('should return the total checked lines', () => {
|
||||||
|
const data = controller.$.model.data;
|
||||||
|
data[0].checked = true;
|
||||||
|
|
||||||
|
const checkedRows = controller.totalChecked;
|
||||||
|
|
||||||
|
expect(checkedRows).toEqual(1);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('preview()', () => {
|
||||||
|
it('should show the summary dialog', () => {
|
||||||
|
controller.$.summary = {show: () => {}};
|
||||||
|
jest.spyOn(controller.$.summary, 'show');
|
||||||
|
|
||||||
|
let event = new MouseEvent('click', {
|
||||||
|
view: $window,
|
||||||
|
bubbles: true,
|
||||||
|
cancelable: true
|
||||||
|
});
|
||||||
|
const route = {id: 1};
|
||||||
|
|
||||||
|
controller.preview(event, route);
|
||||||
|
|
||||||
|
expect(controller.$.summary.show).toHaveBeenCalledWith();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('createInvoiceIn()', () => {
|
||||||
|
it('should throw an error if more than one autonomous are checked', () => {
|
||||||
|
jest.spyOn(controller.vnApp, 'showError');
|
||||||
|
const data = controller.$.model.data;
|
||||||
|
data[0].checked = true;
|
||||||
|
data[2].checked = true;
|
||||||
|
|
||||||
|
controller.createInvoiceIn();
|
||||||
|
|
||||||
|
expect(controller.vnApp.showError).toHaveBeenCalled();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should call the function go() on $state to go to the file management', () => {
|
||||||
|
jest.spyOn(controller.$state, 'go');
|
||||||
|
const data = controller.$.model.data;
|
||||||
|
data[0].checked = true;
|
||||||
|
|
||||||
|
controller.createInvoiceIn();
|
||||||
|
|
||||||
|
delete data[0].checked;
|
||||||
|
const params = JSON.stringify({
|
||||||
|
supplierName: data[0].supplierName,
|
||||||
|
rows: [data[0]]
|
||||||
|
});
|
||||||
|
|
||||||
|
expect(controller.$state.go).toHaveBeenCalledWith('route.agencyTerm.createInvoiceIn', {q: params});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
|
@ -0,0 +1,32 @@
|
||||||
|
@import "variables";
|
||||||
|
|
||||||
|
vn-item-product {
|
||||||
|
display: block;
|
||||||
|
|
||||||
|
.id {
|
||||||
|
background-color: $color-main;
|
||||||
|
color: $color-font-dark;
|
||||||
|
margin-bottom: 0;
|
||||||
|
}
|
||||||
|
.image {
|
||||||
|
height: 112px;
|
||||||
|
width: 112px;
|
||||||
|
|
||||||
|
& > img {
|
||||||
|
max-height: 100%;
|
||||||
|
max-width: 100%;
|
||||||
|
border-radius: 3px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
vn-label-value:first-of-type section{
|
||||||
|
margin-top: 9px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
table {
|
||||||
|
img {
|
||||||
|
border-radius: 50%;
|
||||||
|
width: 50px;
|
||||||
|
height: 50px;
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,5 @@
|
||||||
|
Agency route: Agencia ruta
|
||||||
|
Agency Agreement: Acuerdo agencia
|
||||||
|
Autonomous: Autónomos
|
||||||
|
Two autonomous cannot be counted at the same time: Dos autonónomos no pueden ser contabilizados al mismo tiempo
|
||||||
|
You cannot attach more than one document: No puedes adjuntar más de un documento
|
|
@ -11,4 +11,6 @@ import './create';
|
||||||
import './basic-data';
|
import './basic-data';
|
||||||
import './log';
|
import './log';
|
||||||
import './tickets';
|
import './tickets';
|
||||||
|
import './agency-term/index';
|
||||||
|
import './agency-term/createInvoiceIn';
|
||||||
import './ticket-popup';
|
import './ticket-popup';
|
||||||
|
|
|
@ -3,10 +3,11 @@
|
||||||
"name": "Routes",
|
"name": "Routes",
|
||||||
"icon": "icon-delivery",
|
"icon": "icon-delivery",
|
||||||
"validations" : true,
|
"validations" : true,
|
||||||
"dependencies": ["client", "worker", "ticket"],
|
"dependencies": ["client", "worker", "ticket", "supplier", "invoiceIn"],
|
||||||
"menus": {
|
"menus": {
|
||||||
"main": [
|
"main": [
|
||||||
{"state": "route.index", "icon": "icon-delivery"}
|
{"state": "route.index", "icon": "icon-delivery"},
|
||||||
|
{"state": "route.agencyTerm.index", "icon": "contact_support"}
|
||||||
],
|
],
|
||||||
"card": [
|
"card": [
|
||||||
{"state": "route.card.basicData", "icon": "settings"},
|
{"state": "route.card.basicData", "icon": "settings"},
|
||||||
|
@ -37,6 +38,26 @@
|
||||||
"state": "route.card",
|
"state": "route.card",
|
||||||
"abstract": true,
|
"abstract": true,
|
||||||
"component": "vn-route-card"
|
"component": "vn-route-card"
|
||||||
|
}, {
|
||||||
|
"url": "/agency-term",
|
||||||
|
"abstract": true,
|
||||||
|
"state": "route.agencyTerm",
|
||||||
|
"component": "ui-view"
|
||||||
|
}, {
|
||||||
|
"url": "/index",
|
||||||
|
"state": "route.agencyTerm.index",
|
||||||
|
"component": "vn-agency-term-index",
|
||||||
|
"description": "Autonomous",
|
||||||
|
"acl": ["administrative"]
|
||||||
|
},{
|
||||||
|
"url": "/createInvoiceIn?q",
|
||||||
|
"state": "route.agencyTerm.createInvoiceIn",
|
||||||
|
"component": "vn-agency-term-create-invoice-in",
|
||||||
|
"description": "File management",
|
||||||
|
"params": {
|
||||||
|
"route": "$ctrl.route"
|
||||||
|
},
|
||||||
|
"acl": ["administrative"]
|
||||||
}, {
|
}, {
|
||||||
"url": "/summary",
|
"url": "/summary",
|
||||||
"state": "route.card.summary",
|
"state": "route.card.summary",
|
||||||
|
|
|
@ -116,8 +116,8 @@ module.exports = Self => {
|
||||||
if (!isEditable)
|
if (!isEditable)
|
||||||
throw new UserError(`The sales of this ticket can't be modified`);
|
throw new UserError(`The sales of this ticket can't be modified`);
|
||||||
|
|
||||||
const isProductionBoss = await models.Account.hasRole(userId, 'productionBoss', myOptions);
|
const isDeliveryBoss = await models.Account.hasRole(userId, 'deliveryBoss', myOptions);
|
||||||
if (!isProductionBoss) {
|
if (!isDeliveryBoss) {
|
||||||
const zoneShipped = await models.Agency.getShipped(
|
const zoneShipped = await models.Agency.getShipped(
|
||||||
args.landed,
|
args.landed,
|
||||||
args.addressFk,
|
args.addressFk,
|
||||||
|
|
|
@ -31,10 +31,10 @@ module.exports = Self => {
|
||||||
}, myOptions);
|
}, myOptions);
|
||||||
|
|
||||||
const isSalesAssistant = await Self.app.models.Account.hasRole(userId, 'salesAssistant', myOptions);
|
const isSalesAssistant = await Self.app.models.Account.hasRole(userId, 'salesAssistant', myOptions);
|
||||||
const isProductionBoss = await Self.app.models.Account.hasRole(userId, 'productionBoss', myOptions);
|
const isDeliveryBoss = await Self.app.models.Account.hasRole(userId, 'deliveryBoss', myOptions);
|
||||||
const isBuyer = await Self.app.models.Account.hasRole(userId, 'buyer', myOptions);
|
const isBuyer = await Self.app.models.Account.hasRole(userId, 'buyer', myOptions);
|
||||||
|
|
||||||
const isValidRole = isSalesAssistant || isProductionBoss || isBuyer;
|
const isValidRole = isSalesAssistant || isDeliveryBoss || isBuyer;
|
||||||
|
|
||||||
let alertLevel = state ? state.alertLevel : null;
|
let alertLevel = state ? state.alertLevel : null;
|
||||||
let ticket = await Self.app.models.Ticket.findById(id, {
|
let ticket = await Self.app.models.Ticket.findById(id, {
|
||||||
|
|
|
@ -78,8 +78,8 @@ module.exports = Self => {
|
||||||
if (!isEditable)
|
if (!isEditable)
|
||||||
throw new UserError(`The sales of this ticket can't be modified`);
|
throw new UserError(`The sales of this ticket can't be modified`);
|
||||||
|
|
||||||
const isProductionBoss = await models.Account.hasRole(userId, 'productionBoss', myOptions);
|
const isDeliveryBoss = await models.Account.hasRole(userId, 'deliveryBoss', myOptions);
|
||||||
if (!isProductionBoss) {
|
if (!isDeliveryBoss) {
|
||||||
const zoneShipped = await models.Agency.getShipped(
|
const zoneShipped = await models.Agency.getShipped(
|
||||||
args.landed,
|
args.landed,
|
||||||
args.addressId,
|
args.addressId,
|
||||||
|
|
|
@ -24,7 +24,7 @@
|
||||||
<vn-th field="stateFk" >State</vn-th>
|
<vn-th field="stateFk" >State</vn-th>
|
||||||
<vn-th field="zoneFk">Zone</vn-th>
|
<vn-th field="zoneFk">Zone</vn-th>
|
||||||
<vn-th field="warehouseFk">Warehouse</vn-th>
|
<vn-th field="warehouseFk">Warehouse</vn-th>
|
||||||
<vn-th number>Total</vn-th>
|
<vn-th field="totalWithVat" number>Total</vn-th>
|
||||||
<vn-th></vn-th>
|
<vn-th></vn-th>
|
||||||
</vn-tr>
|
</vn-tr>
|
||||||
</vn-thead>
|
</vn-thead>
|
||||||
|
|
|
@ -69,14 +69,14 @@ class Controller extends Section {
|
||||||
if (!$events.length) return;
|
if (!$events.length) return;
|
||||||
|
|
||||||
const day = $days[0];
|
const day = $days[0];
|
||||||
const zonesIds = [];
|
const zoneIds = [];
|
||||||
for (let event of $events)
|
for (let event of $events)
|
||||||
zonesIds.push(event.zoneFk);
|
zoneIds.push(event.zoneFk);
|
||||||
|
|
||||||
this.$.zoneEvents.show($event.target);
|
this.$.zoneEvents.show($event.target);
|
||||||
|
|
||||||
const params = {
|
const params = {
|
||||||
zonesId: zonesIds,
|
zoneIds: zoneIds,
|
||||||
date: day
|
date: day
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -110,7 +110,7 @@ describe('Zone Component vnZoneDeliveryDays', () => {
|
||||||
{zoneFk: 8}
|
{zoneFk: 8}
|
||||||
];
|
];
|
||||||
const params = {
|
const params = {
|
||||||
zonesId: [1, 2, 8],
|
zoneIds: [1, 2, 8],
|
||||||
date: [day][0]
|
date: [day][0]
|
||||||
};
|
};
|
||||||
const response = [{id: 1, hour: ''}];
|
const response = [{id: 1, hour: ''}];
|
||||||
|
|
Loading…
Reference in New Issue