Merge branch 'test' into dev
gitea/salix/pipeline/head This commit looks good
Details
gitea/salix/pipeline/head This commit looks good
Details
This commit is contained in:
commit
5c51d7fb04
|
@ -18,6 +18,7 @@ describe('setSaleQuantity()', () => {
|
||||||
|
|
||||||
it('should change quantity sale', async() => {
|
it('should change quantity sale', async() => {
|
||||||
const tx = await models.Ticket.beginTransaction({});
|
const tx = await models.Ticket.beginTransaction({});
|
||||||
|
spyOn(models.Item, 'getVisibleAvailable').and.returnValue((new Promise(resolve => resolve({available: 100}))));
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const options = {transaction: tx};
|
const options = {transaction: tx};
|
||||||
|
|
|
@ -30434,6 +30434,7 @@ CREATE TABLE `item` (
|
||||||
`editorFk` int(10) unsigned DEFAULT NULL,
|
`editorFk` int(10) unsigned DEFAULT NULL,
|
||||||
`recycledPlastic` int(11) DEFAULT NULL,
|
`recycledPlastic` int(11) DEFAULT NULL,
|
||||||
`nonRecycledPlastic` int(11) DEFAULT NULL,
|
`nonRecycledPlastic` int(11) DEFAULT NULL,
|
||||||
|
`minQuantity` int(10) unsigned DEFAULT NULL COMMENT 'Cantidad mínima para una línea de venta',
|
||||||
PRIMARY KEY (`id`),
|
PRIMARY KEY (`id`),
|
||||||
UNIQUE KEY `item_supplyResponseFk_idx` (`supplyResponseFk`),
|
UNIQUE KEY `item_supplyResponseFk_idx` (`supplyResponseFk`),
|
||||||
KEY `Color` (`inkFk`),
|
KEY `Color` (`inkFk`),
|
||||||
|
|
|
@ -189,5 +189,6 @@
|
||||||
"The sales do not exists": "The sales do not exists",
|
"The sales do not exists": "The sales do not exists",
|
||||||
"Ticket without Route": "Ticket without route",
|
"Ticket without Route": "Ticket without route",
|
||||||
"Booking completed": "Booking complete",
|
"Booking completed": "Booking complete",
|
||||||
"The ticket is in preparation": "The ticket [{{ticketId}}]({{{ticketUrl}}}) of the sales person {{salesPersonId}} is in preparation"
|
"The ticket is in preparation": "The ticket [{{ticketId}}]({{{ticketUrl}}}) of the sales person {{salesPersonId}} is in preparation",
|
||||||
}
|
"You can only add negative amounts in refund tickets": "You can only add negative amounts in refund tickets"
|
||||||
|
}
|
|
@ -320,5 +320,7 @@
|
||||||
"The response is not a PDF": "La respuesta no es un PDF",
|
"The response is not a PDF": "La respuesta no es un PDF",
|
||||||
"Ticket without Route": "Ticket sin ruta",
|
"Ticket without Route": "Ticket sin ruta",
|
||||||
"Booking completed": "Reserva completada",
|
"Booking completed": "Reserva completada",
|
||||||
"The ticket is in preparation": "El ticket [{{ticketId}}]({{{ticketUrl}}}) del comercial {{salesPersonId}} está en preparación"
|
"The ticket is in preparation": "El ticket [{{ticketId}}]({{{ticketUrl}}}) del comercial {{salesPersonId}} está en preparación",
|
||||||
|
"The amount cannot be less than the minimum": "La cantidad no puede ser menor que la cantidad mímina",
|
||||||
|
"quantityLessThanMin": "La cantidad no puede ser menor que la cantidad mímina"
|
||||||
}
|
}
|
||||||
|
|
|
@ -64,7 +64,7 @@ describe('claim regularizeClaim()', () => {
|
||||||
|
|
||||||
claimEnds = await importTicket(ticketId, claimId, userId, options);
|
claimEnds = await importTicket(ticketId, claimId, userId, options);
|
||||||
|
|
||||||
for (claimEnd of claimEnds)
|
for (const claimEnd of claimEnds)
|
||||||
await claimEnd.updateAttributes({claimDestinationFk: trashDestination}, options);
|
await claimEnd.updateAttributes({claimDestinationFk: trashDestination}, options);
|
||||||
|
|
||||||
let claimBefore = await models.Claim.findById(claimId, null, options);
|
let claimBefore = await models.Claim.findById(claimId, null, options);
|
||||||
|
|
|
@ -17,6 +17,7 @@ Search claim by id or client name: Buscar reclamaciones por identificador o nomb
|
||||||
Claim deleted!: Reclamación eliminada!
|
Claim deleted!: Reclamación eliminada!
|
||||||
claim: reclamación
|
claim: reclamación
|
||||||
Photos: Fotos
|
Photos: Fotos
|
||||||
|
Development: Trazabilidad
|
||||||
Go to the claim: Ir a la reclamación
|
Go to the claim: Ir a la reclamación
|
||||||
Sale tracking: Líneas preparadas
|
Sale tracking: Líneas preparadas
|
||||||
Ticket tracking: Estados del ticket
|
Ticket tracking: Estados del ticket
|
||||||
|
|
|
@ -131,6 +131,9 @@
|
||||||
"nonRecycledPlastic": {
|
"nonRecycledPlastic": {
|
||||||
"type": "number"
|
"type": "number"
|
||||||
},
|
},
|
||||||
|
"minQuantity": {
|
||||||
|
"type": "number"
|
||||||
|
},
|
||||||
"packingOut": {
|
"packingOut": {
|
||||||
"type": "number"
|
"type": "number"
|
||||||
},
|
},
|
||||||
|
@ -154,6 +157,10 @@
|
||||||
"mysql":{
|
"mysql":{
|
||||||
"columnName": "doPhoto"
|
"columnName": "doPhoto"
|
||||||
}
|
}
|
||||||
|
},
|
||||||
|
"minQuantity": {
|
||||||
|
"type": "number",
|
||||||
|
"description": "Min quantity"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"relations": {
|
"relations": {
|
||||||
|
|
|
@ -33,6 +33,8 @@
|
||||||
rule
|
rule
|
||||||
info="Full name calculates based on tags 1-3. Is not recommended to change it manually">
|
info="Full name calculates based on tags 1-3. Is not recommended to change it manually">
|
||||||
</vn-textfield>
|
</vn-textfield>
|
||||||
|
</vn-horizontal>
|
||||||
|
<vn-horizontal>
|
||||||
<vn-autocomplete
|
<vn-autocomplete
|
||||||
url="ItemTypes"
|
url="ItemTypes"
|
||||||
label="Type"
|
label="Type"
|
||||||
|
@ -50,6 +52,30 @@
|
||||||
</div>
|
</div>
|
||||||
</tpl-item>
|
</tpl-item>
|
||||||
</vn-autocomplete>
|
</vn-autocomplete>
|
||||||
|
<vn-autocomplete
|
||||||
|
label="Generic"
|
||||||
|
url="Items/withName"
|
||||||
|
ng-model="$ctrl.item.genericFk"
|
||||||
|
vn-name="generic"
|
||||||
|
show-field="name"
|
||||||
|
value-field="id"
|
||||||
|
search-function="$ctrl.itemSearchFunc($search)"
|
||||||
|
order="id DESC"
|
||||||
|
tabindex="1">
|
||||||
|
<tpl-item>
|
||||||
|
<div>{{::name}}</div>
|
||||||
|
<div class="text-caption text-secondary">
|
||||||
|
#{{::id}}
|
||||||
|
</div>
|
||||||
|
</tpl-item>
|
||||||
|
<append>
|
||||||
|
<vn-icon-button
|
||||||
|
icon="filter_alt"
|
||||||
|
vn-click-stop="$ctrl.showFilterDialog($ctrl.item)"
|
||||||
|
vn-tooltip="Filter...">
|
||||||
|
</vn-icon-button>
|
||||||
|
</append>
|
||||||
|
</vn-autocomplete>
|
||||||
</vn-horizontal>
|
</vn-horizontal>
|
||||||
<vn-horizontal>
|
<vn-horizontal>
|
||||||
<vn-autocomplete
|
<vn-autocomplete
|
||||||
|
@ -128,30 +154,13 @@
|
||||||
ng-model="$ctrl.item.stemMultiplier"
|
ng-model="$ctrl.item.stemMultiplier"
|
||||||
vn-name="stemMultiplier">
|
vn-name="stemMultiplier">
|
||||||
</vn-input-number>
|
</vn-input-number>
|
||||||
<vn-autocomplete
|
<vn-input-number
|
||||||
label="Generic"
|
min="1"
|
||||||
url="Items/withName"
|
label="Minimum sales quantity"
|
||||||
ng-model="$ctrl.item.genericFk"
|
ng-model="$ctrl.item.minQuantity"
|
||||||
vn-name="generic"
|
vn-name="minQuantity"
|
||||||
show-field="name"
|
rule>
|
||||||
value-field="id"
|
</vn-input-number>
|
||||||
search-function="$ctrl.itemSearchFunc($search)"
|
|
||||||
order="id DESC"
|
|
||||||
tabindex="1">
|
|
||||||
<tpl-item>
|
|
||||||
<div>{{::name}}</div>
|
|
||||||
<div class="text-caption text-secondary">
|
|
||||||
#{{::id}}
|
|
||||||
</div>
|
|
||||||
</tpl-item>
|
|
||||||
<append>
|
|
||||||
<vn-icon-button
|
|
||||||
icon="filter_alt"
|
|
||||||
vn-click-stop="$ctrl.showFilterDialog($ctrl.item)"
|
|
||||||
vn-tooltip="Filter...">
|
|
||||||
</vn-icon-button>
|
|
||||||
</append>
|
|
||||||
</vn-autocomplete>
|
|
||||||
</vn-horizontal>
|
</vn-horizontal>
|
||||||
<vn-horizontal>
|
<vn-horizontal>
|
||||||
<vn-input-number
|
<vn-input-number
|
||||||
|
|
|
@ -16,3 +16,4 @@ This item does need a photo: Este artículo necesita una foto
|
||||||
Do photo: Hacer foto
|
Do photo: Hacer foto
|
||||||
Recycled Plastic: Plástico reciclado
|
Recycled Plastic: Plástico reciclado
|
||||||
Non recycled plastic: Plástico no reciclado
|
Non recycled plastic: Plástico no reciclado
|
||||||
|
Minimum sales quantity: Cantidad mínima de venta
|
||||||
|
|
|
@ -128,6 +128,9 @@
|
||||||
<vn-label-value label="Non recycled plastic"
|
<vn-label-value label="Non recycled plastic"
|
||||||
value="{{$ctrl.summary.item.nonRecycledPlastic}}">
|
value="{{$ctrl.summary.item.nonRecycledPlastic}}">
|
||||||
</vn-label-value>
|
</vn-label-value>
|
||||||
|
<vn-label-value label="Minimum sales quantity"
|
||||||
|
value="{{$ctrl.summary.item.minQuantity}}">
|
||||||
|
</vn-label-value>
|
||||||
</vn-one>
|
</vn-one>
|
||||||
<vn-one name="tags">
|
<vn-one name="tags">
|
||||||
<h4 ng-show="$ctrl.isBuyer || $ctrl.isReplenisher">
|
<h4 ng-show="$ctrl.isBuyer || $ctrl.isReplenisher">
|
||||||
|
|
|
@ -2,3 +2,4 @@ Barcode: Códigos de barras
|
||||||
Other data: Otros datos
|
Other data: Otros datos
|
||||||
Go to the item: Ir al artículo
|
Go to the item: Ir al artículo
|
||||||
WarehouseFk: Calculado sobre el almacén de {{ warehouseName }}
|
WarehouseFk: Calculado sobre el almacén de {{ warehouseName }}
|
||||||
|
Minimum sales quantity: Cantidad mínima de venta
|
||||||
|
|
|
@ -100,31 +100,32 @@ module.exports = Self => {
|
||||||
));
|
));
|
||||||
|
|
||||||
stmt = new ParameterizedSQL(`
|
stmt = new ParameterizedSQL(`
|
||||||
SELECT
|
SELECT i.id,
|
||||||
i.id,
|
i.name,
|
||||||
i.name,
|
i.subName,
|
||||||
i.subName,
|
i.image,
|
||||||
i.image,
|
i.tag5,
|
||||||
i.tag5,
|
i.value5,
|
||||||
i.value5,
|
i.tag6,
|
||||||
i.tag6,
|
i.value6,
|
||||||
i.value6,
|
i.tag7,
|
||||||
i.tag7,
|
i.value7,
|
||||||
i.value7,
|
i.tag8,
|
||||||
i.tag8,
|
i.value8,
|
||||||
i.value8,
|
i.stars,
|
||||||
i.stars,
|
tci.price,
|
||||||
tci.price,
|
tci.available,
|
||||||
tci.available,
|
w.lastName,
|
||||||
w.lastName AS lastName,
|
w.firstName,
|
||||||
w.firstName,
|
tci.priceKg,
|
||||||
tci.priceKg,
|
ink.hex,
|
||||||
ink.hex
|
i.minQuantity
|
||||||
FROM tmp.ticketCalculateItem tci
|
FROM tmp.ticketCalculateItem tci
|
||||||
JOIN vn.item i ON i.id = tci.itemFk
|
JOIN vn.item i ON i.id = tci.itemFk
|
||||||
JOIN vn.itemType it ON it.id = i.typeFk
|
JOIN vn.itemType it ON it.id = i.typeFk
|
||||||
JOIN vn.worker w on w.id = it.workerFk
|
JOIN vn.worker w on w.id = it.workerFk
|
||||||
LEFT JOIN vn.ink ON ink.id = i.inkFk`);
|
LEFT JOIN vn.ink ON ink.id = i.inkFk
|
||||||
|
`);
|
||||||
|
|
||||||
// Apply order by tag
|
// Apply order by tag
|
||||||
if (orderBy.isTag) {
|
if (orderBy.isTag) {
|
||||||
|
|
|
@ -8,12 +8,12 @@
|
||||||
<div class="item-color" ng-style="{'background-color': '#' + item.hex}"></div>
|
<div class="item-color" ng-style="{'background-color': '#' + item.hex}"></div>
|
||||||
</div>
|
</div>
|
||||||
<img
|
<img
|
||||||
ng-src="{{::$root.imagePath('catalog', '200x200', item.id)}}"
|
ng-src="{{::$root.imagePath('catalog', '200x200', item.id)}}"
|
||||||
zoom-image="{{::$root.imagePath('catalog', '1600x900', item.id)}}"
|
zoom-image="{{::$root.imagePath('catalog', '1600x900', item.id)}}"
|
||||||
on-error-src/>
|
on-error-src/>
|
||||||
</div>
|
</div>
|
||||||
<div class="description">
|
<div class="description">
|
||||||
<h3 class="link"
|
<h3 class="link"
|
||||||
ng-click="itemDescriptor.show($event, item.id)">
|
ng-click="itemDescriptor.show($event, item.id)">
|
||||||
{{::item.name}}
|
{{::item.name}}
|
||||||
</h3>
|
</h3>
|
||||||
|
@ -37,13 +37,28 @@
|
||||||
value="{{::item.value7}}">
|
value="{{::item.value7}}">
|
||||||
</vn-label-value>
|
</vn-label-value>
|
||||||
</div>
|
</div>
|
||||||
<vn-rating ng-if="::item.stars"
|
<vn-horizontal>
|
||||||
ng-model="::item.stars">
|
<vn-one>
|
||||||
</vn-rating>
|
<vn-rating ng-if="::item.stars"
|
||||||
|
ng-model="::item.stars"/>
|
||||||
|
</vn-one>
|
||||||
|
<vn-horizontal
|
||||||
|
class="text-right text-caption alert vn-mr-xs"
|
||||||
|
ng-if="::item.minQuantity">
|
||||||
|
<vn-one>
|
||||||
|
<vn-icon
|
||||||
|
icon="production_quantity_limits"
|
||||||
|
translate-attr="{title: 'Minimal quantity'}"
|
||||||
|
class="text-subtitle1">
|
||||||
|
</vn-icon>
|
||||||
|
</vn-one>
|
||||||
|
{{::item.minQuantity}}
|
||||||
|
</vn-horizontal>
|
||||||
|
</vn-horizontal>
|
||||||
<div class="footer">
|
<div class="footer">
|
||||||
<div class="price">
|
<div class="price">
|
||||||
<vn-one>
|
<vn-one>
|
||||||
<span>{{::item.available}}</span>
|
<span>{{::item.available}}</span>
|
||||||
<span translate>to</span>
|
<span translate>to</span>
|
||||||
<span>{{::item.price | currency:'EUR':2}}</span>
|
<span>{{::item.price | currency:'EUR':2}}</span>
|
||||||
</vn-one>
|
</vn-one>
|
||||||
|
@ -54,7 +69,7 @@
|
||||||
</vn-icon-button>
|
</vn-icon-button>
|
||||||
</div>
|
</div>
|
||||||
<div class="priceKg" ng-show="::item.priceKg">
|
<div class="priceKg" ng-show="::item.priceKg">
|
||||||
<span>Precio por kilo {{::item.priceKg | currency: 'EUR'}}</span>
|
<span>Precio por kilo {{::item.priceKg | currency: 'EUR'}}</span>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
@ -69,4 +84,4 @@
|
||||||
<vn-item-descriptor-popover
|
<vn-item-descriptor-popover
|
||||||
vn-id="item-descriptor"
|
vn-id="item-descriptor"
|
||||||
warehouse-fk="$ctrl.vnConfig.warehouseFk">
|
warehouse-fk="$ctrl.vnConfig.warehouseFk">
|
||||||
</vn-item-descriptor-popover>
|
</vn-item-descriptor-popover>
|
||||||
|
|
|
@ -1 +1,2 @@
|
||||||
Order created: Orden creada
|
Order created: Orden creada
|
||||||
|
Minimal quantity: Cantidad mínima
|
|
@ -44,4 +44,7 @@ vn-order-catalog {
|
||||||
height: 30px;
|
height: 30px;
|
||||||
position: relative;
|
position: relative;
|
||||||
}
|
}
|
||||||
}
|
.alert {
|
||||||
|
color: $color-alert;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -55,7 +55,7 @@ module.exports = Self => {
|
||||||
|
|
||||||
const refoundZoneId = refundAgencyMode.zones()[0].id;
|
const refoundZoneId = refundAgencyMode.zones()[0].id;
|
||||||
|
|
||||||
if (salesIds) {
|
if (salesIds.length) {
|
||||||
const salesFilter = {
|
const salesFilter = {
|
||||||
where: {id: {inq: salesIds}},
|
where: {id: {inq: salesIds}},
|
||||||
include: {
|
include: {
|
||||||
|
@ -91,16 +91,14 @@ module.exports = Self => {
|
||||||
await models.SaleComponent.create(components, myOptions);
|
await models.SaleComponent.create(components, myOptions);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!refundTicket) {
|
if (!refundTicket) {
|
||||||
const servicesFilter = {
|
const servicesFilter = {
|
||||||
where: {id: {inq: servicesIds}}
|
where: {id: {inq: servicesIds}}
|
||||||
};
|
};
|
||||||
const services = await models.TicketService.find(servicesFilter, myOptions);
|
const services = await models.TicketService.find(servicesFilter, myOptions);
|
||||||
const ticketsIds = [...new Set(services.map(service => service.ticketFk))];
|
const firstTicketId = services[0].ticketFk;
|
||||||
|
|
||||||
const now = Date.vnNew();
|
const now = Date.vnNew();
|
||||||
const [firstTicketId] = ticketsIds;
|
|
||||||
|
|
||||||
// eslint-disable-next-line max-len
|
// eslint-disable-next-line max-len
|
||||||
refundTicket = await createTicketRefund(firstTicketId, now, refundAgencyMode, refoundZoneId, withWarehouse, myOptions);
|
refundTicket = await createTicketRefund(firstTicketId, now, refundAgencyMode, refoundZoneId, withWarehouse, myOptions);
|
||||||
|
@ -114,8 +112,8 @@ module.exports = Self => {
|
||||||
for (const service of services) {
|
for (const service of services) {
|
||||||
await models.TicketService.create({
|
await models.TicketService.create({
|
||||||
description: service.description,
|
description: service.description,
|
||||||
quantity: service.quantity,
|
quantity: - service.quantity,
|
||||||
price: - service.price,
|
price: service.price,
|
||||||
taxClassFk: service.taxClassFk,
|
taxClassFk: service.taxClassFk,
|
||||||
ticketFk: refundTicket.id,
|
ticketFk: refundTicket.id,
|
||||||
ticketServiceTypeFk: service.ticketServiceTypeFk,
|
ticketServiceTypeFk: service.ticketServiceTypeFk,
|
||||||
|
|
|
@ -1,21 +1,9 @@
|
||||||
|
/* eslint max-len: ["error", { "code": 150 }]*/
|
||||||
|
|
||||||
const models = require('vn-loopback/server/server').models;
|
const models = require('vn-loopback/server/server').models;
|
||||||
const LoopBackContext = require('loopback-context');
|
const LoopBackContext = require('loopback-context');
|
||||||
|
|
||||||
describe('sale updateQuantity()', () => {
|
describe('sale updateQuantity()', () => {
|
||||||
beforeAll(async() => {
|
|
||||||
const activeCtx = {
|
|
||||||
accessToken: {userId: 9},
|
|
||||||
http: {
|
|
||||||
req: {
|
|
||||||
headers: {origin: 'http://localhost'}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
|
||||||
spyOn(LoopBackContext, 'getCurrentContext').and.returnValue({
|
|
||||||
active: activeCtx
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
const ctx = {
|
const ctx = {
|
||||||
req: {
|
req: {
|
||||||
accessToken: {userId: 9},
|
accessToken: {userId: 9},
|
||||||
|
@ -23,6 +11,18 @@ describe('sale updateQuantity()', () => {
|
||||||
__: () => {}
|
__: () => {}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
function getActiveCtx(userId) {
|
||||||
|
return {
|
||||||
|
active: {
|
||||||
|
accessToken: {userId},
|
||||||
|
http: {
|
||||||
|
req: {
|
||||||
|
headers: {origin: 'http://localhost'}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
it('should throw an error if the quantity is greater than it should be', async() => {
|
it('should throw an error if the quantity is greater than it should be', async() => {
|
||||||
const ctx = {
|
const ctx = {
|
||||||
|
@ -32,13 +32,16 @@ describe('sale updateQuantity()', () => {
|
||||||
__: () => {}
|
__: () => {}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
spyOn(LoopBackContext, 'getCurrentContext').and.returnValue(getActiveCtx(1));
|
||||||
|
spyOn(models.Item, 'getVisibleAvailable').and.returnValue((new Promise(resolve => resolve({available: 100}))));
|
||||||
|
|
||||||
const tx = await models.Sale.beginTransaction({});
|
const tx = await models.Sale.beginTransaction({});
|
||||||
|
|
||||||
let error;
|
let error;
|
||||||
try {
|
try {
|
||||||
const options = {transaction: tx};
|
const options = {transaction: tx};
|
||||||
|
|
||||||
await models.Sale.updateQuantity(ctx, 17, 99, options);
|
await models.Sale.updateQuantity(ctx, 17, 31, options);
|
||||||
|
|
||||||
await tx.rollback();
|
await tx.rollback();
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
|
@ -50,7 +53,6 @@ describe('sale updateQuantity()', () => {
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should add quantity if the quantity is greater than it should be and is role advanced', async() => {
|
it('should add quantity if the quantity is greater than it should be and is role advanced', async() => {
|
||||||
const tx = await models.Sale.beginTransaction({});
|
|
||||||
const saleId = 17;
|
const saleId = 17;
|
||||||
const buyerId = 35;
|
const buyerId = 35;
|
||||||
const ctx = {
|
const ctx = {
|
||||||
|
@ -60,6 +62,9 @@ describe('sale updateQuantity()', () => {
|
||||||
__: () => {}
|
__: () => {}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
const tx = await models.Sale.beginTransaction({});
|
||||||
|
spyOn(LoopBackContext, 'getCurrentContext').and.returnValue(getActiveCtx(buyerId));
|
||||||
|
spyOn(models.Item, 'getVisibleAvailable').and.returnValue((new Promise(resolve => resolve({available: 100}))));
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const options = {transaction: tx};
|
const options = {transaction: tx};
|
||||||
|
@ -87,6 +92,8 @@ describe('sale updateQuantity()', () => {
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should update the quantity of a given sale current line', async() => {
|
it('should update the quantity of a given sale current line', async() => {
|
||||||
|
spyOn(LoopBackContext, 'getCurrentContext').and.returnValue(getActiveCtx(9));
|
||||||
|
|
||||||
const tx = await models.Sale.beginTransaction({});
|
const tx = await models.Sale.beginTransaction({});
|
||||||
const saleId = 25;
|
const saleId = 25;
|
||||||
const newQuantity = 4;
|
const newQuantity = 4;
|
||||||
|
@ -119,6 +126,8 @@ describe('sale updateQuantity()', () => {
|
||||||
__: () => {}
|
__: () => {}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
spyOn(LoopBackContext, 'getCurrentContext').and.returnValue(getActiveCtx(1));
|
||||||
|
|
||||||
const saleId = 17;
|
const saleId = 17;
|
||||||
const newQuantity = -10;
|
const newQuantity = -10;
|
||||||
|
|
||||||
|
@ -140,6 +149,8 @@ describe('sale updateQuantity()', () => {
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should update a negative quantity when is a ticket refund', async() => {
|
it('should update a negative quantity when is a ticket refund', async() => {
|
||||||
|
spyOn(LoopBackContext, 'getCurrentContext').and.returnValue(getActiveCtx(9));
|
||||||
|
|
||||||
const tx = await models.Sale.beginTransaction({});
|
const tx = await models.Sale.beginTransaction({});
|
||||||
const saleId = 13;
|
const saleId = 13;
|
||||||
const newQuantity = -10;
|
const newQuantity = -10;
|
||||||
|
@ -159,4 +170,70 @@ describe('sale updateQuantity()', () => {
|
||||||
throw e;
|
throw e;
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it('should throw an error if the quantity is less than the minimum quantity of the item', async() => {
|
||||||
|
const ctx = {
|
||||||
|
req: {
|
||||||
|
accessToken: {userId: 1},
|
||||||
|
headers: {origin: 'localhost:5000'},
|
||||||
|
__: () => {}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
spyOn(LoopBackContext, 'getCurrentContext').and.returnValue(getActiveCtx(1));
|
||||||
|
|
||||||
|
const tx = await models.Sale.beginTransaction({});
|
||||||
|
const itemId = 2;
|
||||||
|
const saleId = 17;
|
||||||
|
const minQuantity = 30;
|
||||||
|
const newQuantity = minQuantity - 1;
|
||||||
|
|
||||||
|
let error;
|
||||||
|
try {
|
||||||
|
const options = {transaction: tx};
|
||||||
|
|
||||||
|
const item = await models.Item.findById(itemId, null, options);
|
||||||
|
await item.updateAttribute('minQuantity', minQuantity, options);
|
||||||
|
|
||||||
|
await models.Sale.updateQuantity(ctx, saleId, newQuantity, options);
|
||||||
|
|
||||||
|
await tx.rollback();
|
||||||
|
} catch (e) {
|
||||||
|
await tx.rollback();
|
||||||
|
error = e;
|
||||||
|
}
|
||||||
|
|
||||||
|
expect(error).toEqual(new Error('The amount cannot be less than the minimum'));
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should change quantity if has minimum quantity and new quantity is equal than item available', async() => {
|
||||||
|
const ctx = {
|
||||||
|
req: {
|
||||||
|
accessToken: {userId: 1},
|
||||||
|
headers: {origin: 'localhost:5000'},
|
||||||
|
__: () => {}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
spyOn(LoopBackContext, 'getCurrentContext').and.returnValue(getActiveCtx(1));
|
||||||
|
|
||||||
|
const tx = await models.Sale.beginTransaction({});
|
||||||
|
const itemId = 2;
|
||||||
|
const saleId = 17;
|
||||||
|
const minQuantity = 30;
|
||||||
|
const newQuantity = minQuantity - 1;
|
||||||
|
|
||||||
|
try {
|
||||||
|
const options = {transaction: tx};
|
||||||
|
|
||||||
|
const item = await models.Item.findById(itemId, null, options);
|
||||||
|
await item.updateAttribute('minQuantity', minQuantity, options);
|
||||||
|
spyOn(models.Item, 'getVisibleAvailable').and.returnValue((new Promise(resolve => resolve({available: newQuantity}))));
|
||||||
|
|
||||||
|
await models.Sale.updateQuantity(ctx, saleId, newQuantity, options);
|
||||||
|
|
||||||
|
await tx.rollback();
|
||||||
|
} catch (e) {
|
||||||
|
await tx.rollback();
|
||||||
|
throw e;
|
||||||
|
}
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|
|
@ -1,4 +1,3 @@
|
||||||
let UserError = require('vn-loopback/util/user-error');
|
|
||||||
|
|
||||||
module.exports = Self => {
|
module.exports = Self => {
|
||||||
Self.remoteMethodCtx('updateQuantity', {
|
Self.remoteMethodCtx('updateQuantity', {
|
||||||
|
@ -64,17 +63,6 @@ module.exports = Self => {
|
||||||
|
|
||||||
const sale = await models.Sale.findById(id, filter, myOptions);
|
const sale = await models.Sale.findById(id, filter, myOptions);
|
||||||
|
|
||||||
const isRoleAdvanced = await models.ACL.checkAccessAcl(ctx, 'Ticket', 'isRoleAdvanced', '*');
|
|
||||||
if (newQuantity > sale.quantity && !isRoleAdvanced)
|
|
||||||
throw new UserError('The new quantity should be smaller than the old one');
|
|
||||||
|
|
||||||
const ticketRefund = await models.TicketRefund.findOne({
|
|
||||||
where: {refundTicketFk: sale.ticketFk},
|
|
||||||
fields: ['id']}
|
|
||||||
, myOptions);
|
|
||||||
if (newQuantity < 0 && !ticketRefund)
|
|
||||||
throw new UserError('You can only add negative amounts in refund tickets');
|
|
||||||
|
|
||||||
const oldQuantity = sale.quantity;
|
const oldQuantity = sale.quantity;
|
||||||
const result = await sale.updateAttributes({quantity: newQuantity}, myOptions);
|
const result = await sale.updateAttributes({quantity: newQuantity}, myOptions);
|
||||||
|
|
||||||
|
|
|
@ -63,17 +63,6 @@ module.exports = Self => {
|
||||||
}
|
}
|
||||||
}, myOptions);
|
}, myOptions);
|
||||||
|
|
||||||
const itemInfo = await models.Item.getVisibleAvailable(
|
|
||||||
itemId,
|
|
||||||
ticket.warehouseFk,
|
|
||||||
ticket.shipped,
|
|
||||||
myOptions
|
|
||||||
);
|
|
||||||
|
|
||||||
const isPackaging = item.family == 'EMB';
|
|
||||||
if (!isPackaging && itemInfo.available < quantity)
|
|
||||||
throw new UserError(`This item is not available`);
|
|
||||||
|
|
||||||
const newSale = await models.Sale.create({
|
const newSale = await models.Sale.create({
|
||||||
ticketFk: id,
|
ticketFk: id,
|
||||||
itemFk: item.id,
|
itemFk: item.id,
|
||||||
|
|
|
@ -1,3 +1,6 @@
|
||||||
|
const UserError = require('vn-loopback/util/user-error');
|
||||||
|
const LoopBackContext = require('loopback-context');
|
||||||
|
|
||||||
module.exports = Self => {
|
module.exports = Self => {
|
||||||
require('../methods/sale/getClaimableFromTicket')(Self);
|
require('../methods/sale/getClaimableFromTicket')(Self);
|
||||||
require('../methods/sale/reserve')(Self);
|
require('../methods/sale/reserve')(Self);
|
||||||
|
@ -13,4 +16,77 @@ module.exports = Self => {
|
||||||
Self.validatesPresenceOf('concept', {
|
Self.validatesPresenceOf('concept', {
|
||||||
message: `Concept cannot be blank`
|
message: `Concept cannot be blank`
|
||||||
});
|
});
|
||||||
|
|
||||||
|
Self.observe('before save', async ctx => {
|
||||||
|
const models = Self.app.models;
|
||||||
|
const changes = ctx.data || ctx.instance;
|
||||||
|
const instance = ctx.currentInstance;
|
||||||
|
|
||||||
|
const newQuantity = changes?.quantity;
|
||||||
|
if (newQuantity == null) return;
|
||||||
|
|
||||||
|
const loopBackContext = LoopBackContext.getCurrentContext();
|
||||||
|
ctx.req = loopBackContext.active;
|
||||||
|
if (await models.ACL.checkAccessAcl(ctx, 'Sale', 'canForceQuantity', 'WRITE')) return;
|
||||||
|
|
||||||
|
const ticketId = changes?.ticketFk || instance?.ticketFk;
|
||||||
|
const itemId = changes?.itemFk || instance?.itemFk;
|
||||||
|
|
||||||
|
const ticket = await models.Ticket.findById(
|
||||||
|
ticketId,
|
||||||
|
{
|
||||||
|
fields: ['id', 'clientFk', 'warehouseFk', 'shipped'],
|
||||||
|
include: {
|
||||||
|
relation: 'client',
|
||||||
|
scope: {
|
||||||
|
fields: ['id', 'clientTypeFk'],
|
||||||
|
include: {
|
||||||
|
relation: 'type',
|
||||||
|
scope: {
|
||||||
|
fields: ['code', 'description']
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
ctx.options);
|
||||||
|
if (ticket?.client()?.type()?.code === 'loses') return;
|
||||||
|
|
||||||
|
const isRefund = await models.TicketRefund.findOne({
|
||||||
|
fields: ['id'],
|
||||||
|
where: {refundTicketFk: ticketId}
|
||||||
|
}, ctx.options);
|
||||||
|
if (isRefund) return;
|
||||||
|
|
||||||
|
if (newQuantity < 0)
|
||||||
|
throw new UserError('You can only add negative amounts in refund tickets');
|
||||||
|
|
||||||
|
const item = await models.Item.findOne({
|
||||||
|
fields: ['family', 'minQuantity'],
|
||||||
|
where: {id: itemId},
|
||||||
|
}, ctx.options);
|
||||||
|
|
||||||
|
if (item.family == 'EMB') return;
|
||||||
|
|
||||||
|
const itemInfo = await models.Item.getVisibleAvailable(
|
||||||
|
itemId,
|
||||||
|
ticket.warehouseFk,
|
||||||
|
ticket.shipped,
|
||||||
|
ctx.options
|
||||||
|
);
|
||||||
|
|
||||||
|
const oldQuantity = instance?.quantity ?? null;
|
||||||
|
const quantityAdded = newQuantity - oldQuantity;
|
||||||
|
if (itemInfo.available < quantityAdded)
|
||||||
|
throw new UserError(`This item is not available`);
|
||||||
|
|
||||||
|
if (await models.ACL.checkAccessAcl(ctx, 'Ticket', 'isRoleAdvanced', '*')) return;
|
||||||
|
|
||||||
|
if (newQuantity < item.minQuantity && itemInfo.available != newQuantity)
|
||||||
|
throw new UserError('The amount cannot be less than the minimum');
|
||||||
|
|
||||||
|
if (!ctx.isNewInstance && newQuantity > oldQuantity)
|
||||||
|
throw new UserError('The new quantity should be smaller than the old one');
|
||||||
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -16,8 +16,8 @@ class Controller extends SearchPanel {
|
||||||
this.$http.get('ItemPackingTypes', {filter}).then(res => {
|
this.$http.get('ItemPackingTypes', {filter}).then(res => {
|
||||||
for (let ipt of res.data) {
|
for (let ipt of res.data) {
|
||||||
itemPackingTypes.push({
|
itemPackingTypes.push({
|
||||||
code: ipt.code,
|
description: this.$t(ipt.description),
|
||||||
description: this.$t(ipt.description)
|
code: ipt.code
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
this.itemPackingTypes = itemPackingTypes;
|
this.itemPackingTypes = itemPackingTypes;
|
||||||
|
|
|
@ -163,26 +163,16 @@ export default class Controller extends Section {
|
||||||
return {'futureId': value};
|
return {'futureId': value};
|
||||||
case 'liters':
|
case 'liters':
|
||||||
return {'liters': value};
|
return {'liters': value};
|
||||||
case 'lines':
|
|
||||||
return {'lines': value};
|
|
||||||
case 'futureLiters':
|
case 'futureLiters':
|
||||||
return {'futureLiters': value};
|
return {'futureLiters': value};
|
||||||
|
case 'lines':
|
||||||
|
return {'lines': value};
|
||||||
case 'futureLines':
|
case 'futureLines':
|
||||||
return {'futureLines': value};
|
return {'futureLines': value};
|
||||||
case 'ipt':
|
case 'ipt':
|
||||||
return {or:
|
return {'ipt': {like: `%${value}%`}};
|
||||||
[
|
|
||||||
{'ipt': {like: `%${value}%`}},
|
|
||||||
{'ipt': null}
|
|
||||||
]
|
|
||||||
};
|
|
||||||
case 'futureIpt':
|
case 'futureIpt':
|
||||||
return {or:
|
return {'futureIpt': {like: `%${value}%`}};
|
||||||
[
|
|
||||||
{'futureIpt': {like: `%${value}%`}},
|
|
||||||
{'futureIpt': null}
|
|
||||||
]
|
|
||||||
};
|
|
||||||
case 'totalWithVat':
|
case 'totalWithVat':
|
||||||
return {'totalWithVat': value};
|
return {'totalWithVat': value};
|
||||||
case 'futureTotalWithVat':
|
case 'futureTotalWithVat':
|
||||||
|
|
|
@ -18,3 +18,4 @@ Multiple invoice: Factura múltiple
|
||||||
Make invoice...: Crear factura...
|
Make invoice...: Crear factura...
|
||||||
Invoice selected tickets: Facturar tickets seleccionados
|
Invoice selected tickets: Facturar tickets seleccionados
|
||||||
Are you sure to invoice tickets: ¿Seguro que quieres facturar {{ticketsAmount}} tickets?
|
Are you sure to invoice tickets: ¿Seguro que quieres facturar {{ticketsAmount}} tickets?
|
||||||
|
Rounding: Redondeo
|
||||||
|
|
|
@ -311,7 +311,7 @@
|
||||||
clear-disabled="true"
|
clear-disabled="true"
|
||||||
suffix="%">
|
suffix="%">
|
||||||
</vn-input-number>
|
</vn-input-number>
|
||||||
<vn-vertical ng-if="$ctrl.usesMana && $ctrl.currentWorkerMana != 0">
|
<vn-vertical ng-if="$ctrl.usesMana">
|
||||||
<vn-radio
|
<vn-radio
|
||||||
label="Promotion mana"
|
label="Promotion mana"
|
||||||
val="mana"
|
val="mana"
|
||||||
|
|
|
@ -97,14 +97,6 @@ class Controller extends Section {
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
this.getUsesMana();
|
this.getUsesMana();
|
||||||
this.getCurrentWorkerMana();
|
|
||||||
}
|
|
||||||
|
|
||||||
getCurrentWorkerMana() {
|
|
||||||
this.$http.get(`WorkerManas/getCurrentWorkerMana`)
|
|
||||||
.then(res => {
|
|
||||||
this.currentWorkerMana = res.data;
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
getUsesMana() {
|
getUsesMana() {
|
||||||
|
|
|
@ -120,12 +120,10 @@ describe('Ticket', () => {
|
||||||
const expectedAmount = 250;
|
const expectedAmount = 250;
|
||||||
$httpBackend.expect('GET', 'Tickets/1/getSalesPersonMana').respond(200, expectedAmount);
|
$httpBackend.expect('GET', 'Tickets/1/getSalesPersonMana').respond(200, expectedAmount);
|
||||||
$httpBackend.expect('GET', 'Sales/usesMana').respond(200);
|
$httpBackend.expect('GET', 'Sales/usesMana').respond(200);
|
||||||
$httpBackend.expect('GET', 'WorkerManas/getCurrentWorkerMana').respond(200, expectedAmount);
|
|
||||||
controller.getMana();
|
controller.getMana();
|
||||||
$httpBackend.flush();
|
$httpBackend.flush();
|
||||||
|
|
||||||
expect(controller.edit.mana).toEqual(expectedAmount);
|
expect(controller.edit.mana).toEqual(expectedAmount);
|
||||||
expect(controller.currentWorkerMana).toEqual(expectedAmount);
|
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
|
@ -29,7 +29,7 @@
|
||||||
disabled="watcher.dataChanged() || !$ctrl.checkeds.length"
|
disabled="watcher.dataChanged() || !$ctrl.checkeds.length"
|
||||||
label="Pay"
|
label="Pay"
|
||||||
ng-click="$ctrl.createRefund()"
|
ng-click="$ctrl.createRefund()"
|
||||||
vn-acl="invoicing, claimManager, salesAssistant"
|
vn-acl="invoicing, claimManager, salesAssistant, buyer"
|
||||||
vn-acl-action="remove">
|
vn-acl-action="remove">
|
||||||
</vn-button>
|
</vn-button>
|
||||||
</vn-button-bar>
|
</vn-button-bar>
|
||||||
|
@ -37,7 +37,9 @@
|
||||||
<vn-check
|
<vn-check
|
||||||
tabindex="1"
|
tabindex="1"
|
||||||
on-change="$ctrl.addChecked(service.id)"
|
on-change="$ctrl.addChecked(service.id)"
|
||||||
disabled="!service.id">
|
disabled="!service.id"
|
||||||
|
vn-acl="invoicing, claimManager, salesAssistant, buyer"
|
||||||
|
vn-acl-action="remove">
|
||||||
</vn-check>
|
</vn-check>
|
||||||
<vn-autocomplete vn-two vn-focus
|
<vn-autocomplete vn-two vn-focus
|
||||||
data="ticketServiceTypes"
|
data="ticketServiceTypes"
|
||||||
|
@ -68,7 +70,8 @@
|
||||||
vn-one
|
vn-one
|
||||||
label="Price"
|
label="Price"
|
||||||
ng-model="service.price"
|
ng-model="service.price"
|
||||||
step="0.01">
|
step="0.01"
|
||||||
|
min="0">
|
||||||
</vn-input-number>
|
</vn-input-number>
|
||||||
<vn-auto>
|
<vn-auto>
|
||||||
<vn-icon-button
|
<vn-icon-button
|
||||||
|
|
|
@ -1,26 +0,0 @@
|
||||||
module.exports = Self => {
|
|
||||||
Self.remoteMethodCtx('getCurrentWorkerMana', {
|
|
||||||
description: 'Returns the mana of the logged worker',
|
|
||||||
accessType: 'READ',
|
|
||||||
accepts: [],
|
|
||||||
returns: {
|
|
||||||
type: 'number',
|
|
||||||
root: true
|
|
||||||
},
|
|
||||||
http: {
|
|
||||||
path: `/getCurrentWorkerMana`,
|
|
||||||
verb: 'GET'
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
Self.getCurrentWorkerMana = async ctx => {
|
|
||||||
let userId = ctx.req.accessToken.userId;
|
|
||||||
|
|
||||||
let workerMana = await Self.app.models.WorkerMana.findOne({
|
|
||||||
where: {workerFk: userId},
|
|
||||||
fields: 'amount'
|
|
||||||
});
|
|
||||||
|
|
||||||
return workerMana ? workerMana.amount : 0;
|
|
||||||
};
|
|
||||||
};
|
|
|
@ -1,15 +0,0 @@
|
||||||
const app = require('vn-loopback/server/server');
|
|
||||||
|
|
||||||
describe('workerMana getCurrentWorkerMana()', () => {
|
|
||||||
it('should get the mana of the logged worker', async() => {
|
|
||||||
let mana = await app.models.WorkerMana.getCurrentWorkerMana({req: {accessToken: {userId: 18}}});
|
|
||||||
|
|
||||||
expect(mana).toEqual(124);
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should return 0 if the user doesnt uses mana', async() => {
|
|
||||||
let mana = await app.models.WorkerMana.getCurrentWorkerMana({req: {accessToken: {userId: 9}}});
|
|
||||||
|
|
||||||
expect(mana).toEqual(0);
|
|
||||||
});
|
|
||||||
});
|
|
|
@ -66,46 +66,36 @@ module.exports = Self => {
|
||||||
stmts.push('DROP TEMPORARY TABLE IF EXISTS tmp.timeControlCalculate');
|
stmts.push('DROP TEMPORARY TABLE IF EXISTS tmp.timeControlCalculate');
|
||||||
stmts.push('DROP TEMPORARY TABLE IF EXISTS tmp.timeBusinessCalculate');
|
stmts.push('DROP TEMPORARY TABLE IF EXISTS tmp.timeBusinessCalculate');
|
||||||
|
|
||||||
|
const destroyAllWhere = {
|
||||||
|
timed: {between: [started, ended]},
|
||||||
|
isSendMail: true
|
||||||
|
};
|
||||||
|
const updateAllWhere = {
|
||||||
|
year: args.year,
|
||||||
|
week: args.week
|
||||||
|
};
|
||||||
|
|
||||||
|
const tmpUserSQL = `
|
||||||
|
CREATE OR REPLACE TEMPORARY TABLE tmp.user
|
||||||
|
SELECT id as userFk
|
||||||
|
FROM vn.worker`;
|
||||||
|
let tmpUser = new ParameterizedSQL(tmpUserSQL);
|
||||||
|
|
||||||
if (args.workerId) {
|
if (args.workerId) {
|
||||||
await models.WorkerTimeControl.destroyAll({
|
destroyAllWhere.userFk = args.workerId;
|
||||||
userFk: args.workerId,
|
updateAllWhere.workerFk = args.workerId;
|
||||||
timed: {between: [started, ended]},
|
tmpUser = new ParameterizedSQL(tmpUserSQL + ' WHERE id = ?', [args.workerId]);
|
||||||
isSendMail: true
|
|
||||||
}, myOptions);
|
|
||||||
|
|
||||||
const where = {
|
|
||||||
workerFk: args.workerId,
|
|
||||||
year: args.year,
|
|
||||||
week: args.week
|
|
||||||
};
|
|
||||||
await models.WorkerTimeControlMail.updateAll(where, {
|
|
||||||
updated: Date.vnNew(), state: 'SENDED'
|
|
||||||
}, myOptions);
|
|
||||||
|
|
||||||
stmt = new ParameterizedSQL('DROP TEMPORARY TABLE IF EXISTS tmp.`user`');
|
|
||||||
stmts.push(stmt);
|
|
||||||
stmt = new ParameterizedSQL('CREATE TEMPORARY TABLE tmp.`user` SELECT id userFk FROM account.user WHERE id = ?', [args.workerId]);
|
|
||||||
stmts.push(stmt);
|
|
||||||
} else {
|
|
||||||
await models.WorkerTimeControl.destroyAll({
|
|
||||||
timed: {between: [started, ended]},
|
|
||||||
isSendMail: true
|
|
||||||
}, myOptions);
|
|
||||||
|
|
||||||
const where = {
|
|
||||||
year: args.year,
|
|
||||||
week: args.week
|
|
||||||
};
|
|
||||||
await models.WorkerTimeControlMail.updateAll(where, {
|
|
||||||
updated: Date.vnNew(), state: 'SENDED'
|
|
||||||
}, myOptions);
|
|
||||||
|
|
||||||
stmt = new ParameterizedSQL('DROP TEMPORARY TABLE IF EXISTS tmp.`user`');
|
|
||||||
stmts.push(stmt);
|
|
||||||
stmt = new ParameterizedSQL('CREATE TEMPORARY TABLE IF NOT EXISTS tmp.`user` SELECT id as userFk FROM vn.worker w JOIN account.`user` u ON u.id = w.id WHERE id IS NOT NULL');
|
|
||||||
stmts.push(stmt);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
await models.WorkerTimeControl.destroyAll(destroyAllWhere, myOptions);
|
||||||
|
|
||||||
|
await models.WorkerTimeControlMail.updateAll(updateAllWhere, {
|
||||||
|
updated: Date.vnNew(),
|
||||||
|
state: 'SENDED'
|
||||||
|
}, myOptions);
|
||||||
|
|
||||||
|
stmts.push(tmpUser);
|
||||||
|
|
||||||
stmt = new ParameterizedSQL(
|
stmt = new ParameterizedSQL(
|
||||||
`CALL vn.timeControl_calculate(?, ?)
|
`CALL vn.timeControl_calculate(?, ?)
|
||||||
`, [started, ended]);
|
`, [started, ended]);
|
||||||
|
|
|
@ -46,7 +46,7 @@ module.exports = Self => {
|
||||||
SELECT DISTINCT w.id, w.code, u.name, u.nickname, u.active, b.departmentFk
|
SELECT DISTINCT w.id, w.code, u.name, u.nickname, u.active, b.departmentFk
|
||||||
FROM worker w
|
FROM worker w
|
||||||
JOIN account.user u ON u.id = w.id
|
JOIN account.user u ON u.id = w.id
|
||||||
JOIN business b ON b.workerFk = w.id
|
LEFT JOIN business b ON b.workerFk = w.id
|
||||||
) w`);
|
) w`);
|
||||||
|
|
||||||
stmt.merge(conn.makeSuffix(filter));
|
stmt.merge(conn.makeSuffix(filter));
|
||||||
|
|
|
@ -1,3 +0,0 @@
|
||||||
module.exports = Self => {
|
|
||||||
require('../methods/worker-mana/getCurrentWorkerMana')(Self);
|
|
||||||
};
|
|
|
@ -117,7 +117,7 @@
|
||||||
<td class="number">{{service.price | currency('EUR', $i18n.locale)}}</td>
|
<td class="number">{{service.price | currency('EUR', $i18n.locale)}}</td>
|
||||||
<td class="centered" width="5%"></td>
|
<td class="centered" width="5%"></td>
|
||||||
<td class="centered">{{service.taxDescription}}</td>
|
<td class="centered">{{service.taxDescription}}</td>
|
||||||
<td class="number">{{service.price | currency('EUR', $i18n.locale)}}</td>
|
<td class="number">{{service.total | currency('EUR', $i18n.locale)}}</td>
|
||||||
</tr>
|
</tr>
|
||||||
</tbody>
|
</tbody>
|
||||||
<tfoot>
|
<tfoot>
|
||||||
|
|
|
@ -1,8 +1,9 @@
|
||||||
SELECT
|
SELECT
|
||||||
tc.code taxDescription,
|
tc.code taxDescription,
|
||||||
ts.description,
|
ts.description,
|
||||||
ts.quantity,
|
ts.quantity,
|
||||||
ts.price
|
ts.price,
|
||||||
|
ts.quantity * ts.price total
|
||||||
FROM ticketService ts
|
FROM ticketService ts
|
||||||
JOIN taxClass tc ON tc.id = ts.taxClassFk
|
JOIN taxClass tc ON tc.id = ts.taxClassFk
|
||||||
WHERE ts.ticketFk = ?
|
WHERE ts.ticketFk = ?
|
Loading…
Reference in New Issue