Merge branch 'dev' of https://gitea.verdnatura.es/verdnatura/salix into 3686-client_create
gitea/salix/pipeline/head This commit looks good Details

This commit is contained in:
Vicent Llopis 2022-03-30 12:35:37 +02:00
commit b29261cb75
47 changed files with 2276 additions and 1145 deletions

View File

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

View File

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

View File

@ -0,0 +1,3 @@
INSERT INTO `salix`.`ACL` (`model`, `property`, `accessType`, `permission`, `principalType`, `principalId`)
VALUES
('SupplierAgencyTerm', '*', '*', 'ALLOW', 'ROLE', 'administrative');

View File

@ -0,0 +1,48 @@
ALTER TABLE `vn`.`agencyTerm` ADD `supplierFk` INT NULL;
ALTER TABLE `vn`.`agencyTerm` CHANGE `supplierFk` `supplierFk` INT NULL AFTER `agencyFk`;
UPDATE `vn`.`agencyTerm` `at`
JOIN `vn`.`agency` `a` ON `a`.`id` = `at`.`agencyFk`
SET `at`.`supplierFk` = `a`.`supplierFk`;
ALTER TABLE `vn`.`agencyTerm` ADD CONSTRAINT `agencyTerm_FK` FOREIGN KEY (`agencyFk`) REFERENCES `vn`.`agency`(`id`) ON DELETE RESTRICT ON UPDATE CASCADE;
ALTER TABLE `vn`.`agencyTerm` ADD CONSTRAINT `agencyTerm_FK_1` FOREIGN KEY (`supplierFk`) REFERENCES `vn`.`supplier`(`id`) ON DELETE RESTRICT ON UPDATE CASCADE;
RENAME TABLE `vn`.`agencyTerm` TO `vn`.`supplierAgencyTerm`;
CREATE OR REPLACE
ALGORITHM = UNDEFINED
DEFINER=`root`@`localhost`
VIEW `vn`.`agencyTerm` AS
SELECT
`sat`.`agencyFk` AS `agencyFk`,
`sat`.`minimumPackages` AS `minimumPackages`,
`sat`.`kmPrice` AS `kmPrice`,
`sat`.`packagePrice` AS `packagePrice`,
`sat`.`routePrice` AS `routePrice`,
`sat`.`minimumKm` AS `minimumKm`,
`sat`.`minimumM3` AS `minimumM3`,
`sat`.`m3Price` AS `m3Price`
FROM
`vn`.`supplierAgencyTerm` `sat`;
ALTER TABLE `vn`.`agency` DROP FOREIGN KEY `agency_ibfk_4`;
ALTER TABLE `vn`.`agency` CHANGE `supplierFk` `supplierFk__` int(11) DEFAULT NULL NULL;
CREATE OR REPLACE
ALGORITHM = UNDEFINED
DEFINER=`root`@`localhost`
VIEW `vn2008`.`agency` AS
SELECT
`a`.`id` AS `agency_id`,
`a`.`name` AS `name`,
`a`.`warehouseFk` AS `warehouse_id`,
`a`.`isVolumetric` AS `por_volumen`,
`a`.`bankFk` AS `Id_Banco`,
`a`.`warehouseAliasFk` AS `warehouse_alias_id`,
`a`.`isOwn` AS `propios`,
`a`.`labelZone` AS `zone_label`,
`a`.`workCenterFk` AS `workCenterFk`,
`a`.`supplierFk__` AS `supplierFk__`
FROM
`vn`.`agency` `a`;

View File

@ -0,0 +1,3 @@
ALTER TABLE `postgresql`.`business_labour_payroll` DROP FOREIGN KEY `business_labour_payroll_cod_contrato`;
ALTER TABLE `vn`.`workerBusinessType` MODIFY COLUMN `id` int(11) NOT NULL;
ALTER TABLE `postgresql`.`business_labour_payroll` ADD CONSTRAINT `business_labour_payroll_FK` FOREIGN KEY (cod_contrato) REFERENCES `vn`.`workerBusinessType`(`id`) ON DELETE RESTRICT ON UPDATE CASCADE;

File diff suppressed because one or more lines are too long

View File

@ -1902,14 +1902,29 @@ INSERT INTO `vn`.`workCenterHoliday` (`workCenterFk`, `days`, `year`)
('1', '24.5', YEAR(DATE_ADD(CURDATE(), INTERVAL -1 YEAR))),
('5', '23', YEAR(DATE_ADD(CURDATE(), INTERVAL -1 YEAR)));
INSERT INTO `postgresql`.`calendar_state` (`calendar_state_id`, `type`, `rgb`, `code`, `holidayEntitlementRate`)
INSERT INTO `postgresql`.`calendar_state` (`calendar_state_id`, `type`, `rgb`, `code`, `holidayEntitlementRate`, `discountRate`)
VALUES
(1, 'Holidays', '#FF4444', 'holiday', 0),
(2, 'Leave of absence', '#C71585', 'absence', 0),
(6, 'Half holiday', '#E65F00', 'halfHoliday', 0),
(15, 'Half Paid Leave', '#5151c0', 'halfPaidLeave', 0),
(20, 'Furlough', '#97B92F', 'furlough', 1),
(21, 'Furlough half day', '#778899', 'halfFurlough', 0.5);
(1, 'Holidays', '#FF4444', 'holiday', 0, 0),
(2, 'Leave of absence', '#C71585', 'absence', 0, 1),
(6, 'Half holiday', '#E65F00', 'halfHoliday', 0, 0.5),
(15, 'Half Paid Leave', '#5151c0', 'halfPaidLeave', 0, 1),
(20, 'Furlough', '#97B92F', 'furlough', 1, 1),
(21, 'Furlough half day', '#778899', 'halfFurlough', 0.5, 1);
ALTER TABLE `postgresql`.`business_labour_payroll` DROP FOREIGN KEY `business_labour_payroll_cod_categoria`;
INSERT INTO `vn`.`workerBusinessType` (`id`, `name`, `isFullTime`, `isPermanent`, `hasHolidayEntitlement`)
VALUES
(1, 'CONTRATO HOLANDA', 1, 0, 1),
(100, 'INDEFINIDO A TIEMPO COMPLETO', 1, 1, 1),
(109, 'CONVERSION DE TEMPORAL EN INDEFINIDO T.COMPLETO', 1, 1, 1);
INSERT INTO `postgresql`.`business_labour_payroll` (`business_id`, `cod_tarifa`, `cod_categoria`, `cod_contrato`, `importepactado`)
VALUES
(1, 7, 12, 100, 900.50),
(1106, 7, 12, 100, 1263.03),
(1107, 7, 12, 100, 2000),
(1108, 7, 12, 100, 1500);
INSERT INTO `postgresql`.`calendar_employee` (`business_id`, `calendar_state_id`, `date`)
VALUES
@ -2462,28 +2477,25 @@ INSERT INTO `bs`.`defaulter` (`clientFk`, `amount`, `created`, `defaulterSinced`
(1107, 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`=1;
SET `supplierFk`=1
WHERE `id`=2;
UPDATE `vn`.`agency`
SET `supplierFk`=1
WHERE `id`=2;
UPDATE `vn`.`agency`
SET `supplierFk`=2
WHERE `id`=3;
SET `supplierFk`=2
WHERE `id`=3;
UPDATE `vn`.`route`
SET `invoiceInFk`=1
WHERE `id`=1;
SET `invoiceInFk`=1
WHERE `id`=1;
UPDATE `vn`.`route`
SET `invoiceInFk`=2
WHERE `id`=2;
SET `invoiceInFk`=2
WHERE `id`=2;
INSERT INTO `bs`.`salesPerson` (`workerFk`, `year`, `month`, `portfolioWeight`)
VALUES
(18, YEAR(CURDATE()), MONTH(CURDATE()), 807.23),
@ -2496,6 +2508,7 @@ INSERT INTO `bs`.`sale` (`saleFk`, `amount`, `dated`, `typeFk`, `clientFk`)
(3, 200.78, CURDATE(), 2, 1101),
(4, 33.8, CURDATE(), 1, 1101),
(30, 34.4, CURDATE(), 1, 1108);
INSERT INTO `vn`.`docuware` (`code`, `fileCabinetName`, `dialogName` , `find`)
VALUES
('deliveryClientTest', 'deliveryClientTest', 'findTest', 'word');
@ -2503,3 +2516,23 @@ INSERT INTO `vn`.`docuware` (`code`, `fileCabinetName`, `dialogName` , `find`)
INSERT INTO `vn`.`docuwareConfig` (`url`)
VALUES
('https://verdnatura.docuware.cloud/docuware/platform');
INSERT INTO `vn`.`calendarHolidaysName` (`id`, `name`)
VALUES
(1, 'dayOfIT');
INSERT INTO `vn`.`calendarHolidaysType` (`id`, `name`, `hexColour`)
VALUES
(1, 'National', '#4169E1');
INSERT INTO `vn`.`calendarHolidays` (`id`, `calendarHolidaysTypeFk`, `dated`, `calendarHolidaysNameFk`, `workCenterFk`)
VALUES
(1, 1, CONCAT(YEAR(CURDATE()), '-12-09'), 1, 1);
INSERT INTO `vn`.`supplierAgencyTerm` (`agencyFk`, `supplierFk`, `minimumPackages`, `kmPrice`, `packagePrice`, `routePrice`, `minimumKm`, `minimumM3`, `m3Price`)
VALUES
(1, 1, 0, 0.00, 0.00, NULL, 0, 0.00, 23),
(2, 1, 60, 0.00, 0.00, NULL, 0, 5.00, 33),
(3, 2, 0, 15.00, 0.00, NULL, 0, 0.00, 0),
(4, 2, 0, 20.00, 0.00, NULL, 0, 0.00, 0),
(5, 442, 0, 0.00, 3.05, NULL, 0, 0.00, 0);

File diff suppressed because it is too large Load Diff

View File

@ -66,7 +66,7 @@ describe('Claim development', () => {
.waitToGetProperty(selectors.claimDevelopment.firstClaimRedelivery, 'value');
expect(reason).toEqual('Calor');
expect(result).toEqual('Cocido');
expect(result).toEqual('Baboso/Cocido');
expect(responsible).toEqual('Calidad general');
expect(worker).toEqual('adminAssistantNick');
expect(redelivery).toEqual('Cliente');

View File

@ -50,7 +50,7 @@ describe('InvoiceOut manual invoice path', () => {
});
it('should create an invoice from a client', async() => {
await page.autocompleteSearch(selectors.invoiceOutIndex.manualInvoiceClient, 'Charles Xavier');
await page.autocompleteSearch(selectors.invoiceOutIndex.manualInvoiceClient, 'Max Eisenhardt');
await page.autocompleteSearch(selectors.invoiceOutIndex.manualInvoiceSerial, 'Global nacional');
await page.autocompleteSearch(selectors.invoiceOutIndex.manualInvoiceTaxArea, 'national');
await page.waitToClick(selectors.invoiceOutIndex.saveInvoice);

View File

@ -221,6 +221,7 @@
"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",
"The item is required": "El artículo es requerido",
"The agency is already assigned to another autonomous": "La agencia ya está asignada a otro autónomo",
"date in the future": "Fecha en el futuro",
"reference duplicated": "Referencia duplicada"
}

View File

@ -52,10 +52,12 @@ class Controller extends Section {
}
scrollToLine(lineFk) {
this.$.$applyAsync(() => {
const hashFk = this.lineFk || lineFk;
const hash = `vnItemDiary-${hashFk}`;
this.$location.hash(hash);
this.$anchorScroll();
});
}
showDescriptor(event, sale) {

View File

@ -47,6 +47,8 @@ describe('Item', () => {
controller.lineFk = 1;
controller.scrollToLine('invalidValue');
$scope.$apply();
expect(controller.$location.hash()).toEqual(`vnItemDiary-${1}`);
expect(controller.$anchorScroll).toHaveBeenCalledWith();
});
@ -56,6 +58,8 @@ describe('Item', () => {
controller.lineFk = undefined;
controller.scrollToLine(1);
$scope.$apply();
expect(controller.$location.hash()).toEqual(`vnItemDiary-${1}`);
expect(controller.$anchorScroll).toHaveBeenCalledWith();
});

View File

@ -222,8 +222,8 @@ describe('SalesMonitor salesFilter()', () => {
const firstTicket = result.shift();
const secondTicket = result.shift();
expect(firstTicket.totalProblems).toEqual(3);
expect(secondTicket.totalProblems).toEqual(2);
expect(firstTicket.totalProblems).toEqual(1);
expect(secondTicket.totalProblems).toEqual(1);
await tx.rollback();
} catch (e) {

View File

@ -87,21 +87,21 @@ module.exports = Self => {
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) )
CAST(IFNULL(sat.routePrice,
(sat.kmPrice * (GREATEST(r.kmEnd - r.kmStart , sat.minimumKm))
+ GREATEST(r.m3 , sat.minimumM3) * sat.m3Price)
+ sat.packagePrice * SUM(t.packages) )
AS DECIMAL(10,2)) price,
r.invoiceInFk,
a.supplierFk,
sat.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
LEFT JOIN vn.supplierAgencyTerm sat ON sat.agencyFk = a.id
LEFT JOIN vn.supplier s ON s.id = sat.supplierFk
WHERE r.created > DATE_ADD(CURDATE(), INTERVAL -2 MONTH) AND sat.supplierFk IS NOT NULL
GROUP BY r.id
) a`
);

View File

@ -18,7 +18,7 @@ describe('AgencyTerm filter()', () => {
const firstAgencyTerm = agencyTerms[0];
expect(firstAgencyTerm.routeFk).toEqual(1);
expect(agencyTerms.length).toEqual(3);
expect(agencyTerms.length).toEqual(5);
await tx.rollback();
} catch (e) {
@ -72,7 +72,7 @@ describe('AgencyTerm filter()', () => {
const results = await models.AgencyTerm.filter(ctx, options);
expect(results.length).toBe(3);
expect(results.length).toBe(5);
await tx.rollback();
} catch (e) {

View File

@ -0,0 +1,42 @@
const ParameterizedSQL = require('loopback-connector').ParameterizedSQL;
const mergeFilters = require('vn-loopback/util/filter').mergeFilters;
module.exports = Self => {
Self.remoteMethod('freeAgencies', {
description: 'Returns a list of agencies without a supplier assigned',
accepts: [{
arg: 'filter',
type: 'object',
description: `Filter defining where, order, offset, and limit - must be a JSON-encoded string`
}],
returns: {
type: ['object'],
root: true
},
http: {
path: `/freeAgencies`,
verb: 'GET'
}
});
Self.freeAgencies = async(filter, options) => {
const conn = Self.dataSource.connector;
const where = {'sat.supplierFk': null};
const myOptions = {};
if (typeof options == 'object')
Object.assign(myOptions, options);
filter = mergeFilters(filter, {where});
let stmt = new ParameterizedSQL(
`SELECT a.name, a.id
FROM agency a
LEFT JOIN supplierAgencyTerm sat ON sat.agencyFk = a.id`,
null, myOptions);
stmt.merge(conn.makeSuffix(filter));
return conn.executeStmt(stmt);
};
};

View File

@ -11,6 +11,9 @@
"SupplierAccount": {
"dataSource": "vn"
},
"SupplierAgencyTerm": {
"dataSource": "vn"
},
"SupplierLog": {
"dataSource": "vn"
},

View File

@ -0,0 +1,9 @@
const UserError = require('vn-loopback/util/user-error');
module.exports = Self => {
Self.rewriteDbError(function(err) {
if (err.code === 'ER_DUP_ENTRY')
return new UserError(`The agency is already assigned to another autonomous`);
return err;
});
};

View File

@ -0,0 +1,46 @@
{
"name": "SupplierAgencyTerm",
"base": "VnModel",
"options": {
"mysql": {
"table": "supplierAgencyTerm"
}
},
"properties": {
"agencyFk": {
"type": "number",
"id": true
},
"supplierFk": {
"type": "number"
},
"minimumPackages": {
"type": "number"
},
"kmPrice": {
"type": "number"
},
"packagePrice": {
"type": "number"
},
"routePrice": {
"type": "number"
},
"minimumKm": {
"type": "number"
},
"minimumM3": {
"type": "number"
},
"m3Price": {
"type": "number"
}
},
"relations": {
"agency": {
"type": "belongsTo",
"model": "Agency",
"foreignKey": "agencyFk"
}
}
}

View File

@ -7,6 +7,7 @@ module.exports = Self => {
require('../methods/supplier/getSummary')(Self);
require('../methods/supplier/updateFiscalData')(Self);
require('../methods/supplier/consumption')(Self);
require('../methods/supplier/freeAgencies')(Self);
Self.validatesPresenceOf('name', {
message: 'The social name cannot be empty'

View File

@ -0,0 +1,74 @@
<vn-watcher
vn-id="watcher"
url="SupplierAgencyTerms"
primary-key="agencyFk"
data="$ctrl.supplierAgencyTerm"
insert-mode="true"
form="form">
</vn-watcher>
<vn-crud-model
auto-load="true"
url="Suppliers/freeAgencies"
data="$ctrl.agencies">
</vn-crud-model>
<form name="form" ng-submit="$ctrl.onSubmit()" class="vn-w-md">
<vn-card class="vn-pa-lg">
<vn-horizontal>
<vn-autocomplete vn-one
label="Agency"
ng-model="$ctrl.supplierAgencyTerm.agencyFk"
data="$ctrl.agencies"
show-field="name"
value-field="id"
rule>
</vn-autocomplete>
<vn-input-number
type="number"
label="Minimum M3"
ng-model="$ctrl.supplierAgencyTerm.minimumM3"
rule>
</vn-input-number>
</vn-horizontal>
<vn-horizontal>
<vn-input-number
type="number"
label="Package Price"
ng-model="$ctrl.supplierAgencyTerm.packagePrice"
rule>
</vn-input-number>
<vn-input-number
type="number"
label="Km Price"
ng-model="$ctrl.supplierAgencyTerm.kmPrice"
rule>
</vn-input-number>
<vn-input-number
type="number"
label="M3 Price"
ng-model="$ctrl.supplierAgencyTerm.m3Price"
rule>
</vn-input-number>
</vn-horizontal>
<vn-horizontal>
<vn-input-number
type="number"
label="Route Price"
ng-model="$ctrl.supplierAgencyTerm.routePrice"
rule>
</vn-input-number>
<vn-input-number
type="number"
label="Minimum Km"
ng-model="$ctrl.supplierAgencyTerm.minimumKm"
rule>
</vn-input-number>
</vn-horizontal>
</vn-card>
<vn-button-bar>
<vn-submit label="Save"></vn-submit>
<vn-button
label="Cancel"
ui-sref="supplier.card.agencyTerm.index">
</vn-button>
</vn-button-bar>
</form>

View File

@ -0,0 +1,26 @@
import ngModule from '../../module';
import Section from 'salix/components/section';
export default class Controller extends Section {
constructor($element, $) {
super($element, $);
this.supplierAgencyTerm = {
supplierFk: this.$params.id
};
}
onSubmit() {
this.$.watcher.submit().then(res => {
this.$state.go('supplier.card.agencyTerm.index');
});
}
}
ngModule.vnComponent('vnSupplierAgencyTermCreate', {
template: require('./index.html'),
controller: Controller,
bindings: {
supplier: '<'
}
});

View File

@ -0,0 +1,28 @@
import './index';
import watcher from 'core/mocks/watcher';
describe('Supplier', () => {
describe('Component vnSupplierAddressCreate', () => {
let $scope;
let controller;
let $element;
beforeEach(ngModule('supplier'));
beforeEach(inject(($componentController, $rootScope, _$state_) => {
$scope = $rootScope.$new();
$scope.watcher = watcher;
$element = angular.element('<vn-supplier-agency-term-create></vn-supplier-agency-term-create>');
controller = $componentController('vnSupplierAgencyTermCreate', {$element, $scope});
}));
describe('onSubmit()', () => {
it(`should redirect to 'supplier.card.agencyTerm.index' state`, () => {
jest.spyOn(controller.$state, 'go');
controller.onSubmit();
expect(controller.$state.go).toHaveBeenCalledWith('supplier.card.agencyTerm.index');
});
});
});
});

View File

@ -0,0 +1,85 @@
<vn-crud-model
vn-id="model"
url="SupplierAgencyTerms"
link="{supplierFk: $ctrl.$params.id}"
primary-key="agencyFk"
filter="$ctrl.filter"
data="$ctrl.supplierAgencyTerms"
auto-load="true">
</vn-crud-model>
<vn-watcher
vn-id="watcher"
data="$ctrl.supplierAgencyTerms"
form="form">
</vn-watcher>
<form name="form" ng-submit="$ctrl.onSubmit()" class="vn-w-lg">
<vn-card class="vn-pa-lg">
<vn-horizontal ng-repeat="supplierAgencyTerm in $ctrl.supplierAgencyTerms">
<vn-textfield
disabled="true"
vn-id="agency"
label="Agency"
ng-model="supplierAgencyTerm.agency.name"
rule>
</vn-textfield>
<vn-input-number
type="number"
label="Minimum M3"
ng-model="supplierAgencyTerm.minimumM3"
rule>
</vn-input-number>
<vn-input-number
type="number"
label="Package Price"
ng-model="supplierAgencyTerm.packagePrice"
rule>
</vn-input-number>
<vn-input-number
type="number"
label="Km Price"
ng-model="supplierAgencyTerm.kmPrice"
rule>
</vn-input-number>
<vn-input-number
type="number"
label="M3 Price"
ng-model="supplierAgencyTerm.m3Price"
rule>
</vn-input-number>
<vn-input-number
type="number"
label="Route Price"
ng-model="supplierAgencyTerm.routePrice"
rule>
</vn-input-number>
<vn-input-number
type="number"
label="Minimum Km"
ng-model="supplierAgencyTerm.minimumKm"
rule>
</vn-input-number>
<vn-none>
<vn-icon-button
vn-tooltip="Remove row"
icon="delete"
ng-click="model.remove($index)"
tabindex="-1">
</vn-icon-button>
</vn-none>
</vn-horizontal>
</vn-card>
<vn-button-bar>
<vn-submit
disabled="!watcher.dataChanged()"
label="Save">
</vn-submit>
</vn-button-bar>
</form>
<vn-float-button
vn-bind="+"
fixed-bottom-right
vn-tooltip="New row"
ui-sref="supplier.card.agencyTerm.create"
icon="add"
label="Add">
</vn-float-button>

View File

@ -0,0 +1,36 @@
import ngModule from '../../module';
import Section from 'salix/components/section';
class Controller extends Section {
constructor($element, $) {
super($element, $);
this.filter = {
include:
{relation: 'agency',
scope: {
fields: ['id', 'name']
}
}
};
}
add() {
this.$.model.insert({});
}
onSubmit() {
this.$.watcher.check();
this.$.model.save().then(() => {
this.$.watcher.notifySaved();
this.$.watcher.updateOriginalData();
});
}
}
ngModule.vnComponent('vnSupplierAgencyTermIndex', {
template: require('./index.html'),
controller: Controller,
bindings: {
supplier: '<'
}
});

View File

@ -0,0 +1,37 @@
import './index';
import watcher from 'core/mocks/watcher';
import crudModel from 'core/mocks/crud-model';
describe('Supplier', () => {
describe('Component vnSupplierAddressCreate', () => {
let $scope;
let controller;
let $element;
beforeEach(ngModule('supplier'));
beforeEach(inject(($componentController, $rootScope, _$state_) => {
$scope = $rootScope.$new();
$scope.model = crudModel;
$scope.watcher = watcher;
$element = angular.element('<vn-supplier-agency-term-index></vn-supplier-agency-term-index>');
controller = $componentController('vnSupplierAgencyTermIndex', {$element, $scope});
}));
describe('onSubmit()', () => {
it('should make HTTP POST request to save values', () => {
jest.spyOn($scope.watcher, 'check');
jest.spyOn($scope.watcher, 'notifySaved');
jest.spyOn($scope.watcher, 'updateOriginalData');
jest.spyOn($scope.model, 'save');
controller.onSubmit();
expect($scope.model.save).toHaveBeenCalledWith();
expect($scope.watcher.updateOriginalData).toHaveBeenCalledWith();
expect($scope.watcher.check).toHaveBeenCalledWith();
expect($scope.watcher.notifySaved).toHaveBeenCalledWith();
});
});
});
});

View File

@ -0,0 +1,8 @@
Minimum M3: M3 minimos
Package Price: Precio bulto
Km Price: Precio Km
M3 Price: Precio M3
Route Price: Precio ruta
Minimum Km: Km minimos
Remove row: Eliminar fila
Add row: Añadir fila

View File

@ -18,3 +18,5 @@ import './billing-data';
import './address/index';
import './address/create';
import './address/edit';
import './agency-term/index';
import './agency-term/create';

View File

@ -15,6 +15,7 @@
{"state": "supplier.card.address.index", "icon": "icon-delivery"},
{"state": "supplier.card.account", "icon": "icon-account"},
{"state": "supplier.card.contact", "icon": "contact_phone"},
{"state": "supplier.card.agencyTerm.index", "icon": "contact_support"},
{"state": "supplier.card.log", "icon": "history"},
{"state": "supplier.card.consumption", "icon": "show_chart"}
]
@ -86,6 +87,30 @@
"supplier": "$ctrl.supplier"
}
},
{
"url": "/agency-term",
"state": "supplier.card.agencyTerm",
"component": "ui-view",
"abstract": true
},
{
"url": "/index",
"state": "supplier.card.agencyTerm.index",
"component": "vn-supplier-agency-term-index",
"description": "Autonomous",
"params": {
"supplier": "$ctrl.supplier"
}
},
{
"url": "/create",
"state": "supplier.card.agencyTerm.create",
"component": "vn-supplier-agency-term-create",
"description": "New autonomous",
"params": {
"supplier": "$ctrl.supplier"
}
},
{
"url": "/consumption?q",
"state": "supplier.card.consumption",

View File

@ -28,16 +28,6 @@ module.exports = Self => {
type: 'date',
description: 'The shipped from date filter'
},
{
arg: 'shippedTo',
type: 'date',
description: 'The shipped to date filter'
},
{
arg: 'landedFrom',
type: 'date',
description: 'The landed from date filter'
},
{
arg: 'landedTo',
type: 'date',
@ -101,10 +91,6 @@ module.exports = Self => {
return {'t.ref': {like: `%${value}%`}};
case 'shippedFrom':
return {'t.shipped': {gte: value}};
case 'shippedTo':
return {'t.shipped': {lte: value}};
case 'landedFrom':
return {'t.landed': {gte: value}};
case 'landedTo':
return {'t.landed': {lte: value}};
case 'continent':
@ -162,6 +148,7 @@ module.exports = Self => {
stmt.merge(conn.makeWhere(filter.where));
stmt.merge(conn.makeGroupBy('t.id'));
stmt.merge(conn.makeOrderBy(filter.order));
stmt.merge(conn.makeLimit(filter));
stmts.push(stmt);

View File

@ -46,15 +46,64 @@ describe('Travel extraCommunityFilter()', () => {
expect(result.length).toEqual(3);
});
it('should return the routes matching "landed from" and "landed to"', async() => {
const from = new Date();
const to = new Date();
from.setHours(0, 0, 0, 0);
to.setHours(23, 59, 59, 999);
to.setDate(to.getDate() + 14);
it('should return the travel matching "warehouse in"', async() => {
const ctx = {
args: {
landedFrom: from,
warehouseInFk: 1
}
};
const result = await app.models.Travel.extraCommunityFilter(ctx, filter);
expect(result.length).toEqual(4);
});
it('should return the travel matching "continent"', async() => {
const ctx = {
args: {
continent: 'AM'
}
};
const result = await app.models.Travel.extraCommunityFilter(ctx, filter);
expect(result.length).toEqual(3);
});
it('should return the travel matching "agencyFk"', async() => {
const ctx = {
args: {
agencyFk: 1
}
};
const result = await app.models.Travel.extraCommunityFilter(ctx, filter);
expect(result.length).toEqual(8);
});
it('should return the travel matching "cargoSupplierFk"', async() => {
const ctx = {
args: {
cargoSupplierFk: 1
}
};
const result = await app.models.Travel.extraCommunityFilter(ctx, filter);
expect(result.length).toEqual(4);
});
it('should return the routes matching "shipped from" and "landed to"', async() => {
const from = new Date();
from.setDate(from.getDate() - 2);
from.setHours(0, 0, 0, 0);
const to = new Date();
to.setHours(23, 59, 59, 999);
to.setDate(to.getDate() + 7);
const ctx = {
args: {
shippedFrom: from,
landedTo: to
}
};

View File

@ -47,17 +47,6 @@
ng-model="$ctrl.landedTo">
</vn-date-picker>
</vn-horizontal>
<vn-horizontal>
<vn-date-picker
vn-one
label="Shipped to"
ng-model="$ctrl.shippedTo">
</vn-date-picker>
<vn-date-picker
vn-one
label="Landed from"
ng-model="$ctrl.landedFrom">
</vn-date-picker>
</vn-horizontal>
<vn-horizontal>
<vn-autocomplete vn-one

View File

@ -14,31 +14,6 @@ class Controller extends SearchPanel {
set shippedFrom(value) {
this.filter.shippedFrom = value;
if (!this.filter.landedFrom)
this.filter.landedFrom = value;
}
get shippedTo() {
return this.filter.shippedTo;
}
set shippedTo(value) {
this.filter.shippedTo = value;
if (!this.filter.landedTo)
this.filter.landedTo = value;
}
get landedFrom() {
return this.filter.landedFrom;
}
set landedFrom(value) {
this.filter.landedFrom = value;
if (!this.filter.shippedFrom)
this.filter.shippedFrom = value;
}
get landedTo() {
@ -47,9 +22,6 @@ class Controller extends SearchPanel {
set landedTo(value) {
this.filter.landedTo = value;
if (!this.filter.shippedTo)
this.filter.shippedTo = value;
}
}

View File

@ -2,7 +2,7 @@
vn-id="model"
url="Travels/extraCommunityFilter"
data="travels"
order="landed ASC, shipped ASC, travelFk, loadPriority, agencyModeFk, evaNotes"
order="shipped ASC, landed ASC, travelFk, loadPriority, agencyModeFk, evaNotes"
limit="20"
auto-load="true">
</vn-crud-model>

View File

@ -17,16 +17,18 @@ class Controller extends Section {
this.draggableElement = 'a[draggable]';
this.droppableElement = 'vn-tbody[vn-droppable]';
const scopeDays = 14;
const landedFrom = new Date();
landedFrom.setHours(0, 0, 0, 0);
const twoDays = 2;
const shippedFrom = new Date();
shippedFrom.setDate(shippedFrom.getDate() - twoDays);
shippedFrom.setHours(0, 0, 0, 0);
const sevenDays = 7;
const landedTo = new Date();
landedTo.setDate(landedTo.getDate() + scopeDays);
landedTo.setDate(landedTo.getDate() + sevenDays);
landedTo.setHours(23, 59, 59, 59);
this.defaultFilter = {
landedFrom: landedFrom,
shippedFrom: shippedFrom,
landedTo: landedTo,
continent: 'AM'
};
@ -34,10 +36,12 @@ class Controller extends Section {
get hasDateRange() {
const userParams = this.$.model.userParams;
const hasLanded = userParams.landedFrom || userParams.landedTo;
const hasShipped = userParams.shippedFrom || userParams.shippedTo;
const hasLanded = userParams.landedTo;
const hasShipped = userParams.shippedFrom;
const hasContinent = userParams.continent;
const hasWarehouseOut = userParams.warehouseOutFk;
return hasLanded || hasShipped;
return hasLanded || hasShipped || hasContinent || hasWarehouseOut;
}
findDraggable($event) {

View File

@ -9,8 +9,8 @@ vn-travel-extra-community {
padding-bottom: 7px;
padding-bottom: 4px;
font-weight: lighter;
background-color: #fde6ca;
color: $color-font-light;
background-color: $color-bg;
color: white;
border-bottom: 1px solid #f7931e;
white-space: nowrap;
overflow: hidden;

View File

@ -1,4 +1,5 @@
const UserError = require('vn-loopback/util/user-error');
const ParameterizedSQL = require('loopback-connector').ParameterizedSQL;
module.exports = Self => {
Self.remoteMethodCtx('holidays', {
@ -33,7 +34,8 @@ module.exports = Self => {
Self.holidays = async(ctx, id, options) => {
const models = Self.app.models;
const args = ctx.args;
const conn = Self.dataSource.connector;
const stmts = [];
const myOptions = {};
if (typeof options == 'object')
@ -56,42 +58,6 @@ module.exports = Self => {
ended.setHours(23, 59, 59, 59);
const filter = {
include: [{
relation: 'holidays',
scope: {
where: {year: args.year}
}
},
{
relation: 'absences',
scope: {
include: {
relation: 'absenceType',
},
where: {
dated: {between: [started, ended]}
}
}
},
{
relation: 'workCenter',
scope: {
include: {
relation: 'holidays',
scope: {
include: [{
relation: 'detail'
},
{
relation: 'type'
}],
where: {
dated: {between: [started, ended]}
}
}
}
}
}],
where: {
and: [
{workerFk: id},
@ -107,76 +73,32 @@ module.exports = Self => {
}
};
if (args.businessFk)
filter.where.and.push({businessFk: args.businessFk});
const contracts = await models.WorkerLabour.find(filter, myOptions);
let [firstContract] = contracts;
let payedHolidays;
const payedHolidays = firstContract.payedHolidays;
if (firstContract.payedHolidays)
payedHolidays = firstContract.payedHolidays;
else payedHolidays = 0;
let totalHolidays = 0;
let holidaysEnjoyed = 0;
for (let contract of contracts) {
const contractStarted = contract.started;
contractStarted.setHours(0, 0, 0, 0);
const contractEnded = contract.ended;
if (contractEnded)
contractEnded.setHours(23, 59, 59, 59);
let startedTime;
if (contractStarted < started)
startedTime = started.getTime();
else startedTime = contractStarted.getTime();
let endedTime;
if (!contractEnded || (contractEnded && contractEnded > ended))
endedTime = ended.getTime();
else endedTime = contractEnded.getTime();
const dayTimestamp = 1000 * 60 * 60 * 24;
// Get number of worked days between dates
let workedDays = Math.floor((endedTime - startedTime) / dayTimestamp);
workedDays += 1; // 1 day inclusion
// Calculates absences
let entitlementRate = 0;
for (let absence of contract.absences()) {
const absenceType = absence.absenceType();
const isHoliday = absenceType.code === 'holiday';
const isHalfHoliday = absenceType.code === 'halfHoliday';
if (isHoliday) holidaysEnjoyed += 1;
if (isHalfHoliday) holidaysEnjoyed += 0.5;
entitlementRate += absenceType.holidayEntitlementRate;
}
workedDays -= entitlementRate;
// Max holidays for the selected year
const maxHolidays = contract.holidays() && contract.holidays().days;
if (workedDays < daysInYear())
totalHolidays += Math.round(2 * maxHolidays * (workedDays) / daysInYear()) / 2;
else totalHolidays = maxHolidays;
}
function daysInYear() {
let queryIndex;
const year = started.getFullYear();
return isLeapYear(year) ? 366 : 365;
if (args.businessFk) {
stmts.push(new ParameterizedSQL('CALL vn.workerCalendar_calculateBusiness(?,?)', [year, args.businessFk]));
queryIndex = stmts.push('SELECT * FROM tmp.workerCalendarCalculateBusiness') - 1;
stmts.push('DROP TEMPORARY TABLE tmp.workerCalendarCalculateBusiness');
} else {
stmts.push(new ParameterizedSQL('CALL vn.workerCalendar_calculateYear(?,?)', [year, id]));
queryIndex = stmts.push('SELECT * FROM tmp.workerCalendarCalculateYear') - 1;
stmts.push('DROP TEMPORARY TABLE tmp.workerCalendarCalculateYear');
}
return {totalHolidays, holidaysEnjoyed, payedHolidays};
};
function isLeapYear(year) {
return year % 400 === 0 || (year % 100 !== 0 && year % 4 === 0);
}
const sql = ParameterizedSQL.join(stmts, ';');
const result = await conn.executeStmt(sql, myOptions);
const [holidays] = result[queryIndex];
const totalHolidays = holidays.days;
const holidaysEnjoyed = holidays.daysEnjoyed;
const totalHours = holidays.hours;
const hoursEnjoyed = holidays.hoursEnjoyed;
return {totalHolidays, holidaysEnjoyed, totalHours, hoursEnjoyed, payedHolidays};
};
};

View File

@ -34,6 +34,9 @@
},
"notificationEmail": {
"type": "string"
},
"hasToMistake": {
"type": "number"
}
}
}

View File

@ -25,20 +25,28 @@
<div class="totalBox vn-mb-sm" style="text-align: center;">
<h6>{{'Contract' | translate}} #{{$ctrl.businessId}}</h6>
<div>
{{'Used' | translate}} {{$ctrl.contractHolidays.holidaysEnjoyed}}
{{'Used' | translate}} {{$ctrl.contractHolidays.holidaysEnjoyed || 0}}
{{'of' | translate}} {{$ctrl.contractHolidays.totalHolidays || 0}} {{'days' | translate}}
</div>
<div>
{{'Paid holidays' | translate}} {{$ctrl.contractHolidays.payedHolidays}} {{'days' | translate}}
{{'Spent' | translate}} {{$ctrl.contractHolidays.hoursEnjoyed || 0}}
{{'of' | translate}} {{$ctrl.contractHolidays.totalHours || 0}} {{'hours' | translate}}
</div>
<div>
{{'Paid holidays' | translate}} {{$ctrl.contractHolidays.payedHolidays || 0}} {{'days' | translate}}
</div>
</div>
<div class="totalBox" style="text-align: center;">
<h6>{{'Year' | translate}} {{$ctrl.year}}</h6>
<div>
{{'Used' | translate}} {{$ctrl.yearHolidays.holidaysEnjoyed}}
{{'Used' | translate}} {{$ctrl.yearHolidays.holidaysEnjoyed || 0}}
{{'of' | translate}} {{$ctrl.yearHolidays.totalHolidays || 0}} {{'days' | translate}}
</div>
<div>
{{'Spent' | translate}} {{$ctrl.yearHolidays.hoursEnjoyed || 0}}
{{'of' | translate}} {{$ctrl.yearHolidays.totalHours || 0}} {{'hours' | translate}}
</div>
</div>
<div class="vn-pt-md">

View File

@ -198,7 +198,7 @@ class Controller extends Section {
const event = this.events[stamp];
const calendar = $event.target.closest('vn-calendar').$ctrl;
if (event) {
if (event && event.absenceId) {
if (event.type == this.absenceType.code)
this.delete(calendar, day, event);
else
@ -230,6 +230,7 @@ class Controller extends Section {
.then(calendar.repaint())
.then(() => this.getContractHolidays())
.then(() => this.getYearHolidays())
.then(() => this.repaint())
);
});
}
@ -266,6 +267,7 @@ class Controller extends Section {
.then(calendar.repaint())
.then(() => this.getContractHolidays())
.then(() => this.getYearHolidays())
.then(() => this.repaint())
);
});
}

View File

@ -191,7 +191,8 @@ describe('Worker', () => {
const selectedDay = new Date();
const expectedEvent = {
dated: selectedDay,
type: 'holiday'
type: 'holiday',
absenceId: 1
};
const $event = {
target: {
@ -214,7 +215,8 @@ describe('Worker', () => {
const selectedDay = new Date();
const expectedEvent = {
dated: selectedDay,
type: 'leaveOfAbsence'
type: 'leaveOfAbsence',
absenceId: 1
};
const $event = {
target: {

View File

@ -2,9 +2,11 @@ Calendar: Calendario
Contract: Contrato
Festive: Festivo
Used: Utilizados
Spent: Utilizadas
Year: Año
of: de
days: días
hours: horas
Choose an absence type from the right menu: Elige un tipo de ausencia desde el menú de la derecha
To start adding absences, click an absence type from the right menu and then on the day you want to add an absence: Para empezar a añadir ausencias, haz clic en un tipo de ausencia desde el menu de la derecha y después en el día que quieres añadir la ausencia
You can just add absences within the current year: Solo puedes añadir ausencias dentro del año actual

View File

@ -16,5 +16,12 @@
"type": "string",
"required": false
}
},
"relations": {
"supplierAgencyTerm": {
"type": "hasOne",
"model": "SupplierAgencyTerm",
"foreignKey": "agencyFk"
}
}
}

View File

@ -8,11 +8,16 @@ module.exports = {
async serverPrefetch() {
this.filters = this.$options.filters;
const args = {
landedFrom: this.landedStart,
landedTo: this.landedEnd,
shippedFrom: this.shippedStart,
shippedTo: this.shippedEnd,
continent: this.continent
continent: this.continent,
id: this.id,
agencyFk: this.agencyFk,
warehouseInFk: this.warehouseInFk,
warehouseOutFk: this.warehouseOutFk,
totalEntries: this.totalEntries,
ref: this.ref,
cargoSupplierFk: this.cargoSupplierFk
};
const travels = await this.fetchTravels(args);
@ -38,11 +43,6 @@ module.exports = {
dated: function() {
return this.filters.date(new Date(), '%d-%m-%Y');
},
landedStart: function() {
if (!this.landedFrom) return;
return this.filters.date(this.landedFrom, '%Y-%m-%d');
},
landedEnd: function() {
if (!this.landedTo) return;
@ -52,11 +52,6 @@ module.exports = {
if (!this.shippedFrom) return;
return this.filters.date(this.shippedFrom, '%Y-%m-%d');
},
shippedEnd: function() {
if (!this.shippedTo) return;
return this.filters.date(this.shippedTo, '%Y-%m-%d');
}
},
methods: {
@ -65,20 +60,29 @@ module.exports = {
switch (key) {
case 'shippedFrom':
return `t.shipped >= ${value}`;
case 'shippedTo':
return `t.shipped <= ${value}`;
case 'landedFrom':
return `t.landed >= ${value}`;
case 'landedTo':
return `t.landed <= ${value}`;
case 'continent':
return `cnt.code = ${value}`;
case 'ref':
return {'t.ref': {like: `%${value}%`}};
case 'id':
return `t.id = ${value}`;
case 'agencyFk':
return `am.id = ${value}`;
case 'warehouseOutFk':
return `wo.id = ${value}`;
case 'warehouseInFk':
return `w.id = ${value}`;
case 'cargoSupplierFk':
return `s.id = ${value}`;
}
});
let query = this.getSqlFromDef('travels');
query = db.merge(query, where);
query = db.merge(query, 'GROUP BY t.id');
query = db.merge(query, 'ORDER BY `shipped` ASC, `landed` ASC, `travelFk`, `loadPriority`, `agencyModeFk`, `evaNotes`');
return this.rawSql(query);
},
@ -92,10 +96,15 @@ module.exports = {
'report-footer': reportFooter.build()
},
props: [
'landedFrom',
'landedTo',
'shippedFrom',
'shippedTo',
'continent'
'continent',
'ref',
'id',
'agencyFk',
'warehouseOutFk',
'warehouseInFk',
'totalEntries',
'cargoSupplierFk'
]
};

View File

@ -4,6 +4,7 @@ SELECT
t.shipped,
t.landed,
t.kg,
am.id AS agencyModeFk,
SUM(b.stickers) AS stickers,
CAST(SUM(i.density * b.stickers * IF(pkg.volume, pkg.volume, pkg.width * pkg.depth * pkg.height) / 1000000 ) as DECIMAL(10,0)) as loadedKg,
CAST(SUM(167.5 * b.stickers * IF(pkg.volume, pkg.volume, pkg.width * pkg.depth * pkg.height) / 1000000 ) as DECIMAL(10,0)) as volumeKg