3604-route_agencyTerm #893

Merged
carlosjr merged 20 commits from 3604-route_agencyTerm into dev 2022-03-15 14:18:27 +00:00
14 changed files with 568 additions and 1 deletions
Showing only changes of commit 19887bdd16 - Show all commits

View File

@ -0,0 +1,2 @@
INSERT INTO salix.ACL (model,property,accessType,principalId)
vicent marked this conversation as resolved Outdated

move to /10430-ash

move to /10430-ash
VALUES ('AgencyTerm','*','*','administrative');

View File

@ -2443,3 +2443,26 @@ INSERT INTO `bs`.`defaulter` (`clientFk`, `amount`, `created`, `defaulterSinced`
(1103, 500, CURDATE(), CURDATE()),
(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`=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;

View File

@ -0,0 +1,143 @@
const ParameterizedSQL = require('loopback-connector').ParameterizedSQL;
const buildFilter = require('vn-loopback/util/filter').buildFilter;
const mergeFilters = require('vn-loopback/util/filter').mergeFilters;
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'}
},
{
arg: 'search',
type: 'string',
description: 'Searchs the route by id',
http: {source: 'query'}
},
{
arg: 'agencyModeFk',
type: 'integer',
description: 'The agencyMode id',
http: {source: 'query'}
},
{
arg: 'to',
type: 'date',
description: 'The to date filter',
http: {source: 'query'}
vicent marked this conversation as resolved Outdated

const

const
},
vicent marked this conversation as resolved Outdated

stmt is initialized once and assigned once, perhaps you want to create a constant directly on definition

stmt is initialized once and assigned once, perhaps you want to create a constant directly on definition
{
arg: 'from',
type: 'date',
description: 'The to date filter',
http: {source: 'query'}
},
{
arg: 'vehicleFk',
type: 'integer',
description: 'The vehicle id',
http: {source: 'query'}
},
{
arg: 'm3',
type: 'number',
description: 'The m3 filter',
http: {source: 'query'}
},
{
arg: 'warehouseFk',
type: 'number',
description: 'The warehouse filter',
http: {source: 'query'}
},
{
arg: 'description',
type: 'string',
description: 'The description filter',
http: {source: 'query'}
}
],
returns: {
type: ['object'],
root: true
vicent marked this conversation as resolved Outdated

const

const
},
vicent marked this conversation as resolved Outdated

const

const
http: {
vicent marked this conversation as resolved Outdated

const

const
path: `/filter`,
verb: 'GET'
}
});
Self.filter = async(ctx, filter) => {
let conn = Self.dataSource.connector;
let where = buildFilter(ctx.args, (param, value) => {
switch (param) {
case 'search':
return {'r.id': value};
case 'from':
return {'r.created': {gte: value}};
case 'to':
return {'r.created': {lte: value}};
case 'm3':
return {'r.m3': value};
case 'description':
return {'r.description': {like: `%${value}%`}};
case 'warehouseFk':
param = `v.${param}`;
return {[param]: value};
case 'vehicleFk':
case 'agencyModeFk':
param = `r.${param}`;
return {[param]: value};
}
});
filter = mergeFilters(ctx.args.filter, {where});
let stmts = [];
let stmt;
stmt = new ParameterizedSQL(
`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;`
);
stmt.merge(conn.makeSuffix(filter));
let agencyTerm = stmts.push(stmt) - 1;
let sql = ParameterizedSQL.join(stmts, ';');
let result = await conn.executeStmt(sql);
return agencyTerm === 0 ? result : result[agencyTerm];
};
};

View File

@ -1,4 +1,7 @@
{
"AgencyTerm": {
"dataSource": "vn"
},
"Route": {
"dataSource": "vn"
},

View File

@ -0,0 +1,3 @@
module.exports = Self => {
require('../methods/agency-term/filter')(Self);
};

View File

@ -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"
}
}
}

View File

@ -0,0 +1,149 @@
<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-table>
<table>
<thead>
<tr>
<th shrink>
<vn-multi-check
model="model">
</vn-multi-check>
</th>
<th 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>{{::agencyTerm.invoiceInFk | dashIfEmpty}}</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.routeFk)"
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>
<vn-contextmenu
vn-id="contextmenu"
targets="['smart-table']"
model="model"
expr-builder="$ctrl.exprBuilder(param, value)">
<slot-menu>
<vn-item translate
ng-if="contextmenu.isFilterAllowed()"
ng-click="contextmenu.filterBySelection()">
Filter by selection
</vn-item>
<vn-item translate
ng-if="contextmenu.isFilterAllowed()"
ng-click="contextmenu.excludeSelection()">
Exclude selection
</vn-item>
<vn-item translate
ng-if="contextmenu.isFilterAllowed()"
ng-click="contextmenu.removeFilter()">
Remove filter
</vn-item>
<vn-item translate
ng-click="contextmenu.removeAllFilters()">
Remove all filters
</vn-item>
</slot-menu>
</vn-contextmenu>

View File

@ -0,0 +1,121 @@
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: false,
shownColumns: false,
},
columns: [
{
field: 'agencyFk',
autocomplete: {
url: 'ItemCategories',
valueField: 'name',
}
},
{
field: 'origin',
autocomplete: {
url: 'Origins',
showField: 'code',
valueField: 'code'
}
},
{
field: 'typeFk',
autocomplete: {
url: 'ItemTypes',
}
},
{
field: 'intrastat',
autocomplete: {
url: 'Intrastats',
showField: 'description',
valueField: 'description'
}
},
{
field: 'buyerFk',
autocomplete: {
url: 'Workers/activeWithRole',
where: `{role: {inq: ['logistic', 'buyer']}}`,
searchFunction: '{firstName: $search}',
showField: 'nickname',
valueField: 'id',
}
},
{
field: 'active',
searchable: false
},
{
field: 'landed',
searchable: false
},
]
};
}
exprBuilder(param, value) {
switch (param) {
case 'category':
return {'ic.name': value};
case 'buyerFk':
return {'it.workerFk': value};
case 'grouping':
return {'b.grouping': value};
case 'packing':
return {'b.packing': value};
case 'origin':
return {'ori.code': value};
case 'typeFk':
return {'i.typeFk': value};
case 'intrastat':
return {'intr.description': value};
case 'name':
return {'i.name': {like: `%${value}%`}};
case 'producer':
return {'pr.name': {like: `%${value}%`}};
case 'id':
case 'size':
case 'subname':
case 'isActive':
case 'density':
case 'stemMultiplier':
case 'stems':
return {[`i.${param}`]: value};
}
}
get checked() {
const routes = this.$.model.data || [];
const checkedRoutes = [];
for (let route of routes) {
if (route.checked)
checkedRoutes.push(route);
}
return checkedRoutes;
}
get totalChecked() {
return this.checked.length;
}
preview(route) {
this.routeSelected = route;
this.$.summary.show();
}
}
ngModule.vnComponent('vnAgencyTerm', {
template: require('./index.html'),
controller: Controller
});

View File

@ -0,0 +1,30 @@
import './index.js';
describe('Item', () => {
describe('Component vnItemIndex', () => {
let controller;
let $httpBackend;
let $scope;
beforeEach(ngModule('item'));
beforeEach(inject(($componentController, _$httpBackend_, $rootScope) => {
$httpBackend = _$httpBackend_;
$scope = $rootScope.$new();
const $element = angular.element('<vn-item-index></vn-item-index>');
controller = $componentController('vnItemIndex', {$element, $scope});
}));
describe('onCloneAccept()', () => {
it('should perform a post query and then call go() then update itemSelected in the controller', () => {
jest.spyOn(controller.$state, 'go');
$httpBackend.expectRoute('POST', `Items/:id/clone`).respond({id: 99});
controller.onCloneAccept(1);
$httpBackend.flush();
expect(controller.$state.go).toHaveBeenCalledWith('item.card.tags', {id: 99});
});
});
});
});

View File

@ -0,0 +1,5 @@
picture: Foto
vicent marked this conversation as resolved
Review

the word picture should be capitalised.

Also some of the translations don't seem to be used at all. please double check their use on your templates.

the word picture should be capitalised. Also some of the translations don't seem to be used at all. please double check their use on your templates.
Buy requests: Peticiones de compra
vicent marked this conversation as resolved Outdated

Acuerdo de agencia? Acuerdo agencia?

Acuerdo de agencia? Acuerdo agencia?
Agency route: Agencia ruta
Agency Agreement: Agencia acuerdo
Autonomous: Autónomo

View File

@ -0,0 +1,11 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- Generator: Adobe Illustrator 21.1.0, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
<svg version="1.1" id="Capa_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
viewBox="0 0 30 24" style="enable-background:new 0 0 30 24;" xml:space="preserve" width="24px" heigth="24px">
<g>
<path d="M27,0H3C1.4,0,0,1.3,0,3v18c0,1.7,1.4,3,3,3h24c1.6,0,3-1.3,3-3V3C30,1.3,28.6,0,27,0z M27.9,19.8c0,1.4-1.2,2.6-2.6,2.6
H4.6c-1.4,0-2.6-1.2-2.6-2.6V4.2c0-1.4,1.2-2.6,2.6-2.6h20.7c1.4,0,2.6,1.2,2.6,2.6V19.8z"/>
<path d="M24.6,2.9H5.4c-1,0-1.9,0.8-1.9,1.9v14.4c0,1,0.8,1.9,1.9,1.9h19.1c1,0,1.9-0.8,1.9-1.9V4.8C26.4,3.8,25.6,2.9,24.6,2.9z"
/>
</g>
</svg>

After

Width:  |  Height:  |  Size: 750 B

View File

@ -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;
}
}

View File

@ -11,3 +11,4 @@ import './create';
import './basic-data';
import './log';
import './tickets';
import './agency-term';

View File

@ -6,7 +6,8 @@
"dependencies": ["client", "worker", "ticket"],
"menus": {
"main": [
{"state": "route.index", "icon": "icon-delivery"}
{"state": "route.index", "icon": "icon-delivery"},
{"state": "route.agencyTerm", "icon": "icon-delivery"}
],
"card": [
{"state": "route.card.basicData", "icon": "settings"},
@ -37,6 +38,12 @@
"state": "route.card",
"abstract": true,
"component": "vn-route-card"
}, {
"url" : "/agency-term?q",
"state": "route.agencyTerm",
"component": "vn-agency-term",
"description": "Autonomous",
"acl": ["administrative"]
}, {
"url": "/summary",
"state": "route.card.summary",