salix/modules/ticket/back/methods/ticket/transferSales.js

173 lines
6.0 KiB
JavaScript

const UserError = require('vn-loopback/util/user-error');
module.exports = Self => {
Self.remoteMethodCtx('transferSales', {
description: 'Transfer sales to a new or a given ticket',
accessType: 'WRITE',
accepts: [{
arg: 'id',
type: 'number',
required: true,
description: 'Origin ticket id',
http: {source: 'path'}
},
{
arg: 'ticketId',
type: 'number',
description: 'Destination ticket id',
required: false
},
{
arg: 'sales',
type: ['object'],
description: 'The sales to transfer',
required: true
}],
returns: {
type: 'object',
root: true
},
http: {
path: `/:id/transferSales`,
verb: 'POST'
}
});
Self.transferSales = async(ctx, id, ticketId, sales, options) => {
const userId = ctx.req.accessToken.userId;
const models = Self.app.models;
const myOptions = {userId};
const $t = ctx.req.__; // $translate
let tx;
if (typeof options == 'object')
Object.assign(myOptions, options);
if (!myOptions.transaction) {
tx = await Self.beginTransaction({});
myOptions.transaction = tx;
}
try {
await models.Ticket.isEditableOrThrow(ctx, id, myOptions);
if (ticketId) {
const isReceiverEditable = await models.Ticket.isEditable(ctx, ticketId, myOptions);
if (!isReceiverEditable)
throw new UserError(`The sales of the receiver ticket can't be modified`);
}
const originalTicket = await models.Ticket.findById(id, null, myOptions);
const originalSales = await models.Sale.find({
where: {ticketFk: id}
}, myOptions);
if (!ticketId) {
const ticket = await models.Ticket.findById(id);
const canCreateTicket = await models.Client.canCreateTicket(ticket.clientFk);
if (!canCreateTicket)
throw new UserError(`You can't create a ticket for an inactive client`);
ticketId = await cloneTicket(originalTicket, myOptions);
}
const map = new Map();
for (const sale of originalSales)
map.set(sale.id, sale);
const saleIds = sales.map(sale => sale.id);
const hasClaimedSales = await models.ClaimBeginning.findOne({where: {saleFk: {inq: saleIds}}});
if (ticketId != id && hasClaimedSales)
throw new UserError(`Can't transfer claimed sales`);
const missingSales = sales.some(({id}) => !map.has(id));
if (missingSales)
throw new UserError($t('Sales already moved'));
for (const sale of sales) {
const originalSale = map.get(sale.id);
if (sale.quantity == originalSale?.quantity) {
query = `UPDATE sale
SET ticketFk = ?
WHERE id = ?`;
await Self.rawSql(query, [ticketId, sale.id], myOptions);
} else if (sale.quantity != originalSale?.quantity) {
await transferPartialSale(
ticketId, originalSale, sale, myOptions);
}
}
const isTicketEmpty = await models.Ticket.isEmpty(id, myOptions);
if (isTicketEmpty) {
try {
await models.Ticket.setDeleted(ctx, id, myOptions);
} catch (e) {
if (e.statusCode === 400) {
throw new UserError(
`This ticket cannot be left empty.`,
'TRANSFER_SET_DELETED',
$t(e.message, ...e.translateArgs)
);
}
throw e;
}
}
if (tx) await tx.commit();
return {id: ticketId};
} catch (e) {
if (tx) await tx.rollback();
throw e;
}
};
async function cloneTicket(ticket, options) {
query = `CALL vn.ticket_Clone(?, @result);
SELECT @result newTicketId;`;
let result = await Self.rawSql(query, [
ticket.id
], options);
return result[1][0].newTicketId;
}
async function transferPartialSale(ticketId, originalSale, sale, options) {
const models = Self.app.models;
if (sale.quantity > originalSale.quantity)
throw new UserError('Invalid quantity');
// Update original sale
const rest = originalSale.quantity - sale.quantity;
query = `UPDATE sale
SET quantity = ?,
originalQuantity = ?
WHERE id = ?`;
await Self.rawSql(query, [rest, rest, sale.id], options);
// Clone sale with new quantity
query = `INSERT INTO sale (itemFk, ticketFk, concept, quantity, price, discount, priceFixed,
reserved, isPicked, isPriceFixed, isAdded)
SELECT itemFk, ?, concept, ?, price, discount, priceFixed,
reserved, isPicked, isPriceFixed, isAdded
FROM sale
WHERE id = ?`;
await Self.rawSql(query, [ticketId, sale.quantity, sale.id], options);
const [lastInsertedSale] = await Self.rawSql('SELECT LAST_INSERT_ID() AS id', null, options);
// Clone sale components
const saleComponents = await models.SaleComponent.find({
where: {saleFk: sale.id}
}, options);
const newComponents = saleComponents.map(component => {
component.saleFk = lastInsertedSale.id;
return component;
});
await models.SaleComponent.create(newComponents, options);
}
};