feat: add section autonomous
gitea/salix/pipeline/head There was a failure building this commit Details

This commit is contained in:
Vicent Llopis 2022-02-24 14:52:32 +01:00
parent 525d16335f
commit 19887bdd16
14 changed files with 568 additions and 1 deletions

View File

@ -0,0 +1,2 @@
INSERT INTO salix.ACL (model,property,accessType,principalId)
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'}
},
{
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
},
http: {
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
Buy requests: Peticiones de compra
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",