173 lines
6.0 KiB
JavaScript
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);
|
|
}
|
|
};
|