Merge branch 'dev' of https://gitea.verdnatura.es/verdnatura/salix into 2970-smart-table
gitea/salix/pipeline/head This commit looks good
Details
gitea/salix/pipeline/head This commit looks good
Details
This commit is contained in:
commit
c011a82edc
|
@ -0,0 +1,4 @@
|
||||||
|
ALTER TABLE vn.payMethod CHANGE ibanRequired ibanRequiredForClients tinyint(3) DEFAULT 0 NULL;
|
||||||
|
ALTER TABLE vn.payMethod ADD ibanRequiredForSuppliers tinyint(3) DEFAULT 0 NULL;
|
||||||
|
ALTER TABLE vn.payMethod CHANGE ibanRequiredForSuppliers ibanRequiredForSuppliers tinyint(3) DEFAULT 0 NULL AFTER ibanRequiredForClients;
|
||||||
|
UPDATE vn.payMethod SET ibanRequiredForSuppliers = 1 WHERE code = 'wireTransfer';
|
|
@ -217,14 +217,14 @@ UPDATE `vn`.`agencyMode` SET `web` = 1, `reportMail` = 'no-reply@gothamcity.com'
|
||||||
|
|
||||||
UPDATE `vn`.`agencyMode` SET `code` = 'refund' WHERE `id` = 23;
|
UPDATE `vn`.`agencyMode` SET `code` = 'refund' WHERE `id` = 23;
|
||||||
|
|
||||||
INSERT INTO `vn`.`payMethod`(`id`,`code`, `name`, `graceDays`, `outstandingDebt`, `ibanRequired`)
|
INSERT INTO `vn`.`payMethod`(`id`,`code`, `name`, `graceDays`, `outstandingDebt`, `ibanRequiredForClients`, `ibanRequiredForSuppliers`)
|
||||||
VALUES
|
VALUES
|
||||||
(1, NULL, 'PayMethod one', 0, 001, 0),
|
(1, NULL, 'PayMethod one', 0, 001, 0, 0),
|
||||||
(2, NULL, 'PayMethod two', 10, 001, 0),
|
(2, NULL, 'PayMethod two', 10, 001, 0, 0),
|
||||||
(3, 'compensation', 'PayMethod three', 0, 001, 0),
|
(3, 'compensation', 'PayMethod three', 0, 001, 0, 0),
|
||||||
(4, NULL, 'PayMethod with IBAN', 0, 001, 1),
|
(4, NULL, 'PayMethod with IBAN', 0, 001, 1, 0),
|
||||||
(5, NULL, 'PayMethod five', 10, 001, 0),
|
(5, NULL, 'PayMethod five', 10, 001, 0, 0),
|
||||||
(8,'wireTransfer', 'WireTransfer', 5, 001, 1);
|
(8,'wireTransfer', 'WireTransfer', 5, 001, 1, 1);
|
||||||
|
|
||||||
INSERT INTO `vn`.`payDem`(`id`, `payDem`)
|
INSERT INTO `vn`.`payDem`(`id`, `payDem`)
|
||||||
VALUES
|
VALUES
|
||||||
|
|
|
@ -33928,7 +33928,8 @@ CREATE TABLE `payMethod` (
|
||||||
`solution` varchar(1) COLLATE utf8_unicode_ci DEFAULT NULL,
|
`solution` varchar(1) COLLATE utf8_unicode_ci DEFAULT NULL,
|
||||||
`outstandingDebt` tinyint(3) unsigned zerofill NOT NULL DEFAULT '000',
|
`outstandingDebt` tinyint(3) unsigned zerofill NOT NULL DEFAULT '000',
|
||||||
`graceDays` int(11) unsigned NOT NULL DEFAULT '0',
|
`graceDays` int(11) unsigned NOT NULL DEFAULT '0',
|
||||||
`ibanRequired` tinyint(3) DEFAULT '0',
|
`ibanRequiredForClients` tinyint(3) DEFAULT '0',
|
||||||
|
`ibanRequiredForSuppliers` tinyint(3) DEFAULT '0',
|
||||||
`isNotified` tinyint(3) NOT NULL DEFAULT '1',
|
`isNotified` tinyint(3) NOT NULL DEFAULT '1',
|
||||||
PRIMARY KEY (`id`)
|
PRIMARY KEY (`id`)
|
||||||
) ENGINE=InnoDBDEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci;
|
) ENGINE=InnoDBDEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci;
|
||||||
|
|
|
@ -119,6 +119,7 @@ export default {
|
||||||
name: 'vn-client-create vn-textfield[ng-model="$ctrl.client.name"]',
|
name: 'vn-client-create vn-textfield[ng-model="$ctrl.client.name"]',
|
||||||
taxNumber: 'vn-client-create vn-textfield[ng-model="$ctrl.client.fi"]',
|
taxNumber: 'vn-client-create vn-textfield[ng-model="$ctrl.client.fi"]',
|
||||||
socialName: 'vn-client-create vn-textfield[ng-model="$ctrl.client.socialName"]',
|
socialName: 'vn-client-create vn-textfield[ng-model="$ctrl.client.socialName"]',
|
||||||
|
businessType: 'vn-client-create vn-autocomplete[ng-model="$ctrl.client.businessTypeFk"]',
|
||||||
street: 'vn-client-create vn-textfield[ng-model="$ctrl.client.street"]',
|
street: 'vn-client-create vn-textfield[ng-model="$ctrl.client.street"]',
|
||||||
addPostCode: 'vn-client-create vn-datalist[ng-model="$ctrl.client.postcode"] vn-icon-button[icon="add_circle"]',
|
addPostCode: 'vn-client-create vn-datalist[ng-model="$ctrl.client.postcode"] vn-icon-button[icon="add_circle"]',
|
||||||
addProvince: 'vn-autocomplete[ng-model="$ctrl.location.provinceFk"] vn-icon-button[icon="add_circle"]',
|
addProvince: 'vn-autocomplete[ng-model="$ctrl.location.provinceFk"] vn-icon-button[icon="add_circle"]',
|
||||||
|
|
|
@ -27,16 +27,19 @@ describe('Client create path', () => {
|
||||||
await page.waitForState('client.create');
|
await page.waitForState('client.create');
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should receive an error when clicking the create button having name and Business name fields empty', async() => {
|
it('should receive an error when clicking the create button having name and Business name fields empty',
|
||||||
|
async() => {
|
||||||
await page.write(selectors.createClientView.taxNumber, '74451390E');
|
await page.write(selectors.createClientView.taxNumber, '74451390E');
|
||||||
await page.write(selectors.createClientView.userName, 'CaptainMarvel');
|
await page.write(selectors.createClientView.userName, 'CaptainMarvel');
|
||||||
await page.write(selectors.createClientView.email, 'CarolDanvers@verdnatura.es');
|
await page.write(selectors.createClientView.email, 'CarolDanvers@verdnatura.es');
|
||||||
await page.autocompleteSearch(selectors.createClientView.salesPerson, 'salesPerson');
|
await page.autocompleteSearch(selectors.createClientView.salesPerson, 'salesPerson');
|
||||||
|
await page.autocompleteSearch(selectors.createClientView.businessType, 'florist');
|
||||||
await page.waitToClick(selectors.createClientView.createButton);
|
await page.waitToClick(selectors.createClientView.createButton);
|
||||||
const message = await page.waitForSnackbar();
|
const message = await page.waitForSnackbar();
|
||||||
|
|
||||||
expect(message.text).toContain('Some fields are invalid');
|
expect(message.text).toContain('Some fields are invalid');
|
||||||
});
|
}
|
||||||
|
);
|
||||||
|
|
||||||
it(`should create a new province`, async() => {
|
it(`should create a new province`, async() => {
|
||||||
await page.waitToClick(selectors.createClientView.addPostCode);
|
await page.waitToClick(selectors.createClientView.addPostCode);
|
||||||
|
@ -80,9 +83,18 @@ describe('Client create path', () => {
|
||||||
expect(message.text).toContain('Some fields are invalid');
|
expect(message.text).toContain('Some fields are invalid');
|
||||||
});
|
});
|
||||||
|
|
||||||
it(`should attempt to create a new user with all it's data but wrong postal code`, async() => {
|
it(`should attempt to create a new user with all it's data but wrong business type`, async() => {
|
||||||
await page.clearInput(selectors.createClientView.email);
|
await page.clearInput(selectors.createClientView.email);
|
||||||
await page.write(selectors.createClientView.email, 'CarolDanvers@verdnatura.es');
|
await page.write(selectors.createClientView.email, 'CarolDanvers@verdnatura.es');
|
||||||
|
await page.clearInput(selectors.createClientView.businessType);
|
||||||
|
await page.waitToClick(selectors.createClientView.createButton);
|
||||||
|
const message = await page.waitForSnackbar();
|
||||||
|
|
||||||
|
expect(message.text).toContain('Some fields are invalid');
|
||||||
|
});
|
||||||
|
|
||||||
|
it(`should attempt to create a new user with all it's data but wrong postal code`, async() => {
|
||||||
|
await page.autocompleteSearch(selectors.createClientView.businessType, 'florist');
|
||||||
await page.clearInput(selectors.createClientView.postcode);
|
await page.clearInput(selectors.createClientView.postcode);
|
||||||
await page.write(selectors.createClientView.postcode, '479999');
|
await page.write(selectors.createClientView.postcode, '479999');
|
||||||
await page.waitToClick(selectors.createClientView.createButton);
|
await page.waitToClick(selectors.createClientView.createButton);
|
||||||
|
|
|
@ -117,5 +117,6 @@
|
||||||
"INACTIVE_PROVIDER": "Inactive provider",
|
"INACTIVE_PROVIDER": "Inactive provider",
|
||||||
"reference duplicated": "reference duplicated",
|
"reference duplicated": "reference duplicated",
|
||||||
"The PDF document does not exists": "The PDF document does not exists. Try regenerating it from 'Regenerate invoice PDF' option",
|
"The PDF document does not exists": "The PDF document does not exists. Try regenerating it from 'Regenerate invoice PDF' option",
|
||||||
"This item is not available": "This item is not available"
|
"This item is not available": "This item is not available",
|
||||||
|
"Deny buy request": "Purchase request for ticket id [{{ticketId}}]({{{url}}}) has been rejected. Reason: {{observation}}"
|
||||||
}
|
}
|
|
@ -133,6 +133,7 @@
|
||||||
"reserved": "reservado",
|
"reserved": "reservado",
|
||||||
"Changed sale reserved state": "He cambiado el estado reservado de las siguientes lineas al ticket [{{ticketId}}]({{{ticketUrl}}}): {{{changes}}}",
|
"Changed sale reserved state": "He cambiado el estado reservado de las siguientes lineas al ticket [{{ticketId}}]({{{ticketUrl}}}): {{{changes}}}",
|
||||||
"Bought units from buy request": "Se ha comprado {{quantity}} unidades de [{{itemId}} {{concept}}]({{{urlItem}}}) para el ticket id [{{ticketId}}]({{{url}}})",
|
"Bought units from buy request": "Se ha comprado {{quantity}} unidades de [{{itemId}} {{concept}}]({{{urlItem}}}) para el ticket id [{{ticketId}}]({{{url}}})",
|
||||||
|
"Deny buy request":"Se ha rechazado la petición de compra para el ticket id [{{ticketId}}]({{{url}}}). Motivo: {{observation}}",
|
||||||
"MESSAGE_INSURANCE_CHANGE": "He cambiado el crédito asegurado del cliente [{{clientName}} ({{clientId}})]({{{url}}}) a *{{credit}} €*",
|
"MESSAGE_INSURANCE_CHANGE": "He cambiado el crédito asegurado del cliente [{{clientName}} ({{clientId}})]({{{url}}}) a *{{credit}} €*",
|
||||||
"Changed client paymethod": "He cambiado la forma de pago del cliente [{{clientName}} ({{clientId}})]({{{url}}})",
|
"Changed client paymethod": "He cambiado la forma de pago del cliente [{{clientName}} ({{clientId}})]({{{url}}})",
|
||||||
"Sent units from ticket": "Envio *{{quantity}}* unidades de [{{concept}} ({{itemId}})]({{{itemUrl}}}) a *\"{{nickname}}\"* provenientes del ticket id [{{ticketId}}]({{{ticketUrl}}})",
|
"Sent units from ticket": "Envio *{{quantity}}* unidades de [{{concept}} ({{itemId}})]({{{itemUrl}}}) a *\"{{nickname}}\"* provenientes del ticket id [{{ticketId}}]({{{ticketUrl}}})",
|
||||||
|
|
|
@ -50,7 +50,8 @@ module.exports = function(Self) {
|
||||||
city: data.city,
|
city: data.city,
|
||||||
provinceFk: data.provinceFk,
|
provinceFk: data.provinceFk,
|
||||||
countryFk: data.countryFk,
|
countryFk: data.countryFk,
|
||||||
isEqualizated: data.isEqualizated
|
isEqualizated: data.isEqualizated,
|
||||||
|
businessTypeFk: data.businessTypeFk
|
||||||
}, myOptions);
|
}, myOptions);
|
||||||
|
|
||||||
const address = await models.Address.create({
|
const address = await models.Address.create({
|
||||||
|
|
|
@ -8,7 +8,8 @@ describe('Client Create', () => {
|
||||||
name: 'Wade',
|
name: 'Wade',
|
||||||
socialName: 'Deadpool Marvel',
|
socialName: 'Deadpool Marvel',
|
||||||
street: 'Wall Street',
|
street: 'Wall Street',
|
||||||
city: 'New York'
|
city: 'New York',
|
||||||
|
businessTypeFk: 'florist'
|
||||||
};
|
};
|
||||||
|
|
||||||
it(`should not find Deadpool as he's not created yet`, async() => {
|
it(`should not find Deadpool as he's not created yet`, async() => {
|
||||||
|
@ -45,6 +46,7 @@ describe('Client Create', () => {
|
||||||
expect(client.email).toEqual(newAccount.email);
|
expect(client.email).toEqual(newAccount.email);
|
||||||
expect(client.fi).toEqual(newAccount.fi);
|
expect(client.fi).toEqual(newAccount.fi);
|
||||||
expect(client.socialName).toEqual(newAccount.socialName);
|
expect(client.socialName).toEqual(newAccount.socialName);
|
||||||
|
expect(client.businessTypeFk).toEqual(newAccount.businessTypeFk);
|
||||||
|
|
||||||
await tx.rollback();
|
await tx.rollback();
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
|
|
|
@ -129,7 +129,7 @@ module.exports = Self => {
|
||||||
|
|
||||||
function hasIban(err, done) {
|
function hasIban(err, done) {
|
||||||
Self.app.models.PayMethod.findById(this.payMethodFk, (_, instance) => {
|
Self.app.models.PayMethod.findById(this.payMethodFk, (_, instance) => {
|
||||||
if (instance && instance.ibanRequired && !this.iban)
|
if (instance && instance.ibanRequiredForClients && !this.iban)
|
||||||
err();
|
err();
|
||||||
done();
|
done();
|
||||||
});
|
});
|
||||||
|
|
|
@ -130,6 +130,13 @@
|
||||||
"mysql": {
|
"mysql": {
|
||||||
"columnName": "transactionTypeSageFk"
|
"columnName": "transactionTypeSageFk"
|
||||||
}
|
}
|
||||||
|
},
|
||||||
|
"businessTypeFk": {
|
||||||
|
"type": "string",
|
||||||
|
"mysql": {
|
||||||
|
"columnName": "businessTypeFk"
|
||||||
|
},
|
||||||
|
"required": true
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"relations": {
|
"relations": {
|
||||||
|
|
|
@ -25,7 +25,10 @@
|
||||||
"outstandingDebt": {
|
"outstandingDebt": {
|
||||||
"type": "Number"
|
"type": "Number"
|
||||||
},
|
},
|
||||||
"ibanRequired": {
|
"ibanRequiredForClients": {
|
||||||
|
"type": "boolean"
|
||||||
|
},
|
||||||
|
"ibanRequiredForSuppliers": {
|
||||||
"type": "boolean"
|
"type": "boolean"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -19,7 +19,7 @@
|
||||||
vn-acl="salesAssistant"
|
vn-acl="salesAssistant"
|
||||||
ng-model="$ctrl.client.payMethodFk"
|
ng-model="$ctrl.client.payMethodFk"
|
||||||
data="paymethods"
|
data="paymethods"
|
||||||
fields="['ibanRequired']"
|
fields="['ibanRequiredForClients']"
|
||||||
initial-data="$ctrl.client.payMethod">
|
initial-data="$ctrl.client.payMethod">
|
||||||
</vn-autocomplete>
|
</vn-autocomplete>
|
||||||
<vn-input-number
|
<vn-input-number
|
||||||
|
|
|
@ -26,18 +26,28 @@
|
||||||
</vn-autocomplete>
|
</vn-autocomplete>
|
||||||
</vn-horizontal>
|
</vn-horizontal>
|
||||||
<vn-horizontal>
|
<vn-horizontal>
|
||||||
<vn-textfield
|
<vn-autocomplete
|
||||||
vn-two
|
vn-id="businessTypeFk"
|
||||||
label="Business name"
|
ng-model="$ctrl.client.businessTypeFk"
|
||||||
ng-model="$ctrl.client.socialName"
|
url="BusinessTypes"
|
||||||
|
show-field="description"
|
||||||
|
value-field="code"
|
||||||
|
label="Business type"
|
||||||
rule>
|
rule>
|
||||||
</vn-textfield>
|
</vn-autocomplete>
|
||||||
<vn-textfield
|
<vn-textfield
|
||||||
label="Tax number"
|
label="Tax number"
|
||||||
ng-model="$ctrl.client.fi"
|
ng-model="$ctrl.client.fi"
|
||||||
rule>
|
rule>
|
||||||
</vn-textfield>
|
</vn-textfield>
|
||||||
</vn-horizontal>
|
</vn-horizontal>
|
||||||
|
<vn-horizontal>
|
||||||
|
<vn-textfield
|
||||||
|
label="Business name"
|
||||||
|
ng-model="$ctrl.client.socialName"
|
||||||
|
rule>
|
||||||
|
</vn-textfield>
|
||||||
|
</vn-horizontal>
|
||||||
<vn-horizontal>
|
<vn-horizontal>
|
||||||
<vn-textfield
|
<vn-textfield
|
||||||
vn-two
|
vn-two
|
||||||
|
|
|
@ -56,7 +56,7 @@ module.exports = Self => {
|
||||||
{
|
{
|
||||||
relation: 'client',
|
relation: 'client',
|
||||||
scope: {
|
scope: {
|
||||||
fields: ['id', 'socialName']
|
fields: ['id', 'socialName', 'email']
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
|
|
|
@ -0,0 +1,139 @@
|
||||||
|
|
||||||
|
<vn-icon-button
|
||||||
|
icon="more_vert"
|
||||||
|
vn-popover="menu">
|
||||||
|
</vn-icon-button>
|
||||||
|
<vn-menu vn-id="menu">
|
||||||
|
<vn-list>
|
||||||
|
<vn-item class="dropdown"
|
||||||
|
vn-click-stop="showInvoiceMenu.show($event, 'left')"
|
||||||
|
name="showInvoicePdf"
|
||||||
|
translate>
|
||||||
|
Show invoice...
|
||||||
|
|
||||||
|
<vn-menu vn-id="showInvoiceMenu">
|
||||||
|
<vn-list>
|
||||||
|
<a class="vn-item"
|
||||||
|
href="api/InvoiceOuts/{{$ctrl.id}}/download?access_token={{$ctrl.vnToken.token}}"
|
||||||
|
target="_blank"
|
||||||
|
name="showInvoicePdf"
|
||||||
|
translate>
|
||||||
|
Show as PDF
|
||||||
|
</a>
|
||||||
|
<vn-item
|
||||||
|
ng-click="$ctrl.showCsvInvoice()"
|
||||||
|
translate>
|
||||||
|
Show as CSV
|
||||||
|
</vn-item>
|
||||||
|
</vn-list>
|
||||||
|
</vn-menu>
|
||||||
|
</vn-item>
|
||||||
|
<vn-item class="dropdown"
|
||||||
|
vn-click-stop="sendInvoiceMenu.show($event, 'left')"
|
||||||
|
name="sendInvoice"
|
||||||
|
translate>
|
||||||
|
Send invoice...
|
||||||
|
|
||||||
|
<vn-menu vn-id="sendInvoiceMenu">
|
||||||
|
<vn-list>
|
||||||
|
<vn-item
|
||||||
|
ng-click="sendPdfConfirmation.show({email: $ctrl.invoiceOut.client.email})"
|
||||||
|
translate>
|
||||||
|
Send PDF
|
||||||
|
</vn-item>
|
||||||
|
<vn-item
|
||||||
|
ng-click="sendCsvConfirmation.show({email: $ctrl.invoiceOut.client.email})"
|
||||||
|
translate>
|
||||||
|
Send CSV
|
||||||
|
</vn-item>
|
||||||
|
</vn-list>
|
||||||
|
</vn-menu>
|
||||||
|
</vn-item>
|
||||||
|
<vn-item
|
||||||
|
ng-click="deleteConfirmation.show()"
|
||||||
|
vn-acl="invoicing"
|
||||||
|
vn-acl-action="remove"
|
||||||
|
name="deleteInvoice"
|
||||||
|
translate>
|
||||||
|
Delete Invoice
|
||||||
|
</vn-item>
|
||||||
|
<vn-item
|
||||||
|
ng-click="bookConfirmation.show()"
|
||||||
|
vn-acl="invoicing"
|
||||||
|
vn-acl-action="remove"
|
||||||
|
name="bookInvoice"
|
||||||
|
translate>
|
||||||
|
Book invoice
|
||||||
|
</vn-item>
|
||||||
|
<vn-item
|
||||||
|
ng-click="createInvoicePdfConfirmation.show()"
|
||||||
|
ng-show="$ctrl.hasInvoicing || !$ctrl.invoiceOut.hasPdf"
|
||||||
|
name="regenerateInvoice"
|
||||||
|
translate>
|
||||||
|
{{!$ctrl.invoiceOut.hasPdf ? 'Generate PDF invoice': 'Regenerate PDF invoice'}}
|
||||||
|
</vn-item>
|
||||||
|
<vn-item
|
||||||
|
ng-click="$ctrl.showExportationLetter()"
|
||||||
|
ng-show="$ctrl.invoiceOut.serial == 'E'"
|
||||||
|
translate>
|
||||||
|
Show CIES letter
|
||||||
|
</vn-item>
|
||||||
|
</vn-list>
|
||||||
|
</vn-menu>
|
||||||
|
<vn-confirm
|
||||||
|
vn-id="deleteConfirmation"
|
||||||
|
on-accept="$ctrl.deleteInvoiceOut()"
|
||||||
|
question="Are you sure you want to delete this invoice?">
|
||||||
|
</vn-confirm>
|
||||||
|
<vn-confirm
|
||||||
|
vn-id="bookConfirmation"
|
||||||
|
on-accept="$ctrl.bookInvoiceOut()"
|
||||||
|
question="Are you sure you want to book this invoice?">
|
||||||
|
</vn-confirm>
|
||||||
|
<vn-client-descriptor-popover
|
||||||
|
vn-id="clientDescriptor">
|
||||||
|
</vn-client-descriptor-popover>
|
||||||
|
|
||||||
|
<!-- Create invoice PDF confirmation dialog -->
|
||||||
|
<vn-confirm
|
||||||
|
vn-id="createInvoicePdfConfirmation"
|
||||||
|
on-accept="$ctrl.createPdfInvoice()"
|
||||||
|
question="Are you sure you want to generate/regenerate the PDF invoice?"
|
||||||
|
message="Generate PDF invoice document">
|
||||||
|
</vn-confirm>
|
||||||
|
|
||||||
|
<!-- Send PDF invoice confirmation popup -->
|
||||||
|
<vn-dialog
|
||||||
|
vn-id="sendPdfConfirmation"
|
||||||
|
on-accept="$ctrl.sendPdfInvoice($data)"
|
||||||
|
message="Send PDF invoice">
|
||||||
|
<tpl-body>
|
||||||
|
<span translate>Are you sure you want to send it?</span>
|
||||||
|
<vn-textfield vn-one
|
||||||
|
label="Email"
|
||||||
|
ng-model="sendPdfConfirmation.data.email">
|
||||||
|
</vn-textfield>
|
||||||
|
</tpl-body>
|
||||||
|
<tpl-buttons>
|
||||||
|
<input type="button" response="cancel" translate-attr="{value: 'Cancel'}"/>
|
||||||
|
<button response="accept" translate>Confirm</button>
|
||||||
|
</tpl-buttons>
|
||||||
|
</vn-dialog>
|
||||||
|
|
||||||
|
<!-- Send CSV invoice confirmation popup -->
|
||||||
|
<vn-dialog
|
||||||
|
vn-id="sendCsvConfirmation"
|
||||||
|
on-accept="$ctrl.sendCsvInvoice($data)"
|
||||||
|
message="Send CSV invoice">
|
||||||
|
<tpl-body>
|
||||||
|
<span translate>Are you sure you want to send it?</span>
|
||||||
|
<vn-textfield vn-one
|
||||||
|
label="Email"
|
||||||
|
ng-model="sendCsvConfirmation.data.email">
|
||||||
|
</vn-textfield>
|
||||||
|
</tpl-body>
|
||||||
|
<tpl-buttons>
|
||||||
|
<input type="button" response="cancel" translate-attr="{value: 'Cancel'}"/>
|
||||||
|
<button response="accept" translate>Confirm</button>
|
||||||
|
</tpl-buttons>
|
||||||
|
</vn-dialog>
|
|
@ -0,0 +1,121 @@
|
||||||
|
import ngModule from '../module';
|
||||||
|
import Section from 'salix/components/section';
|
||||||
|
import './style.scss';
|
||||||
|
|
||||||
|
class Controller extends Section {
|
||||||
|
constructor($element, $, vnReport, vnEmail) {
|
||||||
|
super($element, $);
|
||||||
|
this.vnReport = vnReport;
|
||||||
|
this.vnEmail = vnEmail;
|
||||||
|
}
|
||||||
|
|
||||||
|
get invoiceOut() {
|
||||||
|
return this._invoiceOut;
|
||||||
|
}
|
||||||
|
|
||||||
|
set invoiceOut(value) {
|
||||||
|
this._invoiceOut = value;
|
||||||
|
if (value)
|
||||||
|
this.id = value.id;
|
||||||
|
}
|
||||||
|
|
||||||
|
loadData() {
|
||||||
|
const filter = {
|
||||||
|
include: [
|
||||||
|
{
|
||||||
|
relation: 'company',
|
||||||
|
scope: {
|
||||||
|
fields: ['id', 'code']
|
||||||
|
}
|
||||||
|
}, {
|
||||||
|
relation: 'client',
|
||||||
|
scope: {
|
||||||
|
fields: ['id', 'name', 'email']
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
};
|
||||||
|
return this.$http.get(`InvoiceOuts/${this.invoiceOut.id}`, {filter})
|
||||||
|
.then(res => this.invoiceOut = res.data);
|
||||||
|
}
|
||||||
|
reload() {
|
||||||
|
return this.loadData().then(() => {
|
||||||
|
if (this.parentReload)
|
||||||
|
this.parentReload();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
cardReload() {
|
||||||
|
// Prevents error when not defined
|
||||||
|
}
|
||||||
|
|
||||||
|
deleteInvoiceOut() {
|
||||||
|
return this.$http.post(`InvoiceOuts/${this.invoiceOut.id}/delete`)
|
||||||
|
.then(() => this.$state.go('invoiceOut.index'))
|
||||||
|
.then(() => this.$state.reload())
|
||||||
|
.then(() => this.vnApp.showSuccess(this.$t('InvoiceOut deleted')));
|
||||||
|
}
|
||||||
|
|
||||||
|
bookInvoiceOut() {
|
||||||
|
return this.$http.post(`InvoiceOuts/${this.invoiceOut.ref}/book`)
|
||||||
|
.then(() => this.$state.reload())
|
||||||
|
.then(() => this.vnApp.showSuccess(this.$t('InvoiceOut booked')));
|
||||||
|
}
|
||||||
|
|
||||||
|
createPdfInvoice() {
|
||||||
|
return this.$http.post(`InvoiceOuts/${this.id}/createPdf`)
|
||||||
|
.then(() => this.reload())
|
||||||
|
.then(() => {
|
||||||
|
const snackbarMessage = this.$t(
|
||||||
|
`The invoice PDF document has been regenerated`);
|
||||||
|
this.vnApp.showSuccess(snackbarMessage);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
showCsvInvoice() {
|
||||||
|
this.vnReport.showCsv('invoice', {
|
||||||
|
recipientId: this.invoiceOut.client.id,
|
||||||
|
invoiceId: this.id
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
sendPdfInvoice($data) {
|
||||||
|
if (!$data.email)
|
||||||
|
return this.vnApp.showError(this.$t(`The email can't be empty`));
|
||||||
|
|
||||||
|
return this.vnEmail.send('invoice', {
|
||||||
|
recipientId: this.invoiceOut.client.id,
|
||||||
|
recipient: $data.email,
|
||||||
|
invoiceId: this.id
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
sendCsvInvoice($data) {
|
||||||
|
if (!$data.email)
|
||||||
|
return this.vnApp.showError(this.$t(`The email can't be empty`));
|
||||||
|
|
||||||
|
return this.vnEmail.sendCsv('invoice', {
|
||||||
|
recipientId: this.invoiceOut.client.id,
|
||||||
|
recipient: $data.email,
|
||||||
|
invoiceId: this.id
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
showExportationLetter() {
|
||||||
|
this.vnReport.show('exportation', {
|
||||||
|
recipientId: this.invoiceOut.client.id,
|
||||||
|
invoiceId: this.id
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Controller.$inject = ['$element', '$scope', 'vnReport', 'vnEmail'];
|
||||||
|
|
||||||
|
ngModule.vnComponent('vnInvoiceOutDescriptorMenu', {
|
||||||
|
template: require('./index.html'),
|
||||||
|
controller: Controller,
|
||||||
|
bindings: {
|
||||||
|
invoiceOut: '<',
|
||||||
|
parentReload: '&'
|
||||||
|
}
|
||||||
|
});
|
|
@ -0,0 +1,96 @@
|
||||||
|
import './index';
|
||||||
|
|
||||||
|
describe('vnInvoiceOutDescriptorMenu', () => {
|
||||||
|
let controller;
|
||||||
|
let $httpBackend;
|
||||||
|
let $httpParamSerializer;
|
||||||
|
const invoiceOut = {
|
||||||
|
id: 1,
|
||||||
|
client: {id: 1101}
|
||||||
|
};
|
||||||
|
|
||||||
|
beforeEach(ngModule('invoiceOut'));
|
||||||
|
|
||||||
|
beforeEach(inject(($componentController, _$httpParamSerializer_, _$httpBackend_) => {
|
||||||
|
$httpBackend = _$httpBackend_;
|
||||||
|
$httpParamSerializer = _$httpParamSerializer_;
|
||||||
|
controller = $componentController('vnInvoiceOutDescriptorMenu', {$element: null});
|
||||||
|
}));
|
||||||
|
|
||||||
|
describe('createPdfInvoice()', () => {
|
||||||
|
it('should make a query to the createPdf() endpoint and show a success snackbar', () => {
|
||||||
|
jest.spyOn(controller.vnApp, 'showSuccess');
|
||||||
|
|
||||||
|
controller.invoiceOut = invoiceOut;
|
||||||
|
|
||||||
|
$httpBackend.whenGET(`InvoiceOuts/${invoiceOut.id}`).respond();
|
||||||
|
$httpBackend.expectPOST(`InvoiceOuts/${invoiceOut.id}/createPdf`).respond();
|
||||||
|
controller.createPdfInvoice();
|
||||||
|
$httpBackend.flush();
|
||||||
|
|
||||||
|
expect(controller.vnApp.showSuccess).toHaveBeenCalled();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('showCsvInvoice()', () => {
|
||||||
|
it('should make a query to the csv invoice download endpoint and show a message snackbar', () => {
|
||||||
|
jest.spyOn(window, 'open').mockReturnThis();
|
||||||
|
|
||||||
|
controller.invoiceOut = invoiceOut;
|
||||||
|
|
||||||
|
const expectedParams = {
|
||||||
|
invoiceId: invoiceOut.id,
|
||||||
|
recipientId: invoiceOut.client.id
|
||||||
|
};
|
||||||
|
const serializedParams = $httpParamSerializer(expectedParams);
|
||||||
|
const expectedPath = `api/csv/invoice/download?${serializedParams}`;
|
||||||
|
controller.showCsvInvoice();
|
||||||
|
|
||||||
|
expect(window.open).toHaveBeenCalledWith(expectedPath);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('sendPdfInvoice()', () => {
|
||||||
|
it('should make a query to the email invoice endpoint and show a message snackbar', () => {
|
||||||
|
jest.spyOn(controller.vnApp, 'showMessage');
|
||||||
|
|
||||||
|
controller.invoiceOut = invoiceOut;
|
||||||
|
|
||||||
|
const $data = {email: 'brucebanner@gothamcity.com'};
|
||||||
|
const expectedParams = {
|
||||||
|
invoiceId: invoiceOut.id,
|
||||||
|
recipient: $data.email,
|
||||||
|
recipientId: invoiceOut.client.id
|
||||||
|
};
|
||||||
|
const serializedParams = $httpParamSerializer(expectedParams);
|
||||||
|
|
||||||
|
$httpBackend.expectGET(`email/invoice?${serializedParams}`).respond();
|
||||||
|
controller.sendPdfInvoice($data);
|
||||||
|
$httpBackend.flush();
|
||||||
|
|
||||||
|
expect(controller.vnApp.showMessage).toHaveBeenCalled();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('sendCsvInvoice()', () => {
|
||||||
|
it('should make a query to the csv invoice send endpoint and show a message snackbar', () => {
|
||||||
|
jest.spyOn(controller.vnApp, 'showMessage');
|
||||||
|
|
||||||
|
controller.invoiceOut = invoiceOut;
|
||||||
|
|
||||||
|
const $data = {email: 'brucebanner@gothamcity.com'};
|
||||||
|
const expectedParams = {
|
||||||
|
invoiceId: invoiceOut.id,
|
||||||
|
recipient: $data.email,
|
||||||
|
recipientId: invoiceOut.client.id
|
||||||
|
};
|
||||||
|
const serializedParams = $httpParamSerializer(expectedParams);
|
||||||
|
|
||||||
|
$httpBackend.expectGET(`csv/invoice/send?${serializedParams}`).respond();
|
||||||
|
controller.sendCsvInvoice($data);
|
||||||
|
$httpBackend.flush();
|
||||||
|
|
||||||
|
expect(controller.vnApp.showMessage).toHaveBeenCalled();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
|
@ -0,0 +1,17 @@
|
||||||
|
Show invoice...: Ver factura...
|
||||||
|
Send invoice...: Enviar factura...
|
||||||
|
Send PDF invoice: Enviar factura en PDF
|
||||||
|
Send CSV invoice: Enviar factura en CSV
|
||||||
|
Delete Invoice: Eliminar factura
|
||||||
|
Clone Invoice: Clonar factura
|
||||||
|
Book invoice: Asentar factura
|
||||||
|
Generate PDF invoice: Generar PDF factura
|
||||||
|
Show CIES letter: Ver carta CIES
|
||||||
|
InvoiceOut deleted: Factura eliminada
|
||||||
|
Are you sure you want to delete this invoice?: Estas seguro de eliminar esta factura?
|
||||||
|
Are you sure you want to clone this invoice?: Estas seguro de clonar esta factura?
|
||||||
|
InvoiceOut booked: Factura asentada
|
||||||
|
Are you sure you want to book this invoice?: Estas seguro de querer asentar esta factura?
|
||||||
|
Regenerate PDF invoice: Regenerar PDF factura
|
||||||
|
The invoice PDF document has been regenerated: El documento PDF de la factura ha sido regenerado
|
||||||
|
The email can't be empty: El correo no puede estar vacío
|
|
@ -0,0 +1,24 @@
|
||||||
|
@import "./effects";
|
||||||
|
@import "variables";
|
||||||
|
|
||||||
|
vn-invoice-out-descriptor-menu {
|
||||||
|
& > vn-icon-button[icon="more_vert"] {
|
||||||
|
display: flex;
|
||||||
|
min-width: 45px;
|
||||||
|
height: 45px;
|
||||||
|
box-sizing: border-box;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
}
|
||||||
|
& > vn-icon-button[icon="more_vert"] {
|
||||||
|
@extend %clickable;
|
||||||
|
color: inherit;
|
||||||
|
|
||||||
|
& > vn-icon {
|
||||||
|
padding: 10px;
|
||||||
|
}
|
||||||
|
vn-icon {
|
||||||
|
font-size: 1.75rem;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,81 +1,12 @@
|
||||||
<vn-descriptor-content
|
<vn-descriptor-content
|
||||||
module="invoiceOut"
|
module="invoiceOut"
|
||||||
description="$ctrl.invoiceOut.ref">
|
description="$ctrl.invoiceOut.ref">
|
||||||
<slot-menu>
|
<slot-dot-menu>
|
||||||
<vn-item class="dropdown"
|
<vn-invoice-out-descriptor-menu
|
||||||
vn-click-stop="showInvoiceMenu.show($event, 'left')"
|
invoice-out="$ctrl.invoiceOut"
|
||||||
name="showInvoicePdf"
|
parent-reload="$ctrl.reload()"
|
||||||
translate>
|
/>
|
||||||
Show invoice...
|
</slot-dot-menu>
|
||||||
|
|
||||||
<vn-menu vn-id="showInvoiceMenu">
|
|
||||||
<vn-list>
|
|
||||||
<a class="vn-item"
|
|
||||||
href="api/InvoiceOuts/{{$ctrl.id}}/download?access_token={{$ctrl.vnToken.token}}"
|
|
||||||
target="_blank"
|
|
||||||
name="showInvoicePdf"
|
|
||||||
translate>
|
|
||||||
Show as PDF
|
|
||||||
</a>
|
|
||||||
<vn-item
|
|
||||||
ng-click="$ctrl.showCsvInvoice()"
|
|
||||||
translate>
|
|
||||||
Show as CSV
|
|
||||||
</vn-item>
|
|
||||||
</vn-list>
|
|
||||||
</vn-menu>
|
|
||||||
</vn-item>
|
|
||||||
<vn-item class="dropdown"
|
|
||||||
vn-click-stop="sendInvoiceMenu.show($event, 'left')"
|
|
||||||
name="sendInvoice"
|
|
||||||
translate>
|
|
||||||
Send invoice...
|
|
||||||
|
|
||||||
<vn-menu vn-id="sendInvoiceMenu">
|
|
||||||
<vn-list>
|
|
||||||
<vn-item
|
|
||||||
ng-click="sendPdfConfirmation.show({email: $ctrl.invoiceOut.client.email})"
|
|
||||||
translate>
|
|
||||||
Send PDF
|
|
||||||
</vn-item>
|
|
||||||
<vn-item
|
|
||||||
ng-click="sendCsvConfirmation.show({email: $ctrl.invoiceOut.client.email})"
|
|
||||||
translate>
|
|
||||||
Send CSV
|
|
||||||
</vn-item>
|
|
||||||
</vn-list>
|
|
||||||
</vn-menu>
|
|
||||||
</vn-item>
|
|
||||||
<vn-item
|
|
||||||
ng-click="deleteConfirmation.show()"
|
|
||||||
vn-acl="invoicing"
|
|
||||||
vn-acl-action="remove"
|
|
||||||
name="deleteInvoice"
|
|
||||||
translate>
|
|
||||||
Delete Invoice
|
|
||||||
</vn-item>
|
|
||||||
<vn-item
|
|
||||||
ng-click="bookConfirmation.show()"
|
|
||||||
vn-acl="invoicing"
|
|
||||||
vn-acl-action="remove"
|
|
||||||
name="bookInvoice"
|
|
||||||
translate>
|
|
||||||
Book invoice
|
|
||||||
</vn-item>
|
|
||||||
<vn-item
|
|
||||||
ng-click="createInvoicePdfConfirmation.show()"
|
|
||||||
ng-show="$ctrl.hasInvoicing || !$ctrl.invoiceOut.hasPdf"
|
|
||||||
name="regenerateInvoice"
|
|
||||||
translate>
|
|
||||||
{{!$ctrl.invoiceOut.hasPdf ? 'Generate PDF invoice': 'Regenerate PDF invoice'}}
|
|
||||||
</vn-item>
|
|
||||||
<vn-item
|
|
||||||
ng-click="$ctrl.showExportationLetter()"
|
|
||||||
ng-show="$ctrl.invoiceOut.serial == 'E'"
|
|
||||||
translate>
|
|
||||||
Show CIES letter
|
|
||||||
</vn-item>
|
|
||||||
</slot-menu>
|
|
||||||
<slot-body>
|
<slot-body>
|
||||||
<div class="attributes">
|
<div class="attributes">
|
||||||
<vn-label-value
|
<vn-label-value
|
||||||
|
@ -119,58 +50,3 @@
|
||||||
</div>
|
</div>
|
||||||
</slot-body>
|
</slot-body>
|
||||||
</vn-descriptor-content>
|
</vn-descriptor-content>
|
||||||
<vn-confirm
|
|
||||||
vn-id="deleteConfirmation"
|
|
||||||
on-accept="$ctrl.deleteInvoiceOut()"
|
|
||||||
question="Are you sure you want to delete this invoice?">
|
|
||||||
</vn-confirm>
|
|
||||||
<vn-confirm
|
|
||||||
vn-id="bookConfirmation"
|
|
||||||
on-accept="$ctrl.bookInvoiceOut()"
|
|
||||||
question="Are you sure you want to book this invoice?">
|
|
||||||
</vn-confirm>
|
|
||||||
<vn-client-descriptor-popover
|
|
||||||
vn-id="clientDescriptor">
|
|
||||||
</vn-client-descriptor-popover>
|
|
||||||
|
|
||||||
<!-- Create invoice PDF confirmation dialog -->
|
|
||||||
<vn-confirm
|
|
||||||
vn-id="createInvoicePdfConfirmation"
|
|
||||||
on-accept="$ctrl.createPdfInvoice()"
|
|
||||||
question="Are you sure you want to generate/regenerate the PDF invoice?"
|
|
||||||
message="Generate PDF invoice document">
|
|
||||||
</vn-confirm>
|
|
||||||
|
|
||||||
<!-- Send PDF invoice confirmation popup -->
|
|
||||||
<vn-dialog
|
|
||||||
vn-id="sendPdfConfirmation"
|
|
||||||
on-accept="$ctrl.sendPdfInvoice($data)"
|
|
||||||
message="Send PDF invoice">
|
|
||||||
<tpl-body>
|
|
||||||
<span translate>Are you sure you want to send it?</span>
|
|
||||||
<vn-textfield vn-one
|
|
||||||
ng-model="sendPdfConfirmation.data.email">
|
|
||||||
</vn-textfield>
|
|
||||||
</tpl-body>
|
|
||||||
<tpl-buttons>
|
|
||||||
<input type="button" response="cancel" translate-attr="{value: 'Cancel'}"/>
|
|
||||||
<button response="accept" translate>Confirm</button>
|
|
||||||
</tpl-buttons>
|
|
||||||
</vn-dialog>
|
|
||||||
|
|
||||||
<!-- Send CSV invoice confirmation popup -->
|
|
||||||
<vn-dialog
|
|
||||||
vn-id="sendCsvConfirmation"
|
|
||||||
on-accept="$ctrl.sendCsvInvoice($data)"
|
|
||||||
message="Send CSV invoice">
|
|
||||||
<tpl-body>
|
|
||||||
<span translate>Are you sure you want to send it?</span>
|
|
||||||
<vn-textfield vn-one
|
|
||||||
ng-model="sendCsvConfirmation.data.email">
|
|
||||||
</vn-textfield>
|
|
||||||
</tpl-body>
|
|
||||||
<tpl-buttons>
|
|
||||||
<input type="button" response="cancel" translate-attr="{value: 'Cancel'}"/>
|
|
||||||
<button response="accept" translate>Confirm</button>
|
|
||||||
</tpl-buttons>
|
|
||||||
</vn-dialog>
|
|
|
@ -41,70 +41,6 @@ class Controller extends Descriptor {
|
||||||
return this.getData(`InvoiceOuts/${this.id}`, {filter})
|
return this.getData(`InvoiceOuts/${this.id}`, {filter})
|
||||||
.then(res => this.entity = res.data);
|
.then(res => this.entity = res.data);
|
||||||
}
|
}
|
||||||
|
|
||||||
reload() {
|
|
||||||
return this.loadData().then(() => {
|
|
||||||
if (this.cardReload)
|
|
||||||
this.cardReload();
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
cardReload() {
|
|
||||||
// Prevents error when not defined
|
|
||||||
}
|
|
||||||
|
|
||||||
deleteInvoiceOut() {
|
|
||||||
return this.$http.post(`InvoiceOuts/${this.id}/delete`)
|
|
||||||
.then(() => this.$state.go('invoiceOut.index'))
|
|
||||||
.then(() => this.vnApp.showSuccess(this.$t('InvoiceOut deleted')));
|
|
||||||
}
|
|
||||||
|
|
||||||
bookInvoiceOut() {
|
|
||||||
return this.$http.post(`InvoiceOuts/${this.invoiceOut.ref}/book`)
|
|
||||||
.then(() => this.$state.reload())
|
|
||||||
.then(() => this.vnApp.showSuccess(this.$t('InvoiceOut booked')));
|
|
||||||
}
|
|
||||||
|
|
||||||
createPdfInvoice() {
|
|
||||||
const invoiceId = this.invoiceOut.id;
|
|
||||||
return this.$http.post(`InvoiceOuts/${invoiceId}/createPdf`)
|
|
||||||
.then(() => this.reload())
|
|
||||||
.then(() => {
|
|
||||||
const snackbarMessage = this.$t(
|
|
||||||
`The invoice PDF document has been regenerated`);
|
|
||||||
this.vnApp.showSuccess(snackbarMessage);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
showCsvInvoice() {
|
|
||||||
this.vnReport.showCsv('invoice', {
|
|
||||||
recipientId: this.invoiceOut.client.id,
|
|
||||||
invoiceId: this.id,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
sendPdfInvoice($data) {
|
|
||||||
return this.vnEmail.send('invoice', {
|
|
||||||
recipientId: this.invoiceOut.client.id,
|
|
||||||
recipient: $data.email,
|
|
||||||
invoiceId: this.id
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
sendCsvInvoice($data) {
|
|
||||||
return this.vnEmail.sendCsv('invoice', {
|
|
||||||
recipientId: this.invoiceOut.client.id,
|
|
||||||
recipient: $data.email,
|
|
||||||
invoiceId: this.id
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
showExportationLetter() {
|
|
||||||
this.vnReport.show('exportation', {
|
|
||||||
recipientId: this.invoiceOut.client.id,
|
|
||||||
invoiceId: this.id,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
ngModule.vnComponent('vnInvoiceOutDescriptor', {
|
ngModule.vnComponent('vnInvoiceOutDescriptor', {
|
||||||
|
@ -112,6 +48,5 @@ ngModule.vnComponent('vnInvoiceOutDescriptor', {
|
||||||
controller: Controller,
|
controller: Controller,
|
||||||
bindings: {
|
bindings: {
|
||||||
invoiceOut: '<',
|
invoiceOut: '<',
|
||||||
cardReload: '&'
|
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
|
@ -3,17 +3,11 @@ import './index';
|
||||||
describe('vnInvoiceOutDescriptor', () => {
|
describe('vnInvoiceOutDescriptor', () => {
|
||||||
let controller;
|
let controller;
|
||||||
let $httpBackend;
|
let $httpBackend;
|
||||||
let $httpParamSerializer;
|
|
||||||
const invoiceOut = {
|
|
||||||
id: 1,
|
|
||||||
client: {id: 1101}
|
|
||||||
};
|
|
||||||
|
|
||||||
beforeEach(ngModule('invoiceOut'));
|
beforeEach(ngModule('invoiceOut'));
|
||||||
|
|
||||||
beforeEach(inject(($componentController, _$httpParamSerializer_, _$httpBackend_) => {
|
beforeEach(inject(($componentController, _$httpBackend_) => {
|
||||||
$httpBackend = _$httpBackend_;
|
$httpBackend = _$httpBackend_;
|
||||||
$httpParamSerializer = _$httpParamSerializer_;
|
|
||||||
controller = $componentController('vnInvoiceOutDescriptor', {$element: null});
|
controller = $componentController('vnInvoiceOutDescriptor', {$element: null});
|
||||||
}));
|
}));
|
||||||
|
|
||||||
|
@ -29,81 +23,4 @@ describe('vnInvoiceOutDescriptor', () => {
|
||||||
expect(controller.invoiceOut).toEqual(response);
|
expect(controller.invoiceOut).toEqual(response);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
describe('createPdfInvoice()', () => {
|
|
||||||
it('should make a query to the createPdf() endpoint and show a success snackbar', () => {
|
|
||||||
jest.spyOn(controller.vnApp, 'showSuccess');
|
|
||||||
|
|
||||||
controller.invoiceOut = invoiceOut;
|
|
||||||
|
|
||||||
$httpBackend.whenGET(`InvoiceOuts/${invoiceOut.id}`).respond();
|
|
||||||
$httpBackend.expectPOST(`InvoiceOuts/${invoiceOut.id}/createPdf`).respond();
|
|
||||||
controller.createPdfInvoice();
|
|
||||||
$httpBackend.flush();
|
|
||||||
|
|
||||||
expect(controller.vnApp.showSuccess).toHaveBeenCalled();
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
describe('showCsvInvoice()', () => {
|
|
||||||
it('should make a query to the csv invoice download endpoint and show a message snackbar', () => {
|
|
||||||
jest.spyOn(window, 'open').mockReturnThis();
|
|
||||||
|
|
||||||
controller.invoiceOut = invoiceOut;
|
|
||||||
|
|
||||||
const expectedParams = {
|
|
||||||
invoiceId: invoiceOut.id,
|
|
||||||
recipientId: invoiceOut.client.id
|
|
||||||
};
|
|
||||||
const serializedParams = $httpParamSerializer(expectedParams);
|
|
||||||
const expectedPath = `api/csv/invoice/download?${serializedParams}`;
|
|
||||||
controller.showCsvInvoice();
|
|
||||||
|
|
||||||
expect(window.open).toHaveBeenCalledWith(expectedPath);
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
describe('sendPdfInvoice()', () => {
|
|
||||||
it('should make a query to the email invoice endpoint and show a message snackbar', () => {
|
|
||||||
jest.spyOn(controller.vnApp, 'showMessage');
|
|
||||||
|
|
||||||
controller.invoiceOut = invoiceOut;
|
|
||||||
|
|
||||||
const $data = {email: 'brucebanner@gothamcity.com'};
|
|
||||||
const expectedParams = {
|
|
||||||
invoiceId: invoiceOut.id,
|
|
||||||
recipient: $data.email,
|
|
||||||
recipientId: invoiceOut.client.id
|
|
||||||
};
|
|
||||||
const serializedParams = $httpParamSerializer(expectedParams);
|
|
||||||
|
|
||||||
$httpBackend.expectGET(`email/invoice?${serializedParams}`).respond();
|
|
||||||
controller.sendPdfInvoice($data);
|
|
||||||
$httpBackend.flush();
|
|
||||||
|
|
||||||
expect(controller.vnApp.showMessage).toHaveBeenCalled();
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
describe('sendCsvInvoice()', () => {
|
|
||||||
it('should make a query to the csv invoice send endpoint and show a message snackbar', () => {
|
|
||||||
jest.spyOn(controller.vnApp, 'showMessage');
|
|
||||||
|
|
||||||
controller.invoiceOut = invoiceOut;
|
|
||||||
|
|
||||||
const $data = {email: 'brucebanner@gothamcity.com'};
|
|
||||||
const expectedParams = {
|
|
||||||
invoiceId: invoiceOut.id,
|
|
||||||
recipient: $data.email,
|
|
||||||
recipientId: invoiceOut.client.id
|
|
||||||
};
|
|
||||||
const serializedParams = $httpParamSerializer(expectedParams);
|
|
||||||
|
|
||||||
$httpBackend.expectGET(`csv/invoice/send?${serializedParams}`).respond();
|
|
||||||
controller.sendCsvInvoice($data);
|
|
||||||
$httpBackend.flush();
|
|
||||||
|
|
||||||
expect(controller.vnApp.showMessage).toHaveBeenCalled();
|
|
||||||
});
|
|
||||||
});
|
|
||||||
});
|
});
|
||||||
|
|
|
@ -1,20 +1,2 @@
|
||||||
Volume exceded: Volumen excedido
|
|
||||||
Volume: Volumen
|
|
||||||
Client card: Ficha del cliente
|
Client card: Ficha del cliente
|
||||||
Invoice ticket list: Listado de tickets de la factura
|
Invoice ticket list: Listado de tickets de la factura
|
||||||
Show invoice...: Ver factura...
|
|
||||||
Send invoice...: Enviar factura...
|
|
||||||
Send PDF invoice: Enviar factura en PDF
|
|
||||||
Send CSV invoice: Enviar factura en CSV
|
|
||||||
Delete Invoice: Eliminar factura
|
|
||||||
Clone Invoice: Clonar factura
|
|
||||||
Book invoice: Asentar factura
|
|
||||||
Generate PDF invoice: Generar PDF factura
|
|
||||||
Show CIES letter: Ver carta CIES
|
|
||||||
InvoiceOut deleted: Factura eliminada
|
|
||||||
Are you sure you want to delete this invoice?: Estas seguro de eliminar esta factura?
|
|
||||||
Are you sure you want to clone this invoice?: Estas seguro de clonar esta factura?
|
|
||||||
InvoiceOut booked: Factura asentada
|
|
||||||
Are you sure you want to book this invoice?: Estas seguro de querer asentar esta factura?
|
|
||||||
Regenerate PDF invoice: Regenerar PDF factura
|
|
||||||
The invoice PDF document has been regenerated: El documento PDF de la factura ha sido regenerado
|
|
|
@ -7,5 +7,6 @@ import './summary';
|
||||||
import './card';
|
import './card';
|
||||||
import './descriptor';
|
import './descriptor';
|
||||||
import './descriptor-popover';
|
import './descriptor-popover';
|
||||||
|
import './descriptor-menu';
|
||||||
import './index/manual';
|
import './index/manual';
|
||||||
import './index/global-invoicing';
|
import './index/global-invoicing';
|
||||||
|
|
|
@ -13,6 +13,10 @@
|
||||||
<vn-icon-button icon="launch"></vn-icon-button>
|
<vn-icon-button icon="launch"></vn-icon-button>
|
||||||
</a>
|
</a>
|
||||||
<span>{{$ctrl.summary.invoiceOut.ref}} - {{$ctrl.summary.invoiceOut.client.socialName}}</span>
|
<span>{{$ctrl.summary.invoiceOut.ref}} - {{$ctrl.summary.invoiceOut.client.socialName}}</span>
|
||||||
|
<vn-invoice-out-descriptor-menu
|
||||||
|
invoice-out="$ctrl.summary.invoiceOut"
|
||||||
|
parent-reload="$ctrl.reload()"
|
||||||
|
/>
|
||||||
</h5>
|
</h5>
|
||||||
<vn-horizontal>
|
<vn-horizontal>
|
||||||
<vn-one>
|
<vn-one>
|
||||||
|
|
|
@ -50,7 +50,7 @@
|
||||||
on-last="$ctrl.scrollToLine(sale.lastPreparedLineFk)"
|
on-last="$ctrl.scrollToLine(sale.lastPreparedLineFk)"
|
||||||
ng-attr-id="vnItemDiary-{{::sale.lineFk}}">
|
ng-attr-id="vnItemDiary-{{::sale.lineFk}}">
|
||||||
<vn-td shrink>
|
<vn-td shrink>
|
||||||
<a ui-sref="claim.card.basicData({id: sale.claimFk})">
|
<a ui-sref="claim.card.summary({id: sale.claimFk})">
|
||||||
<vn-icon icon="icon-claims"
|
<vn-icon icon="icon-claims"
|
||||||
ng-show="sale.claimFk"
|
ng-show="sale.claimFk"
|
||||||
vn-tooltip="{{::$ctrl.$t('Claim')}}: {{::sale.claimFk}}">
|
vn-tooltip="{{::$ctrl.$t('Claim')}}: {{::sale.claimFk}}">
|
||||||
|
|
|
@ -1,12 +1,13 @@
|
||||||
const app = require('vn-loopback/server/server');
|
const models = require('vn-loopback/server/server').models;
|
||||||
|
const LoopBackContext = require('loopback-context');
|
||||||
|
|
||||||
describe('loopback model Supplier', () => {
|
describe('loopback model Supplier', () => {
|
||||||
let supplierOne;
|
let supplierOne;
|
||||||
let supplierTwo;
|
let supplierTwo;
|
||||||
|
|
||||||
beforeAll(async() => {
|
beforeAll(async() => {
|
||||||
supplierOne = await app.models.Supplier.findById(1);
|
supplierOne = await models.Supplier.findById(1);
|
||||||
supplierTwo = await app.models.Supplier.findById(442);
|
supplierTwo = await models.Supplier.findById(442);
|
||||||
});
|
});
|
||||||
|
|
||||||
afterAll(async() => {
|
afterAll(async() => {
|
||||||
|
@ -18,9 +19,9 @@ describe('loopback model Supplier', () => {
|
||||||
it('should throw an error when attempting to set an invalid payMethod id in the supplier', async() => {
|
it('should throw an error when attempting to set an invalid payMethod id in the supplier', async() => {
|
||||||
let error;
|
let error;
|
||||||
const expectedError = 'You can not select this payment method without a registered bankery account';
|
const expectedError = 'You can not select this payment method without a registered bankery account';
|
||||||
const supplier = await app.models.Supplier.findById(1);
|
const supplier = await models.Supplier.findById(1);
|
||||||
|
|
||||||
await supplier.updateAttribute('payMethodFk', 4)
|
await supplier.updateAttribute('payMethodFk', 8)
|
||||||
.catch(e => {
|
.catch(e => {
|
||||||
error = e;
|
error = e;
|
||||||
|
|
||||||
|
@ -31,14 +32,27 @@ describe('loopback model Supplier', () => {
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should not throw if the payMethod id is valid', async() => {
|
it('should not throw if the payMethod id is valid', async() => {
|
||||||
|
const activeCtx = {
|
||||||
|
accessToken: {userId: 9},
|
||||||
|
http: {
|
||||||
|
req: {
|
||||||
|
headers: {origin: 'http://localhost'}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
spyOn(LoopBackContext, 'getCurrentContext').and.returnValue({
|
||||||
|
active: activeCtx
|
||||||
|
});
|
||||||
|
|
||||||
let error;
|
let error;
|
||||||
const supplier = await app.models.Supplier.findById(442);
|
const supplier = await models.Supplier.findById(442);
|
||||||
await supplier.updateAttribute('payMethodFk', 4)
|
await supplier.updateAttribute('payMethodFk', 4)
|
||||||
.catch(e => {
|
.catch(e => {
|
||||||
error = e;
|
error = e;
|
||||||
});
|
});
|
||||||
|
|
||||||
expect(error).toBeDefined();
|
expect(error).not.toBeDefined();
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
|
@ -9,40 +9,40 @@
|
||||||
"properties": {
|
"properties": {
|
||||||
"id": {
|
"id": {
|
||||||
"id": true,
|
"id": true,
|
||||||
"type": "Number",
|
"type": "number",
|
||||||
"forceId": false
|
"forceId": false
|
||||||
},
|
},
|
||||||
"originFk": {
|
"originFk": {
|
||||||
"type": "Number",
|
"type": "number",
|
||||||
"required": true
|
"required": true
|
||||||
},
|
},
|
||||||
"userFk": {
|
"userFk": {
|
||||||
"type": "Number"
|
"type": "number"
|
||||||
},
|
},
|
||||||
"action": {
|
"action": {
|
||||||
"type": "String",
|
"type": "string",
|
||||||
"required": true
|
"required": true
|
||||||
},
|
},
|
||||||
"changedModel": {
|
"changedModel": {
|
||||||
"type": "String"
|
"type": "string"
|
||||||
},
|
},
|
||||||
"oldInstance": {
|
"oldInstance": {
|
||||||
"type": "Object"
|
"type": "object"
|
||||||
},
|
},
|
||||||
"newInstance": {
|
"newInstance": {
|
||||||
"type": "Object"
|
"type": "object"
|
||||||
},
|
},
|
||||||
"creationDate": {
|
"creationDate": {
|
||||||
"type": "Date"
|
"type": "date"
|
||||||
},
|
},
|
||||||
"changedModelId": {
|
"changedModelId": {
|
||||||
"type": "String"
|
"type": "string"
|
||||||
},
|
},
|
||||||
"changedModelValue": {
|
"changedModelValue": {
|
||||||
"type": "String"
|
"type": "string"
|
||||||
},
|
},
|
||||||
"description": {
|
"description": {
|
||||||
"type": "String"
|
"type": "string"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"relations": {
|
"relations": {
|
||||||
|
|
|
@ -80,7 +80,7 @@ module.exports = Self => {
|
||||||
const supplierAccount = await Self.app.models.SupplierAccount.findOne({where: {supplierFk: this.id}});
|
const supplierAccount = await Self.app.models.SupplierAccount.findOne({where: {supplierFk: this.id}});
|
||||||
const hasIban = supplierAccount && supplierAccount.iban;
|
const hasIban = supplierAccount && supplierAccount.iban;
|
||||||
|
|
||||||
if (payMethod && payMethod.ibanRequired && !hasIban)
|
if (payMethod && payMethod.ibanRequiredForSuppliers && !hasIban)
|
||||||
err();
|
err();
|
||||||
|
|
||||||
done();
|
done();
|
||||||
|
|
|
@ -24,7 +24,7 @@
|
||||||
vn-acl="salesAssistant"
|
vn-acl="salesAssistant"
|
||||||
ng-model="$ctrl.supplier.payMethodFk"
|
ng-model="$ctrl.supplier.payMethodFk"
|
||||||
data="paymethods"
|
data="paymethods"
|
||||||
fields="['ibanRequired']"
|
fields="['ibanRequiredForSuppliers']"
|
||||||
initial-data="$ctrl.supplier.payMethod">
|
initial-data="$ctrl.supplier.payMethod">
|
||||||
</vn-autocomplete>
|
</vn-autocomplete>
|
||||||
<vn-autocomplete
|
<vn-autocomplete
|
||||||
|
|
|
@ -24,6 +24,8 @@ module.exports = Self => {
|
||||||
});
|
});
|
||||||
|
|
||||||
Self.deny = async(ctx, options) => {
|
Self.deny = async(ctx, options) => {
|
||||||
|
const models = Self.app.models;
|
||||||
|
const $t = ctx.req.__; // $translate
|
||||||
const myOptions = {};
|
const myOptions = {};
|
||||||
let tx;
|
let tx;
|
||||||
|
|
||||||
|
@ -48,6 +50,17 @@ module.exports = Self => {
|
||||||
const request = await Self.app.models.TicketRequest.findById(ctx.args.id, null, myOptions);
|
const request = await Self.app.models.TicketRequest.findById(ctx.args.id, null, myOptions);
|
||||||
await request.updateAttributes(params, myOptions);
|
await request.updateAttributes(params, myOptions);
|
||||||
|
|
||||||
|
const origin = ctx.req.headers.origin;
|
||||||
|
const requesterId = request.requesterFk;
|
||||||
|
|
||||||
|
const message = $t('Deny buy request', {
|
||||||
|
ticketId: request.ticketFk,
|
||||||
|
url: `${origin}/#!/ticket/${request.ticketFk}/request/index`,
|
||||||
|
observation: params.response
|
||||||
|
});
|
||||||
|
|
||||||
|
await models.Chat.sendCheckingPresence(ctx, requesterId, message, myOptions);
|
||||||
|
|
||||||
if (tx) await tx.commit();
|
if (tx) await tx.commit();
|
||||||
|
|
||||||
return request;
|
return request;
|
||||||
|
|
|
@ -1,13 +1,23 @@
|
||||||
const models = require('vn-loopback/server/server').models;
|
const models = require('vn-loopback/server/server').models;
|
||||||
|
|
||||||
describe('ticket-request deny()', () => {
|
describe('ticket-request deny()', () => {
|
||||||
it('should return the dinied ticket request', async() => {
|
it('should return the denied ticket request', async() => {
|
||||||
const tx = await models.TicketRequest.beginTransaction({});
|
const tx = await models.TicketRequest.beginTransaction({});
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const options = {transaction: tx};
|
const options = {transaction: tx};
|
||||||
|
|
||||||
const ctx = {req: {accessToken: {userId: 9}}, args: {id: 4, observation: 'my observation'}};
|
const ctx = {
|
||||||
|
req: {
|
||||||
|
accessToken: {userId: 9},
|
||||||
|
headers: {origin: 'http://localhost'}
|
||||||
|
},
|
||||||
|
args: {id: 4, observation: 'my observation'},
|
||||||
|
};
|
||||||
|
|
||||||
|
ctx.req.__ = value => {
|
||||||
|
return value;
|
||||||
|
};
|
||||||
|
|
||||||
const result = await models.TicketRequest.deny(ctx, options);
|
const result = await models.TicketRequest.deny(ctx, options);
|
||||||
|
|
||||||
|
|
|
@ -25,7 +25,7 @@
|
||||||
<vn-th number>Quantity</vn-th>
|
<vn-th number>Quantity</vn-th>
|
||||||
<vn-th number>Price</vn-th>
|
<vn-th number>Price</vn-th>
|
||||||
<vn-th number>Item id</vn-th>
|
<vn-th number>Item id</vn-th>
|
||||||
<vn-th number>Ok</vn-th>
|
<vn-th number>State</vn-th>
|
||||||
</vn-tr>
|
</vn-tr>
|
||||||
</vn-thead>
|
</vn-thead>
|
||||||
<vn-tbody>
|
<vn-tbody>
|
||||||
|
@ -78,13 +78,9 @@
|
||||||
{{::request.saleFk | zeroFill:6}}
|
{{::request.saleFk | zeroFill:6}}
|
||||||
</span>
|
</span>
|
||||||
</vn-td>
|
</vn-td>
|
||||||
<vn-td number>
|
<vn-td number
|
||||||
<vn-check vn-one
|
translate>
|
||||||
ng-model="::request.isOk"
|
{{$ctrl.getRequestState(request.isOk)}}
|
||||||
triple-state="true"
|
|
||||||
title="{{$ctrl.getRequestState(request.isOk)}}"
|
|
||||||
disabled="true">
|
|
||||||
</vn-check>
|
|
||||||
</vn-td>
|
</vn-td>
|
||||||
<vn-td number>
|
<vn-td number>
|
||||||
<vn-icon-button
|
<vn-icon-button
|
||||||
|
|
|
@ -5,3 +5,6 @@ New request: Crear petición
|
||||||
Sale id: Id linea
|
Sale id: Id linea
|
||||||
Requester: Solicitante
|
Requester: Solicitante
|
||||||
New purchase request: Nueva petición de compra
|
New purchase request: Nueva petición de compra
|
||||||
|
Denied: Denegada
|
||||||
|
Acepted: Aceptada
|
||||||
|
New: Nueva
|
||||||
|
|
Loading…
Reference in New Issue