Reviewed-on: #2863 Reviewed-by: Alex Moreno <alexm@verdnatura.es>
This commit is contained in:
commit
527028ee07
|
@ -1,28 +0,0 @@
|
||||||
import selectors from '../../helpers/selectors.js';
|
|
||||||
import getBrowser from '../../helpers/puppeteer';
|
|
||||||
|
|
||||||
describe('InvoiceIn summary path', () => {
|
|
||||||
let browser;
|
|
||||||
let page;
|
|
||||||
|
|
||||||
beforeAll(async() => {
|
|
||||||
browser = await getBrowser();
|
|
||||||
page = browser.page;
|
|
||||||
await page.loginAndModule('administrative', 'invoiceIn');
|
|
||||||
await page.accessToSearchResult('1');
|
|
||||||
});
|
|
||||||
|
|
||||||
afterAll(async() => {
|
|
||||||
await browser.close();
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should reach the summary section', async() => {
|
|
||||||
await page.waitForState('invoiceIn.card.summary');
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should contain some basic data from the invoice', async() => {
|
|
||||||
const result = await page.waitToGetProperty(selectors.invoiceInSummary.supplierRef, 'innerText');
|
|
||||||
|
|
||||||
expect(result).toEqual('1234');
|
|
||||||
});
|
|
||||||
});
|
|
|
@ -1,52 +0,0 @@
|
||||||
import selectors from '../../helpers/selectors.js';
|
|
||||||
import getBrowser from '../../helpers/puppeteer';
|
|
||||||
|
|
||||||
describe('InvoiceIn descriptor path', () => {
|
|
||||||
let browser;
|
|
||||||
let page;
|
|
||||||
|
|
||||||
beforeAll(async() => {
|
|
||||||
browser = await getBrowser();
|
|
||||||
page = browser.page;
|
|
||||||
await page.loginAndModule('administrative', 'invoiceIn');
|
|
||||||
await page.accessToSearchResult('10');
|
|
||||||
await page.accessToSection('invoiceIn.card.basicData');
|
|
||||||
});
|
|
||||||
|
|
||||||
afterAll(async() => {
|
|
||||||
await browser.close();
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should clone the invoiceIn using the descriptor more menu', async() => {
|
|
||||||
await page.waitToClick(selectors.invoiceInDescriptor.moreMenu);
|
|
||||||
await page.waitToClick(selectors.invoiceInDescriptor.moreMenuCloneInvoiceIn);
|
|
||||||
await page.waitToClick(selectors.invoiceInDescriptor.acceptButton);
|
|
||||||
const message = await page.waitForSnackbar();
|
|
||||||
|
|
||||||
expect(message.text).toContain('InvoiceIn cloned');
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should have been redirected to the created invoiceIn summary', async() => {
|
|
||||||
await page.waitForState('invoiceIn.card.summary');
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should delete the cloned invoiceIn using the descriptor more menu', async() => {
|
|
||||||
await page.waitToClick(selectors.invoiceInDescriptor.moreMenu);
|
|
||||||
await page.waitToClick(selectors.invoiceInDescriptor.moreMenuDeleteInvoiceIn);
|
|
||||||
await page.waitToClick(selectors.invoiceInDescriptor.acceptButton);
|
|
||||||
const message = await page.waitForSnackbar();
|
|
||||||
|
|
||||||
expect(message.text).toContain('InvoiceIn deleted');
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should have been relocated to the invoiceOut index', async() => {
|
|
||||||
await page.waitForState('invoiceIn.index');
|
|
||||||
});
|
|
||||||
|
|
||||||
it(`should search for the deleted invouceOut to find no results`, async() => {
|
|
||||||
await page.doSearch('10');
|
|
||||||
const nResults = await page.countElement(selectors.invoiceOutIndex.searchResult);
|
|
||||||
|
|
||||||
expect(nResults).toEqual(0);
|
|
||||||
});
|
|
||||||
});
|
|
|
@ -1,196 +0,0 @@
|
||||||
import selectors from '../../helpers/selectors.js';
|
|
||||||
import getBrowser from '../../helpers/puppeteer';
|
|
||||||
|
|
||||||
describe('InvoiceIn basic data path', () => {
|
|
||||||
let browser;
|
|
||||||
let page;
|
|
||||||
let newDms;
|
|
||||||
|
|
||||||
beforeAll(async() => {
|
|
||||||
browser = await getBrowser();
|
|
||||||
page = browser.page;
|
|
||||||
await page.loginAndModule('administrative', 'invoiceIn');
|
|
||||||
await page.accessToSearchResult('1');
|
|
||||||
await page.accessToSection('invoiceIn.card.basicData');
|
|
||||||
});
|
|
||||||
|
|
||||||
afterAll(async() => {
|
|
||||||
await browser.close();
|
|
||||||
});
|
|
||||||
|
|
||||||
it(`should edit the invoiceIn basic data`, async() => {
|
|
||||||
const now = Date.vnNew();
|
|
||||||
await page.pickDate(selectors.invoiceInBasicData.issued, now);
|
|
||||||
await page.pickDate(selectors.invoiceInBasicData.operated, now);
|
|
||||||
await page.autocompleteSearch(selectors.invoiceInBasicData.supplier, 'Verdnatura');
|
|
||||||
await page.clearInput(selectors.invoiceInBasicData.supplierRef);
|
|
||||||
await page.write(selectors.invoiceInBasicData.supplierRef, '9999');
|
|
||||||
await page.clearInput(selectors.invoiceInBasicData.dms);
|
|
||||||
await page.write(selectors.invoiceInBasicData.dms, '2');
|
|
||||||
await page.pickDate(selectors.invoiceInBasicData.bookEntried, now);
|
|
||||||
await page.pickDate(selectors.invoiceInBasicData.booked, now);
|
|
||||||
await page.autocompleteSearch(selectors.invoiceInBasicData.currency, 'USD');
|
|
||||||
await page.autocompleteSearch(selectors.invoiceInBasicData.company, 'ORN');
|
|
||||||
await page.waitToClick(selectors.invoiceInBasicData.save);
|
|
||||||
const message = await page.waitForSnackbar();
|
|
||||||
|
|
||||||
expect(message.text).toContain('Data saved!');
|
|
||||||
});
|
|
||||||
|
|
||||||
it(`should confirm the invoiceIn supplier was edited`, async() => {
|
|
||||||
await page.reloadSection('invoiceIn.card.basicData');
|
|
||||||
const result = await page.waitToGetProperty(selectors.invoiceInBasicData.supplier, 'value');
|
|
||||||
|
|
||||||
expect(result).toContain('Verdnatura');
|
|
||||||
});
|
|
||||||
|
|
||||||
it(`should confirm the invoiceIn supplierRef was edited`, async() => {
|
|
||||||
const result = await page
|
|
||||||
.waitToGetProperty(selectors.invoiceInBasicData.supplierRef, 'value');
|
|
||||||
|
|
||||||
expect(result).toEqual('9999');
|
|
||||||
});
|
|
||||||
|
|
||||||
it(`should confirm the invoiceIn currency was edited`, async() => {
|
|
||||||
const result = await page
|
|
||||||
.waitToGetProperty(selectors.invoiceInBasicData.currency, 'value');
|
|
||||||
|
|
||||||
expect(result).toEqual('USD');
|
|
||||||
});
|
|
||||||
|
|
||||||
it(`should confirm the invoiceIn company was edited`, async() => {
|
|
||||||
const result = await page
|
|
||||||
.waitToGetProperty(selectors.invoiceInBasicData.company, 'value');
|
|
||||||
|
|
||||||
expect(result).toEqual('ORN');
|
|
||||||
});
|
|
||||||
|
|
||||||
it(`should confirm the invoiceIn dms was edited`, async() => {
|
|
||||||
const result = await page
|
|
||||||
.waitToGetProperty(selectors.invoiceInBasicData.dms, 'value');
|
|
||||||
|
|
||||||
expect(result).toEqual('2');
|
|
||||||
});
|
|
||||||
|
|
||||||
it(`should create a new invoiceIn dms and save the changes`, async() => {
|
|
||||||
await page.clearInput(selectors.invoiceInBasicData.dms);
|
|
||||||
await page.waitToClick(selectors.invoiceInBasicData.create);
|
|
||||||
|
|
||||||
await page.clearInput(selectors.invoiceInBasicData.reference);
|
|
||||||
await page.write(selectors.invoiceInBasicData.reference, 'New Dms');
|
|
||||||
|
|
||||||
await page.waitToClick(selectors.invoiceInBasicData.confirm);
|
|
||||||
let message = await page.waitForSnackbar();
|
|
||||||
|
|
||||||
await page.clearInput(selectors.invoiceInBasicData.companyId);
|
|
||||||
await page.autocompleteSearch(selectors.invoiceInBasicData.companyId, 'VNL');
|
|
||||||
|
|
||||||
await page.waitToClick(selectors.invoiceInBasicData.confirm);
|
|
||||||
message = await page.waitForSnackbar();
|
|
||||||
|
|
||||||
await page.clearInput(selectors.invoiceInBasicData.warehouseId);
|
|
||||||
await page.autocompleteSearch(selectors.invoiceInBasicData.warehouseId, 'Warehouse One');
|
|
||||||
|
|
||||||
await page.waitToClick(selectors.invoiceInBasicData.confirm);
|
|
||||||
message = await page.waitForSnackbar();
|
|
||||||
|
|
||||||
await page.clearInput(selectors.invoiceInBasicData.dmsTypeId);
|
|
||||||
await page.autocompleteSearch(selectors.invoiceInBasicData.dmsTypeId, 'Ticket');
|
|
||||||
|
|
||||||
await page.waitToClick(selectors.invoiceInBasicData.confirm);
|
|
||||||
message = await page.waitForSnackbar();
|
|
||||||
|
|
||||||
await page.waitToClick(selectors.invoiceInBasicData.description);
|
|
||||||
await page.write(selectors.invoiceInBasicData.description, 'Dms without edition.');
|
|
||||||
|
|
||||||
await page.waitToClick(selectors.invoiceInBasicData.confirm);
|
|
||||||
message = await page.waitForSnackbar();
|
|
||||||
|
|
||||||
expect(message.text).toContain('The files can\'t be empty');
|
|
||||||
|
|
||||||
let currentDir = process.cwd();
|
|
||||||
let filePath = `${currentDir}/e2e/assets/thermograph.jpeg`;
|
|
||||||
|
|
||||||
const [fileChooser] = await Promise.all([
|
|
||||||
page.waitForFileChooser(),
|
|
||||||
page.waitToClick(selectors.invoiceInBasicData.inputFile)
|
|
||||||
]);
|
|
||||||
await fileChooser.accept([filePath]);
|
|
||||||
|
|
||||||
await page.waitToClick(selectors.invoiceInBasicData.confirm);
|
|
||||||
message = await page.waitForSnackbar();
|
|
||||||
|
|
||||||
expect(message.text).toContain('Data saved!');
|
|
||||||
|
|
||||||
newDms = await page
|
|
||||||
.waitToGetProperty(selectors.invoiceInBasicData.dms, 'value');
|
|
||||||
});
|
|
||||||
|
|
||||||
it(`should confirm the invoiceIn was edited with the new dms`, async() => {
|
|
||||||
await page.reloadSection('invoiceIn.card.basicData');
|
|
||||||
const result = await page
|
|
||||||
.waitToGetProperty(selectors.invoiceInBasicData.dms, 'value');
|
|
||||||
|
|
||||||
expect(result).toEqual(newDms);
|
|
||||||
});
|
|
||||||
|
|
||||||
it(`should edit the invoiceIn`, async() => {
|
|
||||||
await page.waitToClick(selectors.invoiceInBasicData.edit);
|
|
||||||
|
|
||||||
await page.clearInput(selectors.invoiceInBasicData.reference);
|
|
||||||
await page.write(selectors.invoiceInBasicData.reference, 'Dms Edited');
|
|
||||||
await page.clearInput(selectors.invoiceInBasicData.companyId);
|
|
||||||
await page.autocompleteSearch(selectors.invoiceInBasicData.companyId, 'CCs');
|
|
||||||
await page.clearInput(selectors.invoiceInBasicData.warehouseId);
|
|
||||||
await page.autocompleteSearch(selectors.invoiceInBasicData.warehouseId, 'Algemesi');
|
|
||||||
await page.clearInput(selectors.invoiceInBasicData.dmsTypeId);
|
|
||||||
await page.autocompleteSearch(selectors.invoiceInBasicData.dmsTypeId, 'Basura');
|
|
||||||
await page.waitToClick(selectors.invoiceInBasicData.description);
|
|
||||||
await page.write(selectors.invoiceInBasicData.description, ' Nevermind, now is edited.');
|
|
||||||
|
|
||||||
await page.waitToClick(selectors.invoiceInBasicData.confirm);
|
|
||||||
let message = await page.waitForSnackbar();
|
|
||||||
|
|
||||||
expect(message.text).toContain('Data saved!');
|
|
||||||
});
|
|
||||||
|
|
||||||
it(`should confirm the new dms has been edited`, async() => {
|
|
||||||
await page.reloadSection('invoiceIn.card.basicData');
|
|
||||||
await page.waitToClick(selectors.invoiceInBasicData.edit);
|
|
||||||
|
|
||||||
const reference = await page
|
|
||||||
.waitToGetProperty(selectors.invoiceInBasicData.reference, 'value');
|
|
||||||
const companyId = await page
|
|
||||||
.waitToGetProperty(selectors.invoiceInBasicData.companyId, 'value');
|
|
||||||
const warehouseId = await page
|
|
||||||
.waitToGetProperty(selectors.invoiceInBasicData.warehouseId, 'value');
|
|
||||||
const dmsTypeId = await page
|
|
||||||
.waitToGetProperty(selectors.invoiceInBasicData.dmsTypeId, 'value');
|
|
||||||
const description = await page
|
|
||||||
.waitToGetProperty(selectors.invoiceInBasicData.description, 'value');
|
|
||||||
|
|
||||||
expect(reference).toEqual('Dms Edited');
|
|
||||||
expect(companyId).toEqual('CCs');
|
|
||||||
expect(warehouseId).toEqual('Algemesi');
|
|
||||||
expect(dmsTypeId).toEqual('Basura');
|
|
||||||
expect(description).toEqual('Dms without edition. Nevermind, now is edited.');
|
|
||||||
|
|
||||||
await page.waitToClick(selectors.invoiceInBasicData.confirm);
|
|
||||||
});
|
|
||||||
|
|
||||||
it(`should disable edit and download if dms doesn't exists, and set back the original dms`, async() => {
|
|
||||||
await page.clearInput(selectors.invoiceInBasicData.dms);
|
|
||||||
await page.write(selectors.invoiceInBasicData.dms, '9999');
|
|
||||||
|
|
||||||
await page.waitForSelector(`${selectors.invoiceInBasicData.download}.disabled`);
|
|
||||||
await page.waitForSelector(`${selectors.invoiceInBasicData.edit}.disabled`);
|
|
||||||
|
|
||||||
await page.clearInput(selectors.invoiceInBasicData.dms);
|
|
||||||
await page.write(selectors.invoiceInBasicData.dms, '1');
|
|
||||||
|
|
||||||
await page.waitToClick(selectors.invoiceInBasicData.save);
|
|
||||||
const message = await page.waitForSnackbar();
|
|
||||||
|
|
||||||
expect(message.text).toContain('Data saved!');
|
|
||||||
});
|
|
||||||
});
|
|
|
@ -1,59 +0,0 @@
|
||||||
import selectors from '../../helpers/selectors.js';
|
|
||||||
import getBrowser from '../../helpers/puppeteer';
|
|
||||||
|
|
||||||
describe('InvoiceIn tax path', () => {
|
|
||||||
let browser;
|
|
||||||
let page;
|
|
||||||
|
|
||||||
beforeAll(async() => {
|
|
||||||
browser = await getBrowser();
|
|
||||||
page = browser.page;
|
|
||||||
await page.loginAndModule('developer', 'invoiceIn');
|
|
||||||
await page.accessToSearchResult('2');
|
|
||||||
await page.accessToSection('invoiceIn.card.tax');
|
|
||||||
});
|
|
||||||
|
|
||||||
afterAll(async() => {
|
|
||||||
await browser.close();
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should add a new tax and check it', async() => {
|
|
||||||
await page.waitToClick(selectors.invoiceInTax.addTaxButton);
|
|
||||||
await page.autocompleteSearch(selectors.invoiceInTax.thirdExpense, '6210000567');
|
|
||||||
await page.write(selectors.invoiceInTax.thirdTaxableBase, '100');
|
|
||||||
await page.autocompleteSearch(selectors.invoiceInTax.thirdTaxType, 'H.P. IVA');
|
|
||||||
await page.autocompleteSearch(selectors.invoiceInTax.thirdTransactionType, 'Operaciones exentas');
|
|
||||||
await page.waitToClick(selectors.invoiceInTax.saveButton);
|
|
||||||
const message = await page.waitForSnackbar();
|
|
||||||
|
|
||||||
await page.waitToClick(selectors.invoiceInDescriptor.summaryIcon);
|
|
||||||
await page.waitForState('invoiceIn.card.summary');
|
|
||||||
const total = await page.waitToGetProperty(selectors.invoiceInSummary.totalTaxableBase, 'innerText');
|
|
||||||
|
|
||||||
await page.accessToSection('invoiceIn.card.tax');
|
|
||||||
|
|
||||||
const thirdExpense = await page.waitToGetProperty(selectors.invoiceInTax.thirdExpense, 'value');
|
|
||||||
const thirdTaxableBase = await page.waitToGetProperty(selectors.invoiceInTax.thirdTaxableBase, 'value');
|
|
||||||
const thirdTaxType = await page.waitToGetProperty(selectors.invoiceInTax.thirdTaxType, 'value');
|
|
||||||
const thirdTransactionType = await page.waitToGetProperty(selectors.invoiceInTax.thirdTransactionType, 'value');
|
|
||||||
const thirdRate = await page.waitToGetProperty(selectors.invoiceInTax.thirdRate, 'value');
|
|
||||||
|
|
||||||
expect(message.text).toContain('Data saved!');
|
|
||||||
|
|
||||||
expect(total).toEqual('Taxable base €1,323.16');
|
|
||||||
|
|
||||||
expect(thirdExpense).toEqual('6210000567');
|
|
||||||
expect(thirdTaxableBase).toEqual('100');
|
|
||||||
expect(thirdTaxType).toEqual('H.P. IVA 4% CEE');
|
|
||||||
expect(thirdTransactionType).toEqual('Operaciones exentas');
|
|
||||||
expect(thirdRate).toEqual('€4.00');
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should delete the added line', async() => {
|
|
||||||
await page.waitToClick(selectors.invoiceInTax.thirdDeleteButton);
|
|
||||||
await page.waitToClick(selectors.invoiceInTax.saveButton);
|
|
||||||
const message = await page.waitForSnackbar();
|
|
||||||
|
|
||||||
expect(message.text).toContain('Data saved!');
|
|
||||||
});
|
|
||||||
});
|
|
|
@ -1,48 +0,0 @@
|
||||||
import selectors from '../../helpers/selectors.js';
|
|
||||||
import getBrowser from '../../helpers/puppeteer';
|
|
||||||
|
|
||||||
describe('InvoiceIn serial path', () => {
|
|
||||||
let browser;
|
|
||||||
let page;
|
|
||||||
let httpRequest;
|
|
||||||
|
|
||||||
beforeAll(async() => {
|
|
||||||
browser = await getBrowser();
|
|
||||||
page = browser.page;
|
|
||||||
await page.loginAndModule('administrative', 'invoiceIn');
|
|
||||||
await page.accessToSection('invoiceIn.serial');
|
|
||||||
page.on('request', req => {
|
|
||||||
if (req.url().includes(`InvoiceIns/getSerial`))
|
|
||||||
httpRequest = req.url();
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
afterAll(async() => {
|
|
||||||
await browser.close();
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should check that passes the correct params to back', async() => {
|
|
||||||
await page.overwrite(selectors.invoiceInSerial.daysAgo, '30');
|
|
||||||
await page.keyboard.press('Enter');
|
|
||||||
|
|
||||||
expect(httpRequest).toContain('daysAgo=30');
|
|
||||||
|
|
||||||
await page.overwrite(selectors.invoiceInSerial.serial, 'R');
|
|
||||||
await page.keyboard.press('Enter');
|
|
||||||
|
|
||||||
expect(httpRequest).toContain('serial=R');
|
|
||||||
await page.click(selectors.invoiceInSerial.chip);
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should go to index and check if the search-panel has the correct params', async() => {
|
|
||||||
await page.waitToClick(selectors.invoiceInSerial.goToIndex);
|
|
||||||
const params = await page.$$(selectors.invoiceInIndex.topbarSearchParams);
|
|
||||||
const serial = await params[0].getProperty('title');
|
|
||||||
const isBooked = await params[1].getProperty('title');
|
|
||||||
const from = await params[2].getProperty('title');
|
|
||||||
|
|
||||||
expect(await serial.jsonValue()).toContain('serial');
|
|
||||||
expect(await isBooked.jsonValue()).toContain('not isBooked');
|
|
||||||
expect(await from.jsonValue()).toContain('from');
|
|
||||||
});
|
|
||||||
});
|
|
|
@ -1,315 +0,0 @@
|
||||||
<mg-ajax path="InvoiceIns/{{patch.params.id}}/updateInvoiceIn" options="vnPatch"></mg-ajax>
|
|
||||||
<vn-watcher
|
|
||||||
vn-id="watcher"
|
|
||||||
data="$ctrl.invoiceIn"
|
|
||||||
form="form"
|
|
||||||
save="patch">
|
|
||||||
</vn-watcher>
|
|
||||||
<vn-crud-model
|
|
||||||
auto-load="true"
|
|
||||||
url="Companies"
|
|
||||||
data="companies"
|
|
||||||
order="code">
|
|
||||||
</vn-crud-model>
|
|
||||||
<vn-crud-model
|
|
||||||
auto-load="true"
|
|
||||||
url="Warehouses"
|
|
||||||
data="warehouses"
|
|
||||||
order="name">
|
|
||||||
</vn-crud-model>
|
|
||||||
<vn-crud-model
|
|
||||||
auto-load="true"
|
|
||||||
url="DmsTypes"
|
|
||||||
data="dmsTypes"
|
|
||||||
order="name">
|
|
||||||
</vn-crud-model>
|
|
||||||
<form name="form" ng-submit="watcher.submit()" class="vn-w-md">
|
|
||||||
<vn-card class="vn-pa-lg">
|
|
||||||
<vn-horizontal>
|
|
||||||
<vn-autocomplete
|
|
||||||
vn-one
|
|
||||||
ng-model="$ctrl.invoiceIn.supplierFk"
|
|
||||||
url="Suppliers"
|
|
||||||
show-field="nickname"
|
|
||||||
search-function="{or: [{id: $search}, {nickname: {like: '%'+ $search +'%'}}]}"
|
|
||||||
value-field="id"
|
|
||||||
order="nickname"
|
|
||||||
label="Supplier"
|
|
||||||
required="true"
|
|
||||||
rule>
|
|
||||||
<tpl-item>
|
|
||||||
{{::id}} - {{::nickname}}
|
|
||||||
</tpl-item>
|
|
||||||
</vn-autocomplete>
|
|
||||||
<vn-textfield
|
|
||||||
label="Supplier ref"
|
|
||||||
ng-model="$ctrl.invoiceIn.supplierRef"
|
|
||||||
rule>
|
|
||||||
</vn-textfield>
|
|
||||||
</vn-horizontal>
|
|
||||||
<vn-horizontal>
|
|
||||||
<vn-date-picker
|
|
||||||
vn-one
|
|
||||||
label="Expedition date"
|
|
||||||
ng-model="$ctrl.invoiceIn.issued"
|
|
||||||
vn-focus
|
|
||||||
rule>
|
|
||||||
</vn-date-picker>
|
|
||||||
<vn-date-picker
|
|
||||||
vn-one
|
|
||||||
label="Operation date"
|
|
||||||
ng-model="$ctrl.invoiceIn.operated"
|
|
||||||
rule>
|
|
||||||
</vn-date-picker>
|
|
||||||
</vn-horizontal>
|
|
||||||
<vn-horizontal>
|
|
||||||
<vn-datalist vn-one
|
|
||||||
label="Undeductible VAT"
|
|
||||||
ng-model="$ctrl.invoiceIn.deductibleExpenseFk"
|
|
||||||
value-field="id"
|
|
||||||
order="name"
|
|
||||||
url="Expenses"
|
|
||||||
fields="['id','name']"
|
|
||||||
rule>
|
|
||||||
<tpl-item>
|
|
||||||
{{id}} - {{name}}
|
|
||||||
</tpl-item>
|
|
||||||
</vn-datalist>
|
|
||||||
<vn-textfield
|
|
||||||
label="Document"
|
|
||||||
ng-model="$ctrl.invoiceIn.dmsFk"
|
|
||||||
ng-change="$ctrl.checkFileExists($ctrl.invoiceIn.dmsFk)"
|
|
||||||
rule>
|
|
||||||
<prepend>
|
|
||||||
<vn-icon-button
|
|
||||||
disabled="$ctrl.editDownloadDisabled"
|
|
||||||
ng-if="$ctrl.invoiceIn.dmsFk"
|
|
||||||
title="{{'Download file' | translate}}"
|
|
||||||
icon="cloud_download"
|
|
||||||
ng-click="$ctrl.downloadFile($ctrl.invoiceIn.dmsFk)">
|
|
||||||
</vn-icon-button>
|
|
||||||
</prepend>
|
|
||||||
<append>
|
|
||||||
<vn-icon-button
|
|
||||||
disabled="$ctrl.editDownloadDisabled"
|
|
||||||
ng-if="$ctrl.invoiceIn.dmsFk"
|
|
||||||
ng-click="$ctrl.openEditDialog($ctrl.invoiceIn.dmsFk)"
|
|
||||||
icon="edit"
|
|
||||||
title="{{'Edit document' | translate}}">
|
|
||||||
</vn-icon-button>
|
|
||||||
<vn-icon-button
|
|
||||||
ng-if="!$ctrl.invoiceIn.dmsFk"
|
|
||||||
ng-click="$ctrl.openCreateDialog()"
|
|
||||||
icon="add_circle"
|
|
||||||
title="{{'Create document' | translate}}">
|
|
||||||
</vn-icon-button>
|
|
||||||
</append>
|
|
||||||
</vn-textfield>
|
|
||||||
</vn-horizontal>
|
|
||||||
<vn-horizontal>
|
|
||||||
<vn-date-picker
|
|
||||||
vn-one
|
|
||||||
label="Entry date"
|
|
||||||
ng-model="$ctrl.invoiceIn.bookEntried"
|
|
||||||
rule>
|
|
||||||
</vn-date-picker>
|
|
||||||
<vn-date-picker
|
|
||||||
vn-one
|
|
||||||
label="Accounted date"
|
|
||||||
ng-model="$ctrl.invoiceIn.booked"
|
|
||||||
rule>
|
|
||||||
</vn-date-picker>
|
|
||||||
</vn-horizontal>
|
|
||||||
<vn-horizontal>
|
|
||||||
<vn-autocomplete
|
|
||||||
vn-one
|
|
||||||
label="Currency"
|
|
||||||
ng-model="$ctrl.invoiceIn.currencyFk"
|
|
||||||
url="Currencies"
|
|
||||||
show-field="code"
|
|
||||||
value-field="id"
|
|
||||||
rule>
|
|
||||||
</vn-autocomplete>
|
|
||||||
<vn-autocomplete
|
|
||||||
url="Companies"
|
|
||||||
label="Company"
|
|
||||||
show-field="code"
|
|
||||||
value-field="id"
|
|
||||||
ng-model="$ctrl.invoiceIn.companyFk"
|
|
||||||
rule>
|
|
||||||
</vn-autocomplete>
|
|
||||||
</vn-horizontal>
|
|
||||||
</vn-card>
|
|
||||||
<vn-button-bar>
|
|
||||||
<vn-submit
|
|
||||||
disabled="!watcher.dataChanged()"
|
|
||||||
label="Save">
|
|
||||||
</vn-submit>
|
|
||||||
<vn-button
|
|
||||||
class="cancel"
|
|
||||||
label="Undo changes"
|
|
||||||
disabled="!watcher.dataChanged()"
|
|
||||||
ng-click="watcher.loadOriginalData()">
|
|
||||||
</vn-button>
|
|
||||||
</vn-button-bar>
|
|
||||||
</form>
|
|
||||||
<!-- Create edit dms dialog -->
|
|
||||||
<vn-dialog
|
|
||||||
vn-id="dmsEditDialog"
|
|
||||||
message="Edit document"
|
|
||||||
on-accept="$ctrl.onEdit()">
|
|
||||||
<tpl-body>
|
|
||||||
<vn-horizontal>
|
|
||||||
<vn-textfield
|
|
||||||
vn-one
|
|
||||||
vn-focus
|
|
||||||
label="Reference"
|
|
||||||
ng-model="$ctrl.dms.reference"
|
|
||||||
rule>
|
|
||||||
</vn-textfield>
|
|
||||||
<vn-autocomplete vn-one required="true"
|
|
||||||
label="Company"
|
|
||||||
ng-model="$ctrl.dms.companyId"
|
|
||||||
url="Companies"
|
|
||||||
show-field="code"
|
|
||||||
value-field="id">
|
|
||||||
</vn-autocomplete>
|
|
||||||
</vn-horizontal>
|
|
||||||
<vn-horizontal>
|
|
||||||
<vn-autocomplete vn-one required="true"
|
|
||||||
label="Warehouse"
|
|
||||||
ng-model="$ctrl.dms.warehouseId"
|
|
||||||
url="Warehouses"
|
|
||||||
show-field="name"
|
|
||||||
value-field="id">
|
|
||||||
</vn-autocomplete>
|
|
||||||
<vn-autocomplete vn-one required="true"
|
|
||||||
label="Type"
|
|
||||||
ng-model="$ctrl.dms.dmsTypeId"
|
|
||||||
url="DmsTypes"
|
|
||||||
show-field="name"
|
|
||||||
value-field="id">
|
|
||||||
</vn-autocomplete>
|
|
||||||
</vn-horizontal>
|
|
||||||
<vn-horizontal>
|
|
||||||
<vn-textarea
|
|
||||||
vn-one
|
|
||||||
required="true"
|
|
||||||
label="Description"
|
|
||||||
ng-model="$ctrl.dms.description"
|
|
||||||
rule>
|
|
||||||
</vn-textarea>
|
|
||||||
</vn-horizontal>
|
|
||||||
<vn-horizontal>
|
|
||||||
<vn-input-file
|
|
||||||
vn-one
|
|
||||||
label="File"
|
|
||||||
ng-model="$ctrl.dms.files"
|
|
||||||
on-change="$ctrl.onFileChange($files)"
|
|
||||||
accept="{{$ctrl.allowedContentTypes}}"
|
|
||||||
required="false"
|
|
||||||
multiple="true">
|
|
||||||
<append>
|
|
||||||
<vn-icon vn-none
|
|
||||||
color-marginal
|
|
||||||
title="{{$ctrl.contentTypesInfo}}"
|
|
||||||
icon="info">
|
|
||||||
</vn-icon>
|
|
||||||
</append>
|
|
||||||
</vn-input-file>
|
|
||||||
</vn-horizontal>
|
|
||||||
<vn-vertical>
|
|
||||||
<vn-check disabled="true"
|
|
||||||
label="Generate identifier for original file"
|
|
||||||
ng-model="$ctrl.dms.hasFile">
|
|
||||||
</vn-check>
|
|
||||||
</vn-vertical>
|
|
||||||
</tpl-body>
|
|
||||||
<tpl-buttons>
|
|
||||||
<input type="button" response="cancel" translate-attr="{value: 'Cancel'}"/>
|
|
||||||
<button response="accept" translate>Save</button>
|
|
||||||
</tpl-buttons>
|
|
||||||
</vn-dialog>
|
|
||||||
<!-- Create new dms dialog -->
|
|
||||||
<vn-dialog
|
|
||||||
vn-id="dmsCreateDialog"
|
|
||||||
message="Create document"
|
|
||||||
on-accept="$ctrl.onCreate()">
|
|
||||||
<tpl-body>
|
|
||||||
<vn-horizontal>
|
|
||||||
<vn-textfield
|
|
||||||
vn-one
|
|
||||||
vn-focus
|
|
||||||
label="Reference"
|
|
||||||
ng-model="$ctrl.dms.reference"
|
|
||||||
rule>
|
|
||||||
</vn-textfield>
|
|
||||||
<vn-autocomplete
|
|
||||||
vn-one
|
|
||||||
label="Company"
|
|
||||||
ng-model="$ctrl.dms.companyId"
|
|
||||||
data="companies"
|
|
||||||
show-field="code"
|
|
||||||
value-field="id"
|
|
||||||
required="true">
|
|
||||||
</vn-autocomplete>
|
|
||||||
</vn-horizontal>
|
|
||||||
<vn-horizontal>
|
|
||||||
<vn-autocomplete
|
|
||||||
vn-one
|
|
||||||
label="Warehouse"
|
|
||||||
ng-model="$ctrl.dms.warehouseId"
|
|
||||||
data="warehouses"
|
|
||||||
show-field="name"
|
|
||||||
value-field="id"
|
|
||||||
required="true">
|
|
||||||
</vn-autocomplete>
|
|
||||||
<vn-autocomplete
|
|
||||||
vn-one
|
|
||||||
label="Type"
|
|
||||||
ng-model="$ctrl.dms.dmsTypeId"
|
|
||||||
data="dmsTypes"
|
|
||||||
show-field="name"
|
|
||||||
value-field="id"
|
|
||||||
required="true">
|
|
||||||
</vn-autocomplete>
|
|
||||||
</vn-horizontal>
|
|
||||||
<vn-horizontal>
|
|
||||||
<vn-textarea
|
|
||||||
vn-one
|
|
||||||
label="Description"
|
|
||||||
ng-model="$ctrl.dms.description"
|
|
||||||
required="true"
|
|
||||||
rule>
|
|
||||||
</vn-textarea>
|
|
||||||
</vn-horizontal>
|
|
||||||
<vn-horizontal>
|
|
||||||
<vn-input-file
|
|
||||||
vn-one
|
|
||||||
label="File"
|
|
||||||
ng-model="$ctrl.dms.files"
|
|
||||||
on-change="$ctrl.onFileChange($files)"
|
|
||||||
accept="{{$ctrl.allowedContentTypes}}"
|
|
||||||
required="true"
|
|
||||||
multiple="true">
|
|
||||||
<append>
|
|
||||||
<vn-icon vn-none
|
|
||||||
color-marginal
|
|
||||||
title="{{$ctrl.contentTypesInfo}}"
|
|
||||||
icon="info">
|
|
||||||
</vn-icon>
|
|
||||||
</append>
|
|
||||||
</vn-input-file>
|
|
||||||
</vn-horizontal>
|
|
||||||
<vn-vertical>
|
|
||||||
<vn-check
|
|
||||||
label="Generate identifier for original file"
|
|
||||||
ng-model="$ctrl.dms.hasFile">
|
|
||||||
</vn-check>
|
|
||||||
</vn-vertical>
|
|
||||||
</tpl-body>
|
|
||||||
<tpl-buttons>
|
|
||||||
<input type="button" response="cancel" translate-attr="{value: 'Cancel'}"/>
|
|
||||||
<button response="accept" translate>Create</button>
|
|
||||||
</tpl-buttons>
|
|
||||||
</vn-dialog>
|
|
|
@ -1,187 +0,0 @@
|
||||||
import ngModule from '../module';
|
|
||||||
import Section from 'salix/components/section';
|
|
||||||
import UserError from 'core/lib/user-error';
|
|
||||||
|
|
||||||
class Controller extends Section {
|
|
||||||
constructor($element, $, vnFile) {
|
|
||||||
super($element, $, vnFile);
|
|
||||||
this.dms = {
|
|
||||||
files: [],
|
|
||||||
hasFile: false,
|
|
||||||
hasFileAttached: false
|
|
||||||
};
|
|
||||||
this.vnFile = vnFile;
|
|
||||||
this.getAllowedContentTypes();
|
|
||||||
this._editDownloadDisabled = false;
|
|
||||||
}
|
|
||||||
|
|
||||||
get contentTypesInfo() {
|
|
||||||
return this.$t('ContentTypesInfo', {
|
|
||||||
allowedContentTypes: this.allowedContentTypes
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
get editDownloadDisabled() {
|
|
||||||
return this._editDownloadDisabled;
|
|
||||||
}
|
|
||||||
|
|
||||||
async checkFileExists(dmsId) {
|
|
||||||
if (!dmsId) return;
|
|
||||||
let filter = {
|
|
||||||
fields: ['id']
|
|
||||||
};
|
|
||||||
await this.$http.get(`Dms/${dmsId}`, {filter})
|
|
||||||
.then(() => this._editDownloadDisabled = false)
|
|
||||||
.catch(() => this._editDownloadDisabled = true);
|
|
||||||
}
|
|
||||||
|
|
||||||
async getFile(dmsId) {
|
|
||||||
const path = `Dms/${dmsId}`;
|
|
||||||
await this.$http.get(path).then(res => {
|
|
||||||
const dms = res.data && res.data;
|
|
||||||
this.dms = {
|
|
||||||
dmsId: dms.id,
|
|
||||||
reference: dms.reference,
|
|
||||||
warehouseId: dms.warehouseFk,
|
|
||||||
companyId: dms.companyFk,
|
|
||||||
dmsTypeId: dms.dmsTypeFk,
|
|
||||||
description: dms.description,
|
|
||||||
hasFile: dms.hasFile,
|
|
||||||
hasFileAttached: false,
|
|
||||||
files: []
|
|
||||||
};
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
getAllowedContentTypes() {
|
|
||||||
this.$http.get('DmsContainers/allowedContentTypes').then(res => {
|
|
||||||
if (res.data.length > 0) {
|
|
||||||
const contentTypes = res.data.join(', ');
|
|
||||||
this.allowedContentTypes = contentTypes;
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
openEditDialog(dmsId) {
|
|
||||||
this.getFile(dmsId).then(() => this.$.dmsEditDialog.show());
|
|
||||||
}
|
|
||||||
|
|
||||||
openCreateDialog() {
|
|
||||||
const params = {filter: {
|
|
||||||
where: {code: 'invoiceIn'}
|
|
||||||
}};
|
|
||||||
this.$http.get('DmsTypes/findOne', {params}).then(res => {
|
|
||||||
this.dms = {
|
|
||||||
reference: this.invoiceIn.supplierRef,
|
|
||||||
warehouseId: this.vnConfig.warehouseFk,
|
|
||||||
companyId: this.vnConfig.companyFk,
|
|
||||||
dmsTypeId: res.data.id,
|
|
||||||
description: this.invoiceIn.supplier.name,
|
|
||||||
hasFile: true,
|
|
||||||
hasFileAttached: true,
|
|
||||||
files: null
|
|
||||||
};
|
|
||||||
this.$.dmsCreateDialog.show();
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
downloadFile(dmsId) {
|
|
||||||
this.vnFile.download(`api/dms/${dmsId}/downloadFile`);
|
|
||||||
}
|
|
||||||
|
|
||||||
onFileChange(files) {
|
|
||||||
let hasFileAttached = false;
|
|
||||||
if (files.length > 0)
|
|
||||||
hasFileAttached = true;
|
|
||||||
|
|
||||||
this.$.$applyAsync(() => {
|
|
||||||
this.dms.hasFileAttached = hasFileAttached;
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
onEdit() {
|
|
||||||
if (!this.dms.companyId)
|
|
||||||
throw new UserError(`The company can't be empty`);
|
|
||||||
if (!this.dms.warehouseId)
|
|
||||||
throw new UserError(`The warehouse can't be empty`);
|
|
||||||
if (!this.dms.dmsTypeId)
|
|
||||||
throw new UserError(`The DMS Type can't be empty`);
|
|
||||||
if (!this.dms.description)
|
|
||||||
throw new UserError(`The description can't be empty`);
|
|
||||||
|
|
||||||
const query = `dms/${this.dms.dmsId}/updateFile`;
|
|
||||||
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(res => {
|
|
||||||
if (res) {
|
|
||||||
this.vnApp.showSuccess(this.$t('Data saved!'));
|
|
||||||
if (res.data.length > 0) this.invoiceIn.dmsFk = res.data[0].id;
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
onCreate() {
|
|
||||||
if (!this.dms.companyId)
|
|
||||||
throw new UserError(`The company can't be empty`);
|
|
||||||
if (!this.dms.warehouseId)
|
|
||||||
throw new UserError(`The warehouse can't be empty`);
|
|
||||||
if (!this.dms.dmsTypeId)
|
|
||||||
throw new UserError(`The DMS Type can't be empty`);
|
|
||||||
if (!this.dms.description)
|
|
||||||
throw new UserError(`The description can't be empty`);
|
|
||||||
if (!this.dms.files)
|
|
||||||
throw new UserError(`The files can't be empty`);
|
|
||||||
|
|
||||||
const query = `Dms/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(res => {
|
|
||||||
if (res) {
|
|
||||||
this.vnApp.showSuccess(this.$t('Data saved!'));
|
|
||||||
if (res.data.length > 0) this.invoiceIn.dmsFk = res.data[0].id;
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Controller.$inject = ['$element', '$scope', 'vnFile'];
|
|
||||||
|
|
||||||
ngModule.vnComponent('vnInvoiceInBasicData', {
|
|
||||||
template: require('./index.html'),
|
|
||||||
controller: Controller,
|
|
||||||
bindings: {
|
|
||||||
invoiceIn: '<'
|
|
||||||
}
|
|
||||||
});
|
|
|
@ -1,102 +0,0 @@
|
||||||
import './index.js';
|
|
||||||
import watcher from 'core/mocks/watcher';
|
|
||||||
|
|
||||||
describe('InvoiceIn', () => {
|
|
||||||
describe('Component vnInvoiceInBasicData', () => {
|
|
||||||
let controller;
|
|
||||||
let $scope;
|
|
||||||
let $httpBackend;
|
|
||||||
let $httpParamSerializer;
|
|
||||||
|
|
||||||
beforeEach(ngModule('invoiceIn'));
|
|
||||||
|
|
||||||
beforeEach(inject(($componentController, $rootScope, _$httpBackend_, _$httpParamSerializer_) => {
|
|
||||||
$scope = $rootScope.$new();
|
|
||||||
$httpBackend = _$httpBackend_;
|
|
||||||
$httpParamSerializer = _$httpParamSerializer_;
|
|
||||||
const $element = angular.element('<vn-invoice-in-basic-data></vn-invoice-in-basic-data>');
|
|
||||||
controller = $componentController('vnInvoiceInBasicData', {$element, $scope});
|
|
||||||
controller.$.watcher = watcher;
|
|
||||||
$httpBackend.expect('GET', `DmsContainers/allowedContentTypes`).respond({});
|
|
||||||
}));
|
|
||||||
|
|
||||||
describe('onFileChange()', () => {
|
|
||||||
it('should set dms hasFileAttached property to true if has any files', () => {
|
|
||||||
const files = [{id: 1, name: 'MyFile'}];
|
|
||||||
controller.onFileChange(files);
|
|
||||||
|
|
||||||
$scope.$apply();
|
|
||||||
|
|
||||||
expect(controller.dms.hasFileAttached).toBeTruthy();
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
describe('checkFileExists()', () => {
|
|
||||||
it(`should return false if a file exists`, () => {
|
|
||||||
const fileIdExists = 1;
|
|
||||||
controller.checkFileExists(fileIdExists);
|
|
||||||
|
|
||||||
expect(controller.editDownloadDisabled).toBe(false);
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
describe('onEdit()', () => {
|
|
||||||
it(`should perform a POST query to edit the dms properties`, () => {
|
|
||||||
jest.spyOn(controller.vnApp, 'showSuccess');
|
|
||||||
|
|
||||||
const dms = {
|
|
||||||
dmsId: 1,
|
|
||||||
reference: 'Ref1',
|
|
||||||
warehouseId: 1,
|
|
||||||
companyId: 442,
|
|
||||||
dmsTypeId: 20,
|
|
||||||
description: 'This is a description',
|
|
||||||
files: []
|
|
||||||
};
|
|
||||||
|
|
||||||
controller.dms = dms;
|
|
||||||
const serializedParams = $httpParamSerializer(controller.dms);
|
|
||||||
const query = `dms/${controller.dms.dmsId}/updateFile?${serializedParams}`;
|
|
||||||
|
|
||||||
$httpBackend.expectPOST(query).respond({});
|
|
||||||
controller.onEdit();
|
|
||||||
$httpBackend.flush();
|
|
||||||
|
|
||||||
expect(controller.vnApp.showSuccess).toHaveBeenCalled();
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
describe('onCreate()', () => {
|
|
||||||
it(`should perform a POST query to create a new dms`, () => {
|
|
||||||
jest.spyOn(controller.vnApp, 'showSuccess');
|
|
||||||
|
|
||||||
const dms = {
|
|
||||||
reference: 'Ref1',
|
|
||||||
warehouseId: 1,
|
|
||||||
companyId: 442,
|
|
||||||
dmsTypeId: 20,
|
|
||||||
description: 'This is a description',
|
|
||||||
files: [{
|
|
||||||
lastModified: 1668673957761,
|
|
||||||
lastModifiedDate: Date.vnNew(),
|
|
||||||
name: 'file-example.png',
|
|
||||||
size: 19653,
|
|
||||||
type: 'image/png',
|
|
||||||
webkitRelativePath: ''
|
|
||||||
}]
|
|
||||||
};
|
|
||||||
|
|
||||||
controller.dms = dms;
|
|
||||||
const serializedParams = $httpParamSerializer(controller.dms);
|
|
||||||
const query = `Dms/uploadFile?${serializedParams}`;
|
|
||||||
|
|
||||||
$httpBackend.expectPOST(query).respond({});
|
|
||||||
controller.onCreate();
|
|
||||||
$httpBackend.flush();
|
|
||||||
|
|
||||||
expect(controller.vnApp.showSuccess).toHaveBeenCalled();
|
|
||||||
});
|
|
||||||
});
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
|
@ -1 +0,0 @@
|
||||||
ContentTypesInfo: Allowed file types {{allowedContentTypes}}
|
|
|
@ -1,15 +0,0 @@
|
||||||
Upload file: Subir fichero
|
|
||||||
Edit file: Editar fichero
|
|
||||||
Upload: Subir
|
|
||||||
Document: Documento
|
|
||||||
ContentTypesInfo: "Tipos de archivo permitidos: {{allowedContentTypes}}"
|
|
||||||
Generate identifier for original file: Generar identificador para archivo original
|
|
||||||
File management: Gestión documental
|
|
||||||
Hard copy: Copia
|
|
||||||
This file will be deleted: Este fichero va a ser borrado
|
|
||||||
Are you sure?: Estas seguro?
|
|
||||||
File deleted: Fichero eliminado
|
|
||||||
Remove file: Eliminar fichero
|
|
||||||
Download file: Descargar fichero
|
|
||||||
Edit document: Editar documento
|
|
||||||
Create document: Crear documento
|
|
|
@ -1,55 +0,0 @@
|
||||||
<vn-watcher
|
|
||||||
vn-id="watcher"
|
|
||||||
url="InvoiceIns"
|
|
||||||
data="$ctrl.invoiceIn"
|
|
||||||
insert-mode="true"
|
|
||||||
form="form">
|
|
||||||
</vn-watcher>
|
|
||||||
<form name="form" vn-http-submit="$ctrl.onSubmit()" class="vn-w-md">
|
|
||||||
<div class="vn-w-md">
|
|
||||||
<vn-card class="vn-pa-lg">
|
|
||||||
<vn-autocomplete
|
|
||||||
vn-focus
|
|
||||||
vn-id="supplier"
|
|
||||||
url="Suppliers"
|
|
||||||
label="Supplier"
|
|
||||||
search-function="{or: [{id: $search}, {name: {like: '%'+ $search +'%'}}, {nif: {like: '%'+ $search +'%'}}]}"
|
|
||||||
fields="['nif']"
|
|
||||||
show-field="name"
|
|
||||||
value-field="id"
|
|
||||||
ng-model="$ctrl.invoiceIn.supplierFk"
|
|
||||||
order="id"
|
|
||||||
vn-focus>
|
|
||||||
<tpl-item>{{id}}: {{nif}}: {{name}}</tpl-item>
|
|
||||||
</vn-autocomplete>
|
|
||||||
<vn-textfield
|
|
||||||
vn-one
|
|
||||||
label="supplierRef"
|
|
||||||
ng-model="$ctrl.invoiceIn.supplierRef">
|
|
||||||
</vn-textfield>
|
|
||||||
<vn-date-picker
|
|
||||||
label="Issued"
|
|
||||||
ng-model="$ctrl.invoiceIn.issued">
|
|
||||||
</vn-date-picker>
|
|
||||||
<vn-autocomplete
|
|
||||||
vn-one
|
|
||||||
label="Company"
|
|
||||||
ng-model="$ctrl.companyFk"
|
|
||||||
url="companies"
|
|
||||||
show-field="code"
|
|
||||||
value-field="id">
|
|
||||||
</vn-autocomplete>
|
|
||||||
</vn-card>
|
|
||||||
<vn-button-bar>
|
|
||||||
<vn-submit
|
|
||||||
disabled="!watcher.dataChanged()"
|
|
||||||
label="Create">
|
|
||||||
</vn-submit>
|
|
||||||
<vn-button
|
|
||||||
class="cancel"
|
|
||||||
label="Cancel"
|
|
||||||
ui-sref="InvoiceIn.index">
|
|
||||||
</vn-button>
|
|
||||||
</vn-button-bar>
|
|
||||||
</div>
|
|
||||||
</form>
|
|
|
@ -1,30 +0,0 @@
|
||||||
import ngModule from '../module';
|
|
||||||
import Section from 'salix/components/section';
|
|
||||||
|
|
||||||
class Controller extends Section {
|
|
||||||
$onInit() {
|
|
||||||
this.invoiceIn = {};
|
|
||||||
if (this.$params && this.$params.supplierFk)
|
|
||||||
this.invoiceIn.supplierFk = this.$params.supplierFk;
|
|
||||||
this.invoiceIn.issued = Date.vnNew();
|
|
||||||
}
|
|
||||||
|
|
||||||
get companyFk() {
|
|
||||||
return this.invoiceIn.companyFk || this.vnConfig.companyFk;
|
|
||||||
}
|
|
||||||
|
|
||||||
set companyFk(value) {
|
|
||||||
this.invoiceIn.companyFk = value;
|
|
||||||
}
|
|
||||||
|
|
||||||
onSubmit() {
|
|
||||||
this.$.watcher.submit().then(
|
|
||||||
res => this.$state.go('invoiceIn.card.basicData', {id: res.data.id})
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
ngModule.vnComponent('vnInvoiceInCreate', {
|
|
||||||
template: require('./index.html'),
|
|
||||||
controller: Controller
|
|
||||||
});
|
|
|
@ -1 +0,0 @@
|
||||||
a:a
|
|
|
@ -1,71 +0,0 @@
|
||||||
<vn-crud-model
|
|
||||||
vn-id="model"
|
|
||||||
url="InvoiceInDueDays"
|
|
||||||
data="InvoiceInDueDaysData"
|
|
||||||
link="{invoiceInFk: $ctrl.$params.id}"
|
|
||||||
auto-load="true">
|
|
||||||
</vn-crud-model>
|
|
||||||
<vn-watcher
|
|
||||||
vn-id="watcher"
|
|
||||||
data="InvoiceInDueDaysData"
|
|
||||||
form="form">
|
|
||||||
</vn-watcher>
|
|
||||||
<form name="form" ng-submit="$ctrl.onSubmit()">
|
|
||||||
<vn-card class="vn-pa-lg">
|
|
||||||
<vn-horizontal ng-repeat="invoiceInDueDay in InvoiceInDueDaysData">
|
|
||||||
<vn-date-picker
|
|
||||||
vn-one
|
|
||||||
label="Date"
|
|
||||||
ng-model="invoiceInDueDay.dueDated"
|
|
||||||
vn-focus
|
|
||||||
rule>
|
|
||||||
</vn-date-picker>
|
|
||||||
<vn-autocomplete vn-three
|
|
||||||
label="Bank"
|
|
||||||
ng-model="invoiceInDueDay.bankFk"
|
|
||||||
url="Accountings"
|
|
||||||
show-field="bank"
|
|
||||||
select-fields="['id','bank']"
|
|
||||||
order="id"
|
|
||||||
search-function="$ctrl.bankSearchFunc($search)"
|
|
||||||
rule>
|
|
||||||
<tpl-item>{{id}}: {{bank}}</tpl-item>
|
|
||||||
</vn-autocomplete>
|
|
||||||
<vn-input-number vn-one
|
|
||||||
label="Amount"
|
|
||||||
ng-model="invoiceInDueDay.amount"
|
|
||||||
step="0.01"
|
|
||||||
rule
|
|
||||||
vn-focus>
|
|
||||||
</vn-input-number>
|
|
||||||
<vn-input-number
|
|
||||||
disabled="$ctrl.invoiceIn.currency.code == 'EUR'"
|
|
||||||
label="Foreign value"
|
|
||||||
ng-model="invoiceInDueDay.foreignValue"
|
|
||||||
rule>
|
|
||||||
</vn-input-number>
|
|
||||||
<vn-none>
|
|
||||||
<vn-icon-button
|
|
||||||
vn-tooltip="Remove due day"
|
|
||||||
icon="delete"
|
|
||||||
ng-click="model.remove($index)"
|
|
||||||
tabindex="-1">
|
|
||||||
</vn-icon-button>
|
|
||||||
</vn-none>
|
|
||||||
</vn-horizontal>
|
|
||||||
<vn-one>
|
|
||||||
<vn-icon-button
|
|
||||||
vn-bind="+"
|
|
||||||
vn-tooltip="Add due day"
|
|
||||||
icon="add_circle"
|
|
||||||
ng-click="$ctrl.add()">
|
|
||||||
</vn-icon-button>
|
|
||||||
</vn-one>
|
|
||||||
</vn-card>
|
|
||||||
<vn-button-bar>
|
|
||||||
<vn-submit
|
|
||||||
disabled="!watcher.dataChanged()"
|
|
||||||
label="Save">
|
|
||||||
</vn-submit>
|
|
||||||
</vn-button-bar>
|
|
||||||
</form>
|
|
|
@ -1,37 +0,0 @@
|
||||||
import ngModule from '../module';
|
|
||||||
import Section from 'salix/components/section';
|
|
||||||
|
|
||||||
class Controller extends Section {
|
|
||||||
add() {
|
|
||||||
this.$.model.insert({
|
|
||||||
dueDated: Date.vnNew(),
|
|
||||||
bankFk: this.vnConfig.local.bankFk
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
onSubmit() {
|
|
||||||
this.$.watcher.check();
|
|
||||||
this.$.model.save().then(() => {
|
|
||||||
this.$.watcher.notifySaved();
|
|
||||||
this.$.watcher.updateOriginalData();
|
|
||||||
this.card.reload();
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
bankSearchFunc($search) {
|
|
||||||
return /^\d+$/.test($search)
|
|
||||||
? {id: $search}
|
|
||||||
: {bank: {like: '%' + $search + '%'}};
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
ngModule.vnComponent('vnInvoiceInDueDay', {
|
|
||||||
template: require('./index.html'),
|
|
||||||
controller: Controller,
|
|
||||||
require: {
|
|
||||||
card: '^vnInvoiceInCard'
|
|
||||||
},
|
|
||||||
bindings: {
|
|
||||||
invoiceIn: '<'
|
|
||||||
}
|
|
||||||
});
|
|
|
@ -1,44 +0,0 @@
|
||||||
import './index.js';
|
|
||||||
import watcher from 'core/mocks/watcher';
|
|
||||||
import crudModel from 'core/mocks/crud-model';
|
|
||||||
|
|
||||||
describe('InvoiceIn', () => {
|
|
||||||
describe('Component due day', () => {
|
|
||||||
let controller;
|
|
||||||
let $scope;
|
|
||||||
let vnApp;
|
|
||||||
|
|
||||||
beforeEach(ngModule('invoiceIn'));
|
|
||||||
|
|
||||||
beforeEach(inject(($componentController, $rootScope, _vnApp_) => {
|
|
||||||
vnApp = _vnApp_;
|
|
||||||
jest.spyOn(vnApp, 'showError');
|
|
||||||
$scope = $rootScope.$new();
|
|
||||||
$scope.model = crudModel;
|
|
||||||
$scope.watcher = watcher;
|
|
||||||
|
|
||||||
const $element = angular.element('<vn-invoice-in-due-day></vn-invoice-in-due-day>');
|
|
||||||
controller = $componentController('vnInvoiceInDueDay', {$element, $scope});
|
|
||||||
controller.invoiceIn = {id: 1};
|
|
||||||
}));
|
|
||||||
|
|
||||||
describe('onSubmit()', () => {
|
|
||||||
it('should make HTTP POST request to save due day values', () => {
|
|
||||||
controller.card = {reload: () => {}};
|
|
||||||
jest.spyOn($scope.watcher, 'check');
|
|
||||||
jest.spyOn($scope.watcher, 'notifySaved');
|
|
||||||
jest.spyOn($scope.watcher, 'updateOriginalData');
|
|
||||||
jest.spyOn(controller.card, 'reload');
|
|
||||||
jest.spyOn($scope.model, 'save');
|
|
||||||
|
|
||||||
controller.onSubmit();
|
|
||||||
|
|
||||||
expect($scope.model.save).toHaveBeenCalledWith();
|
|
||||||
expect($scope.watcher.updateOriginalData).toHaveBeenCalledWith();
|
|
||||||
expect($scope.watcher.check).toHaveBeenCalledWith();
|
|
||||||
expect($scope.watcher.notifySaved).toHaveBeenCalledWith();
|
|
||||||
expect(controller.card.reload).toHaveBeenCalledWith();
|
|
||||||
});
|
|
||||||
});
|
|
||||||
});
|
|
||||||
});
|
|
|
@ -1,17 +1,7 @@
|
||||||
export * from './module';
|
export * from './module';
|
||||||
|
|
||||||
import './main';
|
import './main';
|
||||||
import './index/';
|
|
||||||
import './search-panel';
|
|
||||||
import './card';
|
import './card';
|
||||||
import './descriptor';
|
import './descriptor';
|
||||||
import './descriptor-popover';
|
import './descriptor-popover';
|
||||||
import './summary';
|
import './summary';
|
||||||
import './basic-data';
|
|
||||||
import './tax';
|
|
||||||
import './dueDay';
|
|
||||||
import './intrastat';
|
|
||||||
import './create';
|
|
||||||
import './log';
|
|
||||||
import './serial';
|
|
||||||
import './serial-search-panel';
|
|
||||||
|
|
|
@ -1,82 +0,0 @@
|
||||||
<vn-auto-search
|
|
||||||
model="model">
|
|
||||||
</vn-auto-search>
|
|
||||||
<vn-data-viewer
|
|
||||||
model="model"
|
|
||||||
class="vn-w-lg">
|
|
||||||
<vn-card>
|
|
||||||
<vn-table model="model">
|
|
||||||
<vn-thead>
|
|
||||||
<vn-tr>
|
|
||||||
<vn-th field="id">ID</vn-th>
|
|
||||||
<vn-th field="supplierFk">Supplier</vn-th>
|
|
||||||
<vn-th field="supplierRef">Supplier ref.</vn-th>
|
|
||||||
<vn-th field="serialNumber">Serial number</vn-th>
|
|
||||||
<vn-th field="serial">Serial</vn-th>
|
|
||||||
<vn-th field="dmsFk">File</vn-th>
|
|
||||||
<vn-th field="issued" expand>Issued</vn-th>
|
|
||||||
<vn-th field="isBooked" center>Is booked</vn-th>
|
|
||||||
<vn-th field="awbCode" vn-tooltip="Air Waybill">AWB</vn-th>
|
|
||||||
<vn-th field="amount" filter-enabled="false">Amount</vn-th>
|
|
||||||
<vn-th></vn-th>
|
|
||||||
</vn-tr>
|
|
||||||
</vn-thead>
|
|
||||||
<vn-tbody>
|
|
||||||
<a
|
|
||||||
ng-repeat="invoiceIn in model.data"
|
|
||||||
class="clickable vn-tr search-result"
|
|
||||||
ui-sref="invoiceIn.card.summary({id: {{::invoiceIn.id}}})">
|
|
||||||
<vn-td>{{::invoiceIn.id}}</vn-td>
|
|
||||||
<vn-td expand>
|
|
||||||
<span
|
|
||||||
class="link"
|
|
||||||
vn-click-stop="supplierDescriptor.show($event, invoiceIn.supplierFk)">
|
|
||||||
{{::invoiceIn.supplierName}}
|
|
||||||
</span>
|
|
||||||
</vn-td>
|
|
||||||
<vn-td>{{::invoiceIn.supplierRef | dashIfEmpty}}</vn-td>
|
|
||||||
<vn-td>{{::invoiceIn.serialNumber}}</vn-td>
|
|
||||||
<vn-td>{{::invoiceIn.serial}}</vn-td>
|
|
||||||
<vn-td>
|
|
||||||
<span title="{{'Download file' | translate}}" class="link"
|
|
||||||
ng-click="$ctrl.downloadFile(invoiceIn.dmsFk)">
|
|
||||||
{{::invoiceIn.file}}
|
|
||||||
</span>
|
|
||||||
</vn-td>
|
|
||||||
<vn-td expand>{{::invoiceIn.issued | date:'dd/MM/yyyy' | dashIfEmpty}}</vn-td>
|
|
||||||
<vn-td center>
|
|
||||||
<vn-check disabled="true"
|
|
||||||
ng-model="invoiceIn.isBooked">
|
|
||||||
</vn-check>
|
|
||||||
</vn-td>
|
|
||||||
<vn-td>{{::invoiceIn.awbCode}}</vn-td>
|
|
||||||
<vn-td>{{::invoiceIn.amount | currency:'EUR'}}</vn-td>
|
|
||||||
<vn-td shrink>
|
|
||||||
<vn-icon-button
|
|
||||||
vn-click-stop="$ctrl.preview(invoiceIn)"
|
|
||||||
vn-tooltip="Preview"
|
|
||||||
icon="preview">
|
|
||||||
</vn-icon-button>
|
|
||||||
</vn-td>
|
|
||||||
<vn-td shrink>
|
|
||||||
<!-- <vn-icon-button
|
|
||||||
ng-show="invoiceIn.dmsFk"
|
|
||||||
vn-click-stop="$ctrl.openPdf(invoiceIn.dmsFk)"
|
|
||||||
icon="cloud_download"
|
|
||||||
title="Download PDF"
|
|
||||||
vn-tooltip="Download PDF">
|
|
||||||
</vn-icon-button> -->
|
|
||||||
</vn-td>
|
|
||||||
</a>
|
|
||||||
</vn-tbody>
|
|
||||||
</vn-table>
|
|
||||||
</vn-card>
|
|
||||||
</vn-data-viewer>
|
|
||||||
<vn-supplier-descriptor-popover
|
|
||||||
vn-id="supplierDescriptor">
|
|
||||||
</vn-supplier-descriptor-popover>
|
|
||||||
<vn-popup vn-id="summary">
|
|
||||||
<vn-invoice-in-summary
|
|
||||||
invoice-in="$ctrl.selectedInvoiceIn">
|
|
||||||
</vn-invoice-in-summary>
|
|
||||||
</vn-popup>
|
|
|
@ -1,58 +0,0 @@
|
||||||
import ngModule from '../module';
|
|
||||||
import Section from 'salix/components/section';
|
|
||||||
|
|
||||||
export default class Controller extends Section {
|
|
||||||
constructor($element, $, vnFile) {
|
|
||||||
super($element, $, vnFile);
|
|
||||||
this.vnFile = vnFile;
|
|
||||||
}
|
|
||||||
|
|
||||||
exprBuilder(param, value) {
|
|
||||||
switch (param) {
|
|
||||||
case 'issued':
|
|
||||||
return {'ii.issued': {
|
|
||||||
between: this.dateRange(value)}
|
|
||||||
};
|
|
||||||
case 'id':
|
|
||||||
case 'supplierFk':
|
|
||||||
case 'supplierRef':
|
|
||||||
case 'serialNumber':
|
|
||||||
case 'serial':
|
|
||||||
case 'created':
|
|
||||||
case 'isBooked':
|
|
||||||
return {[`ii.${param}`]: value};
|
|
||||||
case 'account':
|
|
||||||
case 'fi':
|
|
||||||
return {[`s.${param}`]: value};
|
|
||||||
case 'awbCode':
|
|
||||||
return {'awb.code': value};
|
|
||||||
default:
|
|
||||||
return {[param]: value};
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
dateRange(value) {
|
|
||||||
const minHour = new Date(value);
|
|
||||||
minHour.setHours(0, 0, 0, 0);
|
|
||||||
const maxHour = new Date(value);
|
|
||||||
maxHour.setHours(23, 59, 59, 59);
|
|
||||||
|
|
||||||
return [minHour, maxHour];
|
|
||||||
}
|
|
||||||
|
|
||||||
preview(invoiceIn) {
|
|
||||||
this.selectedInvoiceIn = invoiceIn;
|
|
||||||
this.$.summary.show();
|
|
||||||
}
|
|
||||||
|
|
||||||
downloadFile(dmsId) {
|
|
||||||
this.vnFile.download(`api/dms/${dmsId}/downloadFile`);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Controller.$inject = ['$element', '$scope', 'vnFile'];
|
|
||||||
|
|
||||||
ngModule.vnComponent('vnInvoiceInIndex', {
|
|
||||||
template: require('./index.html'),
|
|
||||||
controller: Controller
|
|
||||||
});
|
|
|
@ -1,6 +0,0 @@
|
||||||
Created: Fecha creación
|
|
||||||
Issued: Fecha emisión
|
|
||||||
Supplier ref.: Ref. proveedor
|
|
||||||
Serial number: Num. serie
|
|
||||||
Serial: Serie
|
|
||||||
Is booked: Conciliada
|
|
|
@ -1,100 +0,0 @@
|
||||||
<vn-crud-model
|
|
||||||
vn-id="model"
|
|
||||||
url="InvoiceInIntrastats"
|
|
||||||
data="$ctrl.invoceInIntrastat"
|
|
||||||
link="{invoiceInFk: $ctrl.$params.id}"
|
|
||||||
auto-load="true"
|
|
||||||
on-data-change="$ctrl.calculateTotals()">
|
|
||||||
</vn-crud-model>
|
|
||||||
<vn-crud-model
|
|
||||||
auto-load="true"
|
|
||||||
url="Countries"
|
|
||||||
data="countries"
|
|
||||||
order="name">
|
|
||||||
</vn-crud-model>
|
|
||||||
<vn-crud-model
|
|
||||||
auto-load="true"
|
|
||||||
url="Intrastats"
|
|
||||||
data="intrastats"
|
|
||||||
order="id">
|
|
||||||
</vn-crud-model>
|
|
||||||
<vn-watcher
|
|
||||||
vn-id="watcher"
|
|
||||||
data="$ctrl.invoceInIntrastat"
|
|
||||||
form="form">
|
|
||||||
</vn-watcher>
|
|
||||||
<vn-card
|
|
||||||
class="vn-mb-md vn-pa-lg vn-w-lg"
|
|
||||||
style="text-align: right"
|
|
||||||
ng-if="$ctrl.invoceInIntrastat.length > 0">
|
|
||||||
<vn-label-value label="Total amount"
|
|
||||||
value="{{$ctrl.amountTotal | currency: 'EUR':2}}">
|
|
||||||
</vn-label-value>
|
|
||||||
<vn-label-value label="Total net"
|
|
||||||
value="{{$ctrl.netTotal}}">
|
|
||||||
</vn-label-value>
|
|
||||||
<vn-label-value label="Total stems"
|
|
||||||
value="{{$ctrl.stemsTotal}}">
|
|
||||||
</vn-label-value>
|
|
||||||
</vn-card>
|
|
||||||
<form name="form" ng-submit="$ctrl.onSubmit()">
|
|
||||||
<vn-card class="vn-pa-lg">
|
|
||||||
<vn-horizontal ng-repeat="intrastat in $ctrl.invoceInIntrastat">
|
|
||||||
<vn-autocomplete vn-three
|
|
||||||
label="Code"
|
|
||||||
data="intrastats"
|
|
||||||
ng-model="intrastat.intrastatFk"
|
|
||||||
show-field="description"
|
|
||||||
rule
|
|
||||||
vn-focus>
|
|
||||||
<tpl-item>{{id}}: {{description}}</tpl-item>
|
|
||||||
</vn-autocomplete>
|
|
||||||
<vn-input-number
|
|
||||||
label="Amount"
|
|
||||||
ng-model="intrastat.amount"
|
|
||||||
step="0.01"
|
|
||||||
rule>
|
|
||||||
</vn-input-number>
|
|
||||||
<vn-input-number
|
|
||||||
label="Net"
|
|
||||||
ng-model="intrastat.net"
|
|
||||||
step="0.01"
|
|
||||||
rule>
|
|
||||||
</vn-input-number>
|
|
||||||
<vn-input-number
|
|
||||||
label="Stems"
|
|
||||||
ng-model="intrastat.stems"
|
|
||||||
rule>
|
|
||||||
</vn-input-number>
|
|
||||||
<vn-autocomplete
|
|
||||||
label="Country"
|
|
||||||
data="countries"
|
|
||||||
ng-model="intrastat.countryFk"
|
|
||||||
show-field="code"
|
|
||||||
rule>
|
|
||||||
</vn-autocomplete>
|
|
||||||
<vn-none>
|
|
||||||
<vn-icon-button
|
|
||||||
vn-tooltip="Remove due day"
|
|
||||||
icon="delete"
|
|
||||||
ng-click="$ctrl.deleteIntrastat($index)"
|
|
||||||
tabindex="-1">
|
|
||||||
</vn-icon-button>
|
|
||||||
</vn-none>
|
|
||||||
</vn-horizontal>
|
|
||||||
<vn-one>
|
|
||||||
<vn-icon-button
|
|
||||||
vn-bind="+"
|
|
||||||
vn-tooltip="Add due day"
|
|
||||||
icon="add_circle"
|
|
||||||
ng-click="$ctrl.add()">
|
|
||||||
</vn-icon-button>
|
|
||||||
</vn-one>
|
|
||||||
</vn-card>
|
|
||||||
<vn-button-bar>
|
|
||||||
<vn-submit
|
|
||||||
disabled="!watcher.dataChanged()"
|
|
||||||
label="Save">
|
|
||||||
</vn-submit>
|
|
||||||
</vn-button-bar>
|
|
||||||
</form>
|
|
|
@ -1,60 +0,0 @@
|
||||||
import ngModule from '../module';
|
|
||||||
import Section from 'salix/components/section';
|
|
||||||
|
|
||||||
class Controller extends Section {
|
|
||||||
set invoceInIntrastat(value) {
|
|
||||||
this._invoceInIntrastat = value;
|
|
||||||
|
|
||||||
if (value) this.calculateTotals();
|
|
||||||
}
|
|
||||||
|
|
||||||
get invoceInIntrastat() {
|
|
||||||
return this._invoceInIntrastat;
|
|
||||||
}
|
|
||||||
|
|
||||||
calculateTotals() {
|
|
||||||
this.amountTotal = 0.0;
|
|
||||||
this.netTotal = 0.0;
|
|
||||||
this.stemsTotal = 0.0;
|
|
||||||
if (!this.invoceInIntrastat) return;
|
|
||||||
|
|
||||||
this.invoceInIntrastat.forEach(intrastat => {
|
|
||||||
this.amountTotal += intrastat.amount;
|
|
||||||
this.netTotal += intrastat.net;
|
|
||||||
this.stemsTotal += intrastat.stems;
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
add() {
|
|
||||||
this.$.model.insert({});
|
|
||||||
}
|
|
||||||
|
|
||||||
deleteIntrastat($index) {
|
|
||||||
this.$.model.remove($index);
|
|
||||||
this.$.model.save().then(() => {
|
|
||||||
this.vnApp.showSuccess(this.$t('Data saved!'));
|
|
||||||
this.calculateTotals();
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
onSubmit() {
|
|
||||||
this.$.watcher.check();
|
|
||||||
this.$.model.save().then(() => {
|
|
||||||
this.$.watcher.notifySaved();
|
|
||||||
this.$.watcher.updateOriginalData();
|
|
||||||
this.calculateTotals();
|
|
||||||
this.card.reload();
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
ngModule.vnComponent('vnInvoiceInIntrastat', {
|
|
||||||
template: require('./index.html'),
|
|
||||||
controller: Controller,
|
|
||||||
require: {
|
|
||||||
card: '^vnInvoiceInCard'
|
|
||||||
},
|
|
||||||
bindings: {
|
|
||||||
invoiceIn: '<'
|
|
||||||
}
|
|
||||||
});
|
|
|
@ -1,85 +0,0 @@
|
||||||
import './index.js';
|
|
||||||
import watcher from 'core/mocks/watcher';
|
|
||||||
import crudModel from 'core/mocks/crud-model';
|
|
||||||
|
|
||||||
describe('InvoiceIn', () => {
|
|
||||||
describe('Component intrastat', () => {
|
|
||||||
let controller;
|
|
||||||
let $scope;
|
|
||||||
let vnApp;
|
|
||||||
|
|
||||||
beforeEach(ngModule('invoiceIn'));
|
|
||||||
|
|
||||||
beforeEach(inject(($componentController, $rootScope, _vnApp_) => {
|
|
||||||
vnApp = _vnApp_;
|
|
||||||
jest.spyOn(vnApp, 'showError');
|
|
||||||
$scope = $rootScope.$new();
|
|
||||||
$scope.model = crudModel;
|
|
||||||
$scope.watcher = watcher;
|
|
||||||
|
|
||||||
const $element = angular.element('<vn-invoice-in-intrastat></vn-invoice-in-intrastat>');
|
|
||||||
controller = $componentController('vnInvoiceInIntrastat', {$element, $scope});
|
|
||||||
controller.invoiceIn = {id: 1};
|
|
||||||
}));
|
|
||||||
|
|
||||||
describe('calculateTotals()', () => {
|
|
||||||
it('should set amountTotal, netTotal and stemsTotal to 0 if salesClaimed has no data', () => {
|
|
||||||
controller.invoceInIntrastat = [];
|
|
||||||
controller.calculateTotals();
|
|
||||||
|
|
||||||
expect(controller.amountTotal).toEqual(0);
|
|
||||||
expect(controller.netTotal).toEqual(0);
|
|
||||||
expect(controller.stemsTotal).toEqual(0);
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should set amountTotal, netTotal and stemsTotal', () => {
|
|
||||||
controller.invoceInIntrastat = [
|
|
||||||
{
|
|
||||||
id: 1,
|
|
||||||
invoiceInFk: 1,
|
|
||||||
net: 30.5,
|
|
||||||
intrastatFk: 5080000,
|
|
||||||
amount: 10,
|
|
||||||
stems: 162,
|
|
||||||
countryFk: 5,
|
|
||||||
statisticalValue: 0
|
|
||||||
},
|
|
||||||
{
|
|
||||||
id: 2,
|
|
||||||
invoiceInFk: 1,
|
|
||||||
net: 10,
|
|
||||||
intrastatFk: 6021010,
|
|
||||||
amount: 20,
|
|
||||||
stems: 205,
|
|
||||||
countryFk: 5,
|
|
||||||
statisticalValue: 0
|
|
||||||
}
|
|
||||||
];
|
|
||||||
controller.calculateTotals();
|
|
||||||
|
|
||||||
expect(controller.amountTotal).toEqual(30);
|
|
||||||
expect(controller.netTotal).toEqual(40.5);
|
|
||||||
expect(controller.stemsTotal).toEqual(367);
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
describe('onSubmit()', () => {
|
|
||||||
it('should make HTTP POST request to save intrastat values', () => {
|
|
||||||
controller.card = {reload: () => {}};
|
|
||||||
jest.spyOn($scope.watcher, 'check');
|
|
||||||
jest.spyOn($scope.watcher, 'notifySaved');
|
|
||||||
jest.spyOn($scope.watcher, 'updateOriginalData');
|
|
||||||
jest.spyOn(controller.card, 'reload');
|
|
||||||
jest.spyOn($scope.model, 'save');
|
|
||||||
|
|
||||||
controller.onSubmit();
|
|
||||||
|
|
||||||
expect($scope.model.save).toHaveBeenCalledWith();
|
|
||||||
expect($scope.watcher.updateOriginalData).toHaveBeenCalledWith();
|
|
||||||
expect($scope.watcher.check).toHaveBeenCalledWith();
|
|
||||||
expect($scope.watcher.notifySaved).toHaveBeenCalledWith();
|
|
||||||
expect(controller.card.reload).toHaveBeenCalledWith();
|
|
||||||
});
|
|
||||||
});
|
|
||||||
});
|
|
||||||
});
|
|
|
@ -1 +0,0 @@
|
||||||
<vn-log url="InvoiceInLogs" origin-id="$ctrl.$params.id"></vn-log>
|
|
|
@ -1,7 +0,0 @@
|
||||||
import ngModule from '../module';
|
|
||||||
import Section from 'salix/components/section';
|
|
||||||
|
|
||||||
ngModule.vnComponent('vnInvoiceInLog', {
|
|
||||||
template: require('./index.html'),
|
|
||||||
controller: Section,
|
|
||||||
});
|
|
|
@ -1,18 +0,0 @@
|
||||||
<vn-crud-model
|
|
||||||
vn-id="model"
|
|
||||||
url="InvoiceIns/filter"
|
|
||||||
limit="20"
|
|
||||||
order="isBooked, issued DESC">
|
|
||||||
</vn-crud-model>
|
|
||||||
<vn-portal slot="topbar">
|
|
||||||
<vn-searchbar
|
|
||||||
vn-focus
|
|
||||||
panel="vn-invoice-in-search-panel"
|
|
||||||
info="Search invoices in by reference"
|
|
||||||
model="model">
|
|
||||||
</vn-searchbar>
|
|
||||||
</vn-portal>
|
|
||||||
<vn-portal slot="menu">
|
|
||||||
<vn-left-menu></vn-left-menu>
|
|
||||||
</vn-portal>
|
|
||||||
<ui-view></ui-view>
|
|
|
@ -1,7 +1,16 @@
|
||||||
import ngModule from '../module';
|
import ngModule from '../module';
|
||||||
import ModuleMain from 'salix/components/module-main';
|
import ModuleMain from 'salix/components/module-main';
|
||||||
|
|
||||||
export default class InvoiceIn extends ModuleMain {}
|
export default class InvoiceIn extends ModuleMain {
|
||||||
|
constructor($element, $) {
|
||||||
|
super($element, $);
|
||||||
|
}
|
||||||
|
|
||||||
|
async $onInit() {
|
||||||
|
this.$state.go('home');
|
||||||
|
window.location.href = await this.vnApp.getUrl(`invoice-in/`);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
ngModule.vnComponent('vnInvoiceIn', {
|
ngModule.vnComponent('vnInvoiceIn', {
|
||||||
controller: InvoiceIn,
|
controller: InvoiceIn,
|
||||||
|
|
|
@ -1,97 +0,0 @@
|
||||||
<div class="search-panel">
|
|
||||||
<form ng-submit="$ctrl.onSearch()">
|
|
||||||
<vn-horizontal>
|
|
||||||
<vn-textfield
|
|
||||||
vn-one
|
|
||||||
label="General search"
|
|
||||||
ng-model="filter.search"
|
|
||||||
info="Search invoices in by id or supplier fiscal name"
|
|
||||||
vn-focus>
|
|
||||||
</vn-textfield>
|
|
||||||
</vn-horizontal>
|
|
||||||
<vn-horizontal>
|
|
||||||
<vn-textfield
|
|
||||||
vn-one
|
|
||||||
label="Supplier ref."
|
|
||||||
ng-model="filter.supplierRef">
|
|
||||||
</vn-textfield>
|
|
||||||
<vn-textfield
|
|
||||||
vn-one
|
|
||||||
label="Supplier fiscal id"
|
|
||||||
ng-model="filter.fi">
|
|
||||||
</vn-textfield>
|
|
||||||
</vn-horizontal>
|
|
||||||
<vn-horizontal>
|
|
||||||
<vn-autocomplete
|
|
||||||
vn-one ng-model="filter.supplierFk"
|
|
||||||
url="Suppliers"
|
|
||||||
show-field="nickname"
|
|
||||||
search-function="{or: [{id: $search}, {nickname: {like: '%'+ $search +'%'}}]}"
|
|
||||||
value-field="id"
|
|
||||||
order="nickname"
|
|
||||||
label="Supplier">
|
|
||||||
<tpl-item>
|
|
||||||
{{::id}} - {{::nickname}}
|
|
||||||
</tpl-item>
|
|
||||||
</vn-autocomplete>
|
|
||||||
</vn-horizontal>
|
|
||||||
<vn-horizontal>
|
|
||||||
<vn-textfield
|
|
||||||
vn-one
|
|
||||||
label="Account"
|
|
||||||
ng-model="filter.account">
|
|
||||||
</vn-textfield>
|
|
||||||
<vn-input-number
|
|
||||||
vn-one
|
|
||||||
label="Amount"
|
|
||||||
ng-model="filter.amount"
|
|
||||||
step="0.01">
|
|
||||||
</vn-input-number>
|
|
||||||
</vn-horizontal>
|
|
||||||
<vn-horizontal>
|
|
||||||
<vn-date-picker
|
|
||||||
vn-one
|
|
||||||
label="From"
|
|
||||||
ng-model="filter.from">
|
|
||||||
</vn-date-picker>
|
|
||||||
<vn-date-picker
|
|
||||||
vn-one
|
|
||||||
label="To"
|
|
||||||
ng-model="filter.to">
|
|
||||||
</vn-date-picker>
|
|
||||||
<vn-date-picker
|
|
||||||
vn-one
|
|
||||||
label="Issued"
|
|
||||||
ng-model="filter.issued">
|
|
||||||
</vn-date-picker>
|
|
||||||
</vn-horizontal>
|
|
||||||
<vn-horizontal>
|
|
||||||
<vn-textfield
|
|
||||||
vn-one
|
|
||||||
label="Serial number"
|
|
||||||
ng-model="filter.serialNumber">
|
|
||||||
</vn-textfield>
|
|
||||||
<vn-textfield
|
|
||||||
vn-one
|
|
||||||
label="Serial"
|
|
||||||
ng-model="filter.serial">
|
|
||||||
</vn-textfield>
|
|
||||||
<vn-textfield
|
|
||||||
vn-one
|
|
||||||
label="AWB"
|
|
||||||
ng-model="filter.awbCode">
|
|
||||||
</vn-textfield>
|
|
||||||
</vn-horizontal>
|
|
||||||
<vn-horizontal>
|
|
||||||
<vn-check
|
|
||||||
vn-one
|
|
||||||
triple-state="true"
|
|
||||||
label="Is booked"
|
|
||||||
ng-model="filter.isBooked">
|
|
||||||
</vn-check>
|
|
||||||
</vn-horizontal>
|
|
||||||
<vn-horizontal class="vn-mt-lg">
|
|
||||||
<vn-submit label="Search"></vn-submit>
|
|
||||||
</vn-horizontal>
|
|
||||||
</form>
|
|
||||||
</div>
|
|
|
@ -1,7 +0,0 @@
|
||||||
import ngModule from '../module';
|
|
||||||
import SearchPanel from 'core/components/searchbar/search-panel';
|
|
||||||
|
|
||||||
ngModule.vnComponent('vnInvoiceInSearchPanel', {
|
|
||||||
template: require('./index.html'),
|
|
||||||
controller: SearchPanel
|
|
||||||
});
|
|
|
@ -1,2 +0,0 @@
|
||||||
Supplier fiscal id: CIF proveedor
|
|
||||||
Search invoices in by id or supplier fiscal name: Buscar facturas recibidas por id o por nombre fiscal del proveedor
|
|
|
@ -1,27 +0,0 @@
|
||||||
<vn-side-menu side="right">
|
|
||||||
<vn-horizontal class="input">
|
|
||||||
<vn-input-number
|
|
||||||
label="Days ago"
|
|
||||||
ng-model="$ctrl.filter.daysAgo"
|
|
||||||
vn-focus
|
|
||||||
ng-keydown="$ctrl.onKeyPress($event)"
|
|
||||||
min="0">
|
|
||||||
</vn-input-number>
|
|
||||||
</vn-horizontal>
|
|
||||||
<vn-horizontal class="input">
|
|
||||||
<vn-textfield
|
|
||||||
label="Serial"
|
|
||||||
ng-model="$ctrl.filter.serial"
|
|
||||||
ng-keydown="$ctrl.onKeyPress($event)">
|
|
||||||
</vn-textfield>
|
|
||||||
</vn-horizontal>
|
|
||||||
<div class="chips">
|
|
||||||
<vn-chip
|
|
||||||
ng-if="$ctrl.filter.serial"
|
|
||||||
removable="true"
|
|
||||||
on-remove="$ctrl.removeItemFilter('serial')"
|
|
||||||
class="colored">
|
|
||||||
<span>{{$ctrl.$t('Serial')}}: {{$ctrl.filter.serial}}</span>
|
|
||||||
</vn-chip>
|
|
||||||
</div>
|
|
||||||
</vn-side-menu>
|
|
|
@ -1,44 +0,0 @@
|
||||||
import ngModule from '../module';
|
|
||||||
import SearchPanel from 'core/components/searchbar/search-panel';
|
|
||||||
import './style.scss';
|
|
||||||
|
|
||||||
class Controller extends SearchPanel {
|
|
||||||
constructor($element, $) {
|
|
||||||
super($element, $);
|
|
||||||
this.filter = {};
|
|
||||||
const filter = {
|
|
||||||
fields: ['daysAgo']
|
|
||||||
};
|
|
||||||
this.$http.get('InvoiceInConfigs', {filter}).then(res => {
|
|
||||||
if (res.data) {
|
|
||||||
this.invoiceInConfig = res.data[0];
|
|
||||||
this.addFilters();
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
removeItemFilter(param) {
|
|
||||||
this.filter[param] = null;
|
|
||||||
this.addFilters();
|
|
||||||
}
|
|
||||||
|
|
||||||
onKeyPress($event) {
|
|
||||||
if ($event.key === 'Enter')
|
|
||||||
this.addFilters();
|
|
||||||
}
|
|
||||||
|
|
||||||
addFilters() {
|
|
||||||
if (!this.filter.daysAgo)
|
|
||||||
this.filter.daysAgo = this.invoiceInConfig.daysAgo;
|
|
||||||
|
|
||||||
return this.model.addFilter({}, this.filter);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
ngModule.component('vnInvoiceInSerialSearchPanel', {
|
|
||||||
template: require('./index.html'),
|
|
||||||
controller: Controller,
|
|
||||||
bindings: {
|
|
||||||
model: '<'
|
|
||||||
}
|
|
||||||
});
|
|
|
@ -1,43 +0,0 @@
|
||||||
import './index.js';
|
|
||||||
|
|
||||||
describe('InvoiceIn', () => {
|
|
||||||
describe('Component serial-search-panel', () => {
|
|
||||||
let controller;
|
|
||||||
let $scope;
|
|
||||||
|
|
||||||
beforeEach(ngModule('invoiceIn'));
|
|
||||||
|
|
||||||
beforeEach(inject(($componentController, $rootScope) => {
|
|
||||||
$scope = $rootScope.$new();
|
|
||||||
const $element = angular.element('<vn-invoice-in-serial-search-panel></vn-invoice-in-serial-search-panel>');
|
|
||||||
controller = $componentController('vnInvoiceInSerialSearchPanel', {$element, $scope});
|
|
||||||
controller.model = {
|
|
||||||
addFilter: jest.fn(),
|
|
||||||
};
|
|
||||||
controller.invoiceInConfig = {
|
|
||||||
daysAgo: 45,
|
|
||||||
};
|
|
||||||
}));
|
|
||||||
|
|
||||||
describe('addFilters()', () => {
|
|
||||||
it('should add default daysAgo if it is not already set', () => {
|
|
||||||
controller.filter = {
|
|
||||||
serial: 'R',
|
|
||||||
};
|
|
||||||
controller.addFilters();
|
|
||||||
|
|
||||||
expect(controller.filter.daysAgo).toEqual(controller.invoiceInConfig.daysAgo);
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should not add default daysAgo if it is already set', () => {
|
|
||||||
controller.filter = {
|
|
||||||
daysAgo: 1,
|
|
||||||
serial: 'R',
|
|
||||||
};
|
|
||||||
controller.addFilters();
|
|
||||||
|
|
||||||
expect(controller.filter.daysAgo).toEqual(1);
|
|
||||||
});
|
|
||||||
});
|
|
||||||
});
|
|
||||||
});
|
|
|
@ -1,24 +0,0 @@
|
||||||
@import "variables";
|
|
||||||
|
|
||||||
vn-invoice-in-serial-search-panel vn-side-menu div {
|
|
||||||
& > .input {
|
|
||||||
padding-left: $spacing-md;
|
|
||||||
padding-right: $spacing-md;
|
|
||||||
border-color: $color-spacer;
|
|
||||||
border-bottom: $border-thin;
|
|
||||||
}
|
|
||||||
& > .horizontal {
|
|
||||||
grid-auto-flow: column;
|
|
||||||
grid-column-gap: $spacing-sm;
|
|
||||||
align-items: center;
|
|
||||||
}
|
|
||||||
& > .chips {
|
|
||||||
display: flex;
|
|
||||||
flex-wrap: wrap;
|
|
||||||
padding: $spacing-md;
|
|
||||||
overflow: hidden;
|
|
||||||
max-width: 100%;
|
|
||||||
border-color: $color-spacer;
|
|
||||||
border-top: $border-thin;
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,40 +0,0 @@
|
||||||
<vn-crud-model
|
|
||||||
vn-id="model"
|
|
||||||
url="InvoiceIns/getSerial"
|
|
||||||
limit="20">
|
|
||||||
</vn-crud-model>
|
|
||||||
<vn-portal slot="topbar">
|
|
||||||
</vn-portal>
|
|
||||||
<vn-invoice-in-serial-search-panel
|
|
||||||
model="model">
|
|
||||||
</vn-invoice-in-serial-search-panel>
|
|
||||||
<vn-data-viewer
|
|
||||||
model="model"
|
|
||||||
class="vn-w-lg">
|
|
||||||
<vn-card>
|
|
||||||
<vn-table model="model">
|
|
||||||
<vn-thead>
|
|
||||||
<vn-tr>
|
|
||||||
<vn-th field="serial">Serial</vn-th>
|
|
||||||
<vn-th field="pending">Pending</vn-th>
|
|
||||||
<vn-th field="total">Total</vn-th>
|
|
||||||
<vn-th></vn-th>
|
|
||||||
</vn-tr>
|
|
||||||
</vn-thead>
|
|
||||||
<vn-tbody>
|
|
||||||
<vn-tr ng-repeat="invoiceIn in model.data">
|
|
||||||
<vn-td>{{::invoiceIn.serial}}</vn-td>
|
|
||||||
<vn-td>{{::invoiceIn.pending}}</vn-td>
|
|
||||||
<vn-td>{{::invoiceIn.total}}</vn-td>
|
|
||||||
<vn-td shrink>
|
|
||||||
<vn-icon-button
|
|
||||||
vn-click-stop="$ctrl.goToIndex(model.userParams.daysAgo, invoiceIn.serial)"
|
|
||||||
vn-tooltip="Go to InvoiceIn"
|
|
||||||
icon="icon-invoice-in">
|
|
||||||
</vn-icon-button>
|
|
||||||
</vn-td>
|
|
||||||
</vn-tr>
|
|
||||||
</vn-tbody>
|
|
||||||
</vn-table>
|
|
||||||
</vn-card>
|
|
||||||
</vn-data-viewer>
|
|
|
@ -1,22 +0,0 @@
|
||||||
import ngModule from '../module';
|
|
||||||
import Section from 'salix/components/section';
|
|
||||||
|
|
||||||
export default class Controller extends Section {
|
|
||||||
constructor($element, $) {
|
|
||||||
super($element, $);
|
|
||||||
}
|
|
||||||
|
|
||||||
goToIndex(daysAgo, serial) {
|
|
||||||
const issued = Date.vnNew();
|
|
||||||
issued.setDate(issued.getDate() - daysAgo);
|
|
||||||
this.$state.go('invoiceIn.index',
|
|
||||||
{q: `{"serial": "${serial}", "isBooked": false, "from": ${issued.getTime()}}`});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Controller.$inject = ['$element', '$scope'];
|
|
||||||
|
|
||||||
ngModule.vnComponent('vnInvoiceInSerial', {
|
|
||||||
template: require('./index.html'),
|
|
||||||
controller: Controller
|
|
||||||
});
|
|
|
@ -1,3 +0,0 @@
|
||||||
Serial: Serie
|
|
||||||
Pending: Pendientes
|
|
||||||
Go to InvoiceIn: Ir al listado de facturas recibidas
|
|
|
@ -1,149 +0,0 @@
|
||||||
<vn-crud-model
|
|
||||||
vn-id="model"
|
|
||||||
url="InvoiceInTaxes"
|
|
||||||
fields="[
|
|
||||||
'id',
|
|
||||||
'invoiceInFk',
|
|
||||||
'taxableBase',
|
|
||||||
'expenseFk',
|
|
||||||
'foreignValue',
|
|
||||||
'taxTypeSageFk',
|
|
||||||
'transactionTypeSageFk']"
|
|
||||||
link="{invoiceInFk: $ctrl.$params.id}"
|
|
||||||
data="$ctrl.taxes"
|
|
||||||
auto-load="true">
|
|
||||||
</vn-crud-model>
|
|
||||||
<vn-crud-model
|
|
||||||
url="Expenses"
|
|
||||||
data="expenses"
|
|
||||||
auto-load="true">
|
|
||||||
</vn-crud-model>
|
|
||||||
<vn-watcher
|
|
||||||
vn-id="watcher"
|
|
||||||
data="$ctrl.taxes"
|
|
||||||
form="form">
|
|
||||||
</vn-watcher>
|
|
||||||
<form name="form" ng-submit="$ctrl.onSubmit()">
|
|
||||||
<vn-card class="vn-pa-lg">
|
|
||||||
<vn-horizontal ng-repeat="invoiceInTax in $ctrl.taxes">
|
|
||||||
<vn-autocomplete vn-three vn-id="expense" vn-focus
|
|
||||||
label="Expense"
|
|
||||||
ng-model="invoiceInTax.expenseFk"
|
|
||||||
data="expenses"
|
|
||||||
show-field="id"
|
|
||||||
rule>
|
|
||||||
<tpl-item>{{id}}: {{name}}</tpl-item>
|
|
||||||
<append>
|
|
||||||
<vn-icon-button
|
|
||||||
vn-tooltip="Create expense"
|
|
||||||
icon="add_circle"
|
|
||||||
vn-click-stop="createExpense.show()">
|
|
||||||
</vn-icon-button>
|
|
||||||
</append>
|
|
||||||
</vn-autocomplete>
|
|
||||||
<vn-input-number vn-one
|
|
||||||
disabled="$ctrl.invoiceIn.currency.code != 'EUR'"
|
|
||||||
step="0.01"
|
|
||||||
label="Taxable base"
|
|
||||||
ng-model="invoiceInTax.taxableBase"
|
|
||||||
rule>
|
|
||||||
</vn-input-number>
|
|
||||||
<vn-autocomplete vn-three
|
|
||||||
label="Sage tax"
|
|
||||||
ng-model="invoiceInTax.taxTypeSageFk"
|
|
||||||
url="SageTaxTypes"
|
|
||||||
show-field="vat"
|
|
||||||
fields="['id', 'vat', 'rate']"
|
|
||||||
search-function="{or: [{id: $search}, {vat: {like: '%'+ $search +'%'}}]}"
|
|
||||||
selection="taxRateSelection"
|
|
||||||
rule>
|
|
||||||
<tpl-item>
|
|
||||||
<div>{{::vat}}</div>
|
|
||||||
<div class="text-secondary text-caption">#{{::id}}</div>
|
|
||||||
</tpl-item>
|
|
||||||
</vn-autocomplete>
|
|
||||||
<vn-autocomplete vn-three
|
|
||||||
label="Sage transaction"
|
|
||||||
ng-model="invoiceInTax.transactionTypeSageFk"
|
|
||||||
url="SageTransactionTypes"
|
|
||||||
show-field="transaction"
|
|
||||||
search-function="{or: [{id: $search}, {transaction: {like: '%'+ $search +'%'}}]}"
|
|
||||||
rule>
|
|
||||||
<tpl-item>
|
|
||||||
<div>{{::transaction}}</div>
|
|
||||||
<div class="text-secondary text-caption">#{{::id}}</div>
|
|
||||||
</tpl-item>
|
|
||||||
</vn-autocomplete>
|
|
||||||
<vn-textfield
|
|
||||||
disabled="true"
|
|
||||||
label="Rate"
|
|
||||||
field="$ctrl.taxRate(invoiceInTax, taxRateSelection) | currency:'EUR':2">
|
|
||||||
</vn-textfield>
|
|
||||||
<vn-input-number
|
|
||||||
disabled="$ctrl.invoiceIn.currency.code == 'EUR'"
|
|
||||||
label="Foreign value"
|
|
||||||
ng-model="invoiceInTax.foreignValue"
|
|
||||||
rule>
|
|
||||||
</vn-input-number>
|
|
||||||
<vn-none>
|
|
||||||
<vn-icon-button
|
|
||||||
vn-tooltip="Remove tax"
|
|
||||||
icon="delete"
|
|
||||||
ng-click="model.remove($index)"
|
|
||||||
tabindex="-1">
|
|
||||||
</vn-icon-button>
|
|
||||||
</vn-none>
|
|
||||||
</vn-horizontal>
|
|
||||||
<vn-one>
|
|
||||||
<vn-icon-button
|
|
||||||
vn-bind="+"
|
|
||||||
vn-tooltip="Add tax"
|
|
||||||
icon="add_circle"
|
|
||||||
ng-click="$ctrl.add()">
|
|
||||||
</vn-icon-button>
|
|
||||||
</vn-one>
|
|
||||||
</vn-card>
|
|
||||||
<vn-button-bar>
|
|
||||||
<vn-submit
|
|
||||||
disabled="!watcher.dataChanged()"
|
|
||||||
label="Save">
|
|
||||||
</vn-submit>
|
|
||||||
</vn-button-bar>
|
|
||||||
</form>
|
|
||||||
|
|
||||||
<!-- Dialog of create expense-->
|
|
||||||
<vn-dialog
|
|
||||||
vn-id="createExpense"
|
|
||||||
on-accept="$ctrl.onResponse()">
|
|
||||||
<tpl-body>
|
|
||||||
<section>
|
|
||||||
<h5 class="vn-py-sm">{{$ctrl.$t('New expense')}}</h5>
|
|
||||||
<vn-horizontal>
|
|
||||||
<vn-textfield vn-one
|
|
||||||
vn-id="code"
|
|
||||||
label="Code"
|
|
||||||
ng-model="$ctrl.expense.code"
|
|
||||||
required="true"
|
|
||||||
vn-focus>
|
|
||||||
</vn-textfield>
|
|
||||||
<vn-check
|
|
||||||
vn-one
|
|
||||||
label="It's a withholding"
|
|
||||||
ng-model="$ctrl.expense.isWithheld">
|
|
||||||
</vn-check>
|
|
||||||
</vn-horizontal>
|
|
||||||
<vn-horizontal>
|
|
||||||
<vn-textfield vn-one
|
|
||||||
vn-id="description"
|
|
||||||
label="Description"
|
|
||||||
ng-model="$ctrl.expense.description"
|
|
||||||
required="true">
|
|
||||||
</vn-textfield>
|
|
||||||
</vn-horizontal>
|
|
||||||
</section>
|
|
||||||
</tpl-body>
|
|
||||||
<tpl-buttons>
|
|
||||||
<input type="button" response="cancel" translate-attr="{value: 'Cancel'}"/>
|
|
||||||
<button response="accept" translate>Save</button>
|
|
||||||
</tpl-buttons>
|
|
||||||
</vn-dialog>
|
|
|
@ -1,66 +0,0 @@
|
||||||
import ngModule from '../module';
|
|
||||||
import Section from 'salix/components/section';
|
|
||||||
import UserError from 'core/lib/user-error';
|
|
||||||
|
|
||||||
class Controller extends Section {
|
|
||||||
constructor($element, $, vnWeekDays) {
|
|
||||||
super($element, $);
|
|
||||||
this.expense = {};
|
|
||||||
}
|
|
||||||
taxRate(invoiceInTax, taxRateSelection) {
|
|
||||||
const taxTypeSage = taxRateSelection && taxRateSelection.rate;
|
|
||||||
const taxableBase = invoiceInTax && invoiceInTax.taxableBase;
|
|
||||||
|
|
||||||
if (taxTypeSage && taxableBase)
|
|
||||||
return (taxTypeSage / 100) * taxableBase;
|
|
||||||
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
add() {
|
|
||||||
this.$.model.insert({
|
|
||||||
invoiceIn: this.$params.id
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
onSubmit() {
|
|
||||||
this.$.watcher.check();
|
|
||||||
this.$.model.save().then(() => {
|
|
||||||
this.$.watcher.notifySaved();
|
|
||||||
this.$.watcher.updateOriginalData();
|
|
||||||
this.card.reload();
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
onResponse() {
|
|
||||||
try {
|
|
||||||
if (!this.expense.code)
|
|
||||||
throw new Error(`The code can't be empty`);
|
|
||||||
if (!this.expense.description)
|
|
||||||
throw new UserError(`The description can't be empty`);
|
|
||||||
|
|
||||||
const data = [{
|
|
||||||
id: this.expense.code,
|
|
||||||
isWithheld: this.expense.isWithheld,
|
|
||||||
name: this.expense.description
|
|
||||||
}];
|
|
||||||
|
|
||||||
this.$http.post(`Expenses`, data) .then(() => {
|
|
||||||
this.vnApp.showSuccess(this.$t('Expense saved!'));
|
|
||||||
});
|
|
||||||
} catch (e) {
|
|
||||||
this.vnApp.showError(this.$t(e.message));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
ngModule.vnComponent('vnInvoiceInTax', {
|
|
||||||
template: require('./index.html'),
|
|
||||||
controller: Controller,
|
|
||||||
require: {
|
|
||||||
card: '^vnInvoiceInCard'
|
|
||||||
},
|
|
||||||
bindings: {
|
|
||||||
invoiceIn: '<'
|
|
||||||
}
|
|
||||||
});
|
|
|
@ -1,113 +0,0 @@
|
||||||
import './index.js';
|
|
||||||
import watcher from 'core/mocks/watcher';
|
|
||||||
import crudModel from 'core/mocks/crud-model';
|
|
||||||
|
|
||||||
describe('InvoiceIn', () => {
|
|
||||||
describe('Component tax', () => {
|
|
||||||
let controller;
|
|
||||||
let $scope;
|
|
||||||
let vnApp;
|
|
||||||
let $httpBackend;
|
|
||||||
|
|
||||||
beforeEach(ngModule('invoiceIn'));
|
|
||||||
|
|
||||||
beforeEach(inject(($componentController, $rootScope, _vnApp_, _$httpBackend_) => {
|
|
||||||
$httpBackend = _$httpBackend_;
|
|
||||||
vnApp = _vnApp_;
|
|
||||||
jest.spyOn(vnApp, 'showError');
|
|
||||||
$scope = $rootScope.$new();
|
|
||||||
$scope.model = crudModel;
|
|
||||||
$scope.watcher = watcher;
|
|
||||||
|
|
||||||
const $element = angular.element('<vn-invoice-in-tax></vn-invoice-in-tax>');
|
|
||||||
controller = $componentController('vnInvoiceInTax', {$element, $scope});
|
|
||||||
controller.$.model = crudModel;
|
|
||||||
controller.invoiceIn = {id: 1};
|
|
||||||
}));
|
|
||||||
|
|
||||||
describe('taxRate()', () => {
|
|
||||||
it('should set tax rate with the Sage tax type value', () => {
|
|
||||||
const taxRateSelection = {
|
|
||||||
rate: 21
|
|
||||||
};
|
|
||||||
const invoiceInTax = {
|
|
||||||
taxableBase: 200
|
|
||||||
};
|
|
||||||
|
|
||||||
const taxRate = controller.taxRate(invoiceInTax, taxRateSelection);
|
|
||||||
|
|
||||||
expect(taxRate).toEqual(42);
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
describe('onSubmit()', () => {
|
|
||||||
it('should make HTTP POST request to save tax values', () => {
|
|
||||||
controller.card = {reload: () => {}};
|
|
||||||
jest.spyOn($scope.watcher, 'check');
|
|
||||||
jest.spyOn($scope.watcher, 'notifySaved');
|
|
||||||
jest.spyOn($scope.watcher, 'updateOriginalData');
|
|
||||||
jest.spyOn(controller.card, 'reload');
|
|
||||||
jest.spyOn($scope.model, 'save');
|
|
||||||
|
|
||||||
controller.onSubmit();
|
|
||||||
|
|
||||||
expect($scope.model.save).toHaveBeenCalledWith();
|
|
||||||
expect($scope.watcher.updateOriginalData).toHaveBeenCalledWith();
|
|
||||||
expect($scope.watcher.check).toHaveBeenCalledWith();
|
|
||||||
expect($scope.watcher.notifySaved).toHaveBeenCalledWith();
|
|
||||||
expect(controller.card.reload).toHaveBeenCalledWith();
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
describe('onResponse()', () => {
|
|
||||||
it('should return success message', () => {
|
|
||||||
controller.expense = {
|
|
||||||
code: 7050000005,
|
|
||||||
isWithheld: 0,
|
|
||||||
description: 'Test'
|
|
||||||
};
|
|
||||||
|
|
||||||
const data = [{
|
|
||||||
id: controller.expense.code,
|
|
||||||
isWithheld: controller.expense.isWithheld,
|
|
||||||
name: controller.expense.description
|
|
||||||
}];
|
|
||||||
|
|
||||||
jest.spyOn(controller.vnApp, 'showSuccess');
|
|
||||||
$httpBackend.expect('POST', `Expenses`, data).respond();
|
|
||||||
|
|
||||||
controller.onResponse();
|
|
||||||
$httpBackend.flush();
|
|
||||||
|
|
||||||
expect(controller.vnApp.showSuccess).toHaveBeenCalledWith('Expense saved!');
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should return an error if code is empty', () => {
|
|
||||||
controller.expense = {
|
|
||||||
code: null,
|
|
||||||
isWithheld: 0,
|
|
||||||
description: 'Test'
|
|
||||||
};
|
|
||||||
|
|
||||||
jest.spyOn(controller.vnApp, 'showError');
|
|
||||||
controller.onResponse();
|
|
||||||
|
|
||||||
expect(controller.vnApp.showError).toHaveBeenCalledWith(`The code can't be empty`);
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should return an error if description is empty', () => {
|
|
||||||
controller.expense = {
|
|
||||||
code: 7050000005,
|
|
||||||
isWithheld: 0,
|
|
||||||
description: null
|
|
||||||
};
|
|
||||||
|
|
||||||
jest.spyOn(controller.vnApp, 'showError');
|
|
||||||
controller.onResponse();
|
|
||||||
|
|
||||||
expect(controller.vnApp.showError).toHaveBeenCalledWith(`The description can't be empty`);
|
|
||||||
});
|
|
||||||
});
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
|
@ -1,7 +0,0 @@
|
||||||
Create expense: Crear gasto
|
|
||||||
New expense: Nuevo gasto
|
|
||||||
It's a withholding: Es una retención
|
|
||||||
The fields can't be empty: Los campos no pueden estar vacíos
|
|
||||||
The code can't be empty: El código no puede estar vacío
|
|
||||||
The description can't be empty: La descripción no puede estar vacía
|
|
||||||
Expense saved!: Gasto guardado!
|
|
Loading…
Reference in New Issue