Merge branch 'dev' into 4547-fix-compensation
gitea/salix/pipeline/head This commit looks good Details

This commit is contained in:
Alexandre Riera 2022-11-24 08:42:09 +00:00
commit 5124b3b681
11 changed files with 661 additions and 17 deletions

View File

@ -0,0 +1 @@
Alter table `vn`.`expedition` RENAME COLUMN itemFk TO itemFk__;

View File

@ -921,7 +921,7 @@ INSERT INTO `vn`.`expeditionStateType`(`id`, `description`, `code`)
(3, 'Perdida', 'LOST');
INSERT INTO `vn`.`expedition`(`id`, `agencyModeFk`, `ticketFk`, `freightItemFk`, `created`, `itemFk`, `counter`, `workerFk`, `externalId`, `packagingFk`, `stateTypeFk`, `hostFk`)
INSERT INTO `vn`.`expedition`(`id`, `agencyModeFk`, `ticketFk`, `freightItemFk`, `created`, `itemFk__`, `counter`, `workerFk`, `externalId`, `packagingFk`, `stateTypeFk`, `hostFk`)
VALUES
(1, 1, 1, 71, DATE_ADD(util.VN_CURDATE(), INTERVAL -1 MONTH), 15, 1, 18, 'UR9000006041', 94, 1, 'pc1'),
(2, 1, 1, 71, DATE_ADD(util.VN_CURDATE(), INTERVAL -1 MONTH), 16, 2, 18, 'UR9000006041', 94, 1, NULL),

View File

@ -1039,6 +1039,17 @@ export default {
booked: 'vn-invoice-in-basic-data vn-date-picker[ng-model="$ctrl.invoiceIn.booked"]',
currency: 'vn-invoice-in-basic-data vn-autocomplete[ng-model="$ctrl.invoiceIn.currencyFk"]',
company: 'vn-invoice-in-basic-data vn-autocomplete[ng-model="$ctrl.invoiceIn.companyFk"]',
dms: 'vn-invoice-in-basic-data vn-textfield[ng-model="$ctrl.invoiceIn.dmsFk"]',
download: 'vn-invoice-in-basic-data vn-textfield[ng-model="$ctrl.invoiceIn.dmsFk"] > div.container > div.prepend > prepend > vn-icon-button',
edit: 'vn-invoice-in-basic-data vn-textfield[ng-model="$ctrl.invoiceIn.dmsFk"] > div.container > div.append > append > vn-icon-button[icon="edit"]',
create: 'vn-invoice-in-basic-data vn-textfield[ng-model="$ctrl.invoiceIn.dmsFk"] > div.container > div.append > append > vn-icon-button[icon="add_circle"]',
reference: 'vn-textfield[ng-model="$ctrl.dms.reference"]',
companyId: 'vn-autocomplete[ng-model="$ctrl.dms.companyId"]',
warehouseId: 'vn-autocomplete[ng-model="$ctrl.dms.warehouseId"]',
dmsTypeId: 'vn-autocomplete[ng-model="$ctrl.dms.dmsTypeId"]',
description: 'vn-textarea[ng-model="$ctrl.dms.description"]',
inputFile: 'vn-input-file[ng-model="$ctrl.dms.files"]',
confirm: 'button[response="accept"]',
save: 'vn-invoice-in-basic-data button[type=submit]'
},
invoiceInTax: {

View File

@ -4,6 +4,7 @@ import getBrowser from '../../helpers/puppeteer';
describe('InvoiceIn basic data path', () => {
let browser;
let page;
let newDms;
beforeAll(async() => {
browser = await getBrowser();
@ -24,6 +25,8 @@ describe('InvoiceIn basic data path', () => {
await page.autocompleteSearch(selectors.invoiceInBasicData.supplier, 'Verdnatura');
await page.clearInput(selectors.invoiceInBasicData.supplierRef);
await page.write(selectors.invoiceInBasicData.supplierRef, '9999');
await page.clearInput(selectors.invoiceInBasicData.dms);
await page.write(selectors.invoiceInBasicData.dms, '2');
await page.pickDate(selectors.invoiceInBasicData.bookEntried, now);
await page.pickDate(selectors.invoiceInBasicData.booked, now);
await page.autocompleteSearch(selectors.invoiceInBasicData.currency, 'USD');
@ -61,4 +64,141 @@ describe('InvoiceIn basic data path', () => {
expect(result).toEqual('ORN');
});
it(`should confirm the invoiceIn dms was edited`, async() => {
const result = await page
.waitToGetProperty(selectors.invoiceInBasicData.dms, 'value');
expect(result).toEqual('2');
});
it(`should create a new invoiceIn dms and save the changes`, async() => {
await page.clearInput(selectors.invoiceInBasicData.dms);
await page.waitToClick(selectors.invoiceInBasicData.create);
await page.clearInput(selectors.invoiceInBasicData.reference);
await page.write(selectors.invoiceInBasicData.reference, 'New Dms');
await page.waitToClick(selectors.invoiceInBasicData.confirm);
let message = await page.waitForSnackbar();
expect(message.text).toContain('The company can\'t be empty');
await page.clearInput(selectors.invoiceInBasicData.companyId);
await page.autocompleteSearch(selectors.invoiceInBasicData.companyId, 'VNL');
await page.waitToClick(selectors.invoiceInBasicData.confirm);
message = await page.waitForSnackbar();
expect(message.text).toContain('The warehouse can\'t be empty');
await page.clearInput(selectors.invoiceInBasicData.warehouseId);
await page.autocompleteSearch(selectors.invoiceInBasicData.warehouseId, 'Warehouse One');
await page.waitToClick(selectors.invoiceInBasicData.confirm);
message = await page.waitForSnackbar();
expect(message.text).toContain('The DMS Type can\'t be empty');
await page.clearInput(selectors.invoiceInBasicData.dmsTypeId);
await page.autocompleteSearch(selectors.invoiceInBasicData.dmsTypeId, 'Ticket');
await page.waitToClick(selectors.invoiceInBasicData.confirm);
message = await page.waitForSnackbar();
expect(message.text).toContain('The description can\'t be empty');
await page.waitToClick(selectors.invoiceInBasicData.description);
await page.write(selectors.invoiceInBasicData.description, 'Dms without edition.');
await page.waitToClick(selectors.invoiceInBasicData.confirm);
message = await page.waitForSnackbar();
expect(message.text).toContain('The files can\'t be empty');
let currentDir = process.cwd();
let filePath = `${currentDir}/e2e/assets/thermograph.jpeg`;
const [fileChooser] = await Promise.all([
page.waitForFileChooser(),
page.waitToClick(selectors.invoiceInBasicData.inputFile)
]);
await fileChooser.accept([filePath]);
await page.waitToClick(selectors.invoiceInBasicData.confirm);
message = await page.waitForSnackbar();
expect(message.text).toContain('Data saved!');
newDms = await page
.waitToGetProperty(selectors.invoiceInBasicData.dms, 'value');
});
it(`should confirm the invoiceIn was edited with the new dms`, async() => {
await page.reloadSection('invoiceIn.card.basicData');
const result = await page
.waitToGetProperty(selectors.invoiceInBasicData.dms, 'value');
expect(result).toEqual(newDms);
});
it(`should edit the invoiceIn`, async() => {
await page.waitToClick(selectors.invoiceInBasicData.edit);
await page.clearInput(selectors.invoiceInBasicData.reference);
await page.write(selectors.invoiceInBasicData.reference, 'Dms Edited');
await page.clearInput(selectors.invoiceInBasicData.companyId);
await page.autocompleteSearch(selectors.invoiceInBasicData.companyId, 'CCs');
await page.clearInput(selectors.invoiceInBasicData.warehouseId);
await page.autocompleteSearch(selectors.invoiceInBasicData.warehouseId, 'Algemesi');
await page.clearInput(selectors.invoiceInBasicData.dmsTypeId);
await page.autocompleteSearch(selectors.invoiceInBasicData.dmsTypeId, 'Basura');
await page.waitToClick(selectors.invoiceInBasicData.description);
await page.write(selectors.invoiceInBasicData.description, ' Nevermind, now is edited.');
await page.waitToClick(selectors.invoiceInBasicData.confirm);
let message = await page.waitForSnackbar();
expect(message.text).toContain('Data saved!');
});
it(`should confirm the new dms has been edited`, async() => {
await page.reloadSection('invoiceIn.card.basicData');
await page.waitToClick(selectors.invoiceInBasicData.edit);
const reference = await page
.waitToGetProperty(selectors.invoiceInBasicData.reference, 'value');
const companyId = await page
.waitToGetProperty(selectors.invoiceInBasicData.companyId, 'value');
const warehouseId = await page
.waitToGetProperty(selectors.invoiceInBasicData.warehouseId, 'value');
const dmsTypeId = await page
.waitToGetProperty(selectors.invoiceInBasicData.dmsTypeId, 'value');
const description = await page
.waitToGetProperty(selectors.invoiceInBasicData.description, 'value');
expect(reference).toEqual('Dms Edited');
expect(companyId).toEqual('CCs');
expect(warehouseId).toEqual('Algemesi');
expect(dmsTypeId).toEqual('Basura');
expect(description).toEqual('Dms without edition. Nevermind, now is edited.');
await page.waitToClick(selectors.invoiceInBasicData.confirm);
});
it(`should disable edit and download if dms doesn't exists, and set back the original dms`, async() => {
await page.clearInput(selectors.invoiceInBasicData.dms);
await page.write(selectors.invoiceInBasicData.dms, '9999');
await page.waitForSelector(`${selectors.invoiceInBasicData.download}.disabled`);
await page.waitForSelector(`${selectors.invoiceInBasicData.edit}.disabled`);
await page.clearInput(selectors.invoiceInBasicData.dms);
await page.write(selectors.invoiceInBasicData.dms, '1');
await page.waitToClick(selectors.invoiceInBasicData.save);
const message = await page.waitForSnackbar();
expect(message.text).toContain('Data saved!');
});
});

View File

@ -5,6 +5,24 @@
form="form"
save="patch">
</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="watcher.submit()" class="vn-w-md">
<vn-card class="vn-pa-lg">
<vn-horizontal>
@ -31,14 +49,14 @@
</vn-horizontal>
<vn-horizontal>
<vn-date-picker
vn-one
label="Expedition date"
vn-one
label="Expedition date"
ng-model="$ctrl.invoiceIn.issued"
vn-focus
rule>
</vn-date-picker>
<vn-date-picker
vn-one
vn-one
label="Operation date"
ng-model="$ctrl.invoiceIn.operated"
rule>
@ -57,17 +75,47 @@
{{id}} - {{name}}
</tpl-item>
</vn-datalist>
<vn-textfield
label="Document"
ng-model="$ctrl.invoiceIn.dmsFk"
ng-change="$ctrl.checkFileExists($ctrl.invoiceIn.dmsFk)"
rule>
<prepend>
<vn-icon-button
disabled="$ctrl.editDownloadDisabled"
ng-if="$ctrl.invoiceIn.dmsFk"
title="{{'Download file' | translate}}"
icon="cloud_download"
ng-click="$ctrl.downloadFile($ctrl.invoiceIn.dmsFk)">
</vn-icon-button>
</prepend>
<append>
<vn-icon-button
disabled="$ctrl.editDownloadDisabled"
ng-if="$ctrl.invoiceIn.dmsFk"
ng-click="$ctrl.openEditDialog($ctrl.invoiceIn.dmsFk)"
icon="edit"
title="{{'Edit document' | translate}}">
</vn-icon-button>
<vn-icon-button
ng-if="!$ctrl.invoiceIn.dmsFk"
ng-click="$ctrl.openCreateDialog()"
icon="add_circle"
title="{{'Create document' | translate}}">
</vn-icon-button>
</append>
</vn-textfield>
</vn-horizontal>
<vn-horizontal>
<vn-date-picker
vn-one
label="Entry date"
vn-one
label="Entry date"
ng-model="$ctrl.invoiceIn.bookEntried"
rule>
</vn-date-picker>
<vn-date-picker
vn-one
label="Accounted date"
vn-one
label="Accounted date"
ng-model="$ctrl.invoiceIn.booked"
rule>
</vn-date-picker>
@ -104,4 +152,164 @@
ng-click="watcher.loadOriginalData()">
</vn-button>
</vn-button-bar>
</form>
</form>
<!-- Create edit dms dialog -->
<vn-dialog
vn-id="dmsEditDialog"
message="Edit document"
on-accept="$ctrl.onEdit()">
<tpl-body>
<vn-horizontal>
<vn-textfield
vn-one
vn-focus
label="Reference"
ng-model="$ctrl.dms.reference"
rule>
</vn-textfield>
<vn-autocomplete vn-one required="true"
label="Company"
ng-model="$ctrl.dms.companyId"
url="Companies"
show-field="code"
value-field="id">
</vn-autocomplete>
</vn-horizontal>
<vn-horizontal>
<vn-autocomplete vn-one required="true"
label="Warehouse"
ng-model="$ctrl.dms.warehouseId"
url="Warehouses"
show-field="name"
value-field="id">
</vn-autocomplete>
<vn-autocomplete vn-one required="true"
label="Type"
ng-model="$ctrl.dms.dmsTypeId"
url="DmsTypes"
show-field="name"
value-field="id">
</vn-autocomplete>
</vn-horizontal>
<vn-horizontal>
<vn-textarea
vn-one
required="true"
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="false"
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 disabled="true"
label="Generate identifier for original file"
ng-model="$ctrl.dms.hasFile">
</vn-check>
</vn-vertical>
</tpl-body>
<tpl-buttons>
<input type="button" response="cancel" translate-attr="{value: 'Cancel'}"/>
<button response="accept" translate>Save</button>
</tpl-buttons>
</vn-dialog>
<!-- Create new dms dialog -->
<vn-dialog
vn-id="dmsCreateDialog"
message="Create document"
on-accept="$ctrl.onCreate()">
<tpl-body>
<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"
required="true">
</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"
required="true">
</vn-autocomplete>
<vn-autocomplete
vn-one
label="Type"
ng-model="$ctrl.dms.dmsTypeId"
data="dmsTypes"
show-field="name"
value-field="id"
required="true">
</vn-autocomplete>
</vn-horizontal>
<vn-horizontal>
<vn-textarea
vn-one
label="Description"
ng-model="$ctrl.dms.description"
required="true"
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>
</tpl-body>
<tpl-buttons>
<input type="button" response="cancel" translate-attr="{value: 'Cancel'}"/>
<button response="accept" translate>Create</button>
</tpl-buttons>
</vn-dialog>

View File

@ -1,9 +1,181 @@
import ngModule from '../module';
import Section from 'salix/components/section';
import UserError from 'core/lib/user-error';
class Controller extends Section {
constructor($element, $, vnFile) {
super($element, $, vnFile);
this.dms = {
files: [],
hasFile: false,
hasFileAttached: false
};
this.vnFile = vnFile;
this.getAllowedContentTypes();
this._editDownloadDisabled = false;
}
get contentTypesInfo() {
return this.$t('ContentTypesInfo', {
allowedContentTypes: this.allowedContentTypes
});
}
get editDownloadDisabled() {
return this._editDownloadDisabled;
}
async checkFileExists(dmsId) {
if (!dmsId) return;
let filter = {
fields: ['id']
};
await this.$http.get(`Dms/${dmsId}`, {filter})
.then(() => this._editDownloadDisabled = false)
.catch(() => this._editDownloadDisabled = true);
}
async getFile(dmsId) {
const path = `Dms/${dmsId}`;
await this.$http.get(path).then(res => {
const dms = res.data && res.data;
this.dms = {
dmsId: dms.id,
reference: dms.reference,
warehouseId: dms.warehouseFk,
companyId: dms.companyFk,
dmsTypeId: dms.dmsTypeFk,
description: dms.description,
hasFile: dms.hasFile,
hasFileAttached: false,
files: []
};
});
}
getAllowedContentTypes() {
this.$http.get('DmsContainers/allowedContentTypes').then(res => {
if (res.data.length > 0) {
const contentTypes = res.data.join(', ');
this.allowedContentTypes = contentTypes;
}
});
}
openEditDialog(dmsId) {
this.getFile(dmsId).then(() => this.$.dmsEditDialog.show());
}
openCreateDialog() {
this.dms = {
reference: null,
warehouseId: null,
companyId: null,
dmsTypeId: null,
description: null,
hasFile: true,
hasFileAttached: true,
files: null
};
this.$.dmsCreateDialog.show();
}
downloadFile(dmsId) {
this.vnFile.download(`api/dms/${dmsId}/downloadFile`);
}
onFileChange(files) {
let hasFileAttached = false;
if (files.length > 0)
hasFileAttached = true;
this.$.$applyAsync(() => {
this.dms.hasFileAttached = hasFileAttached;
});
}
onEdit() {
if (!this.dms.companyId)
throw new UserError(`The company can't be empty`);
if (!this.dms.warehouseId)
throw new UserError(`The warehouse can't be empty`);
if (!this.dms.dmsTypeId)
throw new UserError(`The DMS Type can't be empty`);
if (!this.dms.description)
throw new UserError(`The description can't be empty`);
const query = `dms/${this.dms.dmsId}/updateFile`;
const options = {
method: 'POST',
url: query,
params: this.dms,
headers: {
'Content-Type': undefined
},
transformRequest: files => {
const formData = new FormData();
for (let i = 0; i < files.length; i++)
formData.append(files[i].name, files[i]);
return formData;
},
data: this.dms.files
};
this.$http(options).then(res => {
if (res) {
this.vnApp.showSuccess(this.$t('Data saved!'));
if (res.data.length > 0) this.invoiceIn.dmsFk = res.data[0].id;
}
});
}
onCreate() {
if (!this.dms.companyId)
throw new UserError(`The company can't be empty`);
if (!this.dms.warehouseId)
throw new UserError(`The warehouse can't be empty`);
if (!this.dms.dmsTypeId)
throw new UserError(`The DMS Type can't be empty`);
if (!this.dms.description)
throw new UserError(`The description can't be empty`);
if (!this.dms.files)
throw new UserError(`The files can't be empty`);
const query = `Dms/uploadFile`;
const options = {
method: 'POST',
url: query,
params: this.dms,
headers: {
'Content-Type': undefined
},
transformRequest: files => {
const formData = new FormData();
for (let i = 0; i < files.length; i++)
formData.append(files[i].name, files[i]);
return formData;
},
data: this.dms.files
};
this.$http(options).then(res => {
if (res) {
this.vnApp.showSuccess(this.$t('Data saved!'));
if (res.data.length > 0) this.invoiceIn.dmsFk = res.data[0].id;
}
});
}
}
Controller.$inject = ['$element', '$scope', 'vnFile'];
ngModule.vnComponent('vnInvoiceInBasicData', {
template: require('./index.html'),
controller: Section,
controller: Controller,
bindings: {
invoiceIn: '<'
}

View File

@ -0,0 +1,102 @@
import './index.js';
import watcher from 'core/mocks/watcher';
describe('InvoiceIn', () => {
describe('Component vnInvoiceInBasicData', () => {
let controller;
let $scope;
let $httpBackend;
let $httpParamSerializer;
beforeEach(ngModule('invoiceIn'));
beforeEach(inject(($componentController, $rootScope, _$httpBackend_, _$httpParamSerializer_) => {
$scope = $rootScope.$new();
$httpBackend = _$httpBackend_;
$httpParamSerializer = _$httpParamSerializer_;
const $element = angular.element('<vn-invoice-in-basic-data></vn-invoice-in-basic-data>');
controller = $componentController('vnInvoiceInBasicData', {$element, $scope});
controller.$.watcher = watcher;
$httpBackend.expect('GET', `DmsContainers/allowedContentTypes`).respond({});
}));
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();
});
});
describe('checkFileExists()', () => {
it(`should return false if a file exists`, () => {
const fileIdExists = 1;
controller.checkFileExists(fileIdExists);
expect(controller.editDownloadDisabled).toBe(false);
});
});
describe('onEdit()', () => {
it(`should perform a POST query to edit the dms properties`, () => {
jest.spyOn(controller.vnApp, 'showSuccess');
const dms = {
dmsId: 1,
reference: 'Ref1',
warehouseId: 1,
companyId: 442,
dmsTypeId: 20,
description: 'This is a description',
files: []
};
controller.dms = dms;
const serializedParams = $httpParamSerializer(controller.dms);
const query = `dms/${controller.dms.dmsId}/updateFile?${serializedParams}`;
$httpBackend.expectPOST(query).respond({});
controller.onEdit();
$httpBackend.flush();
expect(controller.vnApp.showSuccess).toHaveBeenCalled();
});
});
describe('onCreate()', () => {
it(`should perform a POST query to create a new dms`, () => {
jest.spyOn(controller.vnApp, 'showSuccess');
const dms = {
reference: 'Ref1',
warehouseId: 1,
companyId: 442,
dmsTypeId: 20,
description: 'This is a description',
files: [{
lastModified: 1668673957761,
lastModifiedDate: new Date(),
name: 'file-example.png',
size: 19653,
type: 'image/png',
webkitRelativePath: ''
}]
};
controller.dms = dms;
const serializedParams = $httpParamSerializer(controller.dms);
const query = `Dms/uploadFile?${serializedParams}`;
$httpBackend.expectPOST(query).respond({});
controller.onCreate();
$httpBackend.flush();
expect(controller.vnApp.showSuccess).toHaveBeenCalled();
});
});
});
});

View File

@ -0,0 +1 @@
ContentTypesInfo: Allowed file types {{allowedContentTypes}}

View File

@ -0,0 +1,15 @@
Upload file: Subir fichero
Edit file: Editar fichero
Upload: Subir
Document: Documento
ContentTypesInfo: "Tipos de archivo permitidos: {{allowedContentTypes}}"
Generate identifier for original file: Generar identificador para archivo original
File management: Gestión documental
Hard copy: Copia
This file will be deleted: Este fichero va a ser borrado
Are you sure?: Estas seguro?
File deleted: Fichero eliminado
Remove file: Eliminar fichero
Download file: Descargar fichero
Edit document: Editar documento
Create document: Crear documento

View File

@ -37,7 +37,6 @@ module.exports = Self => {
i1.name packageItemName,
e.counter,
i2.name freightItemName,
e.itemFk,
u.name userName,
e.created,
e.externalId,
@ -50,10 +49,10 @@ module.exports = Self => {
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.freightItemFk
LEFT JOIN vn.packaging p ON p.id = e.packagingFk
LEFT JOIN vn.item i3 ON i3.id = p.itemFk
LEFT JOIN vn.item i2 ON i2.id = p.itemFk
LEFT JOIN account.user u ON u.id = e.workerFk
LEFT JOIN vn.expeditionScan es ON es.expeditionFk = e.id
LEFT JOIN account.user su ON su.id = es.workerFk

View File

@ -37,11 +37,6 @@
"model": "agency-mode",
"foreignKey": "agencyModeFk"
},
"packageItem": {
"type": "belongsTo",
"model": "Item",
"foreignKey": "itemFk"
},
"worker": {
"type": "belongsTo",
"model": "Worker",