Merge branch 'dev' of https://gitea.verdnatura.es/verdnatura/salix into gulp-jasmine_deprecation

This commit is contained in:
Carlos Jimenez Ruiz 2022-05-13 18:37:48 +02:00
commit 990f7fe4b1
29 changed files with 628 additions and 51 deletions

View File

@ -0,0 +1,57 @@
const fs = require('fs-extra');
const path = require('path');
module.exports = Self => {
Self.remoteMethod('deleteTrashFiles', {
description: 'Deletes files that have trash type',
accessType: 'WRITE',
returns: {
type: 'object',
root: true
},
http: {
path: `/deleteTrashFiles`,
verb: 'POST'
}
});
Self.deleteTrashFiles = async(options) => {
const tx = await Self.beginTransaction({});
const myOptions = {};
if (typeof options == 'object')
Object.assign(myOptions, options);
if (!myOptions.transaction)
myOptions.transaction = tx;
try {
const models = Self.app.models;
const DmsContainer = models.DmsContainer;
const trashDmsType = await models.DmsType.findOne({
where: {code: 'trash'}
}, myOptions);
const dmsToDelete = await models.Dms.find({
where: {
dmsTypeFk: trashDmsType.id
}
}, myOptions);
for (let dms of dmsToDelete) {
const pathHash = DmsContainer.getHash(dms.id);
const dmsContainer = await DmsContainer.container(pathHash);
const dstFile = path.join(dmsContainer.client.root, pathHash, dms.file);
await fs.unlink(dstFile);
await dms.destroy(myOptions);
}
if (tx) await tx.commit();
} catch (e) {
if (tx) await tx.rollback();
throw e;
}
};
};

View File

@ -5,6 +5,7 @@ module.exports = Self => {
require('../methods/dms/uploadFile')(Self);
require('../methods/dms/removeFile')(Self);
require('../methods/dms/updateFile')(Self);
require('../methods/dms/deleteTrashFiles')(Self);
Self.checkRole = async function(ctx, id) {
const models = Self.app.models;

View File

@ -0,0 +1,2 @@
INSERT INTO `salix`.`ACL`(`model`, `property`, `accessType`, `permission`, `principalType`, `principalId`)
VALUES('ExpeditionState', '*', 'READ', 'ALLOW', 'ROLE', 'employee');

View File

@ -0,0 +1,5 @@
INSERT INTO `salix`.`ACL`(`model`, `property`, `accessType`, `permission`, `principalType`, `principalId`)
VALUES('Expense', '*', 'READ', 'ALLOW', 'ROLE', 'employee');
INSERT INTO `salix`.`ACL`(`model`, `property`, `accessType`, `permission`, `principalType`, `principalId`)
VALUES('Expense', '*', 'WRITE', 'ALLOW', 'ROLE', 'administrative');

View File

@ -0,0 +1,8 @@
ALTER TABLE vn.propertyDms DROP FOREIGN KEY propertyDms_FK;
ALTER TABLE vn.propertyDms ADD CONSTRAINT propertyDms_FK FOREIGN KEY (dmsFk) REFERENCES vn.dms(id) ON DELETE CASCADE ON UPDATE CASCADE;
ALTER TABLE vn.clientDms DROP FOREIGN KEY clientDms_ibfk_2;
ALTER TABLE vn.clientDms ADD CONSTRAINT clientDms_ibfk_2 FOREIGN KEY (dmsFk) REFERENCES vn.dms(id) ON DELETE CASCADE ON UPDATE CASCADE;
ALTER TABLE vn.workerDocument DROP FOREIGN KEY workerDocument_ibfk_2;
ALTER TABLE vn.workerDocument ADD CONSTRAINT workerDocument_ibfk_2 FOREIGN KEY (document) REFERENCES vn.dms(id) ON DELETE CASCADE ON UPDATE CASCADE;

View File

@ -0,0 +1,5 @@
ALTER TABLE vn.dmsType ADD monthToDelete INT UNSIGNED DEFAULT NULL NULL;
ALTER TABLE vn.dmsType MODIFY COLUMN monthToDelete int(10) unsigned DEFAULT NULL NULL COMMENT 'Meses en el pasado para ir borrando registros, dejar a null para no borrarlos nunca';
UPDATE vn.dmsType
SET monthToDelete=6
WHERE id=20;

View File

@ -0,0 +1,18 @@
DELIMITER $$
$$
CREATE TRIGGER dms_beforeDelete
BEFORE DELETE
ON dms FOR EACH ROW
BEGIN
DECLARE vCanNotBeDeleted INT;
SELECT COUNT(*) INTO vCanNotBeDeleted
FROM dmsType dt
WHERE NOT (code <=> 'trash')
AND dt.id = OLD.dmsTypeFk;
IF vCanNotBeDeleted THEN
CALL util.throw('A dms can not be deleted');
END IF;
END$$
DELIMITER ;

View File

@ -0,0 +1,175 @@
DROP PROCEDURE IF EXISTS vn.clean;
DELIMITER $$
$$
CREATE DEFINER=`root`@`localhost` PROCEDURE `vn`.`clean`()
BEGIN
DECLARE vDateShort DATETIME;
DECLARE vOneYearAgo DATE;
DECLARE vFourYearsAgo DATE;
DECLARE v18Month DATE;
DECLARE v26Month DATE;
DECLARE v3Month DATE;
DECLARE vTrashId varchar(15);
SET vDateShort = TIMESTAMPADD(MONTH, -2, CURDATE());
SET vOneYearAgo = TIMESTAMPADD(YEAR,-1,CURDATE());
SET vFourYearsAgo = TIMESTAMPADD(YEAR,-4,CURDATE());
SET v18Month = TIMESTAMPADD(MONTH, -18,CURDATE());
SET v26Month = TIMESTAMPADD(MONTH, -26,CURDATE());
SET v3Month = TIMESTAMPADD(MONTH, -3, CURDATE());
DELETE FROM ticketParking WHERE created < vDateShort;
DELETE FROM routesMonitor WHERE dated < vDateShort;
DELETE FROM workerTimeControlLog WHERE created < vDateShort;
DELETE FROM `message` WHERE sendDate < vDateShort;
DELETE FROM messageInbox WHERE sendDate < vDateShort;
DELETE FROM messageInbox WHERE sendDate < vDateShort;
DELETE FROM workerTimeControl WHERE timed < vFourYearsAgo;
DELETE FROM itemShelving WHERE created < CURDATE() AND visible = 0;
DELETE FROM ticketDown WHERE created < TIMESTAMPADD(DAY,-1,CURDATE());
DELETE FROM entryLog WHERE creationDate < vDateShort;
DELETE IGNORE FROM expedition WHERE created < v26Month;
DELETE FROM sms WHERE created < v18Month;
DELETE FROM saleTracking WHERE created < vOneYearAgo;
DELETE tobs FROM ticketObservation tobs
JOIN ticket t ON tobs.ticketFk = t.id WHERE t.shipped < TIMESTAMPADD(YEAR,-2,CURDATE());
DELETE sc.* FROM saleCloned sc JOIN sale s ON s.id = sc.saleClonedFk JOIN ticket t ON t.id = s.ticketFk WHERE t.shipped < vOneYearAgo;
DELETE FROM sharingCart where ended < vDateShort;
DELETE FROM sharingClient where ended < vDateShort;
DELETE tw.* FROM ticketWeekly tw
LEFT JOIN sale s ON s.ticketFk = tw.ticketFk WHERE s.itemFk IS NULL;
DELETE FROM claim WHERE ticketCreated < vFourYearsAgo;
DELETE FROM message WHERE sendDate < vDateShort;
-- Robert ubicacion anterior de trevelLog comentario para debug
DELETE sc FROM saleChecked sc
JOIN sale s ON sc.saleFk = s.id WHERE s.created < vDateShort;
DELETE FROM zoneEvent WHERE `type` = 'day' AND dated < v3Month;
DELETE bm
FROM buyMark bm
JOIN buy b ON b.id = bm.id
JOIN entry e ON e.id = b.entryFk
JOIN travel t ON t.id = e.travelFk
WHERE t.landed <= vDateShort;
DELETE FROM stowaway WHERE created < v3Month;
DELETE FROM vn.buy WHERE created < vDateShort AND entryFk = 9200;
DELETE FROM vn.itemShelvingLog WHERE created < vDateShort;
DELETE FROM vn.stockBuyed WHERE creationDate < vDateShort;
-- Equipos duplicados
DELETE w.*
FROM workerTeam w
JOIN (SELECT id, team, workerFk, COUNT(*) - 1 as duplicated
FROM workerTeam
GROUP BY team,workerFk
HAVING duplicated
) d ON d.team = w.team AND d.workerFk = w.workerFk AND d.id != w.id;
DELETE sc
FROM saleComponent sc
JOIN sale s ON s.id= sc.saleFk
JOIN ticket t ON t.id= s.ticketFk
WHERE t.shipped < v18Month;
DELETE c
FROM vn.claim c
JOIN vn.claimState cs ON cs.id = c.claimStateFk
WHERE cs.description = "Anulado" AND
c.created < vDateShort;
DELETE
FROM vn.expeditionTruck
WHERE ETD < v3Month;
-- borrar travels sin entradas
DROP TEMPORARY TABLE IF EXISTS tmp.thermographToDelete;
CREATE TEMPORARY TABLE tmp.thermographToDelete
SELECT th.id,th.dmsFk
FROM vn.travel t
LEFT JOIN vn.entry e ON e.travelFk = t.id
JOIN vn.travelThermograph th ON th.travelFk = t.id
WHERE t.shipped < TIMESTAMPADD(MONTH, -3, CURDATE()) AND e.travelFk IS NULL;
SELECT dt.id into vTrashId
FROM vn.dmsType dt
WHERE dt.code = 'trash';
UPDATE tmp.thermographToDelete th
JOIN vn.dms d ON d.id = th.dmsFk
SET d.dmsTypeFk = vTrashId;
DELETE th
FROM tmp.thermographToDelete tmp
JOIN vn.travelThermograph th ON th.id = tmp.id;
DELETE t
FROM vn.travel t
LEFT JOIN vn.entry e ON e.travelFk = t.id
WHERE t.shipped < TIMESTAMPADD(MONTH, -3, CURDATE()) AND e.travelFk IS NULL;
UPDATE dms d
JOIN dmsType dt ON dt.id = d.dmsTypeFk
SET d.dmsTypeFk = vTrashId
WHERE created < TIMESTAMPADD(MONTH, -dt.monthToDelete, CURDATE());
-- borrar entradas sin compras
DROP TEMPORARY TABLE IF EXISTS tmp.entryToDelete;
CREATE TEMPORARY TABLE tmp.entryToDelete
SELECT e.*
FROM vn.entry e
LEFT JOIN vn.buy b ON b.entryFk = e.id
JOIN vn.entryConfig ec ON e.id != ec.defaultEntry
WHERE e.dated < TIMESTAMPADD(MONTH, -3, CURDATE()) AND b.entryFK IS NULL;
DELETE e
FROM vn.entry e
JOIN tmp.entryToDelete tmp ON tmp.id = e.id;
-- borrar de route registros menores a 4 años
DROP TEMPORARY TABLE IF EXISTS tmp.routeToDelete;
CREATE TEMPORARY TABLE tmp.routeToDelete
SELECT *
FROM vn.route r
WHERE created < TIMESTAMPADD(YEAR,-4,CURDATE());
UPDATE tmp.routeToDelete tmp
JOIN vn.dms d ON d.id = tmp.gestdocFk
SET d.dmsTypeFk = vTrashId;
DELETE r
FROM tmp.routeToDelete tmp
JOIN vn.route r ON r.id = tmp.id;
-- borrar registros de dua y awb menores a 2 años
DROP TEMPORARY TABLE IF EXISTS tmp.duaToDelete;
CREATE TEMPORARY TABLE tmp.duaToDelete
SELECT *
FROM vn.dua
WHERE operated < TIMESTAMPADD(YEAR,-2,CURDATE());
UPDATE tmp.duaToDelete tm
JOIN vn.dms d ON d.id = tm.gestdocFk
SET d.dmsTypeFk = vTrashId;
DELETE d
FROM tmp.duaToDelete tmp
JOIN vn.dua d ON d.id = tmp.id;
DELETE FROM vn.awb WHERE created < TIMESTAMPADD(YEAR,-2,CURDATE());
-- Borra los ficheros gestDoc
INSERT INTO vn.printServerQueue(priorityFk, labelReportFk)VALUES(1,11);
-- Borra los registros de collection y ticketcollection
DELETE FROM vn.collection WHERE created < vDateShort;
DROP TEMPORARY TABLE IF EXISTS tmp.thermographToDelete;
DROP TEMPORARY TABLE IF EXISTS tmp.entryToDelete;
DROP TEMPORARY TABLE IF EXISTS tmp.duaToDelete;
DELETE FROM travelLog WHERE creationDate < v3Month;
CALL shelving_clean;
END$$
DELIMITER ;

View File

@ -0,0 +1,2 @@
INSERT INTO salix.ACL (model,property,accessType,permission,principalType,principalId)
VALUES ('Dms','deleteTrashFiles','WRITE','ALLOW','ROLE','employee')

View File

@ -854,18 +854,35 @@ INSERT INTO `vn`.`packaging`(`id`, `volume`, `width`, `height`, `depth`, `isPack
('cc', 1640038.00, 56.00, 220.00, 128.00, 1, CURDATE(), 15, 90.00),
('pallet 100', 2745600.00, 100.00, 220.00, 120.00, 1, CURDATE(), 16, 0.00);
INSERT INTO `vn`.`expedition`(`id`, `agencyModeFk`, `ticketFk`, `isBox`, `created`, `itemFk`, `counter`, `checked`, `workerFk`, `externalId`, `packagingFk`)
INSERT INTO `vn`.`expeditionStateType`(`id`, `description`, `code`)
VALUES
(1, 1, 1, 71, DATE_ADD(CURDATE(), INTERVAL -1 MONTH), 15, 1, 1, 18, 'UR9000006041', 94),
(2, 1, 1, 71, DATE_ADD(CURDATE(), INTERVAL -1 MONTH), 16, 2, 1, 18, 'UR9000006041', 94),
(3, 1, 1, 71, DATE_ADD(CURDATE(), INTERVAL -1 MONTH), NULL, 3, 1, 18, 'UR9000006041', 94),
(4, 1, 1, 71, DATE_ADD(CURDATE(), INTERVAL -1 MONTH), NULL, 4, 1, 18, 'UR9000006041', 94),
(5, 1, 2, 71, DATE_ADD(CURDATE(), INTERVAL -1 MONTH), NULL, 1, 1, 18, NULL, 94),
(6, 7, 3, 71, DATE_ADD(CURDATE(), INTERVAL -2 MONTH), NULL, 1, 1, 18, NULL, 94),
(7, 2, 4, 71, DATE_ADD(CURDATE(), INTERVAL -3 MONTH), NULL, 1, 1, 18, NULL, 94),
(8, 3, 5, 71, DATE_ADD(CURDATE(), INTERVAL -4 MONTH), NULL, 1, 1, 18, NULL, 94),
(9, 3, 6, 71, DATE_ADD(CURDATE(), INTERVAL -1 MONTH), NULL, 1, 1, 18, NULL, 94),
(10, 7, 7, 71, NOW(), NULL, 1, 1, 18, NULL, 94);
(1, 'En reparto', 'ON DELIVERY'),
(2, 'Entregada', 'DELIVERED'),
(3, 'Perdida', 'LOST');
INSERT INTO `vn`.`expedition`(`id`, `agencyModeFk`, `ticketFk`, `isBox`, `created`, `itemFk`, `counter`, `checked`, `workerFk`, `externalId`, `packagingFk`, `stateTypeFk`)
VALUES
(1, 1, 1, 71, DATE_ADD(CURDATE(), INTERVAL -1 MONTH), 15, 1, 1, 18, 'UR9000006041', 94, 1),
(2, 1, 1, 71, DATE_ADD(CURDATE(), INTERVAL -1 MONTH), 16, 2, 1, 18, 'UR9000006041', 94, 1),
(3, 1, 1, 71, DATE_ADD(CURDATE(), INTERVAL -1 MONTH), NULL, 3, 1, 18, 'UR9000006041', 94, 2),
(4, 1, 1, 71, DATE_ADD(CURDATE(), INTERVAL -1 MONTH), NULL, 4, 1, 18, 'UR9000006041', 94, 2),
(5, 1, 2, 71, DATE_ADD(CURDATE(), INTERVAL -1 MONTH), NULL, 1, 1, 18, NULL, 94, 3),
(6, 7, 3, 71, DATE_ADD(CURDATE(), INTERVAL -2 MONTH), NULL, 1, 1, 18, NULL, 94, 3),
(7, 2, 4, 71, DATE_ADD(CURDATE(), INTERVAL -3 MONTH), NULL, 1, 1, 18, NULL, 94, NULL),
(8, 3, 5, 71, DATE_ADD(CURDATE(), INTERVAL -4 MONTH), NULL, 1, 1, 18, NULL, 94, 1),
(9, 3, 6, 71, DATE_ADD(CURDATE(), INTERVAL -1 MONTH), NULL, 1, 1, 18, NULL, 94, 2),
(10, 7, 7, 71, NOW(), NULL, 1, 1, 18, NULL, 94, 3);
INSERT INTO `vn`.`expeditionState`(`id`, `created`, `expeditionFk`, `typeFk`, `userFk`)
VALUES
(1, CURDATE(), 1, 1, 1),
(2, CURDATE(), 2, 1, 1),
(3, CURDATE(), 3, 1, 1),
(4, CURDATE(), 3, 2, 1106),
(5, CURDATE(), 5, 1, 1106),
(6, CURDATE(), 5, 3, 1106);
INSERT INTO `vn`.`ticketPackaging`(`id`, `ticketFk`, `packagingFk`, `quantity`, `created`, `pvp`)
VALUES

View File

@ -982,7 +982,7 @@ export default {
save: 'vn-invoice-in-basic-data button[type=submit]'
},
invoiceInTax: {
addTaxButton: 'vn-invoice-in-tax vn-icon-button[icon="add_circle"]',
addTaxButton: 'vn-invoice-in-tax vn-icon-button[vn-tooltip="Add tax"]',
thirdExpense: 'vn-invoice-in-tax vn-horizontal:nth-child(3) > vn-autocomplete[ng-model="invoiceInTax.expenseFk"]',
thirdTaxableBase: 'vn-invoice-in-tax vn-horizontal:nth-child(3) > vn-input-number[ng-model="invoiceInTax.taxableBase"]',
thirdTaxType: 'vn-invoice-in-tax vn-horizontal:nth-child(3) > vn-autocomplete[ng-model="invoiceInTax.taxTypeSageFk"]',

View File

@ -25,6 +25,9 @@
ng-model="invoiceInDueDay.bankFk"
url="Banks"
show-field="bank"
select-fields="['id','bank']"
order="id"
search-function="$ctrl.bankSearchFunc($search)"
rule>
<tpl-item>{{id}}: {{bank}}</tpl-item>
</vn-autocomplete>

View File

@ -17,6 +17,12 @@ class Controller extends Section {
this.card.reload();
});
}
bankSearchFunc($search) {
return /^\d+$/.test($search)
? {id: $search}
: {bank: {like: '%' + $search + '%'}};
}
}
ngModule.vnComponent('vnInvoiceInDueDay', {

View File

@ -33,6 +33,13 @@
show-field="id"
rule>
<tpl-item>{{id}}: {{name}}</tpl-item>
<append>
<vn-icon-button
vn-tooltip="Create expense"
icon="add_circle"
vn-click-stop="createExpense.show()">
</vn-icon-button>
</append>
</vn-autocomplete>
<vn-input-number vn-one
disabled="$ctrl.invoiceIn.currency.code != 'EUR'"
@ -97,3 +104,40 @@
</vn-submit>
</vn-button-bar>
</form>
<!-- Dialog of create expense-->
<vn-dialog
vn-id="createExpense"
on-accept="$ctrl.onResponse()">
<tpl-body>
<section>
<h5 class="vn-py-sm">{{$ctrl.$t('New expense')}}</h5>
<vn-horizontal>
<vn-textfield vn-one
vn-id="code"
label="Code"
ng-model="$ctrl.expense.code"
required="true"
vn-focus>
</vn-textfield>
<vn-check
vn-one
label="It's a withholding"
ng-model="$ctrl.expense.isWithheld">
</vn-check>
</vn-horizontal>
<vn-horizontal>
<vn-textfield vn-one
vn-id="description"
label="Description"
ng-model="$ctrl.expense.description"
required="true">
</vn-textfield>
</vn-horizontal>
</section>
</tpl-body>
<tpl-buttons>
<input type="button" response="cancel" translate-attr="{value: 'Cancel'}"/>
<button response="accept" translate>Save</button>
</tpl-buttons>
</vn-dialog>

View File

@ -1,7 +1,12 @@
import ngModule from '../module';
import Section from 'salix/components/section';
import UserError from 'core/lib/user-error';
class Controller extends Section {
constructor($element, $, vnWeekDays) {
super($element, $);
this.expense = {};
}
taxRate(invoiceInTax, taxRateSelection) {
const taxTypeSage = taxRateSelection && taxRateSelection.rate;
const taxableBase = invoiceInTax && invoiceInTax.taxableBase;
@ -26,6 +31,27 @@ class Controller extends Section {
this.card.reload();
});
}
onResponse() {
try {
if (!this.expense.code)
throw new Error(`The code can't be empty`);
if (!this.expense.description)
throw new UserError(`The description can't be empty`);
const data = [{
id: this.expense.code,
isWithheld: this.expense.isWithheld,
name: this.expense.description
}];
this.$http.post(`Expenses`, data) .then(() => {
this.vnApp.showSuccess(this.$t('Expense saved!'));
});
} catch (e) {
this.vnApp.showError(this.$t(e.message));
}
}
}
ngModule.vnComponent('vnInvoiceInTax', {

View File

@ -1,16 +1,19 @@
import './index.js';
import watcher from 'core/mocks/watcher';
import crudModel from 'core/mocks/crud-model';
const UserError = require('vn-loopback/util/user-error');
describe('InvoiceIn', () => {
describe('Component tax', () => {
let controller;
let $scope;
let vnApp;
let $httpBackend;
beforeEach(ngModule('invoiceIn'));
beforeEach(inject(($componentController, $rootScope, _vnApp_) => {
beforeEach(inject(($componentController, $rootScope, _vnApp_, _$httpBackend_) => {
$httpBackend = _$httpBackend_;
vnApp = _vnApp_;
jest.spyOn(vnApp, 'showError');
$scope = $rootScope.$new();
@ -19,6 +22,7 @@ describe('InvoiceIn', () => {
const $element = angular.element('<vn-invoice-in-tax></vn-invoice-in-tax>');
controller = $componentController('vnInvoiceInTax', {$element, $scope});
controller.$.model = crudModel;
controller.invoiceIn = {id: 1};
}));
@ -55,5 +59,56 @@ describe('InvoiceIn', () => {
expect(controller.card.reload).toHaveBeenCalledWith();
});
});
describe('onResponse()', () => {
it('should return success message', () => {
controller.expense = {
code: 7050000005,
isWithheld: 0,
description: 'Test'
};
const data = [{
id: controller.expense.code,
isWithheld: controller.expense.isWithheld,
name: controller.expense.description
}];
jest.spyOn(controller.vnApp, 'showSuccess');
$httpBackend.expect('POST', `Expenses`, data).respond();
controller.onResponse();
$httpBackend.flush();
expect(controller.vnApp.showSuccess).toHaveBeenCalledWith('Expense saved!');
});
it('should return an error if code is empty', () => {
controller.expense = {
code: null,
isWithheld: 0,
description: 'Test'
};
jest.spyOn(controller.vnApp, 'showError');
controller.onResponse();
expect(controller.vnApp.showError).toHaveBeenCalledWith(`The code can't be empty`);
});
it('should return an error if description is empty', () => {
controller.expense = {
code: 7050000005,
isWithheld: 0,
description: null
};
jest.spyOn(controller.vnApp, 'showError');
controller.onResponse();
expect(controller.vnApp.showError).toHaveBeenCalledWith(`The description can't be empty`);
});
});
});
});

View File

@ -0,0 +1,7 @@
Create expense: Crear gasto
New expense: Nuevo gasto
It's a withholding: Es una retención
The fields can't be empty: Los campos no pueden estar vacíos
The code can't be empty: El código no puede estar vacío
The description can't be empty: La descripción no puede estar vacía
Expense saved!: Gasto guardado!

View File

@ -17,9 +17,6 @@
},
"isWithheld": {
"type": "number"
},
"taxTypeFk": {
"type": "number"
}
},
"relations": {
@ -28,13 +25,5 @@
"model": "TaxType",
"foreignKey": "taxTypeFk"
}
},
"acls": [
{
"accessType": "READ",
"principalType": "ROLE",
"principalId": "$everyone",
"permission": "ALLOW"
}
]
}

View File

@ -12,12 +12,12 @@
{"state": "supplier.card.basicData", "icon": "settings"},
{"state": "supplier.card.fiscalData", "icon": "account_balance"},
{"state": "supplier.card.billingData", "icon": "icon-payment"},
{"state": "supplier.card.address.index", "icon": "icon-delivery"},
{"state": "supplier.card.log", "icon": "history"},
{"state": "supplier.card.account", "icon": "icon-account"},
{"state": "supplier.card.contact", "icon": "contact_phone"},
{"state": "supplier.card.agencyTerm.index", "icon": "icon-agency-term"},
{"state": "supplier.card.log", "icon": "history"},
{"state": "supplier.card.consumption", "icon": "show_chart"}
{"state": "supplier.card.address.index", "icon": "icon-delivery"},
{"state": "supplier.card.consumption", "icon": "show_chart"},
{"state": "supplier.card.agencyTerm.index", "icon": "icon-agency-term"}
]
},
"keybindings": [
@ -97,7 +97,7 @@
"url": "/index",
"state": "supplier.card.agencyTerm.index",
"component": "vn-supplier-agency-term-index",
"description": "Autonomous",
"description": "Agency Agreement",
"params": {
"supplier": "$ctrl.supplier"
}

View File

@ -0,0 +1,41 @@
const ParameterizedSQL = require('loopback-connector').ParameterizedSQL;
module.exports = Self => {
Self.remoteMethod('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(filter, options) => {
const myOptions = {};
if (typeof options == 'object')
Object.assign(myOptions, options);
const stmt = new ParameterizedSQL(
`SELECT es.created, u.name, u.id workerFk, est.description state
FROM vn.expeditionState es
JOIN vn.expeditionStateType est ON est.id = es.typeFk
JOIN account.user u ON u.id = es.userFk
`);
stmt.merge(Self.buildSuffix(filter, 'es'));
return Self.rawStmt(stmt, myOptions);
};
};

View File

@ -0,0 +1,21 @@
const models = require('vn-loopback/server/server').models;
describe('expeditionState filter()', () => {
it('should return the expedition states matching the filter', async() => {
const tx = await models.ExpeditionState.beginTransaction({});
try {
const options = {transaction: tx};
const filter = {where: {expeditionFk: 5}};
const response = await models.ExpeditionState.filter(filter, options);
expect(response.length).toEqual(2);
await tx.rollback();
} catch (e) {
await tx.rollback();
throw e;
}
});
});

View File

@ -47,9 +47,10 @@ module.exports = Self => {
e.packagingFk,
es.workerFk expeditionScanWorkerFk,
su.name scannerUserName,
es.scanned
FROM
vn.expedition e
es.scanned,
est.description state
FROM vn.expedition e
LEFT JOIN vn.expeditionStateType est ON est.id = e.stateTypeFk
LEFT JOIN vn.item i2 ON i2.id = e.itemFk
INNER JOIN vn.item i1 ON i1.id = e.isBox
LEFT JOIN vn.packaging p ON p.id = e.packagingFk

View File

@ -14,6 +14,9 @@
"Expedition": {
"dataSource": "vn"
},
"ExpeditionState": {
"dataSource": "vn"
},
"Packaging": {
"dataSource": "vn"
},

View File

@ -0,0 +1,3 @@
module.exports = function(Self) {
require('../methods/expedition-state/filter')(Self);
};

View File

@ -0,0 +1,28 @@
{
"name": "ExpeditionState",
"base": "VnModel",
"options": {
"mysql": {
"table": "expeditionState"
}
},
"properties": {
"id": {
"id": true,
"type": "number",
"description": "Identifier"
},
"created": {
"type": "date"
},
"expeditionFk": {
"type": "number"
},
"typeFk": {
"type": "number"
},
"userFk": {
"type": "number"
}
}
}

View File

@ -19,10 +19,9 @@
<vn-th field="freightItemName">Package type</vn-th>
<vn-th field="counter" number>Counter</vn-th>
<vn-th field="externalId" number>externalId</vn-th>
<vn-th field="workerFk">Packager</vn-th>
<vn-th field="created" expand>Created</vn-th>
<vn-th field="expeditionScanWorkerFk">Palletizer</vn-th>
<vn-th field="scanned" expand>Scanned</vn-th>
<vn-th field="state" expand>State</vn-th>
<vn-th></vn-th>
</vn-tr>
</vn-thead>
<vn-tbody>
@ -33,7 +32,7 @@
vn-tooltip="Delete expedition">
</vn-icon-button>
</vn-td>
<vn-td number>{{expedition.id | zeroFill:6}}</vn-td>
<vn-td number expand>{{expedition.id | zeroFill:6}}</vn-td>
<vn-td number>
<span
ng-class="{link: expedition.packagingItemFk}"
@ -45,20 +44,15 @@
<vn-td>{{::expedition.freightItemName}}</vn-td>
<vn-td number>{{::expedition.counter}}</vn-td>
<vn-td expand>{{::expedition.externalId}}</vn-td>
<vn-td>
<span
class="link"
ng-click="workerDescriptor.show($event, expedition.workerFk)">
{{::expedition.userName | dashIfEmpty}}
</span>
</vn-td>
<vn-td shrink-datetime>{{::expedition.created | date:'dd/MM/yyyy HH:mm'}}</vn-td>
<vn-td>{{::expedition.state}}</vn-td>
<vn-td>
<span class="link" ng-click="workerDescriptor.show($event, expedition.expeditionScanWorkerFk)">
{{::expedition.scannerUserName | dashIfEmpty}}
</span>
<vn-icon-button
vn-click-stop="$ctrl.showLog(expedition)"
vn-tooltip="Status log"
icon="history">
</vn-icon-button>
</vn-td>
<vn-td shrink-datetime>{{::expedition.scanned | date:'dd/MM/yyyy HH:mm'}}</vn-td>
</vn-tr>
</vn-tbody>
</vn-table>
@ -78,3 +72,43 @@
question="Delete expedition"
message="Are you sure you want to delete this expedition?">
</vn-confirm>
<vn-popup vn-id="statusLog">
<vn-crud-model
vn-id="model"
url="ExpeditionStates/filter"
link="{expeditionFk: $ctrl.expedition.id}"
data="expeditionStates"
order="created DESC"
auto-load="true">
</vn-crud-model>
<vn-data-viewer model="model">
<vn-card class="vn-w-md">
<vn-table model="model">
<vn-thead>
<vn-tr>
<vn-th field="state">State</vn-th>
<vn-th field="worker">Worker</vn-th>
<vn-th field="created">Created</vn-th>
</vn-tr>
</vn-thead>
<vn-tbody>
<vn-tr ng-repeat="expeditionState in expeditionStates">
<vn-td>{{::expeditionState.state}}</vn-td>
<vn-td expand>
<span
ng-class="{'link': expeditionState.workerFk}"
ng-click="workerDescriptor.show($event, expeditionState.workerFk)">
{{::expeditionState.name || 'System' | translate}}
</span>
</vn-td>
<vn-td shrink-datetime>{{::expeditionState.created | date:'dd/MM/yyyy HH:mm'}}</vn-td>
</vn-tr>
</vn-tbody>
</vn-table>
</vn-card>
</vn-data-viewer>
<vn-worker-descriptor-popover
vn-id="workerDescriptor">
</vn-worker-descriptor-popover>
</vn-popup>

View File

@ -6,6 +6,11 @@ class Controller extends Section {
return this.$http.delete(`Expeditions/${id}`)
.then(() => this.$.model.refresh());
}
showLog(expedition) {
this.expedition = expedition;
this.$.statusLog.show();
}
}
ngModule.vnComponent('vnTicketExpedition', {

View File

@ -5,10 +5,12 @@ describe('Ticket', () => {
let controller;
let $scope;
let $httpBackend;
let $window;
beforeEach(ngModule('ticket'));
beforeEach(inject(($componentController, $rootScope, _$httpBackend_) => {
beforeEach(inject(($componentController, $rootScope, _$httpBackend_, _$window_) => {
$window = _$window_;
$httpBackend = _$httpBackend_;
$scope = $rootScope.$new();
$scope.model = {
@ -30,5 +32,23 @@ describe('Ticket', () => {
expect($scope.model.refresh).toHaveBeenCalledWith();
});
});
describe('showLog()', () => {
it('should show the popover status log', () => {
controller.$.statusLog = {show: () => {}};
jest.spyOn(controller.$.statusLog, 'show');
const expedition = {id: 1};
const event = new MouseEvent('click', {
view: $window,
bubbles: true,
cancelable: true
});
controller.showLog(event, expedition);
expect(controller.$.statusLog.show).toHaveBeenCalledWith();
});
});
});
});

View File

@ -0,0 +1 @@
Status log: Hitorial de estados