Merge branch 'dev' of http://git.verdnatura.es/salix into dev

This commit is contained in:
Carlos Jimenez 2018-10-16 15:37:27 +02:00
commit c794b5538a
26 changed files with 4094 additions and 3790 deletions

View File

@ -10,19 +10,38 @@
<vn-title>Pay method</vn-title> <vn-title>Pay method</vn-title>
<vn-horizontal> <vn-horizontal>
<vn-autocomplete <vn-autocomplete
vn-one vn-two
label="Pay method" label="Pay method"
vn-acl="administrative, salesAssistant" vn-acl="salesAssistant"
field="$ctrl.client.payMethodFk" field="$ctrl.client.payMethodFk"
url="/client/api/PayMethods" url="/client/api/PayMethods"
select-fields="ibanRequired" select-fields="ibanRequired"
initial-data="$ctrl.client.payMethod"> initial-data="$ctrl.client.payMethod">
</vn-autocomplete> </vn-autocomplete>
<vn-autocomplete vn-two
label="Swift / BIC"
url="/client/api/BankEntities"
field="$ctrl.client.bankEntityFk"
select-fields="['name']"
initial-data="$ctrl.client.bankEntityFk"
where="{or: [{bic: {regexp: 'search'}}, {name: {regexp: 'search'}}]}"
value-field="id"
show-field="bic"
vn-acl="salesAssistant">
<tpl-item>
<vn-horizontal>
<vn-one>{{bic}}</vn-one>
<vn-one>
<div class="ellipsize" style="max-width: 10em">{{name}}</div>
</vn-one>
</vn-horizontal>
</tpl-item>
</vn-autocomplete>
<vn-textfield <vn-textfield
vn-one vn-one
label="Due day" label="Due day"
field="$ctrl.client.dueDay" field="$ctrl.client.dueDay"
vn-acl="administrative, salesAssistant"> vn-acl="salesAssistant">
</vn-textfield> </vn-textfield>
</vn-horizontal> </vn-horizontal>
<vn-horizontal> <vn-horizontal>
@ -30,7 +49,7 @@
vn-one vn-one
label="IBAN" label="IBAN"
field="$ctrl.client.iban" field="$ctrl.client.iban"
vn-acl="administrative, salesAssistant"> vn-acl="salesAssistant">
</vn-textfield> </vn-textfield>
</vn-horizontal> </vn-horizontal>
<vn-horizontal pad-small-v> <vn-horizontal pad-small-v>
@ -38,26 +57,26 @@
<vn-check <vn-check
label="Received LCR" label="Received LCR"
field="$ctrl.client.hasLcr" field="$ctrl.client.hasLcr"
vn-acl="administrative, salesAssistant"> vn-acl="salesAssistant">
</vn-check> </vn-check>
</vn-one> </vn-one>
<vn-one> <vn-one>
<vn-check <vn-check
label="Received core VNL" label="Received core VNL"
field="$ctrl.client.hasCoreVnl" field="$ctrl.client.hasCoreVnl"
vn-acl="administrative, salesAssistant"> vn-acl="salesAssistant">
</vn-check> </vn-check>
</vn-one> </vn-one>
<vn-one> <vn-one>
<vn-check <vn-check
label="Received B2B VNL" label="Received B2B VNL"
field="$ctrl.client.hasSepaVnl" field="$ctrl.client.hasSepaVnl"
vn-acl="administrative, salesAssistant"> vn-acl="salesAssistant">
</vn-check> </vn-check>
</vn-one> </vn-one>
</vn-horizontal> </vn-horizontal>
</vn-card> </vn-card>
<vn-button-bar> <vn-button-bar>
<vn-submit label="Save" vn-acl="administrative, salesAssistant"></vn-submit> <vn-submit label="Save" vn-acl="salesAssistant"></vn-submit>
</vn-button-bar> </vn-button-bar>
</form> </form>

View File

@ -158,45 +158,49 @@
</vn-vertical> </vn-vertical>
</vn-one> </vn-one>
<vn-one margin-medium> <vn-one margin-medium>
<h5 translate>Recovery</h5> <h5 translate>Business data</h5>
<vn-vertical ng-if="$ctrl.summary.recovery"> <vn-label-value label="Total greuge"
<vn-label-value label="Since" value="{{$ctrl.summary.totalGreuge | currency:'€ ':2}}">
value="{{$ctrl.summary.recovery.started | date:'dd/MM/yyyy'}}">
</vn-label-value> </vn-label-value>
<vn-label-value label="To"
value="{{$ctrl.summary.recovery.finished | date:'dd/MM/yyyy'}}">
</vn-label-value>
<vn-label-value label="Amount"
value="{{$ctrl.summary.recovery.amount | currency:'€ ':2}}">
</vn-label-value>
<vn-label-value label="Period"
value="{{$ctrl.summary.recovery.period}}">
</vn-label-value>
</vn-vertical>
</vn-one>
<vn-one margin-medium>
<h5 translate>Financial data</h5>
<vn-label-value label="Mana" <vn-label-value label="Mana"
value="{{$ctrl.summary.mana.mana | currency:'€ ':2}}"> value="{{$ctrl.summary.mana.mana | currency:'€ ':2}}">
</vn-label-value> </vn-label-value>
<vn-label-value label="Risk" <vn-label-value label="Rate"
value="{{$ctrl.summary.debt.debt | currency:'€ ':2}}"> value="{{$ctrl.summary.claimsRatio[0].priceIncreasing | percentage}}">
</vn-label-value> </vn-label-value>
<vn-label-value label="Average invoiced" <vn-label-value label="Average invoiced"
value="{{$ctrl.summary.averageInvoiced.invoiced | currency:'€ ':2}}"> value="{{$ctrl.summary.averageInvoiced.invoiced | currency:'€ ':2}}">
</vn-label-value> </vn-label-value>
<vn-label-value label="Total greuge" <vn-label-value label="Claims"
value="{{$ctrl.summary.totalGreuge | currency:'€ ':2}}"> value="{{$ctrl.summary.claimsRatio[0].claimingRate | percentage}}">
</vn-label-value>
</vn-one>
<vn-one margin-medium>
<h5 translate>Financial information</h5>
<vn-label-value label="Risk"
value="{{$ctrl.summary.debt.debt | currency:'€ ':2}}"
ng-class="{bold: $ctrl.summary.debt.debt > $ctrl.summary.credit}">
</vn-label-value> </vn-label-value>
<vn-label-value label="Credit" <vn-label-value label="Credit"
value="{{$ctrl.summary.credit | currency:'€ ':2}}"> value="{{$ctrl.summary.credit | currency:'€ ':2}}"
ng-class="{bold: $ctrl.summary.credit > $ctrl.summary.creditInsurance ||
($ctrl.summary.credit && $ctrl.summary.creditInsurance == null)}">
</vn-label-value> </vn-label-value>
<vn-label-value label="Secured credit" <vn-label-value label="Secured credit"
value="{{$ctrl.summary.creditInsurance | currency:'€ ':2}}"> value="{{$ctrl.summary.creditInsurance | currency:'€ ':2}}">
</vn-label-value> </vn-label-value>
<vn-label-value label="Grade" <vn-label-value label="Balance"
value="{{$ctrl.grade}}"> value="{{$ctrl.summary.clientRisks[0].amount | currency:'€ ':2}}">
</vn-label-value> </vn-label-value>
<vn-label-value label="Balance due"
value="{{$ctrl.summary.defaulters[0].amount | currency:'€ ':2}}"
ng-class="{bold: $ctrl.summary.defaulters[0].amount}">
</vn-label-value>
<vn-vertical ng-if="$ctrl.summary.recovery.started">
<vn-label-value label="Recovery since"
value="{{$ctrl.summary.recovery.started | date:'dd/MM/yyyy'}}">
</vn-label-value>
</vn-vertical>
</vn-one> </vn-one>
</vn-horizontal> </vn-horizontal>
</vn-vertical> </vn-vertical>

View File

@ -1,9 +1,13 @@
Default address: Consignatario pred. Default address: Consignatario pred.
Total greuge: Greuge total Total greuge: Greuge total
Financial data: Datos financieros Financial information: Datos financieros
Mana: Maná Mana: Maná
Risk: Riesgo Risk: Riesgo
Secured credit: Crédito asegurado Secured credit: Crédito asegurado
Average invoiced: Consumo medio Average invoiced: Consumo medio
Sales person: Comercial Sales person: Comercial
Recovery: Recobro Recovery: Recobro
Balance due: Saldo vencido
Rate: Tarifa
Business data: Datos comerciales
Recovery since: Recobro desde

View File

@ -3,3 +3,7 @@ vn-dialog {
min-width: 10em; min-width: 10em;
} }
} }
vn-client-summary .bold {
font-family: vn-font-bold;
}

View File

@ -0,0 +1,17 @@
import ngModule from '../module';
/**
* Override angular currency formats a number adding symbol after value.
*
* @return {String} The formated number
*/
export default function currency() {
return function(input, symbol, fractionSize) {
if (input == null || input === '')
return null;
if (typeof input == 'number' && fractionSize)
input = input.toFixed(fractionSize);
return `${input} ${symbol}`;
};
}
ngModule.filter('currency', currency);

View File

@ -2,3 +2,5 @@ import './phone';
import './ucwords'; import './ucwords';
import './dash-if-empty'; import './dash-if-empty';
import './dateTime'; import './dateTime';
import './percentage';
import './currency';

View File

@ -0,0 +1,15 @@
import ngModule from '../module';
/**
* Formats a number multiplying by 100 and adding character %.
*
* @return {String} The formated number
*/
export default function percentage() {
return function(input) {
if (input == null || input === '')
return null;
return input * 100 + ' %';
};
}
ngModule.filter('percentage', percentage);

View File

@ -1,7 +1,7 @@
import selectors from '../../helpers/selectors.js'; import selectors from '../../helpers/selectors.js';
import createNightmare from '../../helpers/helpers'; import createNightmare from '../../helpers/helpers';
describe('Client', () => { xdescribe('Client', () => {
describe('Edit pay method path', () => { describe('Edit pay method path', () => {
const nightmare = createNightmare(); const nightmare = createNightmare();

7135
package-lock.json generated

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,30 @@
{
"name": "BankEntity",
"base": "VnModel",
"options": {
"mysql": {
"table": "bankEntity",
"database": "vn"
}
},
"properties": {
"id": {
"type": "Number",
"id": true,
"description": "Identifier"
},
"bic": {
"type": "String"
},
"name": {
"type": "String"
}
},
"relations": {
"country": {
"type": "belongsTo",
"model": "Country",
"foreignKey": "countryFk"
}
}
}

View File

@ -8,6 +8,14 @@
} }
}, },
"properties": { "properties": {
"clientFk": {
"type": "Number",
"id": true
},
"companyFk": {
"type": "Number",
"id": true
},
"amount": { "amount": {
"type": "Number" "type": "Number"
} }

View File

@ -0,0 +1,63 @@
{
"name": "Receipt",
"base": "VnModel",
"options": {
"mysql": {
"table": "receipt"
}
},
"properties": {
"id": {
"id": true,
"type": "Number",
"description": "Identifier"
},
"ref": {
"id": true,
"type": "String",
"required": true
},
"amountPaid": {
"type": "Number"
},
"amountUnpaid": {
"type": "Number"
},
"payed": {
"type": "date"
},
"worcreated": {
"type": "date"
},
"isConciliate": {
"type": "date"
}
},
"relations": {
"client": {
"type": "belongsTo",
"model": "Client",
"foreignKey": "clientFk"
},
"company": {
"type": "belongsTo",
"model": "Company",
"foreignKey": "companyFk"
},
"worker": {
"type": "belongsTo",
"model": "Worker",
"foreignKey": "workerFk"
},
"bank": {
"type": "belongsTo",
"model": "Bank",
"foreignKey": "bankFk"
},
"invoice": {
"type": "belongsTo",
"model": "InvoiceOut",
"foreignKey": "invoiceFk"
}
}
}

View File

@ -2,6 +2,9 @@
"AddressObservation": { "AddressObservation": {
"dataSource": "vn" "dataSource": "vn"
}, },
"BankEntity": {
"dataSource": "vn"
},
"ClientCredit": { "ClientCredit": {
"dataSource": "vn" "dataSource": "vn"
}, },
@ -58,5 +61,8 @@
}, },
"ClientRisk": { "ClientRisk": {
"dataSource": "vn" "dataSource": "vn"
},
"Receipt": {
"dataSource": "vn"
} }
} }

View File

@ -0,0 +1,64 @@
/**
* Cambios para evitar fallos en local.
* Ya está en producción
**/
ALTER TABLE `vn2008`.`Clientes`
ADD COLUMN `bankEntityFk` INT(10) UNSIGNED NULL AFTER `hasLcr`;
USE `vn`;
CREATE
OR REPLACE ALGORITHM = UNDEFINED
DEFINER = `root`@`%`
SQL SECURITY DEFINER
VIEW `client` AS
SELECT
`c`.`id_cliente` AS `id`,
`c`.`cliente` AS `name`,
`c`.`if` AS `fi`,
`c`.`razonSocial` AS `socialName`,
`c`.`contacto` AS `contact`,
`c`.`domicilio` AS `street`,
`c`.`poblacion` AS `city`,
`c`.`codPostal` AS `postcode`,
`c`.`telefono` AS `phone`,
`c`.`movil` AS `mobile`,
`c`.`fax` AS `fax`,
`c`.`real` AS `isRelevant`,
`c`.`e-mail` AS `email`,
`c`.`iban` AS `iban`,
`c`.`vencimiento` AS `dueDay`,
`c`.`Cuenta` AS `accountingAccount`,
`c`.`RE` AS `isEqualizated`,
`c`.`province_id` AS `provinceFk`,
`c`.`invoice` AS `hasToInvoice`,
`c`.`credito` AS `credit`,
`c`.`Id_Pais` AS `countryFk`,
`c`.`activo` AS `isActive`,
`c`.`gestdoc_id` AS `gestdocFk`,
`c`.`calidad` AS `quality`,
`c`.`pay_met_id` AS `payMethodFk`,
`c`.`created` AS `created`,
`c`.`mail` AS `isToBeMailed`,
`c`.`chanel_id` AS `contactChannelFk`,
`c`.`sepaVnl` AS `hasSepaVnl`,
`c`.`coreVnl` AS `hasCoreVnl`,
`c`.`coreVnh` AS `hasCoreVnh`,
`c`.`hasLcr` AS `hasLcr`,
`c`.`default_address` AS `defaultAddressFk`,
`c`.`risk_calculated` AS `riskCalculated`,
`c`.`clientes_tipo_id` AS `clientTypeFk`,
`c`.`mail_address` AS `mailAddress`,
`c`.`cplusTerIdNifFk` AS `cplusTerIdNifFk`,
`c`.`invoiceByAddress` AS `hasToInvoiceByAddress`,
`c`.`contabilizado` AS `isTaxDataChecked`,
`c`.`congelado` AS `isFreezed`,
`c`.`creditInsurance` AS `creditInsurance`,
`c`.`isCreatedAsServed` AS `isCreatedAsServed`,
`c`.`hasInvoiceSimplified` AS `hasInvoiceSimplified`,
`c`.`Id_Trabajador` AS `salesPersonFk`,
`c`.`vies` AS `isVies`,
`c`.`EYPBC` AS `eypbc`,
bankEntityFk
FROM
`vn2008`.`Clientes` `c`;

View File

@ -1002,3 +1002,10 @@ INSERT INTO `vn`.`orderTicket`(`orderFk`, `ticketFk`)
INSERT INTO `vn`.`userConfig` (`userFk`, `warehouseFk`, `companyFk`) INSERT INTO `vn`.`userConfig` (`userFk`, `warehouseFk`, `companyFk`)
VALUES (9, 1, 442); VALUES (9, 1, 442);
INSERT INTO `vn`.`receipt`(`id`, `invoiceFk`, `amountPaid`, `amountUnpaid`, `payed`, `workerFk`, `bankFk`, `clientFk`, `created`, `companyFk`, `isConciliate`)
VALUES
(1, 'Cobro web', 100.50, 0.00, CURDATE(), 9, 1, 101, CURDATE(), 442, 1),
(2, 'Cobro web', 200.50, 0.00, CURDATE(), 9, 1, 101, CURDATE(), 442, 1),
(3, 'Cobro en efectivo', 300.00, 100.00, CURDATE(), 9, 1, 102, CURDATE(), 442, 0),
(4, 'Cobro en efectivo', 400.00, -50.00, CURDATE(), 9, 1, 103, CURDATE(), 442, 0);

View File

@ -7,6 +7,6 @@ describe('Client card', () => {
expect(result.id).toEqual(101); expect(result.id).toEqual(101);
expect(result.name).toEqual('Bruce Wayne'); expect(result.name).toEqual('Bruce Wayne');
expect(result.debt).toEqual(880.1); expect(result.debt).toEqual(579.1);
}); });
}); });

View File

@ -4,7 +4,7 @@ describe('client getDebt()', () => {
it('should return the client debt', async() => { it('should return the client debt', async() => {
let result = await app.models.Client.getDebt(101); let result = await app.models.Client.getDebt(101);
expect(result.debt).toEqual(880.1); expect(result.debt).toEqual(579.1);
}); });
}); });

View File

@ -17,7 +17,7 @@ describe('client summary()', () => {
it('should return a summary object containing debt', async() => { it('should return a summary object containing debt', async() => {
let result = await app.models.Client.summary(101); let result = await app.models.Client.summary(101);
expect(result.debt.debt).toEqual(880.1); expect(result.debt.debt).toEqual(579.1);
}); });
it('should return a summary object containing averageInvoiced', async() => { it('should return a summary object containing averageInvoiced', async() => {

View File

@ -37,6 +37,7 @@ describe('Client updateBillingData', () => {
let params = { let params = {
phone: 2222222222, phone: 2222222222,
payMethodFk: 4, payMethodFk: 4,
bankEntityFk: 128,
dueDay: 30, dueDay: 30,
iban: 'ES91 2100 0418 4502 0005 1332', iban: 'ES91 2100 0418 4502 0005 1332',
hasLcr: 1, hasLcr: 1,

View File

@ -60,6 +60,29 @@ module.exports = Self => {
}, },
where: {finished: null} where: {finished: null}
} }
},
{
relation: 'defaulters',
scope: {
fields: ['amount'],
order: 'created DESC',
limit: 1
}
},
{
relation: 'clientsRisk',
scope: {
where: {companyFk: 442},
fields: ['amount'],
limit: 1
}
},
{
relation: 'claimsRatio',
scope: {
fields: ['claimingRate', 'priceIncreasing'],
limit: 1
}
} }
], ],
where: {id: clientId} where: {id: clientId}

View File

@ -33,6 +33,7 @@ module.exports = Self => {
let data = filterAttributes(params, [ let data = filterAttributes(params, [
'payMethodFk', 'payMethodFk',
'bankEntityFk',
'dueDay', 'dueDay',
'iban', 'iban',
'hasLcr', 'hasLcr',

View File

@ -0,0 +1,39 @@
{
"name": "ClaimRatio",
"base": "VnModel",
"options": {
"mysql": {
"table": "claimRatio",
"database": "vn"
}
},
"validateUpsert": true,
"properties": {
"clientFk": {
"type": "Number",
"id": true
},
"yearSale": {
"type": "Number"
},
"claimAmount": {
"type": "Number"
},
"claimingRate": {
"type": "Number"
},
"priceIncreasing": {
"type": "Number"
},
"packingRate": {
"type": "Number"
}
},
"relations": {
"client": {
"type": "belongsTo",
"model": "Client",
"foreignKey": "clientFk"
}
}
}

View File

@ -84,6 +84,7 @@ module.exports = Self => {
Self.validate('payMethod', hasSalesMan, { Self.validate('payMethod', hasSalesMan, {
message: 'Cannot change the payment method if no salesperson' message: 'Cannot change the payment method if no salesperson'
}); });
function hasSalesMan(err) { function hasSalesMan(err) {
if (this.payMethod && !this.salesPerson) if (this.payMethod && !this.salesPerson)
err(); err();
@ -92,9 +93,10 @@ module.exports = Self => {
Self.validateAsync('payMethodFk', hasIban, { Self.validateAsync('payMethodFk', hasIban, {
message: 'That payment method requires an IBAN' message: 'That payment method requires an IBAN'
}); });
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.ibanRequired && (!this.iban || !this.bankEntityFk))
err(); err();
done(); done();
}); });

View File

@ -172,6 +172,26 @@
"type": "hasMany", "type": "hasMany",
"model": "ClientContact", "model": "ClientContact",
"foreignKey": "clientFk" "foreignKey": "clientFk"
},
"bank": {
"type": "belongsTo",
"model": "BankEntity",
"foreignKey": "bankEntityFk"
},
"defaulters": {
"type": "hasMany",
"model": "Defaulter",
"foreignKey": "clientFk"
},
"clientsRisk": {
"type": "hasMany",
"model": "ClientRisk",
"foreignKey": "clientFk"
},
"claimsRatio": {
"type": "hasMany",
"model": "ClaimRatio",
"foreignKey": "clientFk"
} }
} }
} }

View File

@ -34,6 +34,9 @@
"isBargain": { "isBargain": {
"type": "Boolean" "type": "Boolean"
}, },
"isActive": {
"type": "Boolean"
},
"comment": { "comment": {
"type": "String" "type": "String"
}, },

View File

@ -134,5 +134,8 @@
}, },
"UserConfig": { "UserConfig": {
"dataSource": "vn" "dataSource": "vn"
},
"ClaimRatio": {
"dataSource": "vn"
} }
} }