Merge pull request '3977-invoiceIn.tax' (#969) from 3977-invoiceIn.tax into dev
gitea/salix/pipeline/head This commit looks good Details

Reviewed-on: #969
Reviewed-by: Joan Sanchez <joan@verdnatura.es>
This commit is contained in:
Joan Sanchez 2022-05-13 13:04:41 +00:00
commit fdd780a470
7 changed files with 141 additions and 15 deletions

View File

@ -0,0 +1,5 @@
INSERT INTO `salix`.`ACL`(`model`, `property`, `accessType`, `permission`, `principalType`, `principalId`)
VALUES('Expense', '*', 'READ', 'ALLOW', 'ROLE', 'employee');
INSERT INTO `salix`.`ACL`(`model`, `property`, `accessType`, `permission`, `principalType`, `principalId`)
VALUES('Expense', '*', 'WRITE', 'ALLOW', 'ROLE', 'administrative');

View File

@ -982,7 +982,7 @@ export default {
save: 'vn-invoice-in-basic-data button[type=submit]' save: 'vn-invoice-in-basic-data button[type=submit]'
}, },
invoiceInTax: { invoiceInTax: {
addTaxButton: 'vn-invoice-in-tax vn-icon-button[icon="add_circle"]', addTaxButton: 'vn-invoice-in-tax vn-icon-button[vn-tooltip="Add tax"]',
thirdExpence: 'vn-invoice-in-tax vn-horizontal:nth-child(3) > vn-autocomplete[ng-model="invoiceInTax.expenseFk"]', thirdExpence: 'vn-invoice-in-tax vn-horizontal:nth-child(3) > vn-autocomplete[ng-model="invoiceInTax.expenseFk"]',
thirdTaxableBase: 'vn-invoice-in-tax vn-horizontal:nth-child(3) > vn-input-number[ng-model="invoiceInTax.taxableBase"]', thirdTaxableBase: 'vn-invoice-in-tax vn-horizontal:nth-child(3) > vn-input-number[ng-model="invoiceInTax.taxableBase"]',
thirdTaxType: 'vn-invoice-in-tax vn-horizontal:nth-child(3) > vn-autocomplete[ng-model="invoiceInTax.taxTypeSageFk"]', thirdTaxType: 'vn-invoice-in-tax vn-horizontal:nth-child(3) > vn-autocomplete[ng-model="invoiceInTax.taxTypeSageFk"]',

View File

@ -33,6 +33,13 @@
show-field="id" show-field="id"
rule> rule>
<tpl-item>{{id}}: {{name}}</tpl-item> <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-autocomplete>
<vn-input-number vn-one <vn-input-number vn-one
disabled="$ctrl.invoiceIn.currency.code != 'EUR'" disabled="$ctrl.invoiceIn.currency.code != 'EUR'"
@ -97,3 +104,40 @@
</vn-submit> </vn-submit>
</vn-button-bar> </vn-button-bar>
</form> </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>

View File

@ -1,7 +1,12 @@
import ngModule from '../module'; import ngModule from '../module';
import Section from 'salix/components/section'; import Section from 'salix/components/section';
import UserError from 'core/lib/user-error';
class Controller extends Section { class Controller extends Section {
constructor($element, $, vnWeekDays) {
super($element, $);
this.expense = {};
}
taxRate(invoiceInTax, taxRateSelection) { taxRate(invoiceInTax, taxRateSelection) {
const taxTypeSage = taxRateSelection && taxRateSelection.rate; const taxTypeSage = taxRateSelection && taxRateSelection.rate;
const taxableBase = invoiceInTax && invoiceInTax.taxableBase; const taxableBase = invoiceInTax && invoiceInTax.taxableBase;
@ -26,6 +31,27 @@ class Controller extends Section {
this.card.reload(); 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', { ngModule.vnComponent('vnInvoiceInTax', {

View File

@ -1,16 +1,19 @@
import './index.js'; import './index.js';
import watcher from 'core/mocks/watcher'; import watcher from 'core/mocks/watcher';
import crudModel from 'core/mocks/crud-model'; import crudModel from 'core/mocks/crud-model';
const UserError = require('vn-loopback/util/user-error');
describe('InvoiceIn', () => { describe('InvoiceIn', () => {
describe('Component tax', () => { describe('Component tax', () => {
let controller; let controller;
let $scope; let $scope;
let vnApp; let vnApp;
let $httpBackend;
beforeEach(ngModule('invoiceIn')); beforeEach(ngModule('invoiceIn'));
beforeEach(inject(($componentController, $rootScope, _vnApp_) => { beforeEach(inject(($componentController, $rootScope, _vnApp_, _$httpBackend_) => {
$httpBackend = _$httpBackend_;
vnApp = _vnApp_; vnApp = _vnApp_;
jest.spyOn(vnApp, 'showError'); jest.spyOn(vnApp, 'showError');
$scope = $rootScope.$new(); $scope = $rootScope.$new();
@ -19,6 +22,7 @@ describe('InvoiceIn', () => {
const $element = angular.element('<vn-invoice-in-tax></vn-invoice-in-tax>'); const $element = angular.element('<vn-invoice-in-tax></vn-invoice-in-tax>');
controller = $componentController('vnInvoiceInTax', {$element, $scope}); controller = $componentController('vnInvoiceInTax', {$element, $scope});
controller.$.model = crudModel;
controller.invoiceIn = {id: 1}; controller.invoiceIn = {id: 1};
})); }));
@ -55,5 +59,56 @@ describe('InvoiceIn', () => {
expect(controller.card.reload).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`);
});
});
}); });
}); });

View File

@ -0,0 +1,7 @@
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!

View File

@ -17,9 +17,6 @@
}, },
"isWithheld": { "isWithheld": {
"type": "number" "type": "number"
},
"taxTypeFk": {
"type": "number"
} }
}, },
"relations": { "relations": {
@ -28,13 +25,5 @@
"model": "TaxType", "model": "TaxType",
"foreignKey": "taxTypeFk" "foreignKey": "taxTypeFk"
} }
},
"acls": [
{
"accessType": "READ",
"principalType": "ROLE",
"principalId": "$everyone",
"permission": "ALLOW"
} }
]
} }