Compare commits

...

5 Commits

Author SHA1 Message Date
Javier Segarra d241feffb3 fix(salix): refs #7648 #7648 buyLabel report style
gitea/salix/pipeline/pr-master There was a failure building this commit Details
2024-07-01 12:19:22 +02:00
Javier Segarra aa2a8a2ce8 feat(salix): refs #7648 #7648 buyLabel
gitea/salix/pipeline/pr-master This commit looks good Details
2024-07-01 11:43:12 +02:00
Javier Segarra 4decedddff Merge pull request 'feat: refs #7648 refs #7644' into #7648 2024-07-01 10:05:54 +02:00
Javier Segarra d160836eb0 test(salix): refs #7648 #7648 test:back
gitea/salix/pipeline/pr-master This commit looks good Details
2024-06-29 00:23:23 +02:00
Javier Segarra 9193c784d0 feat(salix): refs #7648 #7648 filter entries and getBuys when !employee 2024-06-28 23:57:35 +02:00
17 changed files with 364 additions and 15 deletions

View File

@ -0,0 +1,10 @@
-- Place your SQL code here
-- Auto-generated SQL script #202406281423
INSERT INTO salix.ACL (model,property,accessType,permission,principalType,principalId)
VALUES ('Entry','filter','READ','ALLOW','ROLE','$authenticated');
-- Auto-generated SQL script #202406281452
INSERT INTO salix.ACL (model,property,accessType,permission,principalType,principalId)
VALUES ('Entry','getBuys','READ','ALLOW','ROLE','$authenticated');
INSERT INTO salix.ACL (model,property,accessType,permission,principalType,principalId)
VALUES ('Entry','buyLabel','READ','ALLOW','ROLE','$authenticated');

View File

@ -0,0 +1,37 @@
module.exports = Self => {
Self.remoteMethodCtx('buyLabel', {
description: 'Returns the entry buys labels',
accessType: 'READ',
accepts: [
{
arg: 'id',
type: 'number',
required: true,
description: 'The entry 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/buy-label',
verb: 'GET'
},
accessScopes: ['DEFAULT', 'read:multimedia']
});
Self.buyLabel = (ctx, id) => Self.printReport(ctx, id, 'buy-label');
};

View File

@ -112,7 +112,7 @@ module.exports = Self => {
if (typeof options == 'object')
Object.assign(myOptions, options);
const isSupplier = await Self.app.models.Supplier.isSupplier(ctx, options);
const conn = Self.dataSource.connector;
const where = buildFilter(ctx.args, (param, value) => {
switch (param) {
@ -146,7 +146,11 @@ module.exports = Self => {
}
});
filter = mergeFilters(ctx.args.filter, {where});
delete filter.order;
if (isSupplier) {
if (!filter.where) filter.where = {};
filter.where.supplierFk = ctx.req.accessToken.userId;
}
const stmts = [];
let stmt;
stmt = new ParameterizedSQL(

View File

@ -1,7 +1,10 @@
const ForbiddenError = require('vn-loopback/util/forbiddenError');
const UserError = require('vn-loopback/util/user-error');
const mergeFilters = require('vn-loopback/util/filter').mergeFilters;
module.exports = Self => {
Self.remoteMethod('getBuys', {
Self.remoteMethodCtx('getBuys', {
description: 'Returns buys for one entry',
accessType: 'READ',
accepts: [{
@ -27,13 +30,18 @@ module.exports = Self => {
}
});
Self.getBuys = async(id, filter, options) => {
Self.getBuys = async(ctx, id, filter, options) => {
const models = Self.app.models;
const myOptions = {};
if (typeof options == 'object')
Object.assign(myOptions, options);
const isSupplier = await Self.app.models.Supplier.isSupplier(ctx, options);
if (isSupplier) {
const isEntryOwner = (await Self.findById(id)).supplierFk === ctx.req.accessToken.userId;
if (! isEntryOwner) throw new UserError('Access Denied');
}
let defaultFilter = {
where: {entryFk: id},
fields: [
@ -49,9 +57,23 @@ module.exports = Self => {
'buyingValue',
'price2',
'price3',
'printedStickers'
'printedStickers',
'entryFk'
],
include: {
include: [{
relation: 'entry',
scope: {
fields: [
'id', 'supplierFk'
],
include: {
relation: 'supplier', scope: {
fields: ['id']
}
}
}
},
{
relation: 'item',
scope: {
fields: [
@ -82,9 +104,9 @@ module.exports = Self => {
}
}
}
}
}]
};
delete filter.order;
defaultFilter = mergeFilters(defaultFilter, filter);
return models.Buy.find(defaultFilter, myOptions);

View File

@ -9,7 +9,8 @@ describe('Entry filter()', () => {
const ctx = {
args: {
search: 1
}
},
req: {accessToken: {userId: 9}}
};
const result = await models.Entry.filter(ctx, options);
@ -32,7 +33,8 @@ describe('Entry filter()', () => {
const ctx = {
args: {
currencyFk: 1
}
},
req: {accessToken: {userId: 9}}
};
const result = await models.Entry.filter(ctx, options);
@ -54,7 +56,8 @@ describe('Entry filter()', () => {
const ctx = {
args: {
supplierFk: 2
}
},
req: {accessToken: {userId: 9}}
};
const result = await models.Entry.filter(ctx, options);
@ -76,7 +79,8 @@ describe('Entry filter()', () => {
const ctx = {
args: {
companyFk: 442
}
},
req: {accessToken: {userId: 9}}
};
const result = await models.Entry.filter(ctx, options);
@ -98,7 +102,8 @@ describe('Entry filter()', () => {
const ctx = {
args: {
isBooked: true,
}
},
req: {accessToken: {userId: 9}}
};
const result = await models.Entry.filter(ctx, options);
@ -121,7 +126,8 @@ describe('Entry filter()', () => {
args: {
reference: 'movement',
travelFk: '2'
}
},
req: {accessToken: {userId: 9}}
};
const result = await models.Entry.filter(ctx, options);

View File

@ -7,7 +7,14 @@ describe('entry getBuys()', () => {
const options = {transaction: tx};
try {
const result = await models.Entry.getBuys(entryId, options);
const ctx = {
args: {
search: 1
},
req: {accessToken: {userId: 2}}
};
const result = await models.Entry.getBuys(ctx, entryId, options);
const length = result.length;
const anyResult = result[Math.floor(Math.random() * Math.floor(length))];

View File

@ -9,6 +9,7 @@ module.exports = Self => {
require('../methods/entry/entryOrderPdf')(Self);
require('../methods/entry/addFromPackaging')(Self);
require('../methods/entry/addFromBuy')(Self);
require('../methods/entry/buyLabel')(Self);
Self.observe('before save', async function(ctx, options) {
if (ctx.isNewInstance) return;

View File

@ -0,0 +1,28 @@
const UserError = require('vn-loopback/util/user-error');
module.exports = Self => {
Self.remoteMethodCtx('isSupplier', {
description: 'Check is supplierFk exists as supplier',
accessType: 'READ',
returns: {
type: 'boolean',
root: true
},
http: {
path: `/isSupplier`,
verb: 'GET'
}
});
Self.isSupplier = async(ctx, options) => {
const myOptions = {validate: false};
if (typeof options == 'object')
Object.assign(myOptions, options);
const userId = ctx.req.accessToken.userId;
const exists = await Self.findById(userId);
return !!exists;
};
};

View File

@ -12,6 +12,7 @@ module.exports = Self => {
require('../methods/supplier/campaignMetricsEmail')(Self);
require('../methods/supplier/newSupplier')(Self);
require('../methods/supplier/getItemsPackaging')(Self);
require('../methods/supplier/isSupplier')(Self);
Self.validatesPresenceOf('name', {
message: 'The social name cannot be empty'

View File

@ -0,0 +1,12 @@
const Stylesheet = require(`vn-print/core/stylesheet`);
const path = require('path');
const vnPrintPath = path.resolve('print');
module.exports = new Stylesheet([
`${vnPrintPath}/common/css/spacing.css`,
`${vnPrintPath}/common/css/misc.css`,
`${vnPrintPath}/common/css/layout.css`,
`${vnPrintPath}/common/css/report.css`,
`${__dirname}/style.css`])
.mergeStyles();

View File

@ -0,0 +1,41 @@
html {
font-family: "Roboto", "Helvetica", "Arial", sans-serif;
margin-top: -7px;
font-size: 28px;
}
table {
border: 1px solid;
width: 100%;
font-size: inherit;
}
td {
border: 1px solid;
padding: 5px;
width: 100%;
}
span {
font-size: 48px;
font-weight: bold;
}
.lbl {
color: gray;
font-weight: lighter;
font-size: 18px;
display: block;
}
.cell {
width: 157px;
height: 50px;
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
}
.barcode {
text-align: center;
}
#variant {
width: 314px;
}
#producer {
width: 471px;
}

View File

@ -0,0 +1,89 @@
<!DOCTYPE html>
<html>
<table v-for="buy in buys" style="break-before: page">
<tbody>
<tr>
<td colspan="2">
<div id="variant" class="cell">
<span class="lbl">{{$t('variety')}}</span>
{{buy.name}}
</div>
</td>
<td>
<div class="cell">
<span class="lbl">{{$t('size')}}</span>
{{buy.size}}
</div>
</td>
</tr>
<tr>
<td>
<div class="cell">
<span class="lbl">{{$t('category')}}</span>
{{buy.category}}
</div>
</td>
<td>
<div class="cell">
<span class="lbl">{{$t('color')}}</span>
{{buy.color}}
</div>
</td>
<td>
<div class="cell">
<span class="lbl">{{$t('origin')}}</span>
{{buy.code}}
</div>
</td>
</tr>
<tr>
<td>
<div class="cell">
<span class="lbl">{{$t('packing')}}</span>
{{buy.packing}}
</div>
</td>
<td>
<div class="cell">
<span class="lbl">{{$t('grouping')}}</span>
{{buy.grouping}}
</div>
</td>
<td>
<div class="cell">
<span class="lbl">{{$t('saleUnit')}}</span>
{{buy.stems}}
</div>
</td>
</tr>
<tr>
<td colspan="3" class="barcode">
<div v-html="getBarcode(buy.id)"></div>
<span>{{buy.id}}</span>
</td>
</tr>
<tr>
<td colspan="3">
<div id="producer" class="cell">
<span class="lbl">{{$t('producer')}}</span>
{{buy.producer}}
</div>
</td>
</tr>
<tr>
<td colspan="2">
<div class="cell">
<span class="lbl">{{$t('control')}}</span>
{{`${weekNum} / ${dayNum}`}}
</div>
</td>
<td>
<div class="cell">
<span class="lbl">{{$t('boxNum')}}</span>
{{`${buy.labelNum} / ${maxLabelNum}`}}
</div>
</td>
</tr>
</tbody>
</table>
</html>

View File

@ -0,0 +1,39 @@
const vnReport = require('../../../core/mixins/vn-report.js');
const {DOMImplementation, XMLSerializer} = require('xmldom');
const jsBarcode = require('jsbarcode');
const moment = require('moment');
module.exports = {
name: 'buy-label',
mixins: [vnReport],
async serverPrefetch() {
this.buys = await this.rawSqlFromDef('buys', [this.id]);
this.maxLabelNum = Math.max(...this.buys.map(buy => buy.labelNum));
const date = new Date();
this.weekNum = moment(date).isoWeek();
this.dayNum = moment(date).day();
},
methods: {
getBarcode(id) {
const xmlSerializer = new XMLSerializer();
const document = new DOMImplementation().createDocument('http://www.w3.org/1999/xhtml', 'html', null);
const svgNode = document.createElementNS('http://www.w3.org/2000/svg', 'svg');
jsBarcode(svgNode, id, {
xmlDocument: document,
format: 'code128',
displayValue: false,
width: 3.8,
height: 115,
});
return xmlSerializer.serializeToString(svgNode);
}
},
props: {
id: {
type: Number,
required: true,
description: 'The entry id'
}
}
};

View File

@ -0,0 +1,12 @@
reportName: Entry buys
variety: Bariety
size: Size
category: Category
color: Color
origin: Origin
packing: Packing
grouping: Grouping
unitSale: Un. sale
producer: Producer
control: Control
boxNum: Box no.

View File

@ -0,0 +1,12 @@
reportName: Etiqueta de compras
variety: Variedad
size: Medida
category: Categoría
color: Color
origin: Origen
packing: Packing
grouping: Grouping
saleUnit: Sale un.
producer: Productor
control: Control
boxNum: Caja nº

View File

@ -0,0 +1,11 @@
{
"width": "10cm",
"height": "10cm",
"margin": {
"top": "0.17cm",
"right": "0.2cm",
"bottom": "0cm",
"left": "0cm"
},
"printBackground": true
}

View File

@ -0,0 +1,17 @@
SELECT ROW_NUMBER() OVER(ORDER BY b.id) labelNum,
i.name,
i.`size`,
i.category,
ink.id color,
o.code,
b.packing,
b.`grouping`,
i.stems,
b.id,
p.name producer
FROM buy b
JOIN item i ON i.id = b.itemFk
LEFT JOIN producer p ON p.id = i.producerFk
LEFT JOIN ink ON ink.id = i.inkFk
LEFT JOIN origin o ON o.id = i.originFk
WHERE b.entryFk = ?