Merge branch 'dev' into 3927-monitor_ticket
gitea/salix/pipeline/head This commit looks good Details

This commit is contained in:
Carlos Jimenez Ruiz 2022-05-09 14:14:01 +00:00
commit 9c085fa653
33 changed files with 313 additions and 69 deletions

View File

@ -46,7 +46,7 @@ module.exports = Self => {
const {data} = await Self.getUserStatus(recipient.name); const {data} = await Self.getUserStatus(recipient.name);
if (data) { if (data) {
if (data.status === 'offline') { if (data.status === 'offline' || data.status === 'busy') {
// Send message to department room // Send message to department room
const workerDepartment = await models.WorkerDepartment.findById(recipientId, { const workerDepartment = await models.WorkerDepartment.findById(recipientId, {
include: { include: {
@ -58,6 +58,8 @@ module.exports = Self => {
if (channelName) if (channelName)
return Self.send(ctx, `#${channelName}`, `@${recipient.name}${message}`); return Self.send(ctx, `#${channelName}`, `@${recipient.name}${message}`);
else
return Self.send(ctx, `@${recipient.name}`, message);
} else } else
return Self.send(ctx, `@${recipient.name}`, message); return Self.send(ctx, `@${recipient.name}`, message);
} }

View File

@ -2271,8 +2271,14 @@ INSERT INTO `vn`.`dms`(`id`, `dmsTypeFk`, `file`, `contentType`, `workerFk`, `wa
(3, 5, '3.txt', 'text/plain', 5, 1, 442, NULL, TRUE, 'Client: 104', 'Client:104 readme', CURDATE()), (3, 5, '3.txt', 'text/plain', 5, 1, 442, NULL, TRUE, 'Client: 104', 'Client:104 readme', CURDATE()),
(4, 3, '4.txt', 'text/plain', 5, 1, 442, NULL, TRUE, 'Worker: 106', 'Worker:106 readme', CURDATE()), (4, 3, '4.txt', 'text/plain', 5, 1, 442, NULL, TRUE, 'Worker: 106', 'Worker:106 readme', CURDATE()),
(5, 5, '5.txt', 'text/plain', 5, 1, 442, NULL, TRUE, 'travel: 1', 'dmsForThermograph', CURDATE()), (5, 5, '5.txt', 'text/plain', 5, 1, 442, NULL, TRUE, 'travel: 1', 'dmsForThermograph', CURDATE()),
(6, 5, '6.txt', 'text/plain', 5, 1, 442, NULL, TRUE, 'NotExists', 'DoesNotExists', CURDATE()); (6, 5, '6.txt', 'text/plain', 5, 1, 442, NULL, TRUE, 'NotExists', 'DoesNotExists', CURDATE()),
(7, 20, '7.jpg', 'image/jpeg', 9, 1, 442, NULL, FALSE, '1', 'TICKET ID DEL CLIENTE BRUCE WAYNE ID 1101', CURDATE()),
(8, 20, '8.mp4', 'video/mp4', 9, 1, 442, NULL, FALSE, '1', 'TICKET ID DEL CLIENTE BRUCE WAYNE ID 1101', CURDATE());
INSERT INTO `vn`.`claimDms`(`claimFk`, `dmsFk`)
VALUES
(1, 7),
(1, 8);
INSERT INTO `vn`.`ticketDms`(`ticketFk`, `dmsFk`) INSERT INTO `vn`.`ticketDms`(`ticketFk`, `dmsFk`)
VALUES VALUES

View File

@ -26,7 +26,7 @@
.icon-agency-term:before { .icon-agency-term:before {
content: "\e950"; content: "\e950";
} }
.icon-deaulter:before { .icon-defaulter:before {
content: "\e94b"; content: "\e94b";
} }
.icon-100:before { .icon-100:before {

View File

@ -39,7 +39,8 @@
"multipart/x-zip", "multipart/x-zip",
"image/png", "image/png",
"image/jpeg", "image/jpeg",
"image/jpg" "image/jpg",
"video/mp4"
] ]
}, },
"dmsStorage": { "dmsStorage": {
@ -84,5 +85,18 @@
"application/octet-stream", "application/octet-stream",
"application/pdf" "application/pdf"
] ]
},
"claimStorage": {
"name": "claimStorage",
"connector": "loopback-component-storage",
"provider": "filesystem",
"root": "./storage/dms",
"maxFileSize": "31457280",
"allowedContentTypes": [
"image/png",
"image/jpeg",
"image/jpg",
"video/mp4"
]
} }
} }

View File

@ -0,0 +1,59 @@
const UserError = require('vn-loopback/util/user-error');
module.exports = Self => {
Self.remoteMethodCtx('downloadFile', {
description: 'Get the claim file',
accessType: 'READ',
accepts: [
{
arg: 'id',
type: 'Number',
description: 'The document id',
http: {source: 'path'}
}
],
returns: [
{
arg: 'body',
type: 'file',
root: true
},
{
arg: 'Content-Type',
type: 'String',
http: {target: 'header'}
},
{
arg: 'Content-Disposition',
type: 'String',
http: {target: 'header'}
}
],
http: {
path: `/:id/downloadFile`,
verb: 'GET'
}
});
Self.downloadFile = async function(ctx, id) {
const models = Self.app.models;
const ClaimContainer = models.ClaimContainer;
const dms = await models.Dms.findById(id);
const pathHash = ClaimContainer.getHash(dms.id);
try {
await ClaimContainer.getFile(pathHash, dms.file);
} catch (e) {
if (e.code != 'ENOENT')
throw e;
const error = new UserError(`File doesn't exists`);
error.statusCode = 404;
throw error;
}
const stream = ClaimContainer.downloadStream(pathHash, dms.file);
return [stream, dms.contentType, `filename="${dms.file}"`];
};
};

View File

@ -0,0 +1,13 @@
const app = require('vn-loopback/server/server');
describe('claim downloadFile()', () => {
const dmsId = 7;
it('should return a response for an employee with image content-type', async() => {
const workerId = 1107;
const ctx = {req: {accessToken: {userId: workerId}}};
const result = await app.models.Claim.downloadFile(ctx, dmsId);
expect(result[1]).toEqual('image/jpeg');
});
});

View File

@ -0,0 +1,18 @@
const app = require('vn-loopback/server/server');
describe('claim uploadFile()', () => {
it(`should return an error for a user without enough privileges`, async() => {
const clientId = 1101;
const ticketDmsTypeId = 14;
const ctx = {req: {accessToken: {userId: clientId}}, args: {dmsTypeId: ticketDmsTypeId}};
let error;
await app.models.Claim.uploadFile(ctx).catch(e => {
error = e;
}).finally(() => {
expect(error.message).toEqual(`You don't have enough privileges`);
});
expect(error).toBeDefined();
});
});

View File

@ -1,6 +1,10 @@
const UserError = require('vn-loopback/util/user-error');
const fs = require('fs-extra');
const path = require('path');
module.exports = Self => { module.exports = Self => {
Self.remoteMethodCtx('uploadFile', { Self.remoteMethodCtx('uploadFile', {
description: 'Upload and attach a document', description: 'Upload and attach a file',
accessType: 'WRITE', accessType: 'WRITE',
accepts: [{ accepts: [{
arg: 'id', arg: 'id',
@ -53,22 +57,54 @@ module.exports = Self => {
}); });
Self.uploadFile = async(ctx, id, options) => { Self.uploadFile = async(ctx, id, options) => {
let tx; const tx = await Self.beginTransaction({});
const myOptions = {}; const myOptions = {};
if (typeof options == 'object') if (typeof options == 'object')
Object.assign(myOptions, options); Object.assign(myOptions, options);
if (!myOptions.transaction) { if (!myOptions.transaction)
tx = await Self.beginTransaction({});
myOptions.transaction = tx; myOptions.transaction = tx;
}
const models = Self.app.models; const models = Self.app.models;
const promises = []; const promises = [];
const TempContainer = models.TempContainer;
const ClaimContainer = models.ClaimContainer;
const fileOptions = {};
const args = ctx.args;
let srcFile;
try { try {
const uploadedFiles = await models.Dms.uploadFile(ctx, myOptions); const hasWriteRole = await models.DmsType.hasWriteRole(ctx, args.dmsTypeId, myOptions);
uploadedFiles.forEach(dms => { if (!hasWriteRole)
throw new UserError(`You don't have enough privileges`);
// Upload file to temporary path
const tempContainer = await TempContainer.container('dms');
const uploaded = await TempContainer.upload(tempContainer.name, ctx.req, ctx.result, fileOptions);
const files = Object.values(uploaded.files).map(file => {
return file[0];
});
const addedDms = [];
for (const uploadedFile of files) {
const newDms = await createDms(ctx, uploadedFile, myOptions);
const pathHash = ClaimContainer.getHash(newDms.id);
const file = await TempContainer.getFile(tempContainer.name, uploadedFile.name);
srcFile = path.join(file.client.root, file.container, file.name);
const claimContainer = await ClaimContainer.container(pathHash);
const dstFile = path.join(claimContainer.client.root, pathHash, newDms.file);
await fs.move(srcFile, dstFile, {
overwrite: true
});
addedDms.push(newDms);
}
addedDms.forEach(dms => {
const newClaimDms = models.ClaimDms.create({ const newClaimDms = models.ClaimDms.create({
claimFk: id, claimFk: id,
dmsFk: dms.id dmsFk: dms.id
@ -83,7 +119,34 @@ module.exports = Self => {
return resolvedPromises; return resolvedPromises;
} catch (e) { } catch (e) {
if (tx) await tx.rollback(); if (tx) await tx.rollback();
if (fs.existsSync(srcFile))
await fs.unlink(srcFile);
throw e; throw e;
} }
}; };
async function createDms(ctx, file, myOptions) {
const models = Self.app.models;
const myUserId = ctx.req.accessToken.userId;
const args = ctx.args;
const newDms = await models.Dms.create({
workerFk: myUserId,
dmsTypeFk: args.dmsTypeId,
companyFk: args.companyId,
warehouseFk: args.warehouseId,
reference: args.reference,
description: args.description,
contentType: file.type,
hasFile: args.hasFile
}, myOptions);
let fileName = file.name;
const extension = models.DmsContainer.getFileExtension(fileName);
fileName = `${newDms.id}.${extension}`;
return newDms.updateAttribute('file', fileName, myOptions);
}
}; };

View File

@ -37,5 +37,8 @@
}, },
"ClaimLog": { "ClaimLog": {
"dataSource": "vn" "dataSource": "vn"
},
"ClaimContainer": {
"dataSource": "claimStorage"
} }
} }

View File

@ -0,0 +1,10 @@
{
"name": "ClaimContainer",
"base": "Container",
"acls": [{
"accessType": "READ",
"principalType": "ROLE",
"principalId": "$everyone",
"permission": "ALLOW"
}]
}

View File

@ -7,4 +7,5 @@ module.exports = Self => {
require('../methods/claim/uploadFile')(Self); require('../methods/claim/uploadFile')(Self);
require('../methods/claim/updateClaimAction')(Self); require('../methods/claim/updateClaimAction')(Self);
require('../methods/claim/isEditable')(Self); require('../methods/claim/isEditable')(Self);
require('../methods/claim/downloadFile')(Self);
}; };

View File

@ -1,6 +1,7 @@
<vn-crud-model <vn-crud-model
vn-id="model" vn-id="model"
auto-load="true" auto-load="true"
filter="::$ctrl.filter"
url="ClaimDms" url="ClaimDms"
link="{claimFk: $ctrl.$params.id}" link="{claimFk: $ctrl.$params.id}"
limit="20" limit="20"
@ -14,8 +15,13 @@
<section class="photo" ng-repeat="photo in $ctrl.photos"> <section class="photo" ng-repeat="photo in $ctrl.photos">
<section class="image vn-shadow" on-error-src <section class="image vn-shadow" on-error-src
ng-style="{'background': 'url(' + $ctrl.getImagePath(photo.dmsFk) + ')'}" ng-style="{'background': 'url(' + $ctrl.getImagePath(photo.dmsFk) + ')'}"
zoom-image="{{$ctrl.getImagePath(photo.dmsFk)}}"> zoom-image="{{$ctrl.getImagePath(photo.dmsFk)}}"
ng-if="photo.dms.contentType != 'video/mp4'">
</section> </section>
<video id="videobcg" muted="muted" controls ng-if="photo.dms.contentType == 'video/mp4'"
class="video">
<source src="{{$ctrl.getImagePath(photo.dmsFk)}}" type="video/mp4">
</video>
<section class="actions"> <section class="actions">
<vn-button <vn-button
class="round" class="round"
@ -35,7 +41,7 @@
</vn-confirm> </vn-confirm>
<vn-float-button <vn-float-button
icon="add" icon="add"
vn-tooltip="Select photo" vn-tooltip="Select file"
vn-bind="+" vn-bind="+"
ng-click="$ctrl.openUploadDialog()" ng-click="$ctrl.openUploadDialog()"
fixed-bottom-right> fixed-bottom-right>

View File

@ -6,6 +6,13 @@ class Controller extends Section {
constructor($element, $, vnFile) { constructor($element, $, vnFile) {
super($element, $); super($element, $);
this.vnFile = vnFile; this.vnFile = vnFile;
this.filter = {
include: [
{
relation: 'dms'
}
]
};
} }
deleteDms(index) { deleteDms(index) {
@ -13,7 +20,7 @@ class Controller extends Section {
return this.$http.post(`ClaimDms/${dmsFk}/removeFile`) return this.$http.post(`ClaimDms/${dmsFk}/removeFile`)
.then(() => { .then(() => {
this.$.model.remove(index); this.$.model.remove(index);
this.vnApp.showSuccess(this.$t('Photo deleted')); this.vnApp.showSuccess(this.$t('File deleted'));
}); });
} }
@ -81,13 +88,13 @@ class Controller extends Section {
data: this.dms.files data: this.dms.files
}; };
this.$http(options).then(() => { this.$http(options).then(() => {
this.vnApp.showSuccess(this.$t('Photo uploaded!')); this.vnApp.showSuccess(this.$t('File uploaded!'));
this.$.model.refresh(); this.$.model.refresh();
}); });
} }
getImagePath(dmsId) { getImagePath(dmsId) {
return this.vnFile.getPath(`/api/dms/${dmsId}/downloadFile`); return this.vnFile.getPath(`/api/Claims/${dmsId}/downloadFile`);
} }
} }

View File

@ -1,5 +1,5 @@
Are you sure you want to continue?: ¿Seguro que quieres continuar? Are you sure you want to continue?: ¿Seguro que quieres continuar?
Drag & Drop photos here...: Arrastra y suelta fotos aquí... Drag & Drop photos here...: Arrastra y suelta fotos aquí...
Photo deleted: Foto eliminada File deleted: Archivo eliminado
Photo uploaded!: Foto subida! File uploaded!: Archivo subido!
Select photo: Seleccionar foto Select file: Seleccionar fichero

View File

@ -29,4 +29,19 @@ vn-claim-photos {
height: 288px; height: 288px;
} }
} }
.video {
width: 100%;
height: 100%;
object-fit: cover;
cursor: pointer;
box-shadow: 0 2px 2px 0 rgba(0,0,0,.14),
0 3px 1px -2px rgba(0,0,0,.2),
0 1px 5px 0 rgba(0,0,0,.12);
border: 2px solid transparent;
}
.video:hover {
border: 2px solid $color-primary
}
} }

View File

@ -33,7 +33,6 @@ describe('Address updateAddress', () => {
error = e; error = e;
} }
expect(error).toBeDefined();
expect(error.message).toEqual('Incoterms is required for a non UEE member'); expect(error.message).toEqual('Incoterms is required for a non UEE member');
}); });
@ -57,7 +56,6 @@ describe('Address updateAddress', () => {
error = e; error = e;
} }
expect(error).toBeDefined();
expect(error.message).toEqual('Customs agent is required for a non UEE member'); expect(error.message).toEqual('Customs agent is required for a non UEE member');
}); });
@ -91,6 +89,8 @@ describe('Address updateAddress', () => {
it('should return an error for a user without enough privileges', async() => { it('should return an error for a user without enough privileges', async() => {
const tx = await models.Client.beginTransaction({}); const tx = await models.Client.beginTransaction({});
let error;
try { try {
const options = {transaction: tx}; const options = {transaction: tx};
ctx.args = { ctx.args = {
@ -124,8 +124,10 @@ describe('Address updateAddress', () => {
expect(address.isLogifloraAllowed).toEqual(true); expect(address.isLogifloraAllowed).toEqual(true);
await tx.rollback(); await tx.rollback();
ctx.req.accessToken.userId = employeeId;
} catch (e) { } catch (e) {
await tx.rollback(); await tx.rollback();
ctx.req.accessToken.userId = employeeId;
throw e; throw e;
} }
}); });

View File

@ -89,11 +89,12 @@ module.exports = function(Self) {
const args = ctx.args; const args = ctx.args;
const userId = ctx.req.accessToken.userId; const userId = ctx.req.accessToken.userId;
const myOptions = {}; const myOptions = {};
const isSalesAssistant = await models.Account.hasRole(userId, 'salesAssistant', myOptions);
if (typeof options == 'object') if (typeof options == 'object')
Object.assign(myOptions, options); Object.assign(myOptions, options);
const isSalesAssistant = await models.Account.hasRole(userId, 'salesAssistant', myOptions);
if (args.isLogifloraAllowed && !isSalesAssistant) if (args.isLogifloraAllowed && !isSalesAssistant)
throw new UserError(`You don't have enough privileges`); throw new UserError(`You don't have enough privileges`);

View File

@ -46,10 +46,6 @@ module.exports = Self => {
message: 'TIN must be unique' message: 'TIN must be unique'
}); });
Self.validatesUniquenessOf('socialName', {
message: 'The company name must be unique'
});
Self.validatesFormatOf('email', { Self.validatesFormatOf('email', {
message: 'Invalid email', message: 'Invalid email',
allowNull: true, allowNull: true,
@ -63,17 +59,37 @@ module.exports = Self => {
min: 3, max: 10 min: 3, max: 10
}); });
Self.validateAsync('socialName', socialNameIsUnique, {
message: 'The company name must be unique'
});
async function socialNameIsUnique(err, done) {
const filter = {
where: {
and: [
{socialName: this.socialName},
{isActive: true},
{id: {neq: this.id}}
]
}
};
const client = await Self.app.models.Client.findOne(filter);
if (client)
err();
done();
}
Self.validateAsync('iban', ibanNeedsValidation, { Self.validateAsync('iban', ibanNeedsValidation, {
message: 'The IBAN does not have the correct format' message: 'The IBAN does not have the correct format'
}); });
async function ibanNeedsValidation(err, done) { async function ibanNeedsValidation(err, done) {
let filter = { const filter = {
fields: ['code'], fields: ['code'],
where: {id: this.countryFk} where: {id: this.countryFk}
}; };
let country = await Self.app.models.Country.findOne(filter); const country = await Self.app.models.Country.findOne(filter);
let code = country ? country.code.toLowerCase() : null; const code = country ? country.code.toLowerCase() : null;
if (code != 'es') if (code != 'es')
return done(); return done();
@ -90,12 +106,12 @@ module.exports = Self => {
if (!this.isTaxDataChecked) if (!this.isTaxDataChecked)
return done(); return done();
let filter = { const filter = {
fields: ['code'], fields: ['code'],
where: {id: this.countryFk} where: {id: this.countryFk}
}; };
let country = await Self.app.models.Country.findOne(filter); const country = await Self.app.models.Country.findOne(filter);
let code = country ? country.code.toLowerCase() : null; const code = country ? country.code.toLowerCase() : null;
if (!this.fi || !validateTin(this.fi, code)) if (!this.fi || !validateTin(this.fi, code))
err(); err();
@ -118,8 +134,8 @@ module.exports = Self => {
function cannotHaveET(err) { function cannotHaveET(err) {
if (!this.fi) return; if (!this.fi) return;
let tin = this.fi.toUpperCase(); const tin = this.fi.toUpperCase();
let cannotHaveET = /^[A-B]/.test(tin); const cannotHaveET = /^[A-B]/.test(tin);
if (cannotHaveET && this.isEqualizated) if (cannotHaveET && this.isEqualizated)
err(); err();

View File

@ -1,12 +1,11 @@
const models = require('vn-loopback/server/server').models; const {models} = require('vn-loopback/server/server');
describe('item lastEntriesFilter()', () => { describe('item lastEntriesFilter()', () => {
const minDate = new Date(value); it('should return one entry for the given item', async() => {
minHour.setHours(0, 0, 0, 0); const minDate = new Date();
const maxDate = new Date(value); minDate.setHours(0, 0, 0, 0);
maxHour.setHours(23, 59, 59, 59); const maxDate = new Date();
maxDate.setHours(23, 59, 59, 59);
it('should return one entry for a given item', async() => {
const tx = await models.Item.beginTransaction({}); const tx = await models.Item.beginTransaction({});
const options = {transaction: tx}; const options = {transaction: tx};
@ -23,13 +22,18 @@ describe('item lastEntriesFilter()', () => {
} }
}); });
it('should return five entries for a given item', async() => { it('should return five entries for the given item', async() => {
const minDate = new Date();
minDate.setHours(0, 0, 0, 0);
minDate.setMonth(minDate.getMonth() - 2, 1);
const maxDate = new Date();
maxDate.setHours(23, 59, 59, 59);
const tx = await models.Item.beginTransaction({}); const tx = await models.Item.beginTransaction({});
const options = {transaction: tx}; const options = {transaction: tx};
try { try {
minDate.setMonth(minDate.getMonth() - 2, 1);
const filter = {where: {itemFk: 1, landed: {between: [minDate, maxDate]}}}; const filter = {where: {itemFk: 1, landed: {between: [minDate, maxDate]}}};
const result = await models.Item.lastEntriesFilter(filter, options); const result = await models.Item.lastEntriesFilter(filter, options);

View File

@ -304,7 +304,7 @@ module.exports = Self => {
{'tp.hasTicketRequest': true}, {'tp.hasTicketRequest': true},
{'tp.hasComponentLack': true}, {'tp.hasComponentLack': true},
{'tp.isTaxDataChecked': false}, {'tp.isTaxDataChecked': false},
{'tp.isAvailable': false} {'tp.itemShortage': {neq: null}}
]}; ]};
} else if (hasProblems === false) { } else if (hasProblems === false) {
whereProblems = {and: [ whereProblems = {and: [
@ -313,7 +313,7 @@ module.exports = Self => {
{'tp.hasTicketRequest': false}, {'tp.hasTicketRequest': false},
{'tp.hasComponentLack': false}, {'tp.hasComponentLack': false},
{'tp.isTaxDataChecked': true}, {'tp.isTaxDataChecked': true},
{'tp.isAvailable': true} {'tp.itemShortage': null}
]}; ]};
} }

View File

@ -98,8 +98,8 @@
icon="icon-buyrequest"> icon="icon-buyrequest">
</vn-icon> </vn-icon>
<vn-icon <vn-icon
ng-show="::ticket.isAvailable === 0" ng-show="::ticket.itemShortage"
translate-attr="{title: 'Not available'}" translate-attr="{title: 'Not visible'}"
class="bright" class="bright"
icon="icon-unavailable"> icon="icon-unavailable">
</vn-icon> </vn-icon>

View File

@ -324,7 +324,7 @@ module.exports = Self => {
case true: case true:
condition = `or`; condition = `or`;
hasProblem = true; hasProblem = true;
range = 0; range = {neq: null};
hasWhere = true; hasWhere = true;
break; break;
@ -340,7 +340,7 @@ module.exports = Self => {
{'tp.isFreezed': hasProblem}, {'tp.isFreezed': hasProblem},
{'tp.risk': hasProblem}, {'tp.risk': hasProblem},
{'tp.hasTicketRequest': hasProblem}, {'tp.hasTicketRequest': hasProblem},
{'tp.isAvailable': range} {'tp.itemShortage': range}
]}; ]};
if (hasWhere) if (hasWhere)

View File

@ -94,7 +94,7 @@ module.exports = Self => {
sale.visible = itemStock.visible; sale.visible = itemStock.visible;
sale.claim = claimedSales.get(sale.id); sale.claim = claimedSales.get(sale.id);
if (problems) { if (problems) {
sale.isAvailable = problems.isAvailable; sale.itemShortage = problems.itemShortage;
sale.hasTicketRequest = problems.hasTicketRequest; sale.hasTicketRequest = problems.hasTicketRequest;
sale.hasComponentLack = problems.hasComponentLack; sale.hasComponentLack = problems.hasComponentLack;
} }

View File

@ -39,7 +39,7 @@ describe('ticket filter()', () => {
const filter = {}; const filter = {};
const result = await models.Ticket.filter(ctx, filter, options); const result = await models.Ticket.filter(ctx, filter, options);
expect(result.length).toEqual(4); expect(result.length).toEqual(6);
await tx.rollback(); await tx.rollback();
} catch (e) { } catch (e) {

View File

@ -52,8 +52,8 @@
icon="icon-buyrequest"> icon="icon-buyrequest">
</vn-icon> </vn-icon>
<vn-icon <vn-icon
ng-show="::ticket.isAvailable === 0" ng-show="::ticket.itemShortage"
translate-attr="{title: 'Not available'}" translate-attr="{title: 'Not visible'}"
class="bright" class="bright"
icon="icon-unavailable"> icon="icon-unavailable">
</vn-icon> </vn-icon>

View File

@ -1,6 +1,7 @@
Weekly tickets: Tickets programados Weekly tickets: Tickets programados
Go to lines: Ir a lineas Go to lines: Ir a lineas
Not available: No disponible Not available: No disponible
Not visible: No visible
Payment on account...: Pago a cuenta... Payment on account...: Pago a cuenta...
Set as delivered and open delivery note(s): Marcar como servido/s y abrir albarán/es Set as delivered and open delivery note(s): Marcar como servido/s y abrir albarán/es
Closure: Cierre Closure: Cierre

View File

@ -95,8 +95,8 @@
translate-attr="{title: 'Reserved'}"> translate-attr="{title: 'Reserved'}">
</vn-icon> </vn-icon>
<vn-icon <vn-icon
ng-show="::sale.isAvailable === 0" ng-show="::sale.itemShortage"
translate-attr="{title: 'Not available'}" translate-attr="{title: 'Not visible'}"
class="bright" class="bright"
icon="icon-unavailable"> icon="icon-unavailable">
</vn-icon> </vn-icon>

View File

@ -157,8 +157,8 @@
translate-attr="{title: 'Reserved'}"> translate-attr="{title: 'Reserved'}">
</vn-icon> </vn-icon>
<vn-icon <vn-icon
ng-show="::sale.isAvailable === 0" ng-show="::sale.itemShortage"
translate-attr="{title: 'Not available'}" translate-attr="{title: 'Not visible'}"
class="bright" class="bright"
icon="icon-unavailable"> icon="icon-unavailable">
</vn-icon> </vn-icon>

View File

@ -20,7 +20,7 @@ class Controller extends Section {
set sales(value) { set sales(value) {
this._sales = value; this._sales = value;
if (value) this.applyVolumes(); if (value && value.length) this.applyVolumes();
} }
applyVolumes() { applyVolumes() {

View File

@ -73,15 +73,14 @@ class Controller extends Section {
for (let event of $events) for (let event of $events)
zoneIds.push(event.zoneFk); zoneIds.push(event.zoneFk);
this.$.zoneEvents.show($event.target);
const params = { const params = {
zoneIds: zoneIds, zoneIds: zoneIds,
date: day date: day
}; };
this.$http.post(`Zones/getZoneClosing`, params) this.$http.post(`Zones/getZoneClosing`, params)
.then(res => this.zoneClosing = res.data); .then(res => this.zoneClosing = res.data)
.then(() => this.$.zoneEvents.show($event.target));
} }
preview(zone) { preview(zone) {

View File

@ -7,6 +7,10 @@ const fs = require('fs-extra');
module.exports = { module.exports = {
name: 'delivery-note', name: 'delivery-note',
created() {
if (!this.type)
this.type = 'deliveryNote';
},
async serverPrefetch() { async serverPrefetch() {
this.client = await this.fetchClient(this.ticketId); this.client = await this.fetchClient(this.ticketId);
this.ticket = await this.fetchTicket(this.ticketId); this.ticket = await this.fetchTicket(this.ticketId);
@ -129,7 +133,7 @@ module.exports = {
}, },
type: { type: {
type: String, type: String,
required: true required: false
} }
} }
}; };

BIN
storage/dms/8f1/7.jpg Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 429 KiB

BIN
storage/dms/c9f/8.mp4 Normal file

Binary file not shown.