Merge branch 'dev' of https://gitea.verdnatura.es/verdnatura/salix into 5858-fiscalData-validations
gitea/salix/pipeline/head There was a failure building this commit Details

This commit is contained in:
Javier Segarra 2024-01-17 11:22:07 +01:00
commit 62b704ac48
41 changed files with 409 additions and 229 deletions

View File

@ -35,10 +35,17 @@ module.exports = Self => {
let html = `<strong>Motivo</strong>:<br/>${reason}<br/>`;
html += `<strong>Usuario</strong>:<br/>${ctx.req.accessToken.userId} ${emailUser.email}<br/>`;
delete additionalData.backError.config.headers.Authorization;
const httpRequest = JSON.parse(additionalData?.httpRequest);
if (httpRequest)
delete httpRequest.config.headers.Authorization;
additionalData.httpRequest = httpRequest;
for (const data in additionalData)
html += `<strong>${data}</strong>:<br/>${tryParse(additionalData[data])}<br/>`;
const subjectReason = JSON.parse(additionalData?.httpRequest)?.data?.error;
const subjectReason = httpRequest?.data?.error;
smtp.send({
to: `${config.app.reportEmail}, ${emailUser.email}`,
subject:

View File

@ -20,7 +20,7 @@ module.exports = Self => {
}
});
Self.internationalExpedition = async expeditionFk => {
Self.internationalExpedition = async (expeditionFk) => {
const models = Self.app.models;
const viaexpressConfig = await models.ViaexpressConfig.findOne({

View File

@ -20,11 +20,11 @@ module.exports = Self => {
}
});
Self.renderer = async expeditionFk => {
Self.renderer = async (expeditionFk) => {
const models = Self.app.models;
const viaexpressConfig = await models.ViaexpressConfig.findOne({
fields: ['client', 'user', 'password', 'defaultWeight', 'deliveryType']
fields: ['client', 'user', 'password', 'defaultWeight', 'deliveryType', 'agencyModeFk']
});
const expedition = await models.Expedition.findOne({
@ -34,7 +34,7 @@ module.exports = Self => {
{
relation: 'ticket',
scope: {
fields: ['shipped', 'addressFk', 'clientFk', 'companyFk'],
fields: ['shipped', 'addressFk', 'clientFk', 'companyFk', 'agencyModeFk'],
include: [
{
relation: 'client',
@ -102,7 +102,6 @@ module.exports = Self => {
}
]
}
}
]
});
@ -110,13 +109,15 @@ module.exports = Self => {
const ticket = expedition.ticket();
const sender = ticket.company().client();
const shipped = ticket.shipped.toISOString();
const isInterdia = (ticket.agencyModeFk === viaexpressConfig.agencyModeFk)
const data = {
viaexpressConfig,
sender,
senderAddress: sender.defaultAddress(),
client: ticket.client(),
address: ticket.address(),
shipped
shipped,
isInterdia
};
const template = fs.readFileSync(__dirname + '/template.ejs', 'utf-8');

View File

@ -13,7 +13,7 @@
<Asegurado>0</Asegurado>
<Imprimir>0</Imprimir>
<ConDevolucionAlbaran>0</ConDevolucionAlbaran>
<Intradia>0</Intradia>
<Intradia><%= isInterdia %></Intradia>
<Observaciones></Observaciones>
<AlbaranRemitente></AlbaranRemitente>
<Modo>0</Modo>

View File

@ -29,6 +29,9 @@
},
"deliveryType": {
"type": "string"
},
"agencyModeFk": {
"type": "number"
}
}
}

View File

View File

@ -0,0 +1,2 @@
ALTER TABLE `vn`.`viaexpressConfig` ADD agencyModeFk int DEFAULT NULL NULL COMMENT 'Indica el agencyMode que es interdia';
ALTER TABLE `vn`.`viaexpressConfig` ADD CONSTRAINT viaexpressConfig_agencyMode_Fk FOREIGN KEY (agencyModeFK) REFERENCES vn.agencyMode(id) ON DELETE RESTRICT ON UPDATE RESTRICT;

View File

@ -0,0 +1,4 @@
REVOKE UPDATE ON TABLE `vn`.`item` FROM `employee`;
GRANT UPDATE(id, equivalent, stems, minPrice, isToPrint, family, box, category, doPhoto, image, inkFk, intrastatFk, hasMinPrice, created, comment, typeFk, generic, producerFk, description, density, relevancy, expenseFk, isActive, subName, tag5, value5, tag6, value6, tag7, value7, tag8, value8, tag9, value9, tag10, value10, minimum, upToDown, supplyResponseFk, hasKgPrice, isFloramondo, isFragile, numberOfItemsPerCask, embalageCode, quality, stemMultiplier, itemPackingTypeFk, packingOut, genericFk, packingShelve, isLaid, lastUsed, weightByPiece, weightByPiece, editorFk, recycledPlastic, nonRecycledPlastic, minQuantity) ON TABLE vn.item TO employee;

View File

@ -0,0 +1,12 @@
ALTER TABLE `vn`.`company` MODIFY COLUMN `supplierAccountFk` mediumint(8) unsigned DEFAULT NULL NULL COMMENT 'Cuenta por defecto para ingresos desde este pais';
ALTER TABLE `vn`.`supplierAccount`
ADD COLUMN `countryFk` mediumint(8) unsigned DEFAULT NULL,
ADD CONSTRAINT `fk_supplierAccount_country`
FOREIGN KEY (`countryFk`) REFERENCES `country` (`id`) ON UPDATE CASCADE;
ALTER TABLE `vn`.`supplierAccount`
ADD UNIQUE KEY `uk_supplier_country` (`supplierFk`, `countryFk`);

View File

@ -0,0 +1,2 @@
ALTER TABLE vn.productionConfig ADD itemPreviousDefaultSize int NULL COMMENT 'Altura por defecto para los artículos de previa';
UPDATE IGNORE vn.productionConfig SET itemPreviousDefaultSize = 40 WHERE id = 1;

View File

@ -0,0 +1,9 @@
UPDATE vn.supplierAccount sa
JOIN vn.country c ON sa.countryFk = c.id AND c.code = 'FR'
SET countryFk = c.id
WHERE iban = 'FR7630003012690002801121597';
UPDATE vn.supplierAccount sa
JOIN vn.country c ON sa.countryFk = c.id AND c.code = 'PT'
SET countryFk = c.id
WHERE iban = 'PT50001000005813059150168';

View File

@ -0,0 +1,2 @@
ALTER TABLE vn.invoiceOutConfig
ADD IF NOT EXISTS refLen TINYINT UNSIGNED DEFAULT 5 NOT NULL COMMENT 'Invoice reference identifier length';

View File

@ -4,17 +4,20 @@ CREATE OR REPLACE DEFINER=`root`@`localhost` TRIGGER `vn`.`invoiceOut_beforeInse
FOR EACH ROW
BEGIN
/**
* Generates the next reference for the invoice serial. There cannot be gaps
* between identifiers of the same serial!
*
* Reference format:
* - 0: Serial [A-Z]
* - 1: Sage company id
* - 2-3: Last two digits of issued year
* - 4-8: Autoincrement identifier
**/
DECLARE vNewRef INT DEFAULT 0;
DECLARE vCompanyCode INT;
* {0} Invoice serial
* {1} The company code
* {2-3} Last two digits of issue year
* {4-$} Autoincrement identifier
*/
DECLARE vRef INT DEFAULT 0;
DECLARE vRefLen INT;
DECLARE vRefPrefix VARCHAR(255);
DECLARE vLastRef VARCHAR(255);
DECLARE vRefStr VARCHAR(255);
DECLARE vRefLen INT DEFAULT 5;
DECLARE vCompanyCode INT;
DECLARE vYearLen INT DEFAULT 2;
DECLARE vPrefixLen INT;
@ -23,36 +26,34 @@ BEGIN
WHERE id = NEW.companyFk;
IF vCompanyCode IS NULL THEN
CALL util.throw('sageCompanyNotDefined');
CALL util.throw('companyCodeNotDefined');
END IF;
SELECT MAX(i.ref) INTO vLastRef
FROM invoiceOut i
WHERE i.serial = NEW.serial
AND i.issued BETWEEN util.firstDayOfYear(NEW.issued) AND util.dayEnd(util.lastDayOfYear(NEW.issued))
AND i.issued BETWEEN util.firstDayOfYear(NEW.issued) AND util.lastDayOfYear(NEW.issued)
AND i.companyFk = NEW.companyFk;
IF vLastRef IS NOT NULL THEN
SET vPrefixLen = LENGTH(NEW.serial) + LENGTH(vCompanyCode) + vYearLen;
SET vRefLen = LENGTH(vLastRef) - vPrefixLen;
SET vRefStr = SUBSTRING(vLastRef, vPrefixLen + 1);
SET vNewRef = vRefStr + 1;
IF LENGTH(vNewRef) > vRefLen THEN
CALL util.throw('refLenExceeded');
END IF;
SET NEW.ref = CONCAT(
SUBSTRING(vLastRef, 1, vPrefixLen),
LPAD(vNewRef, LENGTH(vRefStr), '0')
);
SET vRefPrefix = LEFT(vLastRef, vPrefixLen);
SET vRef = RIGHT(vLastRef, vRefLen);
ELSE
SET NEW.ref = CONCAT(
SELECT refLen INTO vRefLen FROM invoiceOutConfig;
SET vRefPrefix = CONCAT(
NEW.serial,
vCompanyCode,
RIGHT(YEAR(NEW.issued), vYearLen),
LPAD(1, vRefLen, '0')
RIGHT(YEAR(NEW.issued), vYearLen)
);
END IF;
SET vRef = vRef + 1;
IF LENGTH(vRef) > vRefLen THEN
CALL util.throw('refIdLenExceeded');
END IF;
SET NEW.ref = CONCAT(vRefPrefix, LPAD(vRef, vRefLen, '0'));
END$$
DELIMITER ;

View File

@ -600,6 +600,9 @@ INSERT INTO `vn`.`taxArea` (`code`, `claveOperacionFactura`, `CodigoTransaccion`
('NATIONAL', 0, 1),
('WORLD', 2, 15);
INSERT INTO vn.invoiceOutConfig
SET parallelism = 8;
INSERT INTO `vn`.`invoiceOutSerial` (`code`, `description`, `isTaxed`, `taxAreaFk`, `isCEE`, `type`)
VALUES
('A', 'Global nacional', 1, 'NATIONAL', 0, 'global'),
@ -623,9 +626,6 @@ UPDATE `vn`.`invoiceOut` SET ref = 'T3333333' WHERE id = 3;
UPDATE `vn`.`invoiceOut` SET ref = 'T4444444' WHERE id = 4;
UPDATE `vn`.`invoiceOut` SET ref = 'A1111111' WHERE id = 5;
INSERT INTO vn.invoiceOutConfig
SET parallelism = 8;
INSERT INTO `vn`.`invoiceOutTax` (`invoiceOutFk`, `taxableBase`, `vat`, `pgcFk`)
VALUES
(1, 895.76, 89.58, 4722000010),

View File

@ -27,11 +27,8 @@ describe('Item Edit basic data path', () => {
it(`should edit the item basic data and confirm the item data was edited`, async() => {
const values = {
name: 'Rose of Purity',
longName: 'RS Rose of Purity',
type: 'Anthurium',
intrastat: 'Coral y materiales similares',
origin: 'Spain',
relevancy: 1,
generic: 'Pallet',
isActive: false,

View File

@ -225,7 +225,7 @@ describe('Ticket Edit sale path', () => {
});
it('should show error trying to delete a ticket with a refund', async() => {
await page.accessToSearchResult('6');
await page.accessToSearchResult('7');
await page.waitToClick(selectors.ticketDescriptor.moreMenu);
await page.waitToClick(selectors.ticketDescriptor.moreMenuDeleteTicket);
await page.waitToClick(selectors.globalItems.acceptButton);

View File

@ -18,22 +18,6 @@
</vn-crud-model>
<form name="form" ng-submit="watcher.submit()" ng-cloak class="vn-w-md">
<vn-card class="vn-pa-lg">
<vn-horizontal>
<vn-textfield
label="Name"
ng-model="$ctrl.item.name"
vn-name="name"
rule
vn-focus>
</vn-textfield>
<vn-textfield
label="Full name"
ng-model="$ctrl.item.longName"
vn-name="longName"
rule
info="Full name calculates based on tags 1-3. Is not recommended to change it manually">
</vn-textfield>
</vn-horizontal>
<vn-horizontal>
<vn-autocomplete
url="ItemTypes"
@ -52,6 +36,34 @@
</div>
</tpl-item>
</vn-autocomplete>
<vn-textfield
label="Reference"
ng-model="$ctrl.item.comment"
vn-name="comment"
rule>
</vn-textfield>
<vn-input-number
min="0"
label="Relevancy"
ng-model="$ctrl.item.relevancy"
vn-name="relevancy"
rule>
</vn-input-number>
</vn-horizontal>
<vn-horizontal>
<vn-input-number
min="0"
label="stems"
ng-model="$ctrl.item.stems"
vn-name="stems"
rule>
</vn-input-number>
<vn-input-number
min="0"
label="Multiplier"
ng-model="$ctrl.item.stemMultiplier"
vn-name="stemMultiplier">
</vn-input-number>
<vn-autocomplete
label="Generic"
url="Items/withName"
@ -105,63 +117,10 @@
url="Expenses"
label="Expense"
ng-model="$ctrl.item.expenseFk"
vn-name="expense"
vn-name="expence"
initial-data="$ctrl.item.expense">
</vn-autocomplete>
</vn-horizontal>
<vn-horizontal>
<vn-autocomplete
data="originsData"
label="Origin"
show-field="name"
value-field="id"
ng-model="$ctrl.item.originFk"
vn-name="origin"
initial-data="$ctrl.item.origin">
</vn-autocomplete>
<vn-input-number
min="0"
label="Size"
ng-model="$ctrl.item.size"
vn-name="size"
rule>
</vn-input-number>
<vn-textfield
label="Reference"
ng-model="$ctrl.item.comment"
vn-name="comment"
rule>
</vn-textfield>
<vn-input-number
min="0"
label="Relevancy"
ng-model="$ctrl.item.relevancy"
vn-name="relevancy"
rule>
</vn-input-number>
</vn-horizontal>
<vn-horizontal>
<vn-input-number
min="0"
label="stems"
ng-model="$ctrl.item.stems"
vn-name="stems"
rule>
</vn-input-number>
<vn-input-number
min="0"
label="Multiplier"
ng-model="$ctrl.item.stemMultiplier"
vn-name="stemMultiplier">
</vn-input-number>
<vn-input-number
min="1"
label="Minimum sales quantity"
ng-model="$ctrl.item.minQuantity"
vn-name="minQuantity"
rule>
</vn-input-number>
</vn-horizontal>
<vn-horizontal>
<vn-input-number
min="0"
@ -192,14 +151,6 @@
rule>
</vn-input-number>
</vn-horizontal>
<vn-horizontal>
<vn-textarea
label="Description"
ng-model="$ctrl.item.description"
vn-name="description"
rule>
</vn-textarea>
</vn-horizontal>
<vn-horizontal>
<vn-check
label="Active"
@ -224,6 +175,14 @@
info="This item does need a photo">
</vn-check>
</vn-horizontal>
<vn-horizontal>
<vn-textarea
label="Description"
ng-model="$ctrl.item.description"
vn-name="description"
rule>
</vn-textarea>
</vn-horizontal>
</vn-card>
<vn-button-bar>
<vn-submit

View File

@ -1,6 +1,9 @@
{
"name": "RoutesMonitor",
"base": "Loggable",
"base": "VnModel",
"mixins": {
"Loggable": true
},
"options": {
"mysql": {
"table": "routesMonitor"

View File

@ -20,6 +20,9 @@
},
"beneficiary": {
"type": "string"
},
"supplierFk": {
"type": "string"
}
},
"relations": {

View File

@ -14,7 +14,11 @@ module.exports = Self => {
}
try {
const salesFilter = {
let sales;
let services;
if (salesIds && salesIds.length) {
sales = await models.Sale.find({
where: {id: {inq: salesIds}},
include: {
relation: 'components',
@ -22,9 +26,18 @@ module.exports = Self => {
fields: ['saleFk', 'componentFk', 'value']
}
}
};
const sales = await models.Sale.find(salesFilter, myOptions);
let ticketsIds = [...new Set(sales.map(sale => sale.ticketFk))];
}, myOptions);
}
if (servicesIds && servicesIds.length) {
services = await models.TicketService.find({
where: {id: {inq: servicesIds}}
}, myOptions);
}
let ticketsIds = sales ?
[...new Set(sales.map(sale => sale.ticketFk))] :
[...new Set(services.map(service => service.ticketFk))];
const mappedTickets = new Map();
@ -39,7 +52,7 @@ module.exports = Self => {
newTickets.push(newTicket);
mappedTickets.set(ticketId, newTicket.id);
}
if (sales) {
for (const sale of sales) {
const newTicketId = mappedTickets.get(sale.ticketFk);
@ -58,13 +71,9 @@ module.exports = Self => {
await models.SaleComponent.create(components, myOptions);
}
}
if (servicesIds && servicesIds.length) {
const servicesFilter = {
where: {id: {inq: servicesIds}}
};
const services = await models.TicketService.find(servicesFilter, myOptions);
if (services) {
for (const service of services) {
const newTicketId = mappedTickets.get(service.ticketFk);

View File

@ -6,7 +6,6 @@ module.exports = Self => {
{
arg: 'salesIds',
type: ['number'],
required: true
},
{
arg: 'servicesIds',

View File

@ -44,24 +44,7 @@ describe('Sale refund()', () => {
const tickets = await models.Sale.refund(ctx, salesIds, servicesIds, withWarehouse, options);
const refundedTicket = await models.Ticket.findOne({
where: {
id: tickets[0].id
},
include: [
{
relation: 'ticketSales',
scope: {
include: {
relation: 'components'
}
}
},
{
relation: 'ticketServices',
}
]
}, options);
const refundedTicket = await getTicketRefund(tickets[0].id, options);
const ticketsAfter = await models.Ticket.find({}, options);
const salesLength = refundedTicket.ticketSales().length;
const componentsLength = refundedTicket.ticketSales()[0].components().length;
@ -77,4 +60,42 @@ describe('Sale refund()', () => {
throw e;
}
});
it('should create a ticket without sales', async() => {
const servicesIds = [4];
const tx = await models.Sale.beginTransaction({});
const options = {transaction: tx};
try {
const tickets = await models.Sale.refund(ctx, null, servicesIds, withWarehouse, options);
const refundedTicket = await getTicketRefund(tickets[0].id, options);
expect(refundedTicket).toBeDefined();
await tx.rollback();
} catch (e) {
await tx.rollback();
throw e;
}
});
});
async function getTicketRefund(id, options) {
return models.Ticket.findOne({
where: {
id
},
include: [
{
relation: 'ticketSales',
scope: {
include: {
relation: 'components'
}
}
},
{
relation: 'ticketServices',
}
]
}, options);
}

View File

@ -3,7 +3,7 @@ const UserError = require('vn-loopback/util/user-error');
module.exports = Self => {
Self.remoteMethodCtx('updateTimeEntry', {
description: 'Updates a time entry for a worker if the user role is above the worker',
accessType: 'READ',
accessType: 'WRITE',
accepts: [{
arg: 'id',
type: 'number',

View File

@ -1,14 +1,33 @@
const Component = require(`vn-print/core/component`);
const emailBody = new Component('email-body');
const attachment = new Component('attachment');
const db = require('../../../core/database');
module.exports = {
name: 'letter-debtor-nd',
async serverPrefetch() {
this.debtor = await this.fetchDebtor(this.id, this.companyId);
if (!this.debtor)
throw new Error('Something went wrong');
this.debtor = await db.findOne(`
SELECT sa.id,
sa.iban,
be.name bankName,
sa.countryFk,
c.countryFk
FROM supplierAccount sa
JOIN bankEntity be ON sa.bankEntityFk = be.id
LEFT JOIN company co ON co.supplierAccountFk = sa.id
JOIN client c ON c.countryFk = sa.countryFk
WHERE c.id = ?;
`, [this.id]);
if (!this.debtor) {
this.debtor = await db.findOne(`
SELECT sa.iban,
be.name bankName
FROM supplierAccount sa
JOIN bankEntity be ON sa.bankEntityFk = be.id
JOIN company co ON co.supplierAccountFk = sa.id
WHERE co.id = ?;
`, [this.companyId]);
}
},
data() {
return {

View File

@ -0,0 +1,21 @@
subject: Reminder of Outstanding Balance Notice
title: Reminder Notice
sections:
introduction:
title: Dear Customer
description: We are writing to you once again to inform you that your debt with our company remains unpaid, as you can verify in the attached statement.
terms: Since the agreed payment deadlines have significantly passed, there should be no further delay in settling the outstanding amount.
payMethod:
description: To do so, you have the following payment options
options:
- Online payment through our website.
- Deposit or transfer to the account number provided at the bottom of this letter, indicating your customer number.
legalAction:
description: If this payment reminder is not heeded, we will be compelled to initiate the necessary legal actions, which may include
options:
- Inclusion in negative credit and financial solvency records.
- Legal proceedings.
- Debt assignment to a debt collection agency.
contactPhone: For inquiries, you can reach us at <strong>96 324 21 00</strong>.
conclusion: We look forward to hearing from you. <br/> Thank you for your attention.
transferAccount: Bank Transfer Details

View File

@ -0,0 +1,26 @@
subject: Réitération de l'avis de solde débiteur
title: Avis réitéré
sections:
introduction:
title: Cher client
description: Nous vous écrivons à nouveau pour vous informer qu'il est toujours en attente
votre dette envers notre société, comme vous pouvez le voir dans le relevé ci-joint.
terms: Étant donné que les délais de paiement convenus sont largement dépassés, il n'est pas approprié
retard plus important dans le règlement du montant dû.
payMethod:
description: Pour cela, vous disposez des modes de paiement suivants
options:
- Paiement en ligne depuis notre site internet.
- Revenu ou virement sur le numéro de compte que nous détaillons en bas de ce courrier,
indiquant le numéro de client.
legalAction:
description: Si cette obligation de paiement n'est pas remplie, nous serons contraints de
d'engager les actions judiciaires qui se déroulent, parmi lesquelles
options:
- Inclusion dans les dossiers négatifs sur la solvabilité financière et le crédit.
- Réclamation judiciaire.
- Cession de créance à une société de gestion de recouvrement.
contactPhone: Pour toute demande, vous pouvez nous contacter au <strong>96
324 21 00</strong>.
conclusion: En attente de vos nouvelles. <br/> Merci pour ton attention.
transferAccount: Données pour virement bancaire

View File

@ -0,0 +1,26 @@
subject: Reiteração de aviso de saldo devedor
title: Aviso reiterado
sections:
introduction:
title: Estimado cliente
description: Estamos escrevendo para você novamente para informar que ainda está pendente
sua dívida para com nossa empresa, conforme demonstrativo anexo.
terms: Dado que os prazos de pagamento acordados são largamente excedidos, não é adequado
maior atraso na liquidação do valor devido.
payMethod:
description: Para isso você tem as seguintes formas de pagamento
options:
- Pagamento online em nosso site.
- Renda ou transferência para o número da conta que detalhamos no final desta carta,
indicando o número do cliente.
legalAction:
description: Se esta obrigação de pagamento não for cumprida, seremos obrigados a
para iniciar as ações legais que procedem, entre as quais estão
options:
- Inclusão em processos negativos de solvência financeira e de crédito.
- Reivindicação judicial.
- Cessão de dívida a uma empresa de gestão de cobranças.
contactPhone: Para consultas, você pode entrar em contato conosco em <strong>96
324 21 00</strong>.
conclusion: Aguardando suas notícias. <br/> Agradecimentos para sua atenção.
transferAccount: Dados para transferência bancária

View File

@ -1,10 +1,9 @@
SELECT
c.dueDay,
c.iban,
SELECT c.dueDay,
sa.iban,
be.name AS bankName
FROM client c
JOIN company AS cny
JOIN supplierAccount AS sa ON sa.id = cny.supplierAccountFk
be.name bankName
FROM client c
JOIN supplierAccount sa ON sa.id = cny.supplierAccountFk
JOIN bankEntity be ON be.id = sa.bankEntityFk
WHERE c.id = ? AND cny.id = ?
JOIN company cny
WHERE c.id = ?
AND cny.id = ?

View File

@ -1,14 +1,33 @@
const Component = require(`vn-print/core/component`);
const emailBody = new Component('email-body');
const attachment = new Component('attachment');
const db = require('../../../core/database');
module.exports = {
name: 'letter-debtor-st',
async serverPrefetch() {
this.debtor = await this.fetchDebtor(this.id, this.companyId);
if (!this.debtor)
throw new Error('Something went wrong');
this.debtor = await db.findOne(`
SELECT sa.id,
sa.iban,
be.name bankName,
sa.countryFk,
c.countryFk
FROM supplierAccount sa
JOIN bankEntity be ON sa.bankEntityFk = be.id
LEFT JOIN company co ON co.supplierAccountFk = sa.id
JOIN client c ON c.countryFk = sa.countryFk
WHERE c.id = ?;
`, [this.id]);
if (!this.debtor) {
this.debtor = await db.findOne(`
SELECT sa.iban,
be.name bankName
FROM supplierAccount sa
JOIN bankEntity be ON sa.bankEntityFk = be.id
JOIN company co ON co.supplierAccountFk = sa.id
WHERE co.id = ?;
`, [this.companyId]);
}
},
data() {
return {

View File

@ -0,0 +1,11 @@
subject: Initial Notice for Outstanding Balance
title: Initial Notice for Outstanding Balance
sections:
introduction:
title: Dear Customer
description: Through this letter, we would like to inform you that, according to our accounting records, your account has an outstanding balance that needs to be settled.
checkExtract: We kindly request you to verify that the attached statement corresponds to the information you have. Our administration department will be happy to clarify any questions you may have and provide any documents you may request.
checkValidData: If, upon reviewing the provided information, everything appears to be accurate, we kindly ask you to proceed with rectifying your situation.
payMethod: If you prefer not to visit our offices in person, you can make the payment through a bank transfer to the account listed at the bottom of this communication, indicating your customer number. Alternatively, you can make the payment online through our website.
conclusion: We sincerely appreciate your kind cooperation.
transferAccount: Bank Transfer Details

View File

@ -1,10 +1,9 @@
SELECT
c.dueDay,
c.iban,
SELECT c.dueDay,
sa.iban,
be.name AS bankName
FROM client c
JOIN company AS cny
JOIN supplierAccount AS sa ON sa.id = cny.supplierAccountFk
be.name bankName
FROM client c
JOIN supplierAccount sa ON sa.id = cny.supplierAccountFk
JOIN bankEntity be ON be.id = sa.bankEntityFk
WHERE c.id = ? AND cny.id = ?
JOIN company cny
WHERE c.id = ?
AND cny.id = ?

View File

@ -15,7 +15,7 @@
<td id="outline" class="ellipsize">{{labelData.workerCode || '---'}}</td>
</tr>
<tr>
<td id="outline" class="ellipsize">{{labelCount || labelData.labelCount || 0}}</td>
<td id="outline" class="ellipsize">{{labelData.labelCount || 0}}</td>
</tr>
<tr>
<td id="outline" class="ellipsize">{{labelData.code == 'V' ? (labelData.size || 0) + 'cm' : (labelData.volume || 0) + 'm³'}}</td>

View File

@ -18,9 +18,9 @@ module.exports = {
}
},
async serverPrefetch() {
await this.rawSql('SET @hasPrevia := 0');
let ticketIds;
const res = await this.rawSqlFromDef('tickets', [this.id]);
if (res.length) {
ticketIds = [];
for (const row of res)

View File

@ -7,14 +7,16 @@ SELECT c.itemPackingTypeFk code,
cc.code color,
t.clientFk,
CAST(SUM(sv.volume) AS DECIMAL(5, 2)) volume,
MAX(i.`size`) `size`,
MAX(
IF(sgd.id, IFNULL(pc.itemPreviousDefaultSize, i.`size`), i.`size`)
) `size`,
w.code workerCode,
TIME_FORMAT(t.shipped, '%H:%i') shippedHour,
TIME_FORMAT(zo.`hour`, '%H:%i') zoneHour,
DATE_FORMAT(t.shipped, '%d/%m/%y') shipped,
tt.labelCount,
t.nickName,
COUNT(*) lineCount,
SUM(IF(sgd.id, IF(@hasPrevia, 0, @hasPrevia := 1), 1)) lineCount,
rm.routeFk
FROM vn.ticket t
JOIN vn.ticketCollection tc ON tc.ticketFk = t.id
@ -33,6 +35,8 @@ SELECT c.itemPackingTypeFk code,
LEFT JOIN vn.`zone` zo ON t.zoneFk = zo.id
LEFT JOIN vn.routesMonitor rm ON rm.routeFk = t.routeFk
LEFT JOIN vn.expeditionTruck et ON et.id = rm.expeditionTruckFk
LEFT JOIN vn.saleGroupDetail sgd ON sgd.saleFk = s.id
JOIN vn.productionConfig pc
WHERE t.id IN (?)
GROUP BY t.id
ORDER BY cc.`code`;
ORDER BY cc.`code`

View File

@ -27,8 +27,8 @@
<tr>
<td>{{$t('supplier.identifier')}}</td>
<th>
<div>ES89000B97367486</div>
<div>B97367486-000</div>
<div>{{supplier.iban}}</div>
<div>{{supplier.nif}}</div>
</th>
</tr>
<tr>

View File

@ -1,4 +1,5 @@
const vnReport = require('../../../core/mixins/vn-report.js');
const db = require('../../../core/database');
module.exports = {
name: 'sepa-core',
@ -18,5 +19,16 @@ module.exports = {
type: Number,
required: true
}
},
methods: {
getSupplierCif() {
return db.findOne(`
SELECT sa.iban, s.nif
FROM supplierAccount sa
JOIN company co ON co.supplierAccountFk = sa.id
JOIN supplier s ON sa.supplierFk = s.id
WHERE co.id = ?`) [this.companyId];
}
}
};

View File

@ -5,13 +5,23 @@ SELECT
sc.country,
s.postCode,
s.city,
sp.name province
FROM client c
LEFT JOIN mandate m ON m.clientFk = c.id
AND m.companyFk = ? AND m.finished IS NULL
sp.name province,
s.nif,
sa.iban,
sa.supplierFk,
be.name bankName
FROM
client c
LEFT JOIN mandate m ON m.clientFk = c.id AND m.companyFk = ? AND m.finished IS NULL
LEFT JOIN supplier s ON s.id = m.companyFk
LEFT JOIN country sc ON sc.id = s.countryFk
LEFT JOIN province sp ON sp.id = s.provinceFk
LEFT JOIN province p ON p.id = c.provinceFk
WHERE (m.companyFk = ? OR m.companyFk IS NULL) AND c.id = ?
ORDER BY m.created DESC LIMIT 1
LEFT JOIN supplierAccount sa ON sa.supplierFk = s.id
LEFT JOIN bankEntity be ON sa.bankEntityFk = be.id
WHERE
(m.companyFk = ? OR m.companyFk IS NULL)
AND (c.id = ? OR (c.id IS NULL AND c.countryFk = sa.countryFk))
ORDER BY
m.created DESC
LIMIT 1;