Merge branch 'dev' into 5517-improveLogs+translations
gitea/salix/pipeline/head There was a failure building this commit
Details
gitea/salix/pipeline/head There was a failure building this commit
Details
This commit is contained in:
commit
2cfd2d9dda
|
@ -8,7 +8,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
||||||
## [2316.01] - 2023-05-04
|
## [2316.01] - 2023-05-04
|
||||||
|
|
||||||
### Added
|
### Added
|
||||||
-
|
- (Usuarios -> Histórico) Nueva sección
|
||||||
|
- (Roles -> Histórico) Nueva sección
|
||||||
|
|
||||||
### Changed
|
### Changed
|
||||||
- (Artículo -> Precio fijado) Modificado el buscador superior por uno lateral
|
- (Artículo -> Precio fijado) Modificado el buscador superior por uno lateral
|
||||||
|
|
|
@ -0,0 +1,3 @@
|
||||||
|
INSERT INTO `salix`.`ACL` (`model`, `property`, `accessType`, `permission`, `principalType`, `principalId`)
|
||||||
|
VALUES
|
||||||
|
('Receipt', 'receiptEmail', '*', 'ALLOW', 'ROLE', 'salesAssistant');
|
|
@ -0,0 +1,3 @@
|
||||||
|
UPDATE `salix`.`ACL`
|
||||||
|
SET model = 'InvoiceOut'
|
||||||
|
WHERE property IN ('negativeBases', 'negativeBasesCsv');
|
|
@ -1,21 +0,0 @@
|
||||||
create or replace definer = root@localhost view User as
|
|
||||||
select `account`.`user`.`id` AS `id`,
|
|
||||||
`account`.`user`.`realm` AS `realm`,
|
|
||||||
`account`.`user`.`name` AS `name`,
|
|
||||||
`account`.`user`.`nickname` AS `nickname`,
|
|
||||||
`account`.`user`.`bcryptPassword` AS `password`,
|
|
||||||
`account`.`user`.`role` AS `role`,
|
|
||||||
`account`.`user`.`active` AS `active`,
|
|
||||||
`account`.`user`.`email` AS `email`,
|
|
||||||
`account`.`user`.`emailVerified` AS `emailVerified`,
|
|
||||||
`account`.`user`.`verificationToken` AS `verificationToken`,
|
|
||||||
`account`.`user`.`lang` AS `lang`,
|
|
||||||
`account`.`user`.`lastPassChange` AS `lastPassChange`,
|
|
||||||
`account`.`user`.`created` AS `created`,
|
|
||||||
`account`.`user`.`updated` AS `updated`,
|
|
||||||
`account`.`user`.`image` AS `image`,
|
|
||||||
`account`.`user`.`recoverPass` AS `recoverPass`,
|
|
||||||
`account`.`user`.`sync` AS `sync`,
|
|
||||||
`account`.`user`.`hasGrant` AS `hasGrant`
|
|
||||||
from `account`.`user`;
|
|
||||||
|
|
|
@ -0,0 +1,2 @@
|
||||||
|
DROP PROCEDURE `vn`.`refund`;
|
||||||
|
DROP PROCEDURE `vn`.`ticket_doRefund`;
|
|
@ -0,0 +1,9 @@
|
||||||
|
-- vn.companyI18n definition
|
||||||
|
CREATE TABLE `vn`.`companyI18n` (
|
||||||
|
`companyFk` smallint(5) unsigned NOT NULL,
|
||||||
|
`lang` char(2) CHARACTER SET utf8mb3 NOT NULL,
|
||||||
|
`footnotes` longtext COLLATE utf8mb3_unicode_ci DEFAULT NULL,
|
||||||
|
PRIMARY KEY (`companyFk`,`lang`),
|
||||||
|
CONSTRAINT `companyI18n_FK` FOREIGN KEY (`companyFk`) REFERENCES `company` (`id`) ON UPDATE CASCADE
|
||||||
|
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb3 COLLATE=utf8mb3_unicode_ci;
|
||||||
|
|
|
@ -0,0 +1 @@
|
||||||
|
ALTER TABLE `vn`.`company` ADD `web` varchar(100) NULL;
|
|
@ -546,7 +546,8 @@ INSERT INTO `vn`.`supplier`(`id`, `name`, `nickname`,`account`,`countryFk`,`nif`
|
||||||
VALUES
|
VALUES
|
||||||
(1, 'Plants SL', 'Plants nick', 4100000001, 1, '06089160W', 0, util.VN_CURDATE(), 1, 'supplier address 1', 'PONTEVEDRA', 1, 15214, 1, 1, 15, 4, 1, 1, 18, 'flowerPlants', 1, '400664487V'),
|
(1, 'Plants SL', 'Plants nick', 4100000001, 1, '06089160W', 0, util.VN_CURDATE(), 1, 'supplier address 1', 'PONTEVEDRA', 1, 15214, 1, 1, 15, 4, 1, 1, 18, 'flowerPlants', 1, '400664487V'),
|
||||||
(2, 'Farmer King', 'The farmer', 4000020002, 1, '87945234L', 0, util.VN_CURDATE(), 1, 'supplier address 2', 'GOTHAM', 2, 43022, 1, 2, 10, 93, 2, 8, 18, 'animals', 1, '400664487V'),
|
(2, 'Farmer King', 'The farmer', 4000020002, 1, '87945234L', 0, util.VN_CURDATE(), 1, 'supplier address 2', 'GOTHAM', 2, 43022, 1, 2, 10, 93, 2, 8, 18, 'animals', 1, '400664487V'),
|
||||||
(442, 'Verdnatura Levante SL', 'Verdnatura', 5115000442, 1, '06815934E', 0, util.VN_CURDATE(), 1, 'supplier address 3', 'GOTHAM', 1, 43022, 1, 2, 15, 6, 9, 3, 18, 'complements', 1, '400664487V');
|
(442, 'Verdnatura Levante SL', 'Verdnatura', 5115000442, 1, '06815934E', 0, util.VN_CURDATE(), 1, 'supplier address 3', 'GOTHAM', 1, 43022, 1, 2, 15, 6, 9, 3, 18, 'complements', 1, '400664487V'),
|
||||||
|
(1381, 'Ornamentales', 'Ornamentales', 7185000440, 1, '03815934E', 0, util.VN_CURDATE(), 1, 'supplier address 4', 'GOTHAM', 1, 43022, 1, 2, 15, 6, 9, 3, 18, 'complements', 1, '400664487V');
|
||||||
|
|
||||||
INSERT INTO `vn`.`supplierContact`(`id`, `supplierFk`, `phone`, `mobile`, `email`, `observation`, `name`)
|
INSERT INTO `vn`.`supplierContact`(`id`, `supplierFk`, `phone`, `mobile`, `email`, `observation`, `name`)
|
||||||
VALUES
|
VALUES
|
||||||
|
@ -2789,7 +2790,7 @@ INSERT INTO `vn`.`profileType` (`id`, `name`)
|
||||||
|
|
||||||
INSERT INTO `salix`.`url` (`appName`, `environment`, `url`)
|
INSERT INTO `salix`.`url` (`appName`, `environment`, `url`)
|
||||||
VALUES
|
VALUES
|
||||||
('lilium', 'dev', 'http://localhost:8080/#/'),
|
('lilium', 'dev', 'http://localhost:9000/#/'),
|
||||||
('salix', 'dev', 'http://localhost:5000/#!/');
|
('salix', 'dev', 'http://localhost:5000/#!/');
|
||||||
|
|
||||||
INSERT INTO `vn`.`report` (`id`, `name`, `paperSizeFk`, `method`)
|
INSERT INTO `vn`.`report` (`id`, `name`, `paperSizeFk`, `method`)
|
||||||
|
|
|
@ -68,6 +68,7 @@ TABLES=(
|
||||||
time
|
time
|
||||||
volumeConfig
|
volumeConfig
|
||||||
workCenter
|
workCenter
|
||||||
|
companyI18n
|
||||||
)
|
)
|
||||||
dump_tables ${TABLES[@]}
|
dump_tables ${TABLES[@]}
|
||||||
|
|
||||||
|
|
|
@ -740,6 +740,7 @@ export default {
|
||||||
anyDocument: 'vn-ticket-dms-index > vn-data-viewer vn-tbody vn-tr'
|
anyDocument: 'vn-ticket-dms-index > vn-data-viewer vn-tbody vn-tr'
|
||||||
},
|
},
|
||||||
ticketFuture: {
|
ticketFuture: {
|
||||||
|
searchResult: 'vn-ticket-future tbody tr',
|
||||||
openAdvancedSearchButton: 'vn-searchbar .append vn-icon[icon="arrow_drop_down"]',
|
openAdvancedSearchButton: 'vn-searchbar .append vn-icon[icon="arrow_drop_down"]',
|
||||||
originDated: 'vn-date-picker[label="Origin date"]',
|
originDated: 'vn-date-picker[label="Origin date"]',
|
||||||
futureDated: 'vn-date-picker[label="Destination date"]',
|
futureDated: 'vn-date-picker[label="Destination date"]',
|
||||||
|
@ -755,7 +756,6 @@ export default {
|
||||||
problems: 'vn-check[label="With problems"]',
|
problems: 'vn-check[label="With problems"]',
|
||||||
tableButtonSearch: 'vn-button[vn-tooltip="Search"]',
|
tableButtonSearch: 'vn-button[vn-tooltip="Search"]',
|
||||||
moveButton: 'vn-button[vn-tooltip="Future tickets"]',
|
moveButton: 'vn-button[vn-tooltip="Future tickets"]',
|
||||||
acceptButton: '.vn-confirm.shown button[response="accept"]',
|
|
||||||
firstCheck: 'tbody > tr:nth-child(1) > td > vn-check',
|
firstCheck: 'tbody > tr:nth-child(1) > td > vn-check',
|
||||||
multiCheck: 'vn-multi-check',
|
multiCheck: 'vn-multi-check',
|
||||||
tableId: 'vn-textfield[name="id"]',
|
tableId: 'vn-textfield[name="id"]',
|
||||||
|
|
|
@ -81,9 +81,7 @@ describe('SmartTable SearchBar integration', () => {
|
||||||
await page.accessToSection('item.fixedPrice');
|
await page.accessToSection('item.fixedPrice');
|
||||||
await page.keyboard.press('Enter');
|
await page.keyboard.press('Enter');
|
||||||
|
|
||||||
const result = await page.waitToGetProperty(selectors.itemFixedPrice.firstItemID, 'value');
|
await page.waitForTextInField(selectors.itemFixedPrice.firstItemID, '1');
|
||||||
|
|
||||||
expect(result).toEqual('1');
|
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should order by last id, reload page and have same order', async() => {
|
it('should order by last id, reload page and have same order', async() => {
|
||||||
|
@ -91,9 +89,7 @@ describe('SmartTable SearchBar integration', () => {
|
||||||
await page.reload({
|
await page.reload({
|
||||||
waitUntil: 'networkidle2'
|
waitUntil: 'networkidle2'
|
||||||
});
|
});
|
||||||
const result = await page.waitToGetProperty(selectors.itemFixedPrice.firstItemID, 'value');
|
await page.waitForTextInField(selectors.itemFixedPrice.firstItemID, '13');
|
||||||
|
|
||||||
expect(result).toEqual('13');
|
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
|
@ -246,6 +246,7 @@ describe('Ticket Edit sale path', () => {
|
||||||
it('should select the third sale and create a claim of it', async() => {
|
it('should select the third sale and create a claim of it', async() => {
|
||||||
await page.accessToSearchResult('16');
|
await page.accessToSearchResult('16');
|
||||||
await page.accessToSection('ticket.card.sale');
|
await page.accessToSection('ticket.card.sale');
|
||||||
|
await page.waitToClick(selectors.ticketSales.firstSaleCheckbox);
|
||||||
await page.waitToClick(selectors.ticketSales.thirdSaleCheckbox);
|
await page.waitToClick(selectors.ticketSales.thirdSaleCheckbox);
|
||||||
await page.waitToClick(selectors.ticketSales.moreMenu);
|
await page.waitToClick(selectors.ticketSales.moreMenu);
|
||||||
await page.waitToClick(selectors.ticketSales.moreMenuCreateClaim);
|
await page.waitToClick(selectors.ticketSales.moreMenuCreateClaim);
|
||||||
|
|
|
@ -126,10 +126,11 @@ describe('Ticket Future path', () => {
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should check the three last tickets and move to the future', async() => {
|
it('should check the three last tickets and move to the future', async() => {
|
||||||
|
await page.waitForNumberOfElements(selectors.ticketFuture.searchResult, 4);
|
||||||
await page.waitToClick(selectors.ticketFuture.multiCheck);
|
await page.waitToClick(selectors.ticketFuture.multiCheck);
|
||||||
await page.waitToClick(selectors.ticketFuture.firstCheck);
|
await page.waitToClick(selectors.ticketFuture.firstCheck);
|
||||||
await page.waitToClick(selectors.ticketFuture.moveButton);
|
await page.waitToClick(selectors.ticketFuture.moveButton);
|
||||||
await page.waitToClick(selectors.ticketFuture.acceptButton);
|
await page.waitToClick(selectors.globalItems.acceptButton);
|
||||||
const message = await page.waitForSnackbar();
|
const message = await page.waitForSnackbar();
|
||||||
|
|
||||||
expect(message.text).toContain('Tickets moved successfully!');
|
expect(message.text).toContain('Tickets moved successfully!');
|
||||||
|
|
|
@ -35,7 +35,7 @@ describe('InvoiceIn serial path', () => {
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should go to index and check if the search-panel has the correct params', async() => {
|
it('should go to index and check if the search-panel has the correct params', async() => {
|
||||||
await page.click(selectors.invoiceInSerial.goToIndex);
|
await page.waitToClick(selectors.invoiceInSerial.goToIndex);
|
||||||
const params = await page.$$(selectors.invoiceInIndex.topbarSearchParams);
|
const params = await page.$$(selectors.invoiceInIndex.topbarSearchParams);
|
||||||
const serial = await params[0].getProperty('title');
|
const serial = await params[0].getProperty('title');
|
||||||
const isBooked = await params[1].getProperty('title');
|
const isBooked = await params[1].getProperty('title');
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
import getBrowser from '../../helpers/puppeteer';
|
import getBrowser from '../../helpers/puppeteer';
|
||||||
|
|
||||||
describe('InvoiceIn negative bases path', () => {
|
describe('InvoiceOut negative bases path', () => {
|
||||||
let browser;
|
let browser;
|
||||||
let page;
|
let page;
|
||||||
const httpRequests = [];
|
const httpRequests = [];
|
||||||
|
@ -9,11 +9,11 @@ describe('InvoiceIn negative bases path', () => {
|
||||||
browser = await getBrowser();
|
browser = await getBrowser();
|
||||||
page = browser.page;
|
page = browser.page;
|
||||||
page.on('request', req => {
|
page.on('request', req => {
|
||||||
if (req.url().includes(`InvoiceIns/negativeBases`))
|
if (req.url().includes(`InvoiceOuts/negativeBases`))
|
||||||
httpRequests.push(req.url());
|
httpRequests.push(req.url());
|
||||||
});
|
});
|
||||||
await page.loginAndModule('administrative', 'invoiceIn');
|
await page.loginAndModule('administrative', 'invoiceOut');
|
||||||
await page.accessToSection('invoiceIn.negative-bases');
|
await page.accessToSection('invoiceOut.negative-bases');
|
||||||
});
|
});
|
||||||
|
|
||||||
afterAll(async() => {
|
afterAll(async() => {
|
|
@ -156,6 +156,19 @@
|
||||||
"Component cost not set": "Componente coste no está estabecido",
|
"Component cost not set": "Componente coste no está estabecido",
|
||||||
"Tickets with associated refunds can't be deleted. This ticket is associated with refund Nº 2": "Tickets with associated refunds can't be deleted. This ticket is associated with refund Nº 2",
|
"Tickets with associated refunds can't be deleted. This ticket is associated with refund Nº 2": "Tickets with associated refunds can't be deleted. This ticket is associated with refund Nº 2",
|
||||||
"Description cannot be blank": "Description cannot be blank",
|
"Description cannot be blank": "Description cannot be blank",
|
||||||
|
"company": "Company",
|
||||||
|
"country": "Country",
|
||||||
|
"clientId": "Id client",
|
||||||
|
"clientSocialName": "Client",
|
||||||
|
"amount": "Amount",
|
||||||
|
"taxableBase": "Taxable base",
|
||||||
|
"ticketFk": "Id ticket",
|
||||||
|
"isActive": "Active",
|
||||||
|
"hasToInvoice": "Invoice",
|
||||||
|
"isTaxDataChecked": "Data checked",
|
||||||
|
"comercialId": "Id Comercial",
|
||||||
|
"comercialName": "Comercial",
|
||||||
"Added observation": "Added observation",
|
"Added observation": "Added observation",
|
||||||
"Comment added to client": "Comment added to client"
|
"Comment added to client": "Comment added to client",
|
||||||
|
"This ticket is already a refund": "This ticket is already a refund"
|
||||||
}
|
}
|
|
@ -275,7 +275,19 @@
|
||||||
"Collection does not exist": "La colección no existe",
|
"Collection does not exist": "La colección no existe",
|
||||||
"Cannot obtain exclusive lock": "No se puede obtener un bloqueo exclusivo",
|
"Cannot obtain exclusive lock": "No se puede obtener un bloqueo exclusivo",
|
||||||
"Insert a date range": "Inserte un rango de fechas",
|
"Insert a date range": "Inserte un rango de fechas",
|
||||||
"Added observation": "{{user}} añadió esta observacion: {{text}}",
|
"Added observation": "{{user}} añadió esta observacion: {{text}}",
|
||||||
"Comment added to client": "Observación añadida al cliente {{clientFk}}",
|
"Comment added to client": "Observación añadida al cliente {{clientFk}}",
|
||||||
"Cannot create a new claimBeginning from a different ticket": "No se puede crear una línea de reclamación de un ticket diferente al origen"
|
"Cannot create a new claimBeginning from a different ticket": "No se puede crear una línea de reclamación de un ticket diferente al origen",
|
||||||
|
"company": "Compañía",
|
||||||
|
"country": "País",
|
||||||
|
"clientId": "Id cliente",
|
||||||
|
"clientSocialName": "Cliente",
|
||||||
|
"amount": "Importe",
|
||||||
|
"taxableBase": "Base",
|
||||||
|
"ticketFk": "Id ticket",
|
||||||
|
"isActive": "Activo",
|
||||||
|
"hasToInvoice": "Facturar",
|
||||||
|
"isTaxDataChecked": "Datos comprobados",
|
||||||
|
"comercialId": "Id comercial",
|
||||||
|
"comercialName": "Comercial"
|
||||||
}
|
}
|
||||||
|
|
|
@ -6,6 +6,7 @@
|
||||||
</vn-crud-model>
|
</vn-crud-model>
|
||||||
<vn-portal slot="topbar">
|
<vn-portal slot="topbar">
|
||||||
<vn-searchbar
|
<vn-searchbar
|
||||||
|
vn-focus
|
||||||
panel="vn-user-search-panel"
|
panel="vn-user-search-panel"
|
||||||
info="Search user by id, name or nickname"
|
info="Search user by id, name or nickname"
|
||||||
model="model"
|
model="model"
|
||||||
|
|
|
@ -109,6 +109,11 @@ module.exports = Self => {
|
||||||
zoneFk: zone.id
|
zoneFk: zone.id
|
||||||
}, myOptions);
|
}, myOptions);
|
||||||
|
|
||||||
|
await models.TicketRefund.create({
|
||||||
|
refundTicketFk: newRefundTicket.id,
|
||||||
|
originalTicketFk: claim.ticket().id
|
||||||
|
}, myOptions);
|
||||||
|
|
||||||
await saveObservation({
|
await saveObservation({
|
||||||
description: `Reclama ticket: ${claim.ticketFk}`,
|
description: `Reclama ticket: ${claim.ticketFk}`,
|
||||||
ticketFk: newRefundTicket.id,
|
ticketFk: newRefundTicket.id,
|
||||||
|
|
|
@ -16,7 +16,7 @@
|
||||||
value="{{$ctrl.claimedTotal | currency: 'EUR':2}}">
|
value="{{$ctrl.claimedTotal | currency: 'EUR':2}}">
|
||||||
</vn-label-value>
|
</vn-label-value>
|
||||||
</vn-card>
|
</vn-card>
|
||||||
<vn-card class="vn-pa-lg vn-w-lg">
|
<vn-card class="vn-pa-md vn-w-lg">
|
||||||
<smart-table
|
<smart-table
|
||||||
model="model"
|
model="model"
|
||||||
options="$ctrl.smartTableOptions"
|
options="$ctrl.smartTableOptions"
|
||||||
|
@ -31,10 +31,10 @@
|
||||||
translate-attr="{title: 'Imports claim details'}">
|
translate-attr="{title: 'Imports claim details'}">
|
||||||
</vn-button>
|
</vn-button>
|
||||||
<vn-button
|
<vn-button
|
||||||
label="Change destination"
|
label="Change destination"
|
||||||
disabled="$ctrl.checked.length == 0"
|
disabled="$ctrl.checked.length == 0"
|
||||||
ng-click="changeDestination.show()">
|
ng-click="changeDestination.show()">
|
||||||
</vn-button>
|
</vn-button>
|
||||||
<vn-range
|
<vn-range
|
||||||
label="Responsability"
|
label="Responsability"
|
||||||
min-label="Company"
|
min-label="Company"
|
||||||
|
@ -45,15 +45,15 @@
|
||||||
step="1"
|
step="1"
|
||||||
on-change="$ctrl.save({responsibility: value})">
|
on-change="$ctrl.save({responsibility: value})">
|
||||||
</vn-range>
|
</vn-range>
|
||||||
|
<vn-check class="right"
|
||||||
|
vn-one
|
||||||
|
label="Is paid with mana"
|
||||||
|
ng-model="$ctrl.claim.isChargedToMana"
|
||||||
|
on-change="$ctrl.save({isChargedToMana: value})">
|
||||||
|
</vn-check>
|
||||||
</vn-tool-bar>
|
</vn-tool-bar>
|
||||||
<vn-check class="right"
|
|
||||||
vn-one
|
|
||||||
label="Is paid with mana"
|
|
||||||
ng-model="$ctrl.claim.isChargedToMana"
|
|
||||||
on-change="$ctrl.save({isChargedToMana: value})">
|
|
||||||
</vn-check>
|
|
||||||
</section>
|
</section>
|
||||||
</slot-actions>
|
</slot-actions>
|
||||||
<slot-table>
|
<slot-table>
|
||||||
<table model="model">
|
<table model="model">
|
||||||
<thead>
|
<thead>
|
||||||
|
|
|
@ -17,6 +17,10 @@ class Controller extends Descriptor {
|
||||||
}
|
}
|
||||||
|
|
||||||
sendPickupOrder() {
|
sendPickupOrder() {
|
||||||
|
if (!this.claim.client.email) {
|
||||||
|
this.vnApp.showError(this.$t('The client does not have an email'));
|
||||||
|
return;
|
||||||
|
}
|
||||||
return this.vnEmail.send(`Claims/${this.claim.id}/claim-pickup-email`, {
|
return this.vnEmail.send(`Claims/${this.claim.id}/claim-pickup-email`, {
|
||||||
recipient: this.claim.client.email,
|
recipient: this.claim.client.email,
|
||||||
recipientId: this.claim.clientFk
|
recipientId: this.claim.clientFk
|
||||||
|
|
|
@ -20,3 +20,4 @@ Photos: Fotos
|
||||||
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
|
||||||
|
The client does not have an email: El cliente no tiene email
|
||||||
|
|
|
@ -1,48 +1 @@
|
||||||
<vn-crud-model
|
|
||||||
vn-id="model"
|
|
||||||
auto-load="true"
|
|
||||||
filter="::$ctrl.filter"
|
|
||||||
url="ClaimDms"
|
|
||||||
link="{claimFk: $ctrl.$params.id}"
|
|
||||||
limit="20"
|
|
||||||
data="$ctrl.photos">
|
|
||||||
</vn-crud-model>
|
|
||||||
<vn-horizontal class="photo-list drop-zone" vn-droppable="$ctrl.onDrop($event)">
|
|
||||||
<section class="empty-rows" ng-if="!$ctrl.photos.length">
|
|
||||||
<section><vn-icon icon="image"></vn-icon></section>
|
|
||||||
<section translate>Drag & Drop photos here...</section>
|
|
||||||
</section>
|
|
||||||
<section class="photo" ng-repeat="photo in $ctrl.photos">
|
|
||||||
<section class="image vn-shadow" on-error-src
|
|
||||||
ng-style="{'background': 'url(' + $ctrl.getImagePath(photo.dmsFk) + ')'}"
|
|
||||||
zoom-image="{{$ctrl.getImagePath(photo.dmsFk)}}"
|
|
||||||
ng-if="photo.dms.contentType != 'video/mp4'">
|
|
||||||
</section>
|
|
||||||
<video id="videobcg" muted="muted" controls ng-if="photo.dms.contentType == 'video/mp4'"
|
|
||||||
class="video">
|
|
||||||
<source src="{{$ctrl.getImagePath(photo.dmsFk)}}" type="video/mp4">
|
|
||||||
</video>
|
|
||||||
<section class="actions">
|
|
||||||
<vn-button
|
|
||||||
class="round"
|
|
||||||
ng-click="confirm.show($index)"
|
|
||||||
title="{{'Remove file' | translate}}"
|
|
||||||
tabindex="-1"
|
|
||||||
icon="delete">
|
|
||||||
</vn-button>
|
|
||||||
</section>
|
|
||||||
</section>
|
|
||||||
</vn-horizontal>
|
|
||||||
<vn-confirm
|
|
||||||
vn-id="confirm"
|
|
||||||
message="This file will be deleted"
|
|
||||||
question="Are you sure you want to continue?"
|
|
||||||
on-accept="$ctrl.deleteDms($data)">
|
|
||||||
</vn-confirm>
|
|
||||||
<vn-float-button
|
|
||||||
icon="add"
|
|
||||||
vn-tooltip="Select file"
|
|
||||||
vn-bind="+"
|
|
||||||
ng-click="$ctrl.openUploadDialog()"
|
|
||||||
fixed-bottom-right>
|
|
||||||
</vn-float-button>
|
|
||||||
|
|
|
@ -1,105 +1,17 @@
|
||||||
import ngModule from '../module';
|
import ngModule from '../module';
|
||||||
import Section from 'salix/components/section';
|
import Section from 'salix/components/section';
|
||||||
import './style.scss';
|
|
||||||
|
|
||||||
class Controller extends Section {
|
class Controller extends Section {
|
||||||
constructor($element, $, vnFile) {
|
constructor($element, $) {
|
||||||
super($element, $);
|
super($element, $);
|
||||||
this.vnFile = vnFile;
|
|
||||||
this.filter = {
|
|
||||||
include: [
|
|
||||||
{
|
|
||||||
relation: 'dms'
|
|
||||||
}
|
|
||||||
]
|
|
||||||
};
|
|
||||||
}
|
}
|
||||||
|
|
||||||
deleteDms(index) {
|
async $onInit() {
|
||||||
const dmsFk = this.photos[index].dmsFk;
|
const url = await this.vnApp.getUrl(`claim/${this.$params.id}/photos`);
|
||||||
return this.$http.post(`ClaimDms/${dmsFk}/removeFile`)
|
window.location.href = url;
|
||||||
.then(() => {
|
|
||||||
this.$.model.remove(index);
|
|
||||||
this.vnApp.showSuccess(this.$t('File deleted'));
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
onDrop($event) {
|
|
||||||
const files = $event.dataTransfer.files;
|
|
||||||
this.setDefaultParams().then(() => {
|
|
||||||
this.dms.files = files;
|
|
||||||
this.create();
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
setDefaultParams() {
|
|
||||||
const filter = {
|
|
||||||
where: {code: 'claim'}
|
|
||||||
};
|
|
||||||
return this.$http.get('DmsTypes/findOne', {filter}).then(res => {
|
|
||||||
const dmsTypeId = res.data && res.data.id;
|
|
||||||
const companyId = this.vnConfig.companyFk;
|
|
||||||
const warehouseId = this.vnConfig.warehouseFk;
|
|
||||||
this.dms = {
|
|
||||||
hasFile: false,
|
|
||||||
hasFileAttached: false,
|
|
||||||
reference: this.claim.id,
|
|
||||||
warehouseId: warehouseId,
|
|
||||||
companyId: companyId,
|
|
||||||
dmsTypeId: dmsTypeId,
|
|
||||||
description: this.$t('FileDescription', {
|
|
||||||
claimId: this.claim.id,
|
|
||||||
clientId: this.claim.client.id,
|
|
||||||
clientName: this.claim.client.name
|
|
||||||
}).toUpperCase()
|
|
||||||
};
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
openUploadDialog() {
|
|
||||||
const element = document.createElement('input');
|
|
||||||
element.setAttribute('type', 'file');
|
|
||||||
element.setAttribute('multiple', true);
|
|
||||||
element.click();
|
|
||||||
|
|
||||||
element.addEventListener('change', () =>
|
|
||||||
this.setDefaultParams().then(() => {
|
|
||||||
this.dms.files = element.files;
|
|
||||||
this.create();
|
|
||||||
})
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
create() {
|
|
||||||
const query = `claims/${this.claim.id}/uploadFile`;
|
|
||||||
const options = {
|
|
||||||
method: 'POST',
|
|
||||||
url: query,
|
|
||||||
params: this.dms,
|
|
||||||
headers: {'Content-Type': undefined},
|
|
||||||
transformRequest: files => {
|
|
||||||
const formData = new FormData();
|
|
||||||
|
|
||||||
for (let i = 0; i < files.length; i++)
|
|
||||||
formData.append(files[i].name, files[i]);
|
|
||||||
|
|
||||||
return formData;
|
|
||||||
},
|
|
||||||
data: this.dms.files
|
|
||||||
};
|
|
||||||
this.$http(options).then(() => {
|
|
||||||
this.vnApp.showSuccess(this.$t('File uploaded!'));
|
|
||||||
this.$.model.refresh();
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
getImagePath(dmsId) {
|
|
||||||
return this.vnFile.getPath(`/api/Claims/${dmsId}/downloadFile`);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Controller.$inject = ['$element', '$scope', 'vnFile'];
|
|
||||||
|
|
||||||
ngModule.vnComponent('vnClaimPhotos', {
|
ngModule.vnComponent('vnClaimPhotos', {
|
||||||
template: require('./index.html'),
|
template: require('./index.html'),
|
||||||
controller: Controller,
|
controller: Controller,
|
||||||
|
|
|
@ -1,70 +0,0 @@
|
||||||
import './index';
|
|
||||||
import crudModel from 'core/mocks/crud-model';
|
|
||||||
|
|
||||||
describe('Claim', () => {
|
|
||||||
describe('Component vnClaimPhotos', () => {
|
|
||||||
let $scope;
|
|
||||||
let $httpBackend;
|
|
||||||
let controller;
|
|
||||||
|
|
||||||
beforeEach(ngModule('claim'));
|
|
||||||
|
|
||||||
beforeEach(inject(($componentController, $rootScope, _$httpBackend_) => {
|
|
||||||
$httpBackend = _$httpBackend_;
|
|
||||||
$scope = $rootScope.$new();
|
|
||||||
controller = $componentController('vnClaimPhotos', {$element: null, $scope});
|
|
||||||
controller.$.model = crudModel;
|
|
||||||
controller.claim = {
|
|
||||||
id: 1,
|
|
||||||
client: {id: 1101, name: 'Bruce Wayne'}
|
|
||||||
};
|
|
||||||
}));
|
|
||||||
|
|
||||||
describe('deleteDms()', () => {
|
|
||||||
it('should make an HTTP Post query', () => {
|
|
||||||
jest.spyOn(controller.vnApp, 'showSuccess');
|
|
||||||
jest.spyOn(controller.$.model, 'remove');
|
|
||||||
|
|
||||||
const dmsId = 1;
|
|
||||||
const dmsIndex = 0;
|
|
||||||
controller.photos = [{dmsFk: 1}];
|
|
||||||
|
|
||||||
$httpBackend.expectPOST(`ClaimDms/${dmsId}/removeFile`).respond();
|
|
||||||
controller.deleteDms(dmsIndex);
|
|
||||||
$httpBackend.flush();
|
|
||||||
|
|
||||||
expect(controller.$.model.remove).toHaveBeenCalledWith(dmsIndex);
|
|
||||||
expect(controller.vnApp.showSuccess).toHaveBeenCalled();
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
describe('setDefaultParams()', () => {
|
|
||||||
it('should make an HTTP GET query, then set all dms properties', () => {
|
|
||||||
$httpBackend.expectRoute('GET', `DmsTypes/findOne`).respond({});
|
|
||||||
controller.setDefaultParams();
|
|
||||||
$httpBackend.flush();
|
|
||||||
|
|
||||||
expect(controller.dms).toBeDefined();
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
describe('create()', () => {
|
|
||||||
it('should make an HTTP Post query, then refresh the model data', () => {
|
|
||||||
const claimId = 1;
|
|
||||||
const dmsIndex = 0;
|
|
||||||
jest.spyOn(controller.vnApp, 'showSuccess');
|
|
||||||
jest.spyOn(controller.$.model, 'refresh');
|
|
||||||
controller.photos = [{dmsFk: 1}];
|
|
||||||
controller.dmsIndex = dmsIndex;
|
|
||||||
controller.dms = {files: []};
|
|
||||||
|
|
||||||
$httpBackend.expectPOST(`claims/${claimId}/uploadFile`).respond({});
|
|
||||||
controller.create();
|
|
||||||
$httpBackend.flush();
|
|
||||||
|
|
||||||
expect(controller.$.model.refresh).toHaveBeenCalled();
|
|
||||||
expect(controller.vnApp.showSuccess).toHaveBeenCalled();
|
|
||||||
});
|
|
||||||
});
|
|
||||||
});
|
|
||||||
});
|
|
|
@ -1,5 +0,0 @@
|
||||||
Are you sure you want to continue?: ¿Seguro que quieres continuar?
|
|
||||||
Drag & Drop photos here...: Arrastra y suelta fotos aquí...
|
|
||||||
File deleted: Archivo eliminado
|
|
||||||
File uploaded!: Archivo subido!
|
|
||||||
Select file: Seleccionar fichero
|
|
|
@ -1,47 +0,0 @@
|
||||||
@import "./variables";
|
|
||||||
|
|
||||||
vn-claim-photos {
|
|
||||||
height: 100%;
|
|
||||||
|
|
||||||
.drop-zone {
|
|
||||||
color: $color-font-secondary;
|
|
||||||
box-sizing: border-box;
|
|
||||||
border-radius: 8px;
|
|
||||||
text-align: center;
|
|
||||||
min-height: 100%;
|
|
||||||
|
|
||||||
.empty-rows {
|
|
||||||
padding: 80px $spacing-md;
|
|
||||||
font-size: 1.375rem
|
|
||||||
}
|
|
||||||
|
|
||||||
vn-icon {
|
|
||||||
font-size: 3rem
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.photo-list {
|
|
||||||
padding: $spacing-md;
|
|
||||||
min-height: 100%;
|
|
||||||
|
|
||||||
.photo {
|
|
||||||
width: 512px;
|
|
||||||
height: 288px;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.video {
|
|
||||||
width: 100%;
|
|
||||||
height: 100%;
|
|
||||||
object-fit: cover;
|
|
||||||
cursor: pointer;
|
|
||||||
box-shadow: 0 2px 2px 0 rgba(0,0,0,.14),
|
|
||||||
0 3px 1px -2px rgba(0,0,0,.2),
|
|
||||||
0 1px 5px 0 rgba(0,0,0,.12);
|
|
||||||
border: 2px solid transparent;
|
|
||||||
|
|
||||||
}
|
|
||||||
.video:hover {
|
|
||||||
border: 2px solid $color-primary
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -76,7 +76,7 @@ module.exports = function(Self) {
|
||||||
|
|
||||||
const date = Date.vnNew();
|
const date = Date.vnNew();
|
||||||
date.setHours(0, 0, 0, 0);
|
date.setHours(0, 0, 0, 0);
|
||||||
const query = `SELECT vn.clientGetDebt(?, ?) AS debt`;
|
const query = `SELECT vn.client_getDebt(?, ?) AS debt`;
|
||||||
const data = await Self.rawSql(query, [id, date], myOptions);
|
const data = await Self.rawSql(query, [id, date], myOptions);
|
||||||
|
|
||||||
client.debt = data[0].debt;
|
client.debt = data[0].debt;
|
||||||
|
|
|
@ -27,7 +27,7 @@ module.exports = Self => {
|
||||||
|
|
||||||
const date = Date.vnNew();
|
const date = Date.vnNew();
|
||||||
date.setHours(0, 0, 0, 0);
|
date.setHours(0, 0, 0, 0);
|
||||||
const query = `SELECT vn.clientGetDebt(?, ?) AS debt`;
|
const query = `SELECT vn.client_getDebt(?, ?) AS debt`;
|
||||||
const [debt] = await Self.rawSql(query, [clientFk, date], myOptions);
|
const [debt] = await Self.rawSql(query, [clientFk, date], myOptions);
|
||||||
|
|
||||||
return debt;
|
return debt;
|
||||||
|
|
|
@ -0,0 +1,57 @@
|
||||||
|
const {Email} = require('vn-print');
|
||||||
|
|
||||||
|
module.exports = Self => {
|
||||||
|
Self.remoteMethodCtx('receiptEmail', {
|
||||||
|
description: 'Returns the receipt pdf',
|
||||||
|
accepts: [
|
||||||
|
{
|
||||||
|
arg: 'id',
|
||||||
|
type: 'number',
|
||||||
|
required: true,
|
||||||
|
description: 'The claim id',
|
||||||
|
http: {source: 'path'}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
arg: 'recipient',
|
||||||
|
type: 'string',
|
||||||
|
description: 'The recipient email',
|
||||||
|
required: true,
|
||||||
|
}
|
||||||
|
],
|
||||||
|
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/receipt-email',
|
||||||
|
verb: 'POST'
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
Self.receiptEmail = async(ctx, id) => {
|
||||||
|
const args = Object.assign({}, ctx.args);
|
||||||
|
const params = {
|
||||||
|
recipient: args.recipient,
|
||||||
|
lang: ctx.req.getLocale()
|
||||||
|
};
|
||||||
|
|
||||||
|
delete args.ctx;
|
||||||
|
for (const param in args)
|
||||||
|
params[param] = args[param];
|
||||||
|
|
||||||
|
const email = new Email('receipt', params);
|
||||||
|
|
||||||
|
return email.send();
|
||||||
|
};
|
||||||
|
};
|
|
@ -1,12 +1,12 @@
|
||||||
module.exports = Self => {
|
module.exports = Self => {
|
||||||
Self.remoteMethodCtx('receiptPdf', {
|
Self.remoteMethodCtx('receiptPdf', {
|
||||||
description: 'Returns the receipt pdf',
|
description: 'Send the receipt pdf to client',
|
||||||
accepts: [
|
accepts: [
|
||||||
{
|
{
|
||||||
arg: 'id',
|
arg: 'id',
|
||||||
type: 'number',
|
type: 'number',
|
||||||
required: true,
|
required: true,
|
||||||
description: 'The claim id',
|
description: 'The receipt id',
|
||||||
http: {source: 'path'}
|
http: {source: 'path'}
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
|
|
@ -5,6 +5,7 @@ module.exports = function(Self) {
|
||||||
require('../methods/receipt/balanceCompensationEmail')(Self);
|
require('../methods/receipt/balanceCompensationEmail')(Self);
|
||||||
require('../methods/receipt/balanceCompensationPdf')(Self);
|
require('../methods/receipt/balanceCompensationPdf')(Self);
|
||||||
require('../methods/receipt/receiptPdf')(Self);
|
require('../methods/receipt/receiptPdf')(Self);
|
||||||
|
require('../methods/receipt/receiptEmail')(Self);
|
||||||
|
|
||||||
Self.validateBinded('amountPaid', isNotZero, {
|
Self.validateBinded('amountPaid', isNotZero, {
|
||||||
message: 'Amount cannot be zero',
|
message: 'Amount cannot be zero',
|
||||||
|
|
|
@ -84,6 +84,10 @@
|
||||||
label="View receipt"
|
label="View receipt"
|
||||||
ng-model="$ctrl.viewReceipt">
|
ng-model="$ctrl.viewReceipt">
|
||||||
</vn-check>
|
</vn-check>
|
||||||
|
<vn-check
|
||||||
|
label="Send email"
|
||||||
|
ng-model="$ctrl.sendEmail">
|
||||||
|
</vn-check>
|
||||||
</vn-horizontal>
|
</vn-horizontal>
|
||||||
</tpl-body>
|
</tpl-body>
|
||||||
<tpl-buttons>
|
<tpl-buttons>
|
||||||
|
|
|
@ -2,9 +2,10 @@ import ngModule from '../../module';
|
||||||
import Dialog from 'core/components/dialog';
|
import Dialog from 'core/components/dialog';
|
||||||
|
|
||||||
class Controller extends Dialog {
|
class Controller extends Dialog {
|
||||||
constructor($element, $, $transclude, vnReport) {
|
constructor($element, $, $transclude, vnReport, vnEmail) {
|
||||||
super($element, $, $transclude);
|
super($element, $, $transclude);
|
||||||
this.vnReport = vnReport;
|
this.vnReport = vnReport;
|
||||||
|
this.vnEmail = vnEmail;
|
||||||
this.receipt = {};
|
this.receipt = {};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -23,6 +24,18 @@ class Controller extends Dialog {
|
||||||
|
|
||||||
set clientFk(value) {
|
set clientFk(value) {
|
||||||
this.receipt.clientFk = value;
|
this.receipt.clientFk = value;
|
||||||
|
|
||||||
|
const filter = {
|
||||||
|
fields: ['email'],
|
||||||
|
where: {
|
||||||
|
id: value
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
this.$http.get(`Clients/findOne`, {filter})
|
||||||
|
.then(res => {
|
||||||
|
this.receipt.email = res.data.email;
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
get clientFk() {
|
get clientFk() {
|
||||||
|
@ -154,10 +167,13 @@ class Controller extends Dialog {
|
||||||
return super.responseHandler(response);
|
return super.responseHandler(response);
|
||||||
|
|
||||||
const exceededAmount = this.receipt.amountPaid > this.maxAmount;
|
const exceededAmount = this.receipt.amountPaid > this.maxAmount;
|
||||||
|
const isCash = this.bankSelection.accountingType.code == 'cash';
|
||||||
if (this.bankSelection.accountingType.code == 'cash' && exceededAmount)
|
if (isCash && exceededAmount)
|
||||||
return this.vnApp.showError(this.$t('Amount exceeded', {maxAmount: this.maxAmount}));
|
return this.vnApp.showError(this.$t('Amount exceeded', {maxAmount: this.maxAmount}));
|
||||||
|
|
||||||
|
if (isCash && this.sendEmail && !this.receipt.email)
|
||||||
|
return this.vnApp.showError(this.$t('There is no assigned email for this client'));
|
||||||
|
|
||||||
let receiptId;
|
let receiptId;
|
||||||
return this.$http.post(`Clients/${this.clientFk}/createReceipt`, this.receipt)
|
return this.$http.post(`Clients/${this.clientFk}/createReceipt`, this.receipt)
|
||||||
.then(res => {
|
.then(res => {
|
||||||
|
@ -165,6 +181,13 @@ class Controller extends Dialog {
|
||||||
super.responseHandler(response);
|
super.responseHandler(response);
|
||||||
})
|
})
|
||||||
.then(() => this.vnApp.showSuccess(this.$t('Data saved!')))
|
.then(() => this.vnApp.showSuccess(this.$t('Data saved!')))
|
||||||
|
.then(() => {
|
||||||
|
if (!this.sendEmail || !isCash) return;
|
||||||
|
const params = {
|
||||||
|
recipient: this.receipt.email
|
||||||
|
};
|
||||||
|
this.vnEmail.send(`Receipts/${receiptId}/receipt-email`, params);
|
||||||
|
})
|
||||||
.then(() => {
|
.then(() => {
|
||||||
if (this.viewReceipt)
|
if (this.viewReceipt)
|
||||||
this.vnReport.show(`Receipts/${receiptId}/receipt-pdf`);
|
this.vnReport.show(`Receipts/${receiptId}/receipt-pdf`);
|
||||||
|
@ -178,7 +201,7 @@ class Controller extends Dialog {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Controller.$inject = ['$element', '$scope', '$transclude', 'vnReport'];
|
Controller.$inject = ['$element', '$scope', '$transclude', 'vnReport', 'vnEmail'];
|
||||||
|
|
||||||
ngModule.vnComponent('vnClientBalanceCreate', {
|
ngModule.vnComponent('vnClientBalanceCreate', {
|
||||||
slotTemplate: require('./index.html'),
|
slotTemplate: require('./index.html'),
|
||||||
|
|
|
@ -192,19 +192,19 @@
|
||||||
{{::buy.entryFk}}
|
{{::buy.entryFk}}
|
||||||
</span>
|
</span>
|
||||||
</td>
|
</td>
|
||||||
<td number>{{::buy.buyingValue | currency: 'EUR':2}}</td>
|
<td number>{{::buy.buyingValue | currency: 'EUR':3}}</td>
|
||||||
<td number>{{::buy.freightValue | currency: 'EUR':2}}</td>
|
<td number>{{::buy.freightValue | currency: 'EUR':3}}</td>
|
||||||
<td number>{{::buy.comissionValue | currency: 'EUR':2}}</td>
|
<td number>{{::buy.comissionValue | currency: 'EUR':3}}</td>
|
||||||
<td number>{{::buy.packageValue | currency: 'EUR':2}}</td>
|
<td number>{{::buy.packageValue | currency: 'EUR':3}}</td>
|
||||||
<td>
|
<td>
|
||||||
<vn-check
|
<vn-check
|
||||||
disabled="true"
|
disabled="true"
|
||||||
ng-model="::buy.isIgnored">
|
ng-model="::buy.isIgnored">
|
||||||
</vn-check>
|
</vn-check>
|
||||||
</td>
|
</td>
|
||||||
<td number>{{::buy.price2 | currency: 'EUR':2}}</td>
|
<td number>{{::buy.price2 | currency: 'EUR':3}}</td>
|
||||||
<td number>{{::buy.price3 | currency: 'EUR':2}}</td>
|
<td number>{{::buy.price3 | currency: 'EUR':3}}</td>
|
||||||
<td number>{{::buy.minPrice | currency: 'EUR':2}}</td>
|
<td number>{{::buy.minPrice | currency: 'EUR':3}}</td>
|
||||||
<td>{{::buy.ektFk | dashIfEmpty}}</td>
|
<td>{{::buy.ektFk | dashIfEmpty}}</td>
|
||||||
<td>{{::buy.weight}}</td>
|
<td>{{::buy.weight}}</td>
|
||||||
<td>{{::buy.packageFk}}</td>
|
<td>{{::buy.packageFk}}</td>
|
||||||
|
|
|
@ -1,53 +0,0 @@
|
||||||
const {toCSV} = require('vn-loopback/util/csv');
|
|
||||||
|
|
||||||
module.exports = Self => {
|
|
||||||
Self.remoteMethodCtx('negativeBasesCsv', {
|
|
||||||
description: 'Returns the negative bases as .csv',
|
|
||||||
accessType: 'READ',
|
|
||||||
accepts: [{
|
|
||||||
arg: 'negativeBases',
|
|
||||||
type: ['object'],
|
|
||||||
required: true
|
|
||||||
},
|
|
||||||
{
|
|
||||||
arg: 'from',
|
|
||||||
type: 'date',
|
|
||||||
description: 'From date'
|
|
||||||
},
|
|
||||||
{
|
|
||||||
arg: 'to',
|
|
||||||
type: 'date',
|
|
||||||
description: 'To date'
|
|
||||||
}],
|
|
||||||
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: '/negativeBasesCsv',
|
|
||||||
verb: 'GET'
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
Self.negativeBasesCsv = async ctx => {
|
|
||||||
const args = ctx.args;
|
|
||||||
const content = toCSV(args.negativeBases);
|
|
||||||
|
|
||||||
return [
|
|
||||||
content,
|
|
||||||
'text/csv',
|
|
||||||
`attachment; filename="negative-bases-${new Date(args.from).toLocaleDateString()}-${new Date(args.to).toLocaleDateString()}.csv"`
|
|
||||||
];
|
|
||||||
};
|
|
||||||
};
|
|
|
@ -7,6 +7,4 @@ module.exports = Self => {
|
||||||
require('../methods/invoice-in/invoiceInPdf')(Self);
|
require('../methods/invoice-in/invoiceInPdf')(Self);
|
||||||
require('../methods/invoice-in/invoiceInEmail')(Self);
|
require('../methods/invoice-in/invoiceInEmail')(Self);
|
||||||
require('../methods/invoice-in/getSerial')(Self);
|
require('../methods/invoice-in/getSerial')(Self);
|
||||||
require('../methods/invoice-in/negativeBases')(Self);
|
|
||||||
require('../methods/invoice-in/negativeBasesCsv')(Self);
|
|
||||||
};
|
};
|
||||||
|
|
|
@ -15,4 +15,3 @@ import './create';
|
||||||
import './log';
|
import './log';
|
||||||
import './serial';
|
import './serial';
|
||||||
import './serial-search-panel';
|
import './serial-search-panel';
|
||||||
import './negative-bases';
|
|
||||||
|
|
|
@ -1,14 +0,0 @@
|
||||||
Has To Invoice: Facturar
|
|
||||||
Download as CSV: Descargar como CSV
|
|
||||||
company: Compañía
|
|
||||||
country: País
|
|
||||||
clientId: Id Cliente
|
|
||||||
clientSocialName: Cliente
|
|
||||||
amount: Importe
|
|
||||||
taxableBase: Base
|
|
||||||
ticketFk: Id Ticket
|
|
||||||
isActive: Activo
|
|
||||||
hasToInvoice: Facturar
|
|
||||||
isTaxDataChecked: Datos comprobados
|
|
||||||
comercialId: Id Comercial
|
|
||||||
comercialName: Comercial
|
|
|
@ -10,8 +10,7 @@
|
||||||
"menus": {
|
"menus": {
|
||||||
"main": [
|
"main": [
|
||||||
{ "state": "invoiceIn.index", "icon": "icon-invoice-in"},
|
{ "state": "invoiceIn.index", "icon": "icon-invoice-in"},
|
||||||
{ "state": "invoiceIn.serial", "icon": "icon-invoice-in"},
|
{ "state": "invoiceIn.serial", "icon": "icon-invoice-in"}
|
||||||
{ "state": "invoiceIn.negative-bases", "icon": "icon-ticket"}
|
|
||||||
],
|
],
|
||||||
"card": [
|
"card": [
|
||||||
{
|
{
|
||||||
|
@ -53,15 +52,6 @@
|
||||||
"administrative"
|
"administrative"
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
{
|
|
||||||
"url": "/negative-bases",
|
|
||||||
"state": "invoiceIn.negative-bases",
|
|
||||||
"component": "vn-negative-bases",
|
|
||||||
"description": "Negative bases",
|
|
||||||
"acl": [
|
|
||||||
"administrative"
|
|
||||||
]
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
"url": "/serial",
|
"url": "/serial",
|
||||||
"state": "invoiceIn.serial",
|
"state": "invoiceIn.serial",
|
||||||
|
|
|
@ -96,16 +96,18 @@ module.exports = Self => {
|
||||||
SELECT f.*
|
SELECT f.*
|
||||||
FROM tmp.filter f`);
|
FROM tmp.filter f`);
|
||||||
|
|
||||||
stmt.merge(conn.makeWhere(args.filter.where));
|
if (args.filter) {
|
||||||
stmt.merge(conn.makeOrderBy(args.filter.order));
|
stmt.merge(conn.makeWhere(args.filter.where));
|
||||||
stmt.merge(conn.makeLimit(args.filter));
|
stmt.merge(conn.makeOrderBy(args.filter.order));
|
||||||
|
stmt.merge(conn.makeLimit(args.filter));
|
||||||
|
}
|
||||||
|
|
||||||
const negativeBasesIndex = stmts.push(stmt) - 1;
|
const negativeBasesIndex = stmts.push(stmt) - 1;
|
||||||
|
|
||||||
stmts.push(`DROP TEMPORARY TABLE tmp.filter, tmp.ticket, tmp.ticketTax, tmp.ticketAmount`);
|
stmts.push(`DROP TEMPORARY TABLE tmp.filter, tmp.ticket, tmp.ticketTax, tmp.ticketAmount`);
|
||||||
|
|
||||||
const sql = ParameterizedSQL.join(stmts, ';');
|
const sql = ParameterizedSQL.join(stmts, ';');
|
||||||
const result = await conn.executeStmt(sql, myOptions);
|
const result = await conn.executeStmt(sql);
|
||||||
|
|
||||||
return negativeBasesIndex === 0 ? result : result[negativeBasesIndex];
|
return negativeBasesIndex === 0 ? result : result[negativeBasesIndex];
|
||||||
};
|
};
|
|
@ -0,0 +1,68 @@
|
||||||
|
const {toCSV} = require('vn-loopback/util/csv');
|
||||||
|
|
||||||
|
module.exports = Self => {
|
||||||
|
Self.remoteMethodCtx('negativeBasesCsv', {
|
||||||
|
description: 'Returns the negative bases as .csv',
|
||||||
|
accessType: 'READ',
|
||||||
|
accepts: [
|
||||||
|
{
|
||||||
|
arg: 'from',
|
||||||
|
type: 'date',
|
||||||
|
description: 'From date',
|
||||||
|
required: true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
arg: 'to',
|
||||||
|
type: 'date',
|
||||||
|
description: 'To date',
|
||||||
|
required: true
|
||||||
|
}],
|
||||||
|
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: '/negativeBasesCsv',
|
||||||
|
verb: 'GET'
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
Self.negativeBasesCsv = async(ctx, options) => {
|
||||||
|
const $t = ctx.req.__; // $translate
|
||||||
|
const args = ctx.args;
|
||||||
|
const myOptions = {};
|
||||||
|
|
||||||
|
if (typeof options == 'object')
|
||||||
|
Object.assign(myOptions, options);
|
||||||
|
|
||||||
|
const negativeBases = await Self.app.models.InvoiceOut.negativeBases(ctx, myOptions);
|
||||||
|
const locatedFields = [];
|
||||||
|
negativeBases.forEach(element => {
|
||||||
|
locatedFields.push(Object.keys(element).map(key => {
|
||||||
|
return {newName: $t(key), value: element[key]};
|
||||||
|
}).filter(item => item !== null)
|
||||||
|
.reduce((result, item) => {
|
||||||
|
result[item.newName] = item.value;
|
||||||
|
return result;
|
||||||
|
}, {}));
|
||||||
|
});
|
||||||
|
const content = toCSV(locatedFields);
|
||||||
|
|
||||||
|
return [
|
||||||
|
content,
|
||||||
|
'text/csv',
|
||||||
|
`attachment; filename="negative-bases-${new Date(args.from).toLocaleDateString()}-${new Date(args.to).toLocaleDateString()}.csv"`
|
||||||
|
];
|
||||||
|
};
|
||||||
|
};
|
|
@ -35,7 +35,7 @@ module.exports = Self => {
|
||||||
const tickets = await models.Ticket.find(filter, myOptions);
|
const tickets = await models.Ticket.find(filter, myOptions);
|
||||||
|
|
||||||
const ticketsIds = tickets.map(ticket => ticket.id);
|
const ticketsIds = tickets.map(ticket => ticket.id);
|
||||||
const refundedTickets = await models.Ticket.refund(ticketsIds, true, myOptions);
|
const refundedTickets = await models.Ticket.refund(ticketsIds, myOptions);
|
||||||
|
|
||||||
if (tx) await tx.commit();
|
if (tx) await tx.commit();
|
||||||
|
|
||||||
|
|
|
@ -1,19 +1,18 @@
|
||||||
const models = require('vn-loopback/server/server').models;
|
const models = require('vn-loopback/server/server').models;
|
||||||
|
|
||||||
describe('invoiceIn negativeBases()', () => {
|
describe('invoiceOut negativeBases()', () => {
|
||||||
it('should return all negative bases in a date range', async() => {
|
it('should return all negative bases in a date range', async() => {
|
||||||
const tx = await models.InvoiceIn.beginTransaction({});
|
const tx = await models.InvoiceOut.beginTransaction({});
|
||||||
const options = {transaction: tx};
|
const options = {transaction: tx};
|
||||||
const ctx = {
|
const ctx = {
|
||||||
args: {
|
args: {
|
||||||
from: new Date().setMonth(new Date().getMonth() - 12),
|
from: new Date().setMonth(new Date().getMonth() - 12),
|
||||||
to: new Date(),
|
to: new Date()
|
||||||
filter: {}
|
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const result = await models.InvoiceIn.negativeBases(ctx, options);
|
const result = await models.InvoiceOut.negativeBases(ctx, options);
|
||||||
|
|
||||||
expect(result.length).toBeGreaterThan(0);
|
expect(result.length).toBeGreaterThan(0);
|
||||||
|
|
||||||
|
@ -26,7 +25,7 @@ describe('invoiceIn negativeBases()', () => {
|
||||||
|
|
||||||
it('should throw an error if a date range is not in args', async() => {
|
it('should throw an error if a date range is not in args', async() => {
|
||||||
let error;
|
let error;
|
||||||
const tx = await models.InvoiceIn.beginTransaction({});
|
const tx = await models.InvoiceOut.beginTransaction({});
|
||||||
const options = {transaction: tx};
|
const options = {transaction: tx};
|
||||||
const ctx = {
|
const ctx = {
|
||||||
args: {
|
args: {
|
||||||
|
@ -35,7 +34,7 @@ describe('invoiceIn negativeBases()', () => {
|
||||||
};
|
};
|
||||||
|
|
||||||
try {
|
try {
|
||||||
await models.InvoiceIn.negativeBases(ctx, options);
|
await models.InvoiceOut.negativeBases(ctx, options);
|
||||||
await tx.rollback();
|
await tx.rollback();
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
error = e;
|
error = e;
|
|
@ -17,7 +17,7 @@ describe('InvoiceOut refund()', () => {
|
||||||
try {
|
try {
|
||||||
const result = await models.InvoiceOut.refund('T1111111', options);
|
const result = await models.InvoiceOut.refund('T1111111', options);
|
||||||
|
|
||||||
expect(result.length).toEqual(1);
|
expect(result).toBeDefined();
|
||||||
|
|
||||||
await tx.rollback();
|
await tx.rollback();
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
|
|
|
@ -17,4 +17,6 @@ module.exports = Self => {
|
||||||
require('../methods/invoiceOut/invoiceCsvEmail')(Self);
|
require('../methods/invoiceOut/invoiceCsvEmail')(Self);
|
||||||
require('../methods/invoiceOut/invoiceOutPdf')(Self);
|
require('../methods/invoiceOut/invoiceOutPdf')(Self);
|
||||||
require('../methods/invoiceOut/getInvoiceDate')(Self);
|
require('../methods/invoiceOut/getInvoiceDate')(Self);
|
||||||
|
require('../methods/invoiceOut/negativeBases')(Self);
|
||||||
|
require('../methods/invoiceOut/negativeBasesCsv')(Self);
|
||||||
};
|
};
|
||||||
|
|
|
@ -118,8 +118,11 @@ class Controller extends Section {
|
||||||
const query = 'InvoiceOuts/refund';
|
const query = 'InvoiceOuts/refund';
|
||||||
const params = {ref: this.invoiceOut.ref};
|
const params = {ref: this.invoiceOut.ref};
|
||||||
this.$http.post(query, params).then(res => {
|
this.$http.post(query, params).then(res => {
|
||||||
const ticketIds = res.data.map(ticket => ticket.id).join(', ');
|
const refundTicket = res.data;
|
||||||
this.vnApp.showSuccess(this.$t('The following refund tickets have been created', {ticketIds}));
|
this.vnApp.showSuccess(this.$t('The following refund ticket have been created', {
|
||||||
|
ticketId: refundTicket.id
|
||||||
|
}));
|
||||||
|
this.$state.go('ticket.card.sale', {id: refundTicket.id});
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -10,3 +10,4 @@ import './descriptor-popover';
|
||||||
import './descriptor-menu';
|
import './descriptor-menu';
|
||||||
import './index/manual';
|
import './index/manual';
|
||||||
import './global-invoicing';
|
import './global-invoicing';
|
||||||
|
import './negative-bases';
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
<vn-crud-model
|
<vn-crud-model
|
||||||
vn-id="model"
|
vn-id="model"
|
||||||
url="InvoiceIns/negativeBases"
|
url="InvoiceOuts/negativeBases"
|
||||||
auto-load="true"
|
auto-load="true"
|
||||||
params="$ctrl.params"
|
params="$ctrl.params"
|
||||||
limit="20">
|
limit="20">
|
||||||
|
@ -43,7 +43,7 @@
|
||||||
<span translate>Country</span>
|
<span translate>Country</span>
|
||||||
</th>
|
</th>
|
||||||
<th field="clientId">
|
<th field="clientId">
|
||||||
<span translate>Id Client</span>
|
<span translate>Client id</span>
|
||||||
</th>
|
</th>
|
||||||
<th field="clientSocialName">
|
<th field="clientSocialName">
|
||||||
<span translate>Client</span>
|
<span translate>Client</span>
|
||||||
|
@ -55,7 +55,7 @@
|
||||||
<span translate>Base</span>
|
<span translate>Base</span>
|
||||||
</th>
|
</th>
|
||||||
<th field="ticketFk">
|
<th field="ticketFk">
|
||||||
<span translate>Id Ticket</span>
|
<span translate>Ticket id</span>
|
||||||
</th>
|
</th>
|
||||||
<th field="isActive">
|
<th field="isActive">
|
||||||
<span translate>Active</span>
|
<span translate>Active</span>
|
|
@ -58,18 +58,7 @@ export default class Controller extends Section {
|
||||||
}
|
}
|
||||||
|
|
||||||
downloadCSV() {
|
downloadCSV() {
|
||||||
const data = [];
|
this.vnReport.show('InvoiceOuts/negativeBasesCsv', {
|
||||||
this.$.model._orgData.forEach(element => {
|
|
||||||
data.push(Object.keys(element).map(key => {
|
|
||||||
return {newName: this.$t(key), value: element[key]};
|
|
||||||
}).filter(item => item !== null)
|
|
||||||
.reduce((result, item) => {
|
|
||||||
result[item.newName] = item.value;
|
|
||||||
return result;
|
|
||||||
}, {}));
|
|
||||||
});
|
|
||||||
this.vnReport.show('InvoiceIns/negativeBasesCsv', {
|
|
||||||
negativeBases: data,
|
|
||||||
from: this.params.from,
|
from: this.params.from,
|
||||||
to: this.params.to
|
to: this.params.to
|
||||||
});
|
});
|
|
@ -0,0 +1,2 @@
|
||||||
|
Has To Invoice: Facturar
|
||||||
|
Download as CSV: Descargar como CSV
|
|
@ -7,8 +7,8 @@
|
||||||
"menus": {
|
"menus": {
|
||||||
"main": [
|
"main": [
|
||||||
{"state": "invoiceOut.index", "icon": "icon-invoice-out"},
|
{"state": "invoiceOut.index", "icon": "icon-invoice-out"},
|
||||||
{"state": "invoiceOut.global-invoicing", "icon": "contact_support"}
|
{"state": "invoiceOut.global-invoicing", "icon": "contact_support"},
|
||||||
|
{ "state": "invoiceOut.negative-bases", "icon": "icon-ticket"}
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
"routes": [
|
"routes": [
|
||||||
|
@ -46,6 +46,15 @@
|
||||||
"state": "invoiceOut.card",
|
"state": "invoiceOut.card",
|
||||||
"abstract": true,
|
"abstract": true,
|
||||||
"component": "vn-invoice-out-card"
|
"component": "vn-invoice-out-card"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"url": "/negative-bases",
|
||||||
|
"state": "invoiceOut.negative-bases",
|
||||||
|
"component": "vn-negative-bases",
|
||||||
|
"description": "Negative bases",
|
||||||
|
"acl": [
|
||||||
|
"administrative"
|
||||||
|
]
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
|
|
@ -42,7 +42,7 @@
|
||||||
<vn-autocomplete
|
<vn-autocomplete
|
||||||
vn-one
|
vn-one
|
||||||
ng-model="filter.requesterFk"
|
ng-model="filter.requesterFk"
|
||||||
url="Workers/activeWithRole"
|
url="Workers/activeWithInheritedRole"
|
||||||
search-function="{firstName: $search}"
|
search-function="{firstName: $search}"
|
||||||
value-field="id"
|
value-field="id"
|
||||||
where="{role: 'salesPerson'}"
|
where="{role: 'salesPerson'}"
|
||||||
|
|
|
@ -238,7 +238,7 @@ module.exports = Self => {
|
||||||
ENGINE = MEMORY
|
ENGINE = MEMORY
|
||||||
SELECT DISTINCT clientFk FROM tmp.filter`);
|
SELECT DISTINCT clientFk FROM tmp.filter`);
|
||||||
|
|
||||||
stmt = new ParameterizedSQL('CALL clientGetDebt(?)', [args.to]);
|
stmt = new ParameterizedSQL('CALL client_getDebt(?)', [args.to]);
|
||||||
stmts.push(stmt);
|
stmts.push(stmt);
|
||||||
stmts.push('DROP TEMPORARY TABLE tmp.clientGetDebt');
|
stmts.push('DROP TEMPORARY TABLE tmp.clientGetDebt');
|
||||||
|
|
||||||
|
|
|
@ -23,6 +23,6 @@ describe('Supplier filter()', () => {
|
||||||
|
|
||||||
let result = await app.models.Supplier.filter(ctx);
|
let result = await app.models.Supplier.filter(ctx);
|
||||||
|
|
||||||
expect(result.length).toEqual(2);
|
expect(result.length).toEqual(3);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
|
@ -53,7 +53,7 @@ module.exports = Self => {
|
||||||
let start = new Date(expedition.created);
|
let start = new Date(expedition.created);
|
||||||
let end = new Date(start.getTime() + (packingSiteConfig.avgBoxingTime * 1000));
|
let end = new Date(start.getTime() + (packingSiteConfig.avgBoxingTime * 1000));
|
||||||
|
|
||||||
if (from && to) {
|
if (from != undefined && to != undefined) {
|
||||||
start.setHours(from, 0, 0);
|
start.setHours(from, 0, 0);
|
||||||
end.setHours(to, 0, 0);
|
end.setHours(to, 0, 0);
|
||||||
}
|
}
|
||||||
|
|
|
@ -11,11 +11,6 @@ module.exports = Self => {
|
||||||
{
|
{
|
||||||
arg: 'servicesIds',
|
arg: 'servicesIds',
|
||||||
type: ['number']
|
type: ['number']
|
||||||
},
|
|
||||||
{
|
|
||||||
arg: 'createSingleTicket',
|
|
||||||
type: 'boolean',
|
|
||||||
required: false
|
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
returns: {
|
returns: {
|
||||||
|
@ -28,7 +23,7 @@ module.exports = Self => {
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
Self.refund = async(salesIds, servicesIds, createSingleTicket = false, options) => {
|
Self.refund = async(salesIds, servicesIds, options) => {
|
||||||
const models = Self.app.models;
|
const models = Self.app.models;
|
||||||
const myOptions = {};
|
const myOptions = {};
|
||||||
let tx;
|
let tx;
|
||||||
|
@ -67,40 +62,14 @@ module.exports = Self => {
|
||||||
const sales = await models.Sale.find(salesFilter, myOptions);
|
const sales = await models.Sale.find(salesFilter, myOptions);
|
||||||
const ticketsIds = [...new Set(sales.map(sale => sale.ticketFk))];
|
const ticketsIds = [...new Set(sales.map(sale => sale.ticketFk))];
|
||||||
|
|
||||||
const refundTickets = [];
|
|
||||||
const mappedTickets = new Map();
|
|
||||||
const now = Date.vnNew();
|
const now = Date.vnNew();
|
||||||
|
|
||||||
const [firstTicketId] = ticketsIds;
|
const [firstTicketId] = ticketsIds;
|
||||||
if (createSingleTicket) {
|
|
||||||
await createTicketRefund(
|
const refundTicket = await createTicketRefund(firstTicketId, now, refundAgencyMode, refoundZoneId, myOptions);
|
||||||
firstTicketId,
|
|
||||||
refundTickets,
|
|
||||||
mappedTickets,
|
|
||||||
now,
|
|
||||||
refundAgencyMode,
|
|
||||||
refoundZoneId,
|
|
||||||
myOptions
|
|
||||||
);
|
|
||||||
} else {
|
|
||||||
for (let ticketId of ticketsIds) {
|
|
||||||
await createTicketRefund(
|
|
||||||
ticketId,
|
|
||||||
refundTickets,
|
|
||||||
mappedTickets,
|
|
||||||
now,
|
|
||||||
refundAgencyMode,
|
|
||||||
refoundZoneId,
|
|
||||||
myOptions
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
for (const sale of sales) {
|
for (const sale of sales) {
|
||||||
const refundTicketId = await getTicketRefundId(createSingleTicket, sale.ticketFk, refundTickets, mappedTickets);
|
|
||||||
|
|
||||||
const createdSale = await models.Sale.create({
|
const createdSale = await models.Sale.create({
|
||||||
ticketFk: refundTicketId,
|
ticketFk: refundTicket.id,
|
||||||
itemFk: sale.itemFk,
|
itemFk: sale.itemFk,
|
||||||
quantity: - sale.quantity,
|
quantity: - sale.quantity,
|
||||||
concept: sale.concept,
|
concept: sale.concept,
|
||||||
|
@ -120,16 +89,13 @@ module.exports = Self => {
|
||||||
where: {id: {inq: servicesIds}}
|
where: {id: {inq: servicesIds}}
|
||||||
};
|
};
|
||||||
const services = await models.TicketService.find(servicesFilter, myOptions);
|
const services = await models.TicketService.find(servicesFilter, myOptions);
|
||||||
|
|
||||||
for (const service of services) {
|
for (const service of services) {
|
||||||
const refundTicketId = await getTicketRefundId(createSingleTicket, service.ticketFk, refundTickets, mappedTickets);
|
|
||||||
|
|
||||||
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: refundTicketId,
|
ticketFk: refundTicket.id,
|
||||||
ticketServiceTypeFk: service.ticketServiceTypeFk,
|
ticketServiceTypeFk: service.ticketServiceTypeFk,
|
||||||
}, myOptions);
|
}, myOptions);
|
||||||
}
|
}
|
||||||
|
@ -137,22 +103,14 @@ module.exports = Self => {
|
||||||
|
|
||||||
if (tx) await tx.commit();
|
if (tx) await tx.commit();
|
||||||
|
|
||||||
return refundTickets;
|
return refundTicket;
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
if (tx) await tx.rollback();
|
if (tx) await tx.rollback();
|
||||||
throw e;
|
throw e;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
async function createTicketRefund(
|
async function createTicketRefund(ticketId, now, refundAgencyMode, refoundZoneId, myOptions) {
|
||||||
ticketId,
|
|
||||||
refundTickets,
|
|
||||||
mappedTickets,
|
|
||||||
now,
|
|
||||||
refundAgencyMode,
|
|
||||||
refoundZoneId,
|
|
||||||
myOptions
|
|
||||||
) {
|
|
||||||
const models = Self.app.models;
|
const models = Self.app.models;
|
||||||
|
|
||||||
const filter = {include: {relation: 'address'}};
|
const filter = {include: {relation: 'address'}};
|
||||||
|
@ -170,20 +128,11 @@ module.exports = Self => {
|
||||||
zoneFk: refoundZoneId
|
zoneFk: refoundZoneId
|
||||||
}, myOptions);
|
}, myOptions);
|
||||||
|
|
||||||
refundTickets.push(refundTicket);
|
|
||||||
|
|
||||||
mappedTickets.set(ticketId, refundTicket.id);
|
|
||||||
|
|
||||||
await models.TicketRefund.create({
|
await models.TicketRefund.create({
|
||||||
refundTicketFk: refundTicket.id,
|
refundTicketFk: refundTicket.id,
|
||||||
originalTicketFk: ticket.id,
|
originalTicketFk: ticket.id,
|
||||||
}, myOptions);
|
}, myOptions);
|
||||||
}
|
|
||||||
|
|
||||||
async function getTicketRefundId(createSingleTicket, ticketId, refundTickets, mappedTickets) {
|
return refundTicket;
|
||||||
if (createSingleTicket) {
|
|
||||||
const [firstRefundTicket] = refundTickets;
|
|
||||||
return firstRefundTicket.id;
|
|
||||||
} else return mappedTickets.get(ticketId);
|
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
|
@ -22,9 +22,9 @@ describe('Sale refund()', () => {
|
||||||
try {
|
try {
|
||||||
const options = {transaction: tx};
|
const options = {transaction: tx};
|
||||||
|
|
||||||
const response = await models.Sale.refund(salesIds, servicesIds, false, options);
|
const refundedTicket = await models.Sale.refund(salesIds, servicesIds, options);
|
||||||
|
|
||||||
expect(response.length).toBeGreaterThanOrEqual(1);
|
expect(refundedTicket).toBeDefined();
|
||||||
|
|
||||||
await tx.rollback();
|
await tx.rollback();
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
|
@ -33,23 +33,18 @@ describe('Sale refund()', () => {
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should create a ticket for each unique ticketFk in the sales', async() => {
|
it('should create one ticket for each unique ticketFk in the sales', async() => {
|
||||||
const tx = await models.Sale.beginTransaction({});
|
const tx = await models.Sale.beginTransaction({});
|
||||||
const salesIds = [6, 7];
|
const salesIds = [6, 7];
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const options = {transaction: tx};
|
const options = {transaction: tx};
|
||||||
|
|
||||||
const createSingleTicket = false;
|
const ticket = await models.Sale.refund(salesIds, servicesIds, options);
|
||||||
const tickets = await models.Sale.refund(salesIds, servicesIds, createSingleTicket, options);
|
|
||||||
|
|
||||||
const ticketsIds = tickets.map(ticket => ticket.id);
|
const refundedTicket = await models.Ticket.findOne({
|
||||||
|
|
||||||
const refundedTickets = await models.Ticket.find({
|
|
||||||
where: {
|
where: {
|
||||||
id: {
|
id: ticket.id
|
||||||
inq: ticketsIds
|
|
||||||
}
|
|
||||||
},
|
},
|
||||||
include: [
|
include: [
|
||||||
{
|
{
|
||||||
|
@ -66,16 +61,12 @@ describe('Sale refund()', () => {
|
||||||
]
|
]
|
||||||
}, options);
|
}, options);
|
||||||
|
|
||||||
const firstRefoundedTicket = refundedTickets[0];
|
const salesLength = refundedTicket.ticketSales().length;
|
||||||
const secondRefoundedTicket = refundedTickets[1];
|
const componentsLength = refundedTicket.ticketSales()[0].components().length;
|
||||||
const salesLength = firstRefoundedTicket.ticketSales().length;
|
|
||||||
const componentsLength = firstRefoundedTicket.ticketSales()[0].components().length;
|
|
||||||
const servicesLength = secondRefoundedTicket.ticketServices().length;
|
|
||||||
|
|
||||||
expect(refundedTickets.length).toEqual(2);
|
expect(refundedTicket).toBeDefined();
|
||||||
expect(salesLength).toEqual(1);
|
expect(salesLength).toEqual(2);
|
||||||
expect(componentsLength).toEqual(4);
|
expect(componentsLength).toEqual(4);
|
||||||
expect(servicesLength).toBeGreaterThanOrEqual(1);
|
|
||||||
|
|
||||||
await tx.rollback();
|
await tx.rollback();
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
|
|
|
@ -7,11 +7,6 @@ module.exports = Self => {
|
||||||
arg: 'ticketsIds',
|
arg: 'ticketsIds',
|
||||||
type: ['number'],
|
type: ['number'],
|
||||||
required: true
|
required: true
|
||||||
},
|
|
||||||
{
|
|
||||||
arg: 'createSingleTicket',
|
|
||||||
type: 'boolean',
|
|
||||||
required: false
|
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
returns: {
|
returns: {
|
||||||
|
@ -24,7 +19,7 @@ module.exports = Self => {
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
Self.refund = async(ticketsIds, createSingleTicket = false, options) => {
|
Self.refund = async(ticketsIds, options) => {
|
||||||
const models = Self.app.models;
|
const models = Self.app.models;
|
||||||
const myOptions = {};
|
const myOptions = {};
|
||||||
let tx;
|
let tx;
|
||||||
|
@ -46,7 +41,7 @@ module.exports = Self => {
|
||||||
const services = await models.TicketService.find(filter, myOptions);
|
const services = await models.TicketService.find(filter, myOptions);
|
||||||
const servicesIds = services.map(service => service.id);
|
const servicesIds = services.map(service => service.id);
|
||||||
|
|
||||||
const refundedTickets = await models.Sale.refund(salesIds, servicesIds, createSingleTicket, myOptions);
|
const refundedTickets = await models.Sale.refund(salesIds, servicesIds, myOptions);
|
||||||
|
|
||||||
if (tx) await tx.commit();
|
if (tx) await tx.commit();
|
||||||
|
|
||||||
|
|
|
@ -300,7 +300,7 @@ class Controller extends Section {
|
||||||
const params = {ticketsIds: [this.id]};
|
const params = {ticketsIds: [this.id]};
|
||||||
const query = 'Tickets/refund';
|
const query = 'Tickets/refund';
|
||||||
return this.$http.post(query, params).then(res => {
|
return this.$http.post(query, params).then(res => {
|
||||||
const [refundTicket] = res.data;
|
const refundTicket = res.data;
|
||||||
this.vnApp.showSuccess(this.$t('The following refund ticket have been created', {
|
this.vnApp.showSuccess(this.$t('The following refund ticket have been created', {
|
||||||
ticketId: refundTicket.id
|
ticketId: refundTicket.id
|
||||||
}));
|
}));
|
||||||
|
@ -326,8 +326,13 @@ class Controller extends Section {
|
||||||
|
|
||||||
return this.$http.post(`Docuwares/${this.id}/upload`, {fileCabinet: 'deliveryNote'})
|
return this.$http.post(`Docuwares/${this.id}/upload`, {fileCabinet: 'deliveryNote'})
|
||||||
.then(() => {
|
.then(() => {
|
||||||
this.vnApp.showSuccess(this.$t('PDF sent!'));
|
this.$.balanceCreate.amountPaid = this.ticket.totalWithVat;
|
||||||
|
this.$.balanceCreate.clientFk = this.ticket.clientFk;
|
||||||
|
this.$.balanceCreate.description = 'Albaran: ';
|
||||||
|
this.$.balanceCreate.description += this.ticket.id;
|
||||||
|
|
||||||
this.$.balanceCreate.show();
|
this.$.balanceCreate.show();
|
||||||
|
this.vnApp.showSuccess(this.$t('PDF sent!'));
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -250,7 +250,7 @@ describe('Ticket Component vnTicketDescriptorMenu', () => {
|
||||||
const params = {
|
const params = {
|
||||||
ticketsIds: [16]
|
ticketsIds: [16]
|
||||||
};
|
};
|
||||||
$httpBackend.expectPOST('Tickets/refund', params).respond([{id: 99}]);
|
$httpBackend.expectPOST('Tickets/refund', params).respond({id: 99});
|
||||||
controller.refund();
|
controller.refund();
|
||||||
$httpBackend.flush();
|
$httpBackend.flush();
|
||||||
|
|
||||||
|
|
|
@ -516,7 +516,7 @@ class Controller extends Section {
|
||||||
const params = {salesIds: salesIds};
|
const params = {salesIds: salesIds};
|
||||||
const query = 'Sales/refund';
|
const query = 'Sales/refund';
|
||||||
this.$http.post(query, params).then(res => {
|
this.$http.post(query, params).then(res => {
|
||||||
const [refundTicket] = res.data;
|
const refundTicket = res.data;
|
||||||
this.vnApp.showSuccess(this.$t('The following refund ticket have been created', {
|
this.vnApp.showSuccess(this.$t('The following refund ticket have been created', {
|
||||||
ticketId: refundTicket.id
|
ticketId: refundTicket.id
|
||||||
}));
|
}));
|
||||||
|
|
|
@ -726,8 +726,7 @@ describe('Ticket', () => {
|
||||||
salesIds: [1, 4],
|
salesIds: [1, 4],
|
||||||
};
|
};
|
||||||
const refundTicket = {id: 99};
|
const refundTicket = {id: 99};
|
||||||
const createdTickets = [refundTicket];
|
$httpBackend.expect('POST', 'Sales/refund', params).respond(200, refundTicket);
|
||||||
$httpBackend.expect('POST', 'Sales/refund', params).respond(200, createdTickets);
|
|
||||||
controller.createRefund();
|
controller.createRefund();
|
||||||
$httpBackend.flush();
|
$httpBackend.flush();
|
||||||
|
|
||||||
|
|
|
@ -4,6 +4,7 @@
|
||||||
margin-right: 2cm;
|
margin-right: 2cm;
|
||||||
font-size: 10px;
|
font-size: 10px;
|
||||||
color: #555;
|
color: #555;
|
||||||
|
width: 100%;
|
||||||
zoom: 0.65
|
zoom: 0.65
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,8 +1 @@
|
||||||
numPages: Page <span class="pageNumber"></span> of <span class="totalPages"></span>
|
numPages: Page <span class="pageNumber"></span> of <span class="totalPages"></span>
|
||||||
law:
|
|
||||||
privacy: 'In compliance with the provisions of Organic Law 15/1999, on the
|
|
||||||
Protection of Personal Data, we inform you that the personal data you provide
|
|
||||||
will be included in automated files of VERDNATURA LEVANTE SL, being able at all
|
|
||||||
times to exercise the rights of access, rectification, cancellation and opposition,
|
|
||||||
communicating it in writing to the registered office of the entity.
|
|
||||||
The purpose of the file is administrative management, accounting, and billing.'
|
|
||||||
|
|
|
@ -1,8 +1 @@
|
||||||
numPages: Página <span class="pageNumber"></span> de <span class="totalPages"></span>
|
numPages: Página <span class="pageNumber"></span> de <span class="totalPages"></span>
|
||||||
law:
|
|
||||||
privacy: En cumplimiento de lo dispuesto en la Ley Orgánica 15/1999, de Protección
|
|
||||||
de Datos de Carácter Personal, le comunicamos que los datos personales que facilite
|
|
||||||
se incluirán en ficheros automatizados de VERDNATURA LEVANTE S.L., pudiendo en
|
|
||||||
todo momento ejercitar los derechos de acceso, rectificación, cancelación y oposición,
|
|
||||||
comunicándolo por escrito al domicilio social de la entidad. La finalidad del
|
|
||||||
fichero es la gestión administrativa, contabilidad, y facturación.
|
|
||||||
|
|
|
@ -1,8 +1 @@
|
||||||
numPages: Page <span class="pageNumber"></span> de <span class="totalPages"></span>
|
numPages: Page <span class="pageNumber"></span> de <span class="totalPages"></span>
|
||||||
law:
|
|
||||||
privacy: Conformément aux dispositions de la loi organique 15/1999 sur la protection
|
|
||||||
des données personnelles, nous vous informons que les données personnelles que
|
|
||||||
vous fournissez seront incluses dans des dossiers. VERDNATURA LEVANTE S.L., vous
|
|
||||||
pouvez à tout moment, exercer les droits d'accès, de rectification, d'annulation
|
|
||||||
et d'opposition, en communiquant par écrit au siège social de la société. Le dossier
|
|
||||||
a pour objet la gestion administrative, la comptabilité et la facturation.
|
|
||||||
|
|
|
@ -1,8 +1 @@
|
||||||
numPages: Página <span class="pageNumber"></span> de <span class="totalPages"></span>
|
numPages: Página <span class="pageNumber"></span> de <span class="totalPages"></span>
|
||||||
law:
|
|
||||||
privacy: Em cumprimento do disposto na lei Orgânica 15/1999, de Protecção de Dados
|
|
||||||
de Carácter Pessoal, comunicamos que os dados pessoais que facilite se incluirão
|
|
||||||
nos ficheiros automatizados de VERDNATURA LEVANTE S.L., podendo em todo momento
|
|
||||||
exercer os direitos de acesso, rectificação, cancelação e oposição, comunicando
|
|
||||||
por escrito ao domicílio social da entidade. A finalidade do ficheiro é a gestão
|
|
||||||
administrativa, contabilidade e facturação.
|
|
||||||
|
|
|
@ -5,6 +5,11 @@
|
||||||
<div class="centerText" v-if="centerText" class="uppercase">{{centerText}}</div>
|
<div class="centerText" v-if="centerText" class="uppercase">{{centerText}}</div>
|
||||||
<div class="pageCount" v-html="$t('numPages')"></div>
|
<div class="pageCount" v-html="$t('numPages')"></div>
|
||||||
</div>
|
</div>
|
||||||
<p class="privacy" v-html="$t('law.privacy')"></p>
|
<p
|
||||||
|
v-if="company?.footnotes"
|
||||||
|
v-html="company.footnotes">
|
||||||
|
</p>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -1,4 +1,17 @@
|
||||||
|
/* eslint-disable no-tabs */
|
||||||
|
const db = require('../../database');
|
||||||
|
|
||||||
module.exports = {
|
module.exports = {
|
||||||
name: 'report-footer',
|
name: 'report-footer',
|
||||||
props: ['leftText', 'centerText']
|
async serverPrefetch() {
|
||||||
|
this.company = await db.findOne(
|
||||||
|
`SELECT
|
||||||
|
ci.footnotes
|
||||||
|
FROM companyI18n ci
|
||||||
|
JOIN company c ON c.id = ci.companyFk
|
||||||
|
WHERE c.code = ? AND ci.lang = (SELECT lang FROM account.user WHERE id = ?)`,
|
||||||
|
[this.companyCode, this.recipientId]);
|
||||||
|
},
|
||||||
|
|
||||||
|
props: ['leftText', 'companyCode', 'recipientId', 'centerText']
|
||||||
};
|
};
|
||||||
|
|
|
@ -8,7 +8,7 @@
|
||||||
{{companyName}}. {{company.street}}.
|
{{companyName}}. {{company.street}}.
|
||||||
{{company.postCode}} {{company.city}}.
|
{{company.postCode}} {{company.city}}.
|
||||||
☎ {{companyPhone}}
|
☎ {{companyPhone}}
|
||||||
· {{$t('company.contactData')}}
|
· {{company.web}} - {{company.email}}
|
||||||
</section>
|
</section>
|
||||||
<section>CIF: {{fiscalAddress.nif}} {{fiscalAddress.register}}</section>
|
<section>CIF: {{fiscalAddress.nif}} {{fiscalAddress.register}}</section>
|
||||||
</header>
|
</header>
|
||||||
|
|
|
@ -43,7 +43,9 @@ module.exports = {
|
||||||
s.postCode,
|
s.postCode,
|
||||||
s.city,
|
s.city,
|
||||||
s.phone,
|
s.phone,
|
||||||
cg.code AS groupName
|
cg.code AS groupName,
|
||||||
|
c.email,
|
||||||
|
c.web
|
||||||
FROM company c
|
FROM company c
|
||||||
JOIN companyGroup cg ON cg.id = c.companyGroupFk
|
JOIN companyGroup cg ON cg.id = c.companyGroupFk
|
||||||
JOIN supplier s ON s.id = c.id
|
JOIN supplier s ON s.id = c.id
|
||||||
|
|
|
@ -0,0 +1,11 @@
|
||||||
|
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/email.css`])
|
||||||
|
.mergeStyles();
|
|
@ -0,0 +1,15 @@
|
||||||
|
<email-body v-bind="$props">
|
||||||
|
<div class="grid-row">
|
||||||
|
<div class="grid-block vn-px-ml centered">
|
||||||
|
<h1>{{ $t('total') }}: {{tickets.length}}</h1>
|
||||||
|
<hr>
|
||||||
|
</div>
|
||||||
|
<div v-for="ticket in tickets" class="grid-block vn-px-ml">
|
||||||
|
<p v-if="ticket.ticketId"><b>{{ $t('ticketId') }}:</b> {{ticket.ticketId}}</p>
|
||||||
|
<p><b>{{ $t('clientId') }}:</b> <a :href="clientGreugeUrl(ticket.clientId)" target="_blank">{{ticket.clientId}}</a></p>
|
||||||
|
<p v-if="ticket.description"><b>{{ $t('description') }}:</b> {{ticket.description}}</p>
|
||||||
|
<p><b>{{ $t('amount') }}:</b> {{ticket.amount}} €</p>
|
||||||
|
<hr>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</email-body>
|
|
@ -0,0 +1,36 @@
|
||||||
|
const Component = require(`vn-print/core/component`);
|
||||||
|
const emailBody = new Component('email-body');
|
||||||
|
const models = require('vn-loopback/server/server').models;
|
||||||
|
|
||||||
|
module.exports = {
|
||||||
|
name: 'greuge-wrong',
|
||||||
|
async serverPrefetch() {
|
||||||
|
this.url = await this.salixUrl();
|
||||||
|
|
||||||
|
if (!this.url)
|
||||||
|
throw new Error('Something went wrong');
|
||||||
|
},
|
||||||
|
components: {
|
||||||
|
'email-body': emailBody.build(),
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
async salixUrl() {
|
||||||
|
const salix = await models.Url.findOne({
|
||||||
|
where: {
|
||||||
|
appName: 'salix',
|
||||||
|
environment: process.env.NODE_ENV || 'dev'
|
||||||
|
}
|
||||||
|
});
|
||||||
|
return salix.url;
|
||||||
|
},
|
||||||
|
clientGreugeUrl(clientId) {
|
||||||
|
return `${this.url}client/${clientId}/greuge/index`
|
||||||
|
},
|
||||||
|
},
|
||||||
|
props: {
|
||||||
|
tickets: {
|
||||||
|
type: Array,
|
||||||
|
required: true
|
||||||
|
},
|
||||||
|
},
|
||||||
|
};
|
|
@ -0,0 +1,6 @@
|
||||||
|
subject: Abnormal greuges have been created
|
||||||
|
total: Total number of abnormal greuges
|
||||||
|
ticketId: Ticket
|
||||||
|
clientId: Client
|
||||||
|
description: Description
|
||||||
|
amount: Amount
|
|
@ -0,0 +1,6 @@
|
||||||
|
subject: Se han creado greuges anormales
|
||||||
|
total: Número total de greuges anormales
|
||||||
|
ticketId: Ticket
|
||||||
|
clientId: Cliente
|
||||||
|
description: Descipción
|
||||||
|
amount: Importe
|
|
@ -0,0 +1,11 @@
|
||||||
|
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/email.css`])
|
||||||
|
.mergeStyles();
|
|
@ -0,0 +1,6 @@
|
||||||
|
[
|
||||||
|
{
|
||||||
|
"filename": "receipt.pdf",
|
||||||
|
"component": "receipt"
|
||||||
|
}
|
||||||
|
]
|
|
@ -0,0 +1,5 @@
|
||||||
|
subject: Recibo
|
||||||
|
title: Recibo
|
||||||
|
dear: Estimado cliente
|
||||||
|
description: Ya está disponible el recibo <strong>{0}</strong>. <br/>
|
||||||
|
Puedes descargarlo haciendo clic en el adjunto de este correo.
|
|
@ -0,0 +1,9 @@
|
||||||
|
<email-body v-bind="$props">
|
||||||
|
<div class="grid-row">
|
||||||
|
<div class="grid-block vn-pa-ml">
|
||||||
|
<h1>{{ $t('title') }}</h1>
|
||||||
|
<p>{{$t('dear')}},</p>
|
||||||
|
<p v-html="$t('description', [id])"></p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</email-body>
|
|
@ -0,0 +1,15 @@
|
||||||
|
const Component = require(`vn-print/core/component`);
|
||||||
|
const emailBody = new Component('email-body');
|
||||||
|
|
||||||
|
module.exports = {
|
||||||
|
name: 'receipt',
|
||||||
|
components: {
|
||||||
|
'email-body': emailBody.build(),
|
||||||
|
},
|
||||||
|
props: {
|
||||||
|
id: {
|
||||||
|
type: Number,
|
||||||
|
required: true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
|
@ -5,7 +5,7 @@
|
||||||
</div>
|
</div>
|
||||||
<report-header v-bind="$props" v-bind:company-code="invoice.companyCode"></report-header>
|
<report-header v-bind="$props" v-bind:company-code="invoice.companyCode"></report-header>
|
||||||
</template>
|
</template>
|
||||||
<div class="grid-row">
|
<div class="grid-row">
|
||||||
<div class="grid-block">
|
<div class="grid-block">
|
||||||
<div class="columns vn-mb-lg">
|
<div class="columns vn-mb-lg">
|
||||||
<div class="size50">
|
<div class="size50">
|
||||||
|
@ -242,7 +242,7 @@
|
||||||
</tfoot>
|
</tfoot>
|
||||||
</table>
|
</table>
|
||||||
</div>
|
</div>
|
||||||
<div class="columns vn-mt-xl" v-if="invoice.payMethodCode == 'wireTransfer' || ticketObservations">
|
<div class="columns vn-mt-xl" v-if="invoice.payMethodCode == 'wireTransfer' && invoice.iban">
|
||||||
<div class="size50 pull-left no-page-break">
|
<div class="size50 pull-left no-page-break">
|
||||||
<div class="panel">
|
<div class="panel">
|
||||||
<div class="header">{{$t('observations')}}</div>
|
<div class="header">{{$t('observations')}}</div>
|
||||||
|
@ -266,7 +266,9 @@
|
||||||
v-bind:company-code="invoice.companyCode"
|
v-bind:company-code="invoice.companyCode"
|
||||||
v-bind:left-text="$t('invoiceRef', [invoice.ref])"
|
v-bind:left-text="$t('invoiceRef', [invoice.ref])"
|
||||||
v-bind:center-text="client.socialName"
|
v-bind:center-text="client.socialName"
|
||||||
|
v-bind:recipient-id="client.id"
|
||||||
v-bind="$props"
|
v-bind="$props"
|
||||||
|
|
||||||
>
|
>
|
||||||
</report-footer>
|
</report-footer>
|
||||||
</template>
|
</template>
|
||||||
|
|
|
@ -11,8 +11,12 @@ module.exports = {
|
||||||
this.client = await this.findOneFromDef('client', [this.reference]);
|
this.client = await this.findOneFromDef('client', [this.reference]);
|
||||||
this.taxes = await this.rawSqlFromDef(`taxes`, [this.reference]);
|
this.taxes = await this.rawSqlFromDef(`taxes`, [this.reference]);
|
||||||
this.hasIntrastat = await this.findValueFromDef(`hasIntrastat`, [this.reference]);
|
this.hasIntrastat = await this.findValueFromDef(`hasIntrastat`, [this.reference]);
|
||||||
this.intrastat = await this.rawSqlFromDef(`intrastat`,
|
this.intrastat = await this.rawSqlFromDef(`intrastat`, [
|
||||||
[this.reference, this.reference, this.reference, this.reference]);
|
this.reference,
|
||||||
|
this.reference,
|
||||||
|
this.reference,
|
||||||
|
this.reference
|
||||||
|
]);
|
||||||
this.rectified = await this.rawSqlFromDef(`rectified`, [this.reference]);
|
this.rectified = await this.rawSqlFromDef(`rectified`, [this.reference]);
|
||||||
this.hasIncoterms = await this.findValueFromDef(`hasIncoterms`, [this.reference]);
|
this.hasIncoterms = await this.findValueFromDef(`hasIncoterms`, [this.reference]);
|
||||||
|
|
||||||
|
|
|
@ -11,7 +11,7 @@ FROM invoiceOut io
|
||||||
JOIN client c ON c.id = io.clientFk
|
JOIN client c ON c.id = io.clientFk
|
||||||
JOIN payMethod pm ON pm.id = c.payMethodFk
|
JOIN payMethod pm ON pm.id = c.payMethodFk
|
||||||
JOIN company cny ON cny.id = io.companyFk
|
JOIN company cny ON cny.id = io.companyFk
|
||||||
JOIN supplierAccount sa ON sa.id = cny.supplierAccountFk
|
LEFT JOIN supplierAccount sa ON sa.id = cny.supplierAccountFk
|
||||||
LEFT JOIN invoiceOutSerial ios ON ios.code = io.serial
|
LEFT JOIN invoiceOutSerial ios ON ios.code = io.serial
|
||||||
LEFT JOIN ticket t ON t.refFk = io.ref
|
LEFT JOIN ticket t ON t.refFk = io.ref
|
||||||
WHERE t.refFk = ?
|
WHERE t.refFk = ?
|
Loading…
Reference in New Issue