Merge pull request 'BREACKING CHANGE: claim redirect to lilium' (!2843) from 6336-claim-redirectToLilium into dev
gitea/salix/pipeline/head This commit looks good Details

Reviewed-on: #2843
Reviewed-by: Pablo Natek <pablone@verdnatura.es>
This commit is contained in:
Alex Moreno 2024-08-08 10:43:38 +00:00
commit 141e116401
57 changed files with 42 additions and 2790 deletions

View File

@ -738,69 +738,6 @@ export default {
worker: 'vn-worker-autocomplete[ng-model="$ctrl.userFk"]', worker: 'vn-worker-autocomplete[ng-model="$ctrl.userFk"]',
saveStateButton: `button[type=submit]` saveStateButton: `button[type=submit]`
}, },
claimsIndex: {
searchResult: 'vn-claim-index vn-card > vn-table > div > vn-tbody > a'
},
claimDescriptor: {
moreMenu: 'vn-claim-descriptor vn-icon-button[icon=more_vert]',
moreMenuDeleteClaim: '.vn-menu [name="deleteClaim"]',
acceptDeleteClaim: '.vn-confirm.shown button[response="accept"]'
},
claimSummary: {
header: 'vn-claim-summary > vn-card > h5',
state: 'vn-claim-summary vn-label-value[label="State"] > section > span',
observation: 'vn-claim-summary vn-horizontal.text',
firstSaleItemId: 'vn-claim-summary vn-horizontal > vn-auto:nth-child(5) vn-table > div > vn-tbody > vn-tr:nth-child(1) > vn-td:nth-child(1) > span',
firstSaleDescriptorImage: '.vn-popover.shown vn-item-descriptor img',
itemDescriptorPopover: '.vn-popover.shown vn-item-descriptor',
itemDescriptorPopoverItemDiaryButton: '.vn-popover vn-item-descriptor vn-quick-link[icon="icon-transaction"] > a',
firstDevelopmentWorker: 'vn-claim-summary vn-horizontal > vn-auto:nth-child(4) vn-table > div > vn-tbody > vn-tr:nth-child(1) > vn-td:nth-child(4) > span',
firstDevelopmentWorkerGoToClientButton: '.vn-popover vn-worker-descriptor vn-quick-link[icon="person"] > a',
firstActionTicketId: 'vn-claim-summary > vn-card > vn-horizontal > vn-auto:nth-child(5) vn-table > div > vn-tbody > vn-tr > vn-td:nth-child(2) > span',
firstActionTicketDescriptor: '.vn-popover.shown vn-ticket-descriptor'
},
claimBasicData: {
claimState: 'vn-claim-basic-data vn-autocomplete[ng-model="$ctrl.claim.claimStateFk"]',
packages: 'vn-input-number[ng-model="$ctrl.claim.packages"]',
saveButton: `button[type=submit]`
},
claimDetail: {
secondItemDiscount: 'vn-claim-detail > vn-vertical > vn-card > vn-vertical > vn-table > div > vn-tbody > vn-tr:nth-child(2) > vn-td:nth-child(6) > span',
discount: '.vn-popover.shown vn-input-number[ng-model="$ctrl.newDiscount"]',
discoutPopoverMana: '.vn-popover.shown .content > div > vn-horizontal > h5',
addItemButton: 'vn-claim-detail a vn-float-button',
firstClaimableSaleFromTicket: '.vn-dialog.shown vn-tbody > vn-tr',
claimDetailLine: 'vn-claim-detail > vn-vertical > vn-card > vn-vertical > vn-table > div > vn-tbody > vn-tr',
totalClaimed: 'vn-claim-detail > vn-vertical > vn-card > vn-vertical > vn-horizontal > div > vn-label-value:nth-child(2) > section > span',
secondItemDeleteButton: 'vn-claim-detail > vn-vertical > vn-card > vn-vertical > vn-table > div > vn-tbody > vn-tr:nth-child(2) > vn-td:nth-child(8) > vn-icon-button > button > vn-icon > i'
},
claimDevelopment: {
addDevelopmentButton: 'vn-claim-development > vn-vertical > vn-card > vn-vertical > vn-one > vn-icon-button > button > vn-icon',
firstDeleteDevelopmentButton: 'vn-claim-development > vn-vertical > vn-card > vn-vertical > form > vn-horizontal:nth-child(2) > vn-icon-button > button > vn-icon',
firstClaimReason: 'vn-claim-development vn-horizontal:nth-child(1) vn-autocomplete[ng-model="claimDevelopment.claimReasonFk"]',
firstClaimResult: 'vn-claim-development vn-horizontal:nth-child(1) vn-autocomplete[ng-model="claimDevelopment.claimResultFk"]',
firstClaimResponsible: 'vn-claim-development vn-horizontal:nth-child(1) vn-autocomplete[ng-model="claimDevelopment.claimResponsibleFk"]',
firstClaimWorker: 'vn-claim-development vn-horizontal:nth-child(1) vn-worker-autocomplete[ng-model="claimDevelopment.workerFk"]',
firstClaimRedelivery: 'vn-claim-development vn-horizontal:nth-child(1) vn-autocomplete[ng-model="claimDevelopment.claimRedeliveryFk"]',
secondClaimReason: 'vn-claim-development vn-horizontal:nth-child(2) vn-autocomplete[ng-model="claimDevelopment.claimReasonFk"]',
secondClaimResult: 'vn-claim-development vn-horizontal:nth-child(2) vn-autocomplete[ng-model="claimDevelopment.claimResultFk"]',
secondClaimResponsible: 'vn-claim-development vn-horizontal:nth-child(2) vn-autocomplete[ng-model="claimDevelopment.claimResponsibleFk"]',
secondClaimWorker: 'vn-claim-development vn-horizontal:nth-child(2) vn-worker-autocomplete[ng-model="claimDevelopment.workerFk"]',
secondClaimRedelivery: 'vn-claim-development vn-horizontal:nth-child(2) vn-autocomplete[ng-model="claimDevelopment.claimRedeliveryFk"]',
saveDevelopmentButton: 'button[type=submit]'
},
claimNote: {
addNoteFloatButton: 'vn-float-button',
note: 'vn-textarea[ng-model="$ctrl.note.text"]',
saveButton: 'button[type=submit]',
firstNoteText: 'vn-claim-note .text'
},
claimAction: {
importClaimButton: 'vn-claim-action vn-button[label="Import claim"]',
anyLine: 'vn-claim-action vn-tbody > vn-tr',
firstDeleteLine: 'vn-claim-action tr:nth-child(2) vn-icon-button[icon="delete"]',
isPaidWithManaCheckbox: 'vn-claim-action vn-check[ng-model="$ctrl.claim.isChargedToMana"]'
},
ordersIndex: { ordersIndex: {
secondSearchResultTotal: 'vn-order-index vn-card > vn-table > div > vn-tbody .vn-tr:nth-child(2) vn-td:nth-child(9)', secondSearchResultTotal: 'vn-order-index vn-card > vn-table > div > vn-tbody .vn-tr:nth-child(2) vn-td:nth-child(9)',
advancedSearchButton: 'vn-order-search-panel vn-submit[label="Search"]', advancedSearchButton: 'vn-order-search-panel vn-submit[label="Search"]',

View File

@ -19,7 +19,9 @@ describe('Ticket Edit sale path', () => {
it(`should click on the first sale claim icon to navigate over there`, async() => { it(`should click on the first sale claim icon to navigate over there`, async() => {
await page.waitToClick(selectors.ticketSales.firstSaleClaimIcon); await page.waitToClick(selectors.ticketSales.firstSaleClaimIcon);
await page.waitForState('claim.card.basicData'); await page.waitForNavigation();
await page.goBack();
await page.goBack();
}); });
it('should navigate to the tickets index', async() => { it('should navigate to the tickets index', async() => {
@ -243,29 +245,13 @@ describe('Ticket Edit sale path', () => {
await page.waitToClick(selectors.ticketSales.moreMenu); await page.waitToClick(selectors.ticketSales.moreMenu);
await page.waitToClick(selectors.ticketSales.moreMenuCreateClaim); await page.waitToClick(selectors.ticketSales.moreMenuCreateClaim);
await page.waitToClick(selectors.globalItems.acceptButton); await page.waitToClick(selectors.globalItems.acceptButton);
await page.waitForState('claim.card.basicData'); await page.waitForNavigation();
});
it('should click on the Claims button of the top bar menu', async() => {
await page.waitToClick(selectors.globalItems.applicationsMenuButton);
await page.waitForSelector(selectors.globalItems.applicationsMenuVisible);
await page.waitToClick(selectors.globalItems.claimsButton);
await page.waitForState('claim.index');
});
it('should search for the claim with id 4', async() => {
await page.accessToSearchResult('4');
await page.waitForState('claim.card.summary');
});
it('should click the Tickets button of the top bar menu', async() => {
await page.waitToClick(selectors.globalItems.applicationsMenuButton);
await page.waitForSelector(selectors.globalItems.applicationsMenuVisible);
await page.waitToClick(selectors.globalItems.ticketsButton);
await page.waitForState('ticket.index');
}); });
it('should search for a ticket then access to the sales section', async() => { it('should search for a ticket then access to the sales section', async() => {
await page.goBack();
await page.goBack();
await page.loginAndModule('salesPerson', 'ticket');
await page.accessToSearchResult('16'); await page.accessToSearchResult('16');
await page.accessToSection('ticket.card.sale'); await page.accessToSection('ticket.card.sale');
}); });

View File

@ -1,61 +0,0 @@
import selectors from '../../helpers/selectors.js';
import getBrowser from '../../helpers/puppeteer';
describe('Claim edit basic data path', () => {
let browser;
let page;
beforeAll(async() => {
browser = await getBrowser();
page = browser.page;
});
afterAll(async() => {
await browser.close();
});
it(`should log in as claimManager then reach basic data of the target claim`, async() => {
await page.loginAndModule('claimManager', 'claim');
await page.accessToSearchResult('1');
await page.accessToSection('claim.card.basicData');
});
it(`should edit claim state and observation fields`, async() => {
await page.autocompleteSearch(selectors.claimBasicData.claimState, 'Resuelto');
await page.clearInput(selectors.claimBasicData.packages);
await page.write(selectors.claimBasicData.packages, '2');
await page.waitToClick(selectors.claimBasicData.saveButton);
const message = await page.waitForSnackbar();
expect(message.text).toContain('Data saved!');
});
it(`should have been redirected to the next section of claims as the role is claimManager`, async() => {
await page.waitForState('claim.card.detail');
});
it('should confirm the claim state was edited', async() => {
await page.reloadSection('claim.card.basicData');
await page.waitForSelector(selectors.claimBasicData.claimState);
const result = await page.waitToGetProperty(selectors.claimBasicData.claimState, 'value');
expect(result).toEqual('Resuelto');
});
it('should confirm the claim packages was edited', async() => {
const result = await page
.waitToGetProperty(selectors.claimBasicData.packages, 'value');
expect(result).toEqual('2');
});
it(`should edit the claim to leave it untainted`, async() => {
await page.autocompleteSearch(selectors.claimBasicData.claimState, 'Pendiente');
await page.clearInput(selectors.claimBasicData.packages);
await page.write(selectors.claimBasicData.packages, '0');
await page.waitToClick(selectors.claimBasicData.saveButton);
const message = await page.waitForSnackbar();
expect(message.text).toContain('Data saved!');
});
});

View File

@ -1,54 +0,0 @@
import selectors from '../../helpers/selectors.js';
import getBrowser from '../../helpers/puppeteer.js';
describe('Claim action path', () => {
let browser;
let page;
beforeAll(async() => {
browser = await getBrowser();
page = browser.page;
await page.loginAndModule('claimManager', 'claim');
await page.accessToSearchResult('2');
await page.accessToSection('claim.card.action');
});
afterAll(async() => {
await browser.close();
});
it('should import the claim', async() => {
await page.waitToClick(selectors.claimAction.importClaimButton);
const message = await page.waitForSnackbar();
expect(message.text).toContain('Data saved!');
});
it('should delete the first line', async() => {
await page.waitToClick(selectors.claimAction.firstDeleteLine);
const message = await page.waitForSnackbar();
expect(message.text).toContain('Data saved!');
});
it('should refresh the view to check not have lines', async() => {
await page.reloadSection('claim.card.action');
const result = await page.countElement(selectors.claimAction.anyLine);
expect(result).toEqual(0);
});
it('should check the "is paid with mana" checkbox', async() => {
await page.waitToClick(selectors.claimAction.isPaidWithManaCheckbox);
const message = await page.waitForSnackbar();
expect(message.text).toContain('Data saved!');
});
it('should confirm the "is paid with mana" is checked', async() => {
await page.reloadSection('claim.card.action');
const isPaidWithManaCheckbox = await page.checkboxState(selectors.claimAction.isPaidWithManaCheckbox);
expect(isPaidWithManaCheckbox).toBe('checked');
});
});

View File

@ -1,96 +0,0 @@
import selectors from '../../helpers/selectors.js';
import getBrowser from '../../helpers/puppeteer.js';
describe('Claim summary path', () => {
let browser;
let page;
const claimId = '4';
beforeAll(async() => {
browser = await getBrowser();
page = browser.page;
});
afterAll(async() => {
await browser.close();
});
it('should navigate to the target claim summary section', async() => {
await page.loginAndModule('salesPerson', 'claim');
await page.accessToSearchResult(claimId);
await page.waitForState('claim.card.summary');
});
it(`should display details from the claim and it's client on the top of the header`, async() => {
await page.waitForTextInElement(selectors.claimSummary.header, 'Tony Stark');
const result = await page.waitToGetProperty(selectors.claimSummary.header, 'innerText');
expect(result).toContain('4 -');
expect(result).toContain('Tony Stark');
});
it('should display the claim state', async() => {
const result = await page.waitToGetProperty(selectors.claimSummary.state, 'innerText');
expect(result).toContain('Resuelto');
});
it('should display the observation', async() => {
const result = await page.waitToGetProperty(selectors.claimSummary.observation, 'innerText');
expect(result).toContain('Wisi forensibus mnesarchum in cum. Per id impetus abhorreant');
});
it('should display the claimed line(s)', async() => {
const result = await page.waitToGetProperty(selectors.claimSummary.firstSaleItemId, 'innerText');
expect(result).toContain('2');
});
it(`should click on the first sale ID making the item descriptor visible`, async() => {
const firstItem = selectors.claimSummary.firstSaleItemId;
await page.evaluate(selectors => {
document.querySelector(selectors).scrollIntoView();
}, firstItem);
await page.click(firstItem);
await page.waitImgLoad(selectors.claimSummary.firstSaleDescriptorImage);
const visible = await page.isVisible(selectors.claimSummary.itemDescriptorPopover);
expect(visible).toBeTruthy();
});
it(`should check the url for the item diary link of the descriptor is for the right item id`, async() => {
await page.waitForSelector(selectors.claimSummary.itemDescriptorPopoverItemDiaryButton, {visible: true});
await page.closePopup();
});
it('should display the claim development details', async() => {
const result = await page.waitToGetProperty(selectors.claimSummary.firstDevelopmentWorker, 'innerText');
expect(result).toContain('salesAssistantNick');
});
it(`should click on the first development worker making the worker descriptor visible`, async() => {
await page.waitToClick(selectors.claimSummary.firstDevelopmentWorker);
const visible = await page.isVisible(selectors.claimSummary.firstDevelopmentWorkerGoToClientButton);
expect(visible).toBeTruthy();
});
it(`should check the url for the go to clientlink of the descriptor is for the right client id`, async() => {
await page.waitForSelector(selectors.claimSummary.firstDevelopmentWorkerGoToClientButton, {visible: true});
await page.closePopup();
});
it(`should click on the first action ticket ID making the ticket descriptor visible`, async() => {
await page.waitToClick(selectors.claimSummary.firstActionTicketId);
await page.waitForSelector(selectors.claimSummary.firstActionTicketDescriptor);
const visible = await page.isVisible(selectors.claimSummary.firstActionTicketDescriptor);
expect(visible).toBeTruthy();
});
});

View File

@ -1,58 +0,0 @@
import selectors from '../../helpers/selectors.js';
import getBrowser from '../../helpers/puppeteer.js';
describe('Claim descriptor path', () => {
let browser;
let page;
const claimId = '1';
beforeAll(async() => {
browser = await getBrowser();
page = browser.page;
});
afterAll(async() => {
await browser.close();
});
it('should now navigate to the target claim summary section', async() => {
await page.loginAndModule('salesPerson', 'claim');
await page.accessToSearchResult(claimId);
await page.waitForState('claim.card.summary');
});
it(`should not be able to see the delete claim button of the descriptor more menu`, async() => {
await page.waitToClick(selectors.claimDescriptor.moreMenu);
await page.waitForSelector(selectors.claimDescriptor.moreMenuDeleteClaim, {hidden: true});
});
it(`should log in as claimManager and navigate to the target claim`, async() => {
await page.loginAndModule('claimManager', 'claim');
await page.accessToSearchResult(claimId);
await page.waitForState('claim.card.summary');
});
it(`should be able to see the delete claim button of the descriptor more menu`, async() => {
await page.waitToClick(selectors.claimDescriptor.moreMenu);
await page.waitForSelector(selectors.claimDescriptor.moreMenuDeleteClaim, {visible: true});
});
it(`should delete the claim`, async() => {
await page.waitToClick(selectors.claimDescriptor.moreMenuDeleteClaim);
await page.waitToClick(selectors.claimDescriptor.acceptDeleteClaim);
const message = await page.waitForSnackbar();
expect(message.text).toContain('Claim deleted!');
});
it(`should have been relocated to the claim index`, async() => {
await page.waitForState('claim.index');
});
it(`should search for the deleted claim to find no results`, async() => {
await page.doSearch(claimId);
const nResults = await page.countElement(selectors.claimsIndex.searchResult);
expect(nResults).toEqual(0);
});
});

View File

@ -1,46 +0,0 @@
import selectors from '../../helpers/selectors';
import getBrowser from '../../helpers/puppeteer';
describe('Claim Add note path', () => {
let browser;
let page;
beforeAll(async() => {
browser = await getBrowser();
page = browser.page;
await page.loginAndModule('salesPerson', 'claim');
await page.accessToSearchResult('2');
await page.accessToSection('claim.card.note.index');
});
afterAll(async() => {
await browser.close();
});
it(`should reach the claim note index`, async() => {
await page.waitForState('claim.card.note.index');
});
it(`should click on the add new note button`, async() => {
await page.waitToClick(selectors.claimNote.addNoteFloatButton);
await page.waitForState('claim.card.note.create');
});
it(`should create a new note`, async() => {
await page.waitForSelector(selectors.claimNote.note);
await page.type(`${selectors.claimNote.note} textarea`, 'The delivery was unsuccessful');
await page.waitToClick(selectors.claimNote.saveButton);
const message = await page.waitForSnackbar();
expect(message.text).toContain('Data saved!');
});
it(`should redirect back to the claim notes page`, async() => {
await page.waitForState('claim.card.note.index');
});
it('should confirm the note was created', async() => {
const result = await page.waitToGetProperty(selectors.claimNote.firstNoteText, 'innerText');
expect(result).toEqual('The delivery was unsuccessful');
});
});

View File

@ -41,7 +41,6 @@ async function test() {
`./e2e/paths/03*/*[sS]pec.js`, `./e2e/paths/03*/*[sS]pec.js`,
`./e2e/paths/04*/*[sS]pec.js`, `./e2e/paths/04*/*[sS]pec.js`,
`./e2e/paths/05*/*[sS]pec.js`, `./e2e/paths/05*/*[sS]pec.js`,
`./e2e/paths/06*/*[sS]pec.js`,
`./e2e/paths/07*/*[sS]pec.js`, `./e2e/paths/07*/*[sS]pec.js`,
`./e2e/paths/08*/*[sS]pec.js`, `./e2e/paths/08*/*[sS]pec.js`,
`./e2e/paths/09*/*[sS]pec.js`, `./e2e/paths/09*/*[sS]pec.js`,

View File

@ -1,188 +0,0 @@
<vn-crud-model vn-id="model"
url="ClaimEnds/filter"
link="{claimFk: $ctrl.$params.id}"
data="$ctrl.salesClaimed"
auto-load="true"
on-save="$ctrl.onSave()">
</vn-crud-model>
<vn-crud-model
auto-load="true"
url="ClaimDestinations"
data="claimDestinations">
</vn-crud-model>
<vn-card class="vn-mb-md vn-pa-lg vn-w-lg" style="text-align: right"
ng-if="$ctrl.salesClaimed.length > 0">
<vn-label-value label="Total claimed"
value="{{$ctrl.claimedTotal | currency: 'EUR':2}}">
</vn-label-value>
</vn-card>
<vn-card class="vn-pa-md vn-w-lg">
<smart-table
model="model"
options="$ctrl.smartTableOptions"
expr-builder="$ctrl.exprBuilder(param, value)">
<slot-actions>
<section class="header">
<vn-tool-bar class="vn-mb-md">
<vn-button
label="Import claim"
disabled="$ctrl.claim.claimStateFk == $ctrl.resolvedStateId"
vn-http-click="$ctrl.importToNewRefundTicket()"
translate-attr="{title: 'Imports claim details'}">
</vn-button>
<vn-button
label="Change destination"
disabled="$ctrl.checked.length == 0"
ng-click="changeDestination.show()">
</vn-button>
<vn-range
label="Responsability"
min-label="Company"
max-label="Sales/Client"
ng-model="$ctrl.claim.responsibility"
max="$ctrl.maxResponsibility"
min="1"
step="1"
on-change="$ctrl.save({responsibility: value})">
</vn-range>
<vn-check class="right"
vn-one
label="Is paid with mana"
ng-model="$ctrl.claim.isChargedToMana"
on-change="$ctrl.save({isChargedToMana: value})">
</vn-check>
</vn-tool-bar>
</section>
</slot-actions>
<slot-table>
<table model="model">
<thead>
<tr>
<th shrink>
<vn-multi-check
model="model"
check-field="$checked">
</vn-multi-check>
</th>
<th number field="itemFk">Id</th>
<th number field="ticketFk">Ticket</th>
<th field="claimDestinationFk">
<span translate>Destination</span>
</th>
<th expand field="landed">
<span translate>Landed</span>
</th>
<th number field="quantity">
<span translate>Quantity</span>
</th>
<th field="concept">
<span translate>Description</span>
</th>
<th number field="price">
<span translate>Price</span>
</th>
<th number field="discount">
<span translate>Disc.</span>
</th>
<th number field="total">Total</th>
</tr>
</thead>
<tbody>
<tr
ng-repeat="saleClaimed in $ctrl.salesClaimed"
vn-repeat-last on-last="$ctrl.focusLastInput()">
<td>
<vn-check
ng-model="saleClaimed.$checked"
vn-click-stop>
</vn-check>
</td>
<td number>
<vn-span
ng-click="itemDescriptor.show($event, saleClaimed.itemFk)"
class="link">
{{::saleClaimed.itemFk}}
</vn-span>
</td>
<td number>
<vn-span
class="link"
ng-click="ticketDescriptor.show($event, saleClaimed.ticketFk)">
{{::saleClaimed.ticketFk}}
</vn-span>
</td>
<td expand>
<vn-autocomplete vn-one id="claimDestinationFk"
ng-model="saleClaimed.claimDestinationFk"
data="claimDestinations"
on-change="$ctrl.updateDestination(saleClaimed, value)"
fields="['id','description']"
value-field="id"
show-field="description">
</vn-autocomplete>
</td>
<td expand>{{::saleClaimed.landed | date: 'dd/MM/yyyy'}}</td>
<td number>{{::saleClaimed.quantity}}</td>
<td expand>{{::saleClaimed.concept}}</td>
<td number>{{::saleClaimed.price | currency: 'EUR':2}}</td>
<td number>{{::saleClaimed.discount}} %</td>
<td number>{{saleClaimed.total | currency: 'EUR':2}}</td>
<td shrink>
<vn-icon-button
vn-tooltip="Remove line"
icon="delete"
ng-click="$ctrl.removeSales(saleClaimed)"
tabindex="-1">
</vn-icon-button>
</td>
</tr>
</tbody>
</table>
</slot-table>
</smart-table>
<button-bar class="vn-pa-md">
<vn-button
label="Regularize"
disabled="$ctrl.claim.claimStateFk == $ctrl.resolvedStateId"
vn-http-click="$ctrl.regularize()">
</vn-button>
</button-bar>
</vn-card>
<vn-item-descriptor-popover
vn-id="item-descriptor"
warehouse-fk="$ctrl.vnConfig.warehouseFk">
</vn-item-descriptor-popover>
<vn-ticket-descriptor-popover
vn-id="ticket-descriptor">
</vn-ticket-descriptor-popover>
<vn-confirm
vn-id="update-greuge"
question="Insert greuges on client card"
message="Do you want to insert greuges?"
on-accept="$ctrl.onUpdateGreugeAccept()">
</vn-confirm>
<!-- Dialog of change destionation -->
<vn-dialog
vn-id="changeDestination"
on-accept="$ctrl.onResponse()">
<tpl-body>
<section class="SMSDialog">
<h5 class="vn-py-sm">{{$ctrl.$t('Change destination to all selected rows', {total: $ctrl.checked.length})}}</h5>
<vn-horizontal>
<vn-autocomplete vn-one id="claimDestinationFk"
ng-model="$ctrl.newDestination"
data="claimDestinations"
fields="['id','description']"
value-field="id"
show-field="description"
vn-focus>
</vn-autocomplete>
</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,233 +0,0 @@
import ngModule from '../module';
import Section from 'salix/components/section';
import './style.scss';
export default class Controller extends Section {
constructor($element, $) {
super($element, $);
this.newDestination;
this.filter = {
include: [
{relation: 'sale',
scope: {
fields: ['concept', 'ticketFk', 'price', 'quantity', 'discount', 'itemFk'],
include: {
relation: 'ticket'
}
}
},
{relation: 'claimBeggining'},
{relation: 'claimDestination'}
]
};
this.getResolvedState();
this.maxResponsibility = 5;
this.smartTableOptions = {
activeButtons: {
search: true
},
columns: [
{
field: 'claimDestinationFk',
autocomplete: {
url: 'ClaimDestinations',
showField: 'description',
valueField: 'id'
}
},
{
field: 'landed',
searchable: false
}
]
};
}
exprBuilder(param, value) {
switch (param) {
case 'itemFk':
case 'ticketFk':
case 'claimDestinationFk':
case 'quantity':
case 'price':
case 'discount':
case 'total':
return {[param]: value};
case 'concept':
return {[param]: {like: `%${value}%`}};
case 'landed':
return {[param]: {between: this.dateRange(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];
}
get checked() {
const salesClaimed = this.$.model.data || [];
const checkedSalesClaimed = [];
for (let saleClaimed of salesClaimed) {
if (saleClaimed.$checked)
checkedSalesClaimed.push(saleClaimed);
}
return checkedSalesClaimed;
}
updateDestination(saleClaimed, claimDestinationFk) {
const data = {rows: [saleClaimed], claimDestinationFk: claimDestinationFk};
this.$http.post(`Claims/updateClaimDestination`, data).then(() => {
this.vnApp.showSuccess(this.$t('Data saved!'));
}).catch(e => {
this.$.model.refresh();
throw e;
});
}
removeSales(saleClaimed) {
const params = {sales: [saleClaimed]};
this.$http.post(`ClaimEnds/deleteClamedSales`, params).then(() => {
this.$.model.refresh();
this.vnApp.showSuccess(this.$t('Data saved!'));
});
}
getResolvedState() {
const query = `ClaimStates/findOne`;
const params = {
filter: {
where: {
code: 'resolved'
}
}
};
this.$http.get(query, params).then(res =>
this.resolvedStateId = res.data.id
);
}
importToNewRefundTicket() {
let query = `ClaimBeginnings/${this.$params.id}/importToNewRefundTicket`;
return this.$http.post(query).then(() => {
this.$.model.refresh();
this.vnApp.showSuccess(this.$t('Data saved!'));
});
}
focusLastInput() {
let inputs = document.querySelectorAll('#claimDestinationFk');
inputs[inputs.length - 1].querySelector('input').focus();
this.calculateTotals();
}
calculateTotals() {
this.claimedTotal = 0;
this.salesClaimed.forEach(sale => {
const price = sale.quantity * sale.price;
const discount = (sale.discount * (sale.quantity * sale.price)) / 100;
this.claimedTotal += price - discount;
});
}
regularize() {
const query = `Claims/${this.$params.id}/regularizeClaim`;
return this.$http.post(query).then(() => {
if (this.claim.responsibility >= Math.ceil(this.maxResponsibility) / 2)
this.$.updateGreuge.show();
else
this.vnApp.showSuccess(this.$t('Data saved!'));
this.card.reload();
});
}
getGreugeTypeId() {
const params = {filter: {where: {code: 'freightPickUp'}}};
const query = `GreugeTypes/findOne`;
return this.$http.get(query, {params}).then(res => {
this.greugeTypeFreightId = res.data.id;
return res;
});
}
getGreugeConfig() {
const query = `GreugeConfigs/findOne`;
return this.$http.get(query).then(res => {
this.freightPickUpPrice = res.data.freightPickUpPrice;
return res;
});
}
onUpdateGreugeAccept() {
const promises = [];
promises.push(this.getGreugeTypeId());
promises.push(this.getGreugeConfig());
return Promise.all(promises).then(() => {
return this.updateGreuge({
clientFk: this.claim.clientFk,
description: this.$t('ClaimGreugeDescription', {
claimId: this.claim.id
}).toUpperCase(),
amount: this.freightPickUpPrice,
greugeTypeFk: this.greugeTypeFreightId,
ticketFk: this.claim.ticketFk
});
});
}
updateGreuge(data) {
return this.$http.post(`Greuges`, data).then(() => {
this.vnApp.showSuccess(this.$t('Data saved!'));
this.vnApp.showMessage(this.$t('Greuge added'));
});
}
save(data) {
const query = `Claims/${this.$params.id}/updateClaimAction`;
this.$http.patch(query, data)
.then(() => this.vnApp.showSuccess(this.$t('Data saved!')));
}
onSave() {
this.vnApp.showSuccess(this.$t('Data saved!'));
}
onResponse() {
const rowsToEdit = [];
for (let row of this.checked)
rowsToEdit.push({id: row.id});
const data = {
rows: rowsToEdit,
claimDestinationFk: this.newDestination
};
const query = `Claims/updateClaimDestination`;
this.$http.post(query, data)
.then(() => {
this.$.model.refresh();
this.vnApp.showSuccess(this.$t('Data saved!'));
});
}
}
ngModule.vnComponent('vnClaimAction', {
template: require('./index.html'),
controller: Controller,
bindings: {
claim: '<'
},
require: {
card: '^vnClaimCard'
}
});

View File

@ -1,167 +0,0 @@
import './index.js';
import crudModel from 'core/mocks/crud-model';
describe('claim', () => {
describe('Component vnClaimAction', () => {
let controller;
let $httpBackend;
let $state;
beforeEach(ngModule('claim'));
beforeEach(inject(($componentController, _$state_, _$httpBackend_) => {
$httpBackend = _$httpBackend_;
$state = _$state_;
$state.params.id = 1;
controller = $componentController('vnClaimAction', {$element: null});
controller.claim = {ticketFk: 1};
controller.$.model = {refresh: () => {}};
controller.$.addSales = {
hide: () => {},
show: () => {}
};
controller.$.lastTicketsModel = crudModel;
controller.$.lastTicketsPopover = {
hide: () => {},
show: () => {}
};
controller.card = {reload: () => {}};
$httpBackend.expectGET(`ClaimStates/findOne`).respond({});
}));
describe('getResolvedState()', () => {
it('should return the resolved state id', () => {
$httpBackend.expectGET(`ClaimStates/findOne`).respond({id: 1});
controller.getResolvedState();
$httpBackend.flush();
expect(controller.resolvedStateId).toEqual(1);
});
});
describe('calculateTotals()', () => {
it('should calculate the total price of the items claimed', () => {
controller.salesClaimed = [
{quantity: 5, price: 2, discount: 0},
{quantity: 10, price: 2, discount: 0},
{quantity: 10, price: 2, discount: 0}
];
controller.calculateTotals();
expect(controller.claimedTotal).toEqual(50);
});
});
describe('importToNewRefundTicket()', () => {
it('should perform a post query and add lines from a new ticket', () => {
jest.spyOn(controller.$.model, 'refresh');
jest.spyOn(controller.vnApp, 'showSuccess');
$httpBackend.expect('POST', `ClaimBeginnings/1/importToNewRefundTicket`).respond({});
controller.importToNewRefundTicket();
$httpBackend.flush();
expect(controller.$.model.refresh).toHaveBeenCalled();
expect(controller.vnApp.showSuccess).toHaveBeenCalled();
});
});
describe('regularize()', () => {
it('should perform a post query and reload the claim card', () => {
jest.spyOn(controller.card, 'reload');
jest.spyOn(controller.vnApp, 'showSuccess');
$httpBackend.expect('POST', `Claims/1/regularizeClaim`).respond({});
controller.regularize();
$httpBackend.flush();
expect(controller.card.reload).toHaveBeenCalledWith();
expect(controller.vnApp.showSuccess).toHaveBeenCalled();
});
});
describe('save()', () => {
it('should perform a patch query and show a success message', () => {
jest.spyOn(controller.vnApp, 'showSuccess');
const data = {pickup: 'agency'};
$httpBackend.expect('PATCH', `Claims/1/updateClaimAction`, data).respond({});
controller.save(data);
$httpBackend.flush();
expect(controller.vnApp.showSuccess).toHaveBeenCalled();
});
});
describe('onUpdateGreugeAccept()', () => {
const greugeTypeId = 7;
const freightPickUpPrice = 11;
it('should make a query and get the greugeTypeId and greuge config', () => {
$httpBackend.expectRoute('GET', `GreugeTypes/findOne`).respond({id: greugeTypeId});
$httpBackend.expectGET(`GreugeConfigs/findOne`).respond({freightPickUpPrice});
controller.onUpdateGreugeAccept();
$httpBackend.flush();
expect(controller.greugeTypeFreightId).toEqual(greugeTypeId);
expect(controller.freightPickUpPrice).toEqual(freightPickUpPrice);
});
it('should perform a insert into greuges', done => {
jest.spyOn(controller, 'getGreugeTypeId').mockReturnValue(new Promise(resolve => {
return resolve({id: greugeTypeId});
}));
jest.spyOn(controller, 'getGreugeConfig').mockReturnValue(new Promise(resolve => {
return resolve({freightPickUpPrice});
}));
jest.spyOn(controller, 'updateGreuge').mockReturnValue(new Promise(resolve => {
return resolve(true);
}));
controller.claim.clientFk = 1101;
controller.claim.id = 11;
controller.onUpdateGreugeAccept().then(() => {
expect(controller.updateGreuge).toHaveBeenCalledWith(jasmine.any(Object));
done();
}).catch(done.fail);
});
});
describe('updateGreuge()', () => {
it('should make a query and then call to showSuccess() and showMessage() methods', () => {
jest.spyOn(controller.vnApp, 'showSuccess');
jest.spyOn(controller.vnApp, 'showMessage');
const freightPickUpPrice = 11;
const greugeTypeId = 7;
const expectedData = {
clientFk: 1101,
description: `claim: ${controller.claim.id}`,
amount: freightPickUpPrice,
greugeTypeFk: greugeTypeId,
ticketFk: controller.claim.ticketFk
};
$httpBackend.expect('POST', `Greuges`, expectedData).respond(200);
controller.updateGreuge(expectedData);
$httpBackend.flush();
expect(controller.vnApp.showSuccess).toHaveBeenCalledWith('Data saved!');
expect(controller.vnApp.showMessage).toHaveBeenCalledWith('Greuge added');
});
});
describe('onResponse()', () => {
it('should perform a post query and show a success message', () => {
jest.spyOn(controller.vnApp, 'showSuccess');
$httpBackend.expect('POST', `Claims/updateClaimDestination`).respond({});
controller.onResponse();
$httpBackend.flush();
expect(controller.vnApp.showSuccess).toHaveBeenCalled();
});
});
});
});

View File

@ -1 +0,0 @@
ClaimGreugeDescription: Claim id {{claimId}}

View File

@ -1,13 +0,0 @@
Destination: Destino
Action: Actuaciones
Total claimed: Total Reclamado
Import claim: Importar reclamacion
Imports claim details: Importa detalles de la reclamacion
Regularize: Regularizar
Do you want to insert greuges?: Desea insertar greuges?
Insert greuges on client card: Insertar greuges en la ficha del cliente
Greuge added: Greuge añadido
ClaimGreugeDescription: Reclamación id {{claimId}}
Change destination: Cambiar destino
Change destination to all selected rows: Cambiar destino a {{total}} fila(s) seleccionada(s)
Add observation to all selected clients: Añadir observación a {{total}} cliente(s) seleccionado(s)

View File

@ -1,46 +0,0 @@
vn-claim-action {
.header {
display: flex;
justify-content: space-between;
align-items: center;
align-content: center;
vn-tool-bar {
flex: none
}
.vn-check {
flex: none;
}
}
vn-dialog[vn-id=addSales] {
tpl-body {
width: 950px;
div {
div.buttons {
display: none;
}
vn-table{
min-width: 950px;
}
}
}
}
vn-popover.lastTicketsPopover {
vn-table {
min-width: 650px;
overflow: auto
}
div.ticketList {
overflow: auto;
max-height: 350px;
}
}
.right {
margin-left: 370px;
}
}

View File

@ -1,66 +0,0 @@
<mg-ajax path="Claims/updateClaim/{{patch.params.id}}" options="vnPatch"></mg-ajax>
<vn-watcher
vn-id="watcher"
data="$ctrl.claim"
form="form"
save="patch">
</vn-watcher>
<vn-crud-model
auto-load="true"
url="ClaimStates"
data="claimStates">
</vn-crud-model>
<form name="form" ng-submit="$ctrl.onSubmit()" class="vn-w-md">
<vn-card class="vn-pa-lg">
<vn-horizontal>
<vn-textfield
label="Client"
ng-model="$ctrl.claim.client.name"
readonly="true">
</vn-textfield>
<vn-textfield
label="Created"
field="::$ctrl.claim.created | date:'yyyy-MM-dd HH:mm'"
readonly="true">
</vn-textfield>
</vn-horizontal>
<vn-horizontal>
<vn-worker-autocomplete
disabled="false"
show-field="nickname"
ng-model="$ctrl.claim.workerFk"
departments="['VT']"
label="Attended by">
</vn-worker-autocomplete>
<vn-autocomplete
ng-model="$ctrl.claim.claimStateFk"
data="claimStates"
show-field="description"
value-field="id"
label="Claim state"
order="priority ASC"
vn-focus>
</vn-autocomplete>
</vn-horizontal>
<vn-horizontal>
<vn-input-number vn-one
min="0"
type="number"
label="Packages received"
ng-model="$ctrl.claim.packages">
</vn-input-number>
</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>

View File

@ -1,20 +0,0 @@
import ngModule from '../module';
import Section from 'salix/components/section';
import './style.scss';
class Controller extends Section {
onSubmit() {
this.$.watcher.submit().then(() => {
if (this.aclService.hasAny(['claimManager']))
this.$state.go('claim.card.detail');
});
}
}
ngModule.vnComponent('vnClaimBasicData', {
template: require('./index.html'),
controller: Controller,
bindings: {
claim: '<'
}
});

View File

@ -1,28 +0,0 @@
import './index.js';
import watcher from 'core/mocks/watcher';
describe('Claim', () => {
describe('Component vnClaimBasicData', () => {
let controller;
let $scope;
beforeEach(ngModule('claim'));
beforeEach(inject(($componentController, $rootScope) => {
$scope = $rootScope.$new();
$scope.watcher = watcher;
const $element = angular.element('<vn-claim-basic-data></vn-claim-basic-data>');
controller = $componentController('vnClaimBasicData', {$element, $scope});
}));
describe('onSubmit()', () => {
it(`should redirect to 'claim.card.detail' state`, () => {
jest.spyOn(controller.aclService, 'hasAny').mockReturnValue(true);
jest.spyOn(controller.$state, 'go');
controller.onSubmit();
expect(controller.$state.go).toHaveBeenCalledWith('claim.card.detail');
});
});
});
});

View File

@ -1,9 +0,0 @@
Contact: Contacto
Claim state: Estado de la reclamación
Is paid with mana: Cargado al maná
Responsability: Responsabilidad
Company: Empresa
Sales/Client: Comercial/Cliente
Pick up: Recoger
When checked will notify to the salesPerson: Cuando se marque enviará una notificación de recogida al comercial
Packages received: Bultos recibidos

View File

@ -1,3 +0,0 @@
vn-claim-basic-data vn-date-picker {
padding-left: 80px;
}

View File

@ -1,5 +0,0 @@
<vn-portal slot="menu">
<vn-claim-descriptor claim="$ctrl.claim"></vn-claim-descriptor>
<vn-left-menu source="card"></vn-left-menu>
</vn-portal>
<ui-view></ui-view>

View File

@ -1,68 +0,0 @@
import ngModule from '../module';
import ModuleCard from 'salix/components/module-card';
class Controller extends ModuleCard {
reload() {
let filter = {
include: [
{
relation: 'worker',
scope: {
fields: ['id'],
include: {
relation: 'user',
scope: {
fields: ['name']
}
}
}
}, {
relation: 'ticket',
scope: {
fields: ['zoneFk', 'addressFk'],
include: [
{
relation: 'zone',
scope: {
fields: ['name']
}
},
{
relation: 'address',
scope: {
fields: ['provinceFk'],
include: {
relation: 'province',
scope: {
fields: ['name']
}
}
}
}]
}
}, {
relation: 'claimState',
scope: {
fields: ['id', 'description']
}
}, {
relation: 'client',
scope: {
fields: ['salesPersonFk', 'name', 'email'],
include: {
relation: 'salesPersonUser'
}
}
}
]
};
this.$http.get(`Claims/${this.$params.id}`, {filter})
.then(res => this.claim = res.data);
}
}
ngModule.vnComponent('vnClaimCard', {
template: require('./index.html'),
controller: Controller
});

View File

@ -1,29 +0,0 @@
import './index.js';
describe('Claim', () => {
describe('Component vnClaimCard', () => {
let controller;
let $httpBackend;
let data = {id: 1, name: 'fooName'};
beforeEach(ngModule('claim'));
beforeEach(inject(($componentController, _$httpBackend_, $stateParams) => {
$httpBackend = _$httpBackend_;
let $element = angular.element('<div></div>');
controller = $componentController('vnClaimCard', {$element});
$stateParams.id = data.id;
$httpBackend.whenRoute('GET', 'Claims/:id').respond(data);
}));
it('should request data and set it on the controller', () => {
controller.reload();
$httpBackend.flush();
expect(controller.claim).toEqual(data);
});
});
});

View File

@ -29,9 +29,9 @@ class Controller extends Descriptor {
deleteClaim() { deleteClaim() {
return this.$http.delete(`Claims/${this.claim.id}`) return this.$http.delete(`Claims/${this.claim.id}`)
.then(() => { .then(async() => {
this.vnApp.showSuccess(this.$t('Claim deleted!')); this.vnApp.showSuccess(this.$t('Claim deleted!'));
this.$state.go('claim.index'); window.location.href = await this.vnApp.getUrl(`claim/`);
}); });
} }
} }

View File

@ -53,14 +53,12 @@ describe('Item Component vnClaimDescriptor', () => {
describe('deleteClaim()', () => { describe('deleteClaim()', () => {
it('should perform a query and call showSuccess if the response is accept', () => { it('should perform a query and call showSuccess if the response is accept', () => {
jest.spyOn(controller.vnApp, 'showSuccess'); jest.spyOn(controller.vnApp, 'showSuccess');
jest.spyOn(controller.$state, 'go');
$httpBackend.expectDELETE(`Claims/${claim.id}`).respond(); $httpBackend.expectDELETE(`Claims/${claim.id}`).respond();
controller.deleteClaim(); controller.deleteClaim();
$httpBackend.flush(); $httpBackend.flush();
expect(controller.vnApp.showSuccess).toHaveBeenCalled(); expect(controller.vnApp.showSuccess).toHaveBeenCalled();
expect(controller.$state.go).toHaveBeenCalledWith('claim.index');
}); });
}); });
}); });

View File

@ -1,178 +0,0 @@
<vn-crud-model
vn-id="model"
auto-load="true"
url="ClaimBeginnings"
filter="$ctrl.filter"
data="$ctrl.salesClaimed"
on-data-change="$ctrl.calculateTotals()">
</vn-crud-model>
<vn-card
class="vn-mb-md vn-pa-lg vn-w-lg"
style="text-align: right"
ng-if="$ctrl.salesClaimed.length > 0">
<vn-label-value label="Total"
value="{{$ctrl.paidTotal | currency: 'EUR':2}}">
</vn-label-value>
<vn-label-value label="Total claimed"
value="{{$ctrl.claimedTotal | currency: 'EUR':2}}">
</vn-label-value>
</vn-card>
<vn-data-viewer model="model">
<vn-card>
<vn-table model="model">
<vn-thead>
<vn-tr>
<vn-th center expand>Landed</vn-th>
<vn-th number>Quantity</vn-th>
<vn-th>Claimed</vn-th>
<vn-th>Description</vn-th>
<vn-th number>Price</vn-th>
<vn-th number>Disc.</vn-th>
<vn-th number>Total</vn-th>
<vn-th></vn-th>
</vn-tr>
</vn-thead>
<vn-tbody>
<vn-tr ng-repeat="saleClaimed in $ctrl.salesClaimed" vn-repeat-last>
<vn-td center expand>{{::saleClaimed.sale.ticket.landed | date:'dd/MM/yyyy'}}</vn-td>
<vn-td number>{{::saleClaimed.sale.quantity}}</vn-td>
<vn-td>
<vn-input-number
min="0"
step="1"
disabled="!$ctrl.isRewritable"
ng-model="saleClaimed.quantity"
on-change="$ctrl.setClaimedQuantity(saleClaimed.id, saleClaimed.quantity)"
class="dense">
</vn-input-number>
</vn-td>
<vn-td expand title="{{::saleClaimed.sale.concept}}">
<span
ng-click="itemDescriptor.show($event, saleClaimed.sale.itemFk)"
class="link">
{{::saleClaimed.sale.concept}}
</span>
</vn-td>
<vn-td number>{{::saleClaimed.sale.price | currency: 'EUR':2}}</vn-td>
<vn-td number>
<span ng-class="{'link': $ctrl.isRewritable && $ctrl.isClaimManager}"
translate-attr="{title: $ctrl.isRewritable && $ctrl.isClaimManager ? 'Edit discount' : ''}"
ng-click="$ctrl.showEditPopover($event, saleClaimed)">
{{saleClaimed.sale.discount}} %
</span>
</vn-td>
<vn-td number>
{{$ctrl.getSaleTotal(saleClaimed.sale) | currency: 'EUR':2}}
</vn-td>
<vn-td shrink>
<vn-icon-button
vn-tooltip="Remove sale"
ng-if ="$ctrl.isRewritable"
icon="delete"
ng-click="$ctrl.showDeleteConfirm($index)"
tabindex="-1">
</vn-icon-button>
</vn-td>
</vn-tr>
</vn-tbody>
</vn-table>
</vn-card>
</vn-data-viewer>
<vn-button
label="Next"
class="next"
ui-sref="claim.card.photos">
</vn-button>
<vn-float-button
icon="add"
ng-if="$ctrl.isRewritable"
ng-click="$ctrl.openAddSalesDialog()"
vn-tooltip="Add sale item" vn-bind="+"
fixed-bottom-right>
</vn-float-button>
<!-- Add Lines Dialog -->
<vn-dialog vn-id="add-sales" class="modal-form">
<tpl-title>
<span translate>Claimable sales from ticket</span> {{$ctrl.claim.ticketFk}}
</tpl-title>
<tpl-body>
<vn-horizontal class="vn-pa-md">
<vn-table>
<vn-thead>
<vn-tr>
<vn-th number>Landed</vn-th>
<vn-th number>Quantity</vn-th>
<vn-th number>Description</vn-th>
<vn-th number>Price</vn-th>
<vn-th number>Disc.</vn-th>
<vn-th number>Total</vn-th>
</vn-tr>
</vn-thead>
<vn-tbody>
<vn-tr
ng-repeat="sale in $ctrl.salesToClaim"
ng-click="$ctrl.addClaimedSale($index)"
class="clickable">
<vn-td number>{{sale.landed | date: 'dd/MM/yyyy'}}</vn-td>
<vn-td number>{{sale.quantity}}</vn-td>
<vn-td expand title="{{::sale.concept}}">
<span
vn-click-stop="itemDescriptor.show($event, sale.itemFk)"
class="link">
{{sale.itemFk}} - {{sale.concept}}
</span>
</vn-td>
<vn-td number>{{sale.price | currency: 'EUR':2}}</vn-td>
<vn-td number>{{sale.discount}} %</vn-td>
<vn-td number>
{{(sale.quantity * sale.price) - ((sale.discount * (sale.quantity * sale.price))/100) | currency: 'EUR':2}}
</vn-td>
</vn-tr>
</vn-tbody>
</vn-table>
</vn-horizontal>
</tpl-body>
</vn-dialog>
<vn-item-descriptor-popover
vn-id="item-descriptor"
warehouse-fk="$ctrl.vnConfig.warehouseFk">
</vn-item-descriptor-popover>
<vn-popover
class="edit"
vn-id="edit-popover"
on-open="$ctrl.getSalespersonMana()"
on-close="$ctrl.mana = null">
<div class="discount-popover">
<vn-spinner
ng-if="$ctrl.mana == null"
style="padding: 1em;"
enable="true">
</vn-spinner>
<div ng-if="$ctrl.mana != null">
<vn-horizontal class="header vn-pa-md">
<h5>MANÁ: {{$ctrl.mana | currency: 'EUR':0}}</h5>
</vn-horizontal>
<div class="vn-pa-md">
<vn-input-number
vn-focus
label="Discount"
ng-model="$ctrl.newDiscount"
type="text"
step="0.01"
on-change="$ctrl.updateDiscount()"
suffix="€">
</vn-input-number>
<div class="simulator">
<p class="simulatorTitle" translate>Total claimed price</p>
<p>{{$ctrl.newPrice | currency: 'EUR':2}}
</p>
</div>
</div>
</div>
</div>
</vn-popover>
<vn-confirm
vn-id="confirm"
question="Delete sale from claim?"
on-accept="$ctrl.deleteClaimedSale()">
</vn-confirm>

View File

@ -1,203 +0,0 @@
import ngModule from '../module';
import Section from 'salix/components/section';
import './style.scss';
class Controller extends Section {
constructor($element, $) {
super($element, $);
this.edit = {};
this.filter = {
where: {claimFk: this.$params.id},
include: [
{
relation: 'sale',
scope: {
fields: ['concept', 'ticketFk', 'price', 'quantity', 'discount', 'itemFk'],
include: {
relation: 'ticket'
}
}
}
]
};
}
get claim() {
return this._claim;
}
set claim(value) {
this._claim = value;
if (value) {
this.isClaimEditable();
this.isTicketEditable();
}
}
set salesClaimed(value) {
this._salesClaimed = value;
if (value) this.calculateTotals();
}
get salesClaimed() {
return this._salesClaimed;
}
get newDiscount() {
return this._newDiscount;
}
set newDiscount(value) {
this._newDiscount = value;
this.updateNewPrice();
}
get isClaimManager() {
return this.aclService.hasAny(['claimManager']);
}
openAddSalesDialog() {
this.getClaimableFromTicket();
this.$.addSales.show();
}
getClaimableFromTicket() {
let config = {params: {ticketFk: this.claim.ticketFk}};
let query = `Sales/getClaimableFromTicket`;
this.$http.get(query, config).then(res => {
if (res.data)
this.salesToClaim = res.data;
});
}
addClaimedSale(index) {
let sale = this.salesToClaim[index];
let saleToAdd = {saleFk: sale.saleFk, claimFk: this.claim.id, quantity: sale.quantity};
let query = `ClaimBeginnings/`;
this.$http.post(query, saleToAdd).then(() => {
this.$.addSales.hide();
this.$.model.refresh();
this.vnApp.showSuccess(this.$t('Data saved!'));
if (this.aclService.hasAny(['claimManager']))
this.$state.go('claim.card.development');
});
}
showDeleteConfirm($index) {
this.claimedIndex = $index;
this.$.confirm.show();
}
deleteClaimedSale() {
this.$.model.remove(this.claimedIndex);
this.$.model.save().then(() => {
this.vnApp.showSuccess(this.$t('Data saved!'));
this.calculateTotals();
});
}
setClaimedQuantity(id, claimedQuantity) {
let params = {quantity: claimedQuantity};
let query = `ClaimBeginnings/${id}`;
this.$http.patch(query, params).then(() => {
this.vnApp.showSuccess(this.$t('Data saved!'));
this.calculateTotals();
});
}
calculateTotals() {
this.paidTotal = 0.0;
this.claimedTotal = 0.0;
if (!this._salesClaimed) return;
this._salesClaimed.forEach(sale => {
let orgSale = sale.sale;
this.paidTotal += this.getSaleTotal(orgSale);
const price = sale.quantity * orgSale.price;
const discount = ((orgSale.discount * price) / 100);
this.claimedTotal += price - discount;
});
}
getSaleTotal(sale) {
let total = 0.0;
const price = sale.quantity * sale.price;
const discount = ((sale.discount * price) / 100);
total += price - discount;
return total;
}
getSalespersonMana() {
this.$http.get(`Tickets/${this.claim.ticketFk}/getSalesPersonMana`).then(res => {
this.mana = res.data;
});
}
isTicketEditable() {
if (!this.claim) return;
this.$http.get(`Tickets/${this.claim.ticketFk}/isEditable`).then(res => {
this.isEditable = res.data;
});
}
isClaimEditable() {
if (!this.claim) return;
this.$http.get(`ClaimStates/${this.claim.claimStateFk}/isEditable`).then(res => {
this.isRewritable = res.data;
});
}
showEditPopover(event, saleClaimed) {
if (this.aclService.hasAny(['claimManager'])) {
this.saleClaimed = saleClaimed;
this.$.editPopover.parent = event.target;
this.$.editPopover.show();
}
}
updateDiscount() {
const claimedSale = this.saleClaimed.sale;
if (this.newDiscount != claimedSale.discount) {
const params = {salesIds: [claimedSale.id], newDiscount: this.newDiscount};
const query = `Tickets/${claimedSale.ticketFk}/updateDiscount`;
this.$http.post(query, params).then(() => {
claimedSale.discount = this.newDiscount;
this.calculateTotals();
this.clearDiscount();
this.vnApp.showSuccess(this.$t('Data saved!'));
});
}
this.$.editPopover.hide();
}
updateNewPrice() {
this.newPrice = (this.saleClaimed.quantity * this.saleClaimed.sale.price) -
((this.newDiscount * (this.saleClaimed.quantity * this.saleClaimed.sale.price)) / 100);
}
clearDiscount() {
this.newDiscount = null;
}
}
Controller.$inject = ['$element', '$scope'];
ngModule.vnComponent('vnClaimDetail', {
template: require('./index.html'),
controller: Controller,
bindings: {
claim: '<'
}
});

View File

@ -1,150 +0,0 @@
import './index.js';
import crudModel from 'core/mocks/crud-model';
describe('claim', () => {
describe('Component vnClaimDetail', () => {
let $scope;
let controller;
let $httpBackend;
beforeEach(ngModule('claim'));
beforeEach(inject(($componentController, _$httpBackend_, $rootScope) => {
$scope = $rootScope.$new();
$scope.descriptor = {
show: () => {}
};
$httpBackend = _$httpBackend_;
$httpBackend.whenGET('Claims/ClaimBeginnings').respond({});
$httpBackend.whenGET(`Tickets/1/isEditable`).respond(true);
$httpBackend.whenGET(`ClaimStates/2/isEditable`).respond(true);
const $element = angular.element('<vn-claim-detail></vn-claim-detail>');
controller = $componentController('vnClaimDetail', {$element, $scope});
controller.claim = {
ticketFk: 1,
id: 2,
claimStateFk: 2}
;
controller.salesToClaim = [{saleFk: 1}, {saleFk: 2}];
controller.salesClaimed = [{id: 1, sale: {}}];
controller.$.model = crudModel;
controller.$.addSales = {
hide: () => {},
show: () => {}
};
controller.$.editPopover = {
hide: () => {}
};
jest.spyOn(controller.aclService, 'hasAny').mockReturnValue(true);
}));
describe('openAddSalesDialog()', () => {
it('should call getClaimableFromTicket and $.addSales.show', () => {
jest.spyOn(controller, 'getClaimableFromTicket');
jest.spyOn(controller.$.addSales, 'show');
controller.openAddSalesDialog();
expect(controller.getClaimableFromTicket).toHaveBeenCalledWith();
expect(controller.$.addSales.show).toHaveBeenCalledWith();
});
});
describe('getClaimableFromTicket()', () => {
it('should make a query and set salesToClaim', () => {
$httpBackend.expectGET(`Sales/getClaimableFromTicket?ticketFk=1`).respond(200, 1);
controller.getClaimableFromTicket();
$httpBackend.flush();
expect(controller.salesToClaim).toEqual(1);
});
});
describe('addClaimedSale(index)', () => {
it('should make a post and call refresh, hide and showSuccess', () => {
jest.spyOn(controller.$.addSales, 'hide');
jest.spyOn(controller.$state, 'go');
$httpBackend.expectPOST(`ClaimBeginnings/`).respond({});
controller.addClaimedSale(1);
$httpBackend.flush();
expect(controller.$.addSales.hide).toHaveBeenCalledWith();
expect(controller.$state.go).toHaveBeenCalledWith('claim.card.development');
});
});
describe('deleteClaimedSale()', () => {
it('should make a delete and call refresh and showSuccess', () => {
const claimedIndex = 1;
controller.claimedIndex = claimedIndex;
jest.spyOn(controller.$.model, 'remove');
jest.spyOn(controller.$.model, 'save');
jest.spyOn(controller.vnApp, 'showSuccess');
controller.deleteClaimedSale();
expect(controller.$.model.remove).toHaveBeenCalledWith(claimedIndex);
expect(controller.$.model.save).toHaveBeenCalledWith();
expect(controller.vnApp.showSuccess).toHaveBeenCalled();
});
});
describe('setClaimedQuantity(id, claimedQuantity)', () => {
it('should make a patch and call refresh and showSuccess', () => {
const id = 1;
const claimedQuantity = 1;
jest.spyOn(controller.vnApp, 'showSuccess');
$httpBackend.expectPATCH(`ClaimBeginnings/${id}`).respond({});
controller.setClaimedQuantity(id, claimedQuantity);
$httpBackend.flush();
expect(controller.vnApp.showSuccess).toHaveBeenCalled();
});
});
describe('calculateTotals()', () => {
it('should set paidTotal and claimedTotal to 0 if salesClaimed has no data', () => {
controller.salesClaimed = [];
controller.calculateTotals();
expect(controller.paidTotal).toEqual(0);
expect(controller.claimedTotal).toEqual(0);
});
});
describe('updateDiscount()', () => {
it('should perform a query if the new discount differs from the claim discount', () => {
controller.saleClaimed = {sale: {
discount: 5,
id: 7,
ticketFk: 1,
price: 2,
quantity: 10}};
controller.newDiscount = 10;
jest.spyOn(controller.vnApp, 'showSuccess');
jest.spyOn(controller, 'calculateTotals');
jest.spyOn(controller, 'clearDiscount');
jest.spyOn(controller.$.editPopover, 'hide');
$httpBackend.when('POST', 'Tickets/1/updateDiscount').respond({});
controller.updateDiscount();
$httpBackend.flush();
expect(controller.calculateTotals).toHaveBeenCalledWith();
expect(controller.clearDiscount).toHaveBeenCalledWith();
expect(controller.vnApp.showSuccess).toHaveBeenCalled();
expect(controller.$.editPopover.hide).toHaveBeenCalledWith();
});
});
describe('isTicketEditable()', () => {
it('should check if the ticket assigned to the claim is editable', () => {
controller.isTicketEditable();
$httpBackend.flush();
expect(controller.isEditable).toBeTruthy();
});
});
});
});

View File

@ -1,11 +0,0 @@
Claimed: Reclamados
Disc.: Dto.
Attended by: Atendida por
Landed: F. entrega
Price: Precio
Claimable sales from ticket: Lineas reclamables del ticket
Detail: Detalles
Add sale item: Añadir artículo
Insuficient permisos: Permisos insuficientes
Total claimed price: Precio total reclamado
Delete sale from claim?: ¿Borrar la linea de la reclamación?

View File

@ -1,30 +0,0 @@
@import "variables";
.vn-popover .discount-popover {
width: 256px;
.header {
background-color: $color-main;
color: $color-font-dark;
h5 {
color: inherit;
margin: 0 auto;
}
}
.simulatorTitle {
margin-bottom: 0;
font-size: .75rem;
color: $color-main;
}
vn-label-value {
padding-bottom: 20px;
}
.simulator{
text-align: center;
}
}
.next{
float: right;
}

View File

@ -1,2 +0,0 @@
<vn-card>
</vn-card>

View File

@ -1,21 +0,0 @@
import ngModule from '../module';
import Section from 'salix/components/section';
class Controller extends Section {
constructor($element, $) {
super($element, $);
}
async $onInit() {
this.$state.go('claim.card.summary', {id: this.$params.id});
window.location.href = await this.vnApp.getUrl(`claim/${this.$params.id}/development`);
}
}
ngModule.vnComponent('vnClaimDevelopment', {
template: require('./index.html'),
controller: Controller,
bindings: {
claim: '<'
}
});

View File

@ -1,16 +1,4 @@
export * from './module'; export * from './module';
import './main'; import './main';
import './index/';
import './action';
import './basic-data';
import './card';
import './detail';
import './descriptor'; import './descriptor';
import './development';
import './search-panel';
import './summary';
import './photos';
import './log';
import './note/index';
import './note/create';

View File

@ -1,83 +0,0 @@
<vn-auto-search
model="model">
</vn-auto-search>
<vn-card>
<smart-table
model="model"
options="$ctrl.smartTableOptions"
expr-builder="$ctrl.exprBuilder(param, value)">
<slot-table>
<table>
<thead>
<tr>
<th field="clientFk" shrink>
<span translate>Id</span>
</th>
<th field="clientName">
<span translate>Client</span>
</th>
<th field="created" center shrink-date>
<span translate>Created</span>
</th>
<th field="workerFk">
<span translate>Worker</span>
</th>
<th field="claimStateFk">
<span translate>State</span>
</th>
<th></th>
</tr>
</thead>
<tbody>
<tr
ng-repeat="claim in model.data"
vn-anchor="::{
state: 'claim.card.summary',
params: {id: claim.id}
}">
<td>{{::claim.id}}</td>
<td>
<span
vn-click-stop="clientDescriptor.show($event, claim.clientFk)"
class="link">
{{::claim.clientName}}
</span>
</td>
<td center shrink-date>{{::claim.created | date:'dd/MM/yyyy'}}</td>
<td>
<span
vn-click-stop="workerDescriptor.show($event, claim.workerFk)"
class="link" >
{{::claim.workerName}}
</span>
</td>
<td>
<span class="chip {{::$ctrl.stateColor(claim.stateCode)}}">
{{::claim.stateDescription}}
</span>
</td>
<td shrink>
<vn-icon-button
vn-click-stop="$ctrl.preview(claim)"
vn-tooltip="Preview"
icon="preview">
</vn-icon-button>
</td>
</tr>
</tbody>
</table>
</slot-table>
</smart-table>
</vn-card>
<vn-client-descriptor-popover
vn-id="clientDescriptor">
</vn-client-descriptor-popover>
<vn-worker-descriptor-popover
vn-id="workerDescriptor">
</vn-worker-descriptor-popover>
<vn-popup vn-id="summary">
<vn-claim-summary
claim="$ctrl.claimSelected"
parent-reload="$ctrl.reload()">
</vn-claim-summary>
</vn-popup>

View File

@ -1,82 +0,0 @@
import ngModule from '../module';
import Section from 'salix/components/section';
class Controller extends Section {
constructor($element, $) {
super($element, $);
this.smartTableOptions = {
activeButtons: {
search: true
},
columns: [
{
field: 'clientName',
autocomplete: {
url: 'Clients',
showField: 'name',
valueField: 'name'
}
},
{
field: 'workerFk',
autocomplete: {
url: 'Workers/activeWithInheritedRole',
where: `{role: 'salesPerson'}`,
searchFunction: '{firstName: $search}',
showField: 'name',
valueField: 'id',
}
},
{
field: 'claimStateFk',
autocomplete: {
url: 'ClaimStates',
showField: 'description',
valueField: 'id',
}
},
{
field: 'created',
searchable: false
}
]
};
}
exprBuilder(param, value) {
switch (param) {
case 'clientName':
return {'cl.clientName': {like: `%${value}%`}};
case 'clientFk':
case 'claimStateFk':
case 'workerFk':
return {[`cl.${param}`]: value};
}
}
stateColor(code) {
switch (code) {
case 'pending':
return 'warning';
case 'managed':
return 'notice';
case 'resolved':
return 'success';
}
}
preview(claim) {
this.claimSelected = claim;
this.$.summary.show();
}
reload() {
this.$.model.refresh();
}
}
ngModule.vnComponent('vnClaimIndex', {
template: require('./index.html'),
controller: Controller
});

View File

@ -1,4 +0,0 @@
<vn-log
url="ClaimLogs"
origin-id="$ctrl.$params.id">
</vn-log>

View File

@ -1,7 +0,0 @@
import ngModule from '../module';
import Section from 'salix/components/section';
ngModule.vnComponent('vnClaimLog', {
template: require('./index.html'),
controller: Section,
});

View File

@ -1,19 +0,0 @@
<vn-crud-model
vn-id="model"
url="Claims/filter"
limit="20"
order="priority ASC, created DESC"
auto-load="true">
</vn-crud-model>
<vn-portal slot="topbar">
<vn-searchbar
vn-focus
panel="vn-claim-search-panel"
info="Search claim by id or client name"
model="model">
</vn-searchbar>
</vn-portal>
<vn-portal slot="menu">
<vn-left-menu></vn-left-menu>
</vn-portal>
<ui-view></ui-view>

View File

@ -1,7 +1,18 @@
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 Claim extends ModuleMain {
constructor($element, $) {
super($element, $);
}
async $onInit() {
this.$state.go('home');
window.location.href = await this.vnApp.getUrl(`Claim/`);
}
}
ngModule.vnComponent('vnClaim', { ngModule.vnComponent('vnClaim', {
controller: ModuleMain, controller: Claim,
template: require('./index.html') template: require('./index.html')
}); });

View File

@ -1,30 +0,0 @@
<vn-watcher
vn-id="watcher"
url="claimObservations"
id-field="id"
data="$ctrl.note"
insert-mode="true"
form="form">
</vn-watcher>
<form name="form" ng-submit="watcher.submitGo('claim.card.note.index')" class="vn-w-md">
<vn-card class="vn-pa-lg">
<vn-horizontal>
<vn-textarea
vn-one
label="Note"
ng-model="$ctrl.note.text"
vn-focus>
</vn-textarea>
</vn-horizontal>
</vn-card>
<vn-button-bar>
<vn-submit
ng-if="watcher.dataChanged()"
label="Save">
</vn-submit>
<vn-button
ng-click="$ctrl.cancel()"
label="Cancel">
</vn-button>
</vn-button-bar>
</form>

View File

@ -1,22 +0,0 @@
import ngModule from '../../module';
import Section from 'salix/components/section';
export default class Controller extends Section {
constructor($element, $) {
super($element, $);
this.note = {
claimFk: parseInt(this.$params.id),
workerFk: window.localStorage.currentUserWorkerId,
text: null
};
}
cancel() {
this.$state.go('claim.card.note.index', {id: this.$params.id});
}
}
ngModule.vnComponent('vnClaimNoteCreate', {
template: require('./index.html'),
controller: Controller
});

View File

@ -1,32 +0,0 @@
<vn-crud-model
vn-id="model"
url="ClaimObservations"
filter="$ctrl.filter"
where="{claimFk: $ctrl.$params.id}"
include="$ctrl.include"
data="notes"
auto-load="true">
</vn-crud-model>
<vn-data-viewer
model="model"
class="vn-w-md">
<vn-card class="vn-pa-md">
<div
ng-repeat="note in notes"
class="note vn-pa-sm border-solid border-radius vn-mb-md">
<vn-horizontal class="vn-mb-sm" style="color: #666">
<vn-one>{{::note.worker.firstName}} {{::note.worker.lastName}}</vn-one>
<vn-auto>{{::note.created | date:'dd/MM/yyyy HH:mm'}}</vn-auto>
</vn-horizontal>
<vn-horizontal class="text">
{{::note.text}}
</vn-horizontal>
</div>
</vn-card>
</vn-data-viewer>
<a vn-tooltip="New note"
ui-sref="claim.card.note.create({id: $ctrl.$params.id})"
vn-bind="+"
fixed-bottom-right>
<vn-float-button icon="add"></vn-float-button>
</a>

View File

@ -1,25 +0,0 @@
import ngModule from '../../module';
import Section from 'salix/components/section';
import './style.scss';
export default class Controller extends Section {
constructor($element, $) {
super($element, $);
this.filter = {
order: 'created DESC',
};
this.include = {
relation: 'worker',
scope: {
fields: ['id', 'firstName', 'lastName']
}
};
}
}
Controller.$inject = ['$element', '$scope'];
ngModule.vnComponent('vnClaimNote', {
template: require('./index.html'),
controller: Controller,
});

View File

@ -1,5 +0,0 @@
vn-client-note {
.note:last-child {
margin-bottom: 0;
}
}

View File

@ -1 +0,0 @@

View File

@ -1,21 +0,0 @@
import ngModule from '../module';
import Section from 'salix/components/section';
class Controller extends Section {
constructor($element, $) {
super($element, $);
}
async $onInit() {
const url = await this.vnApp.getUrl(`claim/${this.$params.id}/photos`);
window.location.href = url;
}
}
ngModule.vnComponent('vnClaimPhotos', {
template: require('./index.html'),
controller: Controller,
bindings: {
claim: '<'
}
});

View File

@ -1,84 +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 claim by id or client name"
vn-focus>
</vn-textfield>
</vn-horizontal>
<vn-horizontal>
<vn-textfield
vn-one
label="Client Id"
ng-model="filter.clientFk">
</vn-textfield>
<vn-textfield
vn-one
label="Client"
ng-model="filter.clientName">
</vn-textfield>
</vn-horizontal>
<vn-horizontal>
<vn-worker-autocomplete
vn-one
ng-model="filter.salesPersonFk"
departments="['VT']"
label="Salesperson">
</vn-worker-autocomplete>
<vn-worker-autocomplete
vn-one
ng-model="filter.attenderFk"
departments="['VT']"
label="Attended by">
</vn-worker-autocomplete>
</vn-horizontal>
<vn-horizontal>
<vn-autocomplete vn-one
label="State"
ng-model="filter.claimStateFk"
url="ClaimStates"
show-field="description"
value-field="id">
<tpl-item>{{description}}</tpl-item>
</vn-autocomplete>
<vn-date-picker
vn-one
label="Created"
ng-model="filter.created">
</vn-date-picker>
</vn-horizontal>
<vn-horizontal>
<vn-autocomplete vn-one class="dense"
label="Item"
url="Items/withName"
ng-model="filter.itemFk"
show-field="name"
value-field="id"
search-function="$ctrl.itemSearchFunc($search)"
order="id DESC">
<tpl-item>{{::id}} - {{::name}}</tpl-item>
</vn-autocomplete>
<vn-autocomplete
vn-one
ng-model="filter.claimResponsibleFk"
url="ClaimResponsibles"
show-field="description"
value-field="id"
label="Responsible">
</vn-autocomplete>
</vn-horizontal>
<vn-horizontal>
<vn-check
vn-one
label="My team"
ng-model="filter.myTeam"
triple-state="true">
</vn-horizontal>
<vn-horizontal class="vn-mt-lg">
<vn-submit label="Search"></vn-submit>
</vn-horizontal>
</form>
</div>

View File

@ -1,14 +0,0 @@
import ngModule from '../module';
import SearchPanel from 'core/components/searchbar/search-panel';
class Controller extends SearchPanel {
itemSearchFunc($search) {
return /^\d+$/.test($search)
? {id: $search}
: {name: {like: '%' + $search + '%'}};
}
}
ngModule.vnComponent('vnClaimSearchPanel', {
template: require('./index.html'),
controller: Controller
});

View File

@ -1,7 +0,0 @@
Ticket id: Id ticket
Client id: Id cliente
Nickname: Alias
From: Desde
To: Hasta
Agency: Agencia
Warehouse: Almacén

View File

@ -1,273 +0,0 @@
<vn-crud-model
vn-id="model"
url="ClaimDms"
filter="::$ctrl.filter"
data="photos">
</vn-crud-model>
<vn-card class="summary">
<h5>
<a
ng-if="::$ctrl.summary.claim.id"
vn-tooltip="Go to the claim"
ui-sref="claim.card.summary({id: {{::$ctrl.summary.claim.id}}})"
name="goToSummary">
<vn-icon-button icon="launch"></vn-icon-button>
</a>
<span>{{::$ctrl.summary.claim.id}} - {{::$ctrl.summary.claim.client.name}}</span>
<vn-button-menu
disabled="!$ctrl.summary.isEditable"
class="message"
label="Change state"
value-field="id"
show-field="description"
url="claimStates"
on-change="$ctrl.changeState(value)">
</vn-button-menu>
</h5>
<vn-horizontal>
<vn-auto>
<h4>
<a
ui-sref="claim.card.basicData({id:$ctrl.claim.id})"
target="_self">
<span translate vn-tooltip="Go to">Basic data</span>
</a>
</h4>
<vn-label-value
label="Created"
value="{{$ctrl.summary.claim.created | date: 'dd/MM/yyyy'}}">
</vn-label-value>
<vn-label-value
label="State"
value="{{$ctrl.summary.claim.claimState.description}}">
</vn-label-value>
<vn-label-value
label="Salesperson"
value="{{$ctrl.summary.claim.client.salesPersonUser.name}}">
</vn-label-value>
<vn-label-value
label="Attended by"
value="{{$ctrl.summary.claim.worker.user.nickname}}">
</vn-label-value>
</vn-auto>
<vn-auto>
<h4 ng-show="$ctrl.isSalesPerson && $ctrl.summary.observations.length">
<a
ui-sref="claim.card.note.index({id:$ctrl.claim.id})"
target="_self">
<span translate vn-tooltip="Go to">Observations</span>
</a>
</h4>
<h4
ng-show="!$ctrl.isSalesPerson && $ctrl.summary.observations.length"
translate>
Observations
</h4>
<div
ng-repeat="note in $ctrl.summary.observations"
class="note vn-pa-sm border-solid border-radius vn-mb-md">
<vn-horizontal class="vn-mb-sm" style="color: #666">
<vn-one>{{::note.worker.firstName}} {{::note.worker.lastName}}</vn-one>
<vn-auto>{{::note.created | date:'dd/MM/yyyy HH:mm'}}</vn-auto>
</vn-horizontal>
<vn-horizontal class="text">
{{::note.text}}
</vn-horizontal>
</div>
</vn-auto>
<vn-auto>
<h4 ng-show="$ctrl.isSalesPerson">
<a
ui-sref="claim.card.detail({id:$ctrl.claim.id})"
target="_self">
<span translate vn-tooltip="Go to">Detail</span>
</a>
</h4>
<h4
ng-show="!$ctrl.isSalesPerson"
translate>
Detail
</h4>
<vn-data-viewer data="::$ctrl.summary.salesClaimed">
<vn-table>
<vn-thead>
<vn-tr>
<vn-th number>Item</vn-th>
<vn-th expand>Landed</vn-th>
<vn-th number>Quantity</vn-th>
<vn-th number>Claimed</vn-th>
<vn-th>Description</vn-th>
<vn-th number>Price</vn-th>
<vn-th number>Disc.</vn-th>
<vn-th number>Total</vn-th>
</vn-tr>
</vn-thead>
<vn-tbody>
<vn-tr ng-repeat="saleClaimed in $ctrl.summary.salesClaimed">
<vn-td number>
<span
ng-click="itemDescriptor.show($event, saleClaimed.sale.itemFk, saleClaimed.sale.id)"
class="link">
{{::saleClaimed.sale.itemFk}}
</span>
</vn-td>
<vn-td expand>{{::saleClaimed.sale.ticket.landed | date: 'dd/MM/yyyy'}}</vn-td>
<vn-td number>{{::saleClaimed.sale.quantity}}</vn-td>
<vn-td number>{{::saleClaimed.quantity}}</vn-td>
<vn-td expand>{{::saleClaimed.sale.concept}}</vn-td>
<vn-td number>{{::saleClaimed.sale.price | currency: 'EUR':2}}</vn-td>
<vn-td number>{{::saleClaimed.sale.discount}} %</vn-td>
<vn-td number>
{{saleClaimed.sale.quantity * saleClaimed.sale.price *
((100 - saleClaimed.sale.discount) / 100) | currency: 'EUR':2}}
</vn-td>
</vn-tr>
</vn-tbody>
</vn-table>
</vn-data-viewer>
</vn-auto>
<vn-auto ng-if="photos.length > 0">
<h4 translate>Photos</h4>
<vn-horizontal class="photo-list">
<section class="photo" ng-repeat="photo in photos">
<section class="image" on-error-src
ng-style="{'background': 'url(' + $ctrl.getImagePath(photo.dmsFk) + ')'}"
zoom-image="{{$ctrl.getImagePath(photo.dmsFk)}}"
ng-if="photo.dms.contentType != 'video/mp4'">
</section>
<video id="videobcg" muted="muted" controls ng-if="photo.dms.contentType == 'video/mp4'"
class="video">
<source src="{{$ctrl.getImagePath(photo.dmsFk)}}" type="video/mp4">
</video>
</section>
</vn-horizontal>
</vn-auto>
<vn-auto>
<h4 ng-show="$ctrl.isClaimManager">
<a
ui-sref="claim.card.development({id:$ctrl.claim.id})"
target="_self">
<span translate vn-tooltip="Go to">Development</span>
</a>
</h4>
<h4
translate
ng-show="!$ctrl.isClaimManager">
Development
</h4>
<vn-data-viewer data="::$ctrl.summary.developments">
<vn-table>
<vn-thead>
<vn-tr>
<vn-th>Reason</vn-th>
<vn-th>Result</vn-th>
<vn-th>Responsible</vn-th>
<vn-th>Worker</vn-th>
<vn-th>Redelivery</vn-th>
</vn-tr>
</vn-thead>
<vn-tbody>
<vn-tr ng-repeat="development in $ctrl.summary.developments">
<vn-td>{{::development.claimReason.description}}</vn-td>
<vn-td>{{::development.claimResult.description}}</vn-td>
<vn-td>{{::development.claimResponsible.description}}</vn-td>
<vn-td expand>
<span
class="link"
ng-click="workerDescriptor.show($event, development.workerFk)">
{{::development.worker.user.nickname}}
</span>
</vn-td>
<vn-td>{{::development.claimRedelivery.description}}</vn-td>
</vn-tr>
</vn-tbody>
</vn-table>
</vn-data-viewer>
</vn-auto>
<vn-auto>
<h4 ng-show="$ctrl.isClaimManager">
<a
ui-sref="claim.card.action({id:$ctrl.claim.id})"
target="_self">
<span translate vn-tooltip="Go to">Action</span>
</a>
</h4>
<h4
translate
ng-show="!$ctrl.isClaimManager">
Action
</h4>
<vn-horizontal>
<vn-one>
<vn-range
vn-one
disabled="true"
label="Responsability"
min-label="Company"
max-label="Sales/Client"
ng-model="$ctrl.summary.claim.responsibility"
max="5"
min="1"
step="1"
vn-acl="claimManager">
</vn-range>
</vn-one>
</vn-horizontal>
<vn-data-viewer data="::$ctrl.summary.actions">
<vn-table>
<vn-thead>
<vn-tr>
<vn-th number>Item</vn-th>
<vn-th number>Ticket</vn-th>
<vn-th>Destination</vn-th>
<vn-th number>Landed</vn-th>
<vn-th number>Quantity</vn-th>
<vn-th>Description</vn-th>
<vn-th number>Price</vn-th>
<vn-th number>Disc.</vn-th>
<vn-th number>Total</vn-th>
</vn-tr>
</vn-thead>
<vn-tbody>
<vn-tr ng-repeat="action in $ctrl.summary.actions">
<vn-td number>
<span
ng-click="itemDescriptor.show($event, action.sale.itemFk, action.sale.id)"
class="link">
{{::action.sale.itemFk}}
</span>
</vn-td>
<vn-td number>
<span
ng-click="ticketDescriptor.show($event, action.sale.ticket.id)"
class="link">
{{::action.sale.ticket.id}}
</span>
</vn-td>
<vn-td expand>{{::action.claimBeggining.description}}</vn-td>
<vn-td number>{{::action.sale.ticket.landed | date: 'dd/MM/yyyy'}}</vn-td>
<vn-td number>{{::action.sale.quantity}}</vn-td>
<vn-td expand>{{::action.sale.concept}}</vn-td>
<vn-td number>{{::action.sale.price}}</vn-td>
<vn-td number>{{::action.sale.discount}} %</vn-td>
<vn-td number>
{{action.sale.quantity * action.sale.price *
((100 - action.sale.discount) / 100) | currency: 'EUR':2}}
</vn-td>
</vn-tr>
</vn-tbody>
</vn-table>
</vn-data-viewer>
</vn-auto>
</vn-horizontal>
</vn-card>
<vn-item-descriptor-popover
vn-id="item-descriptor"
warehouse-fk="$ctrl.vnConfig.warehouseFk">
</vn-item-descriptor-popover>
<vn-worker-descriptor-popover
vn-id="worker-descriptor">
</vn-worker-descriptor-popover>
<vn-ticket-descriptor-popover
vn-id="ticket-descriptor">
</vn-ticket-descriptor-popover>

View File

@ -1,103 +0,0 @@
import ngModule from '../module';
import Summary from 'salix/components/summary';
import './style.scss';
class Controller extends Summary {
constructor($element, $, vnFile) {
super($element, $);
this.vnFile = vnFile;
this.filter = {
include: [
{
relation: 'dms'
}
]
};
}
$onChanges() {
if (this.claim && this.claim.id)
this.loadData();
}
loadData() {
return this.$http.get(`Claims/${this.claim.id}/getSummary`).then(res => {
if (res && res.data)
this.summary = res.data;
});
}
reload() {
this.loadData()
.then(() => {
if (this.card)
this.card.reload();
if (this.parentReload)
this.parentReload();
});
}
get isSalesPerson() {
return this.aclService.hasAny(['salesPerson']);
}
get isClaimManager() {
return this.aclService.hasAny(['claimManager']);
}
get claim() {
return this._claim;
}
set claim(value) {
this._claim = value;
// Get DMS on summary load
if (value) {
this.$.$applyAsync(() => this.loadDms());
this.loadData();
}
}
loadDms() {
this.$.model.where = {
claimFk: this.claim.id
};
this.$.model.refresh();
}
getImagePath(dmsId) {
return this.vnFile.getPath(`/api/dms/${dmsId}/downloadFile`);
}
changeState(value) {
const params = {
id: this.claim.id,
claimStateFk: value
};
this.$http.patch(`Claims/updateClaim/${this.claim.id}`, params)
.then(() => {
this.reload();
})
.then(() => {
this.vnApp.showSuccess(this.$t('Data saved!'));
});
}
}
Controller.$inject = ['$element', '$scope', 'vnFile'];
ngModule.vnComponent('vnClaimSummary', {
template: require('./index.html'),
controller: Controller,
bindings: {
claim: '<',
model: '<?',
parentReload: '&'
},
require: {
card: '?^vnClaimCard'
}
});

View File

@ -1,55 +0,0 @@
import './index.js';
import crudModel from 'core/mocks/crud-model';
describe('Claim', () => {
describe('Component summary', () => {
let controller;
let $httpBackend;
let $scope;
beforeEach(ngModule('claim'));
beforeEach(inject(($componentController, _$httpBackend_, $rootScope) => {
$scope = $rootScope.$new();
$httpBackend = _$httpBackend_;
const $element = angular.element('<vn-claim-summary></vn-claim-summary>');
controller = $componentController('vnClaimSummary', {$element, $scope});
controller.claim = {id: 1};
controller.$.model = crudModel;
}));
describe('loadData()', () => {
it('should perform a query to set summary', () => {
$httpBackend.when('GET', `Claims/1/getSummary`).respond(200, 24);
controller.loadData();
$httpBackend.flush();
expect(controller.summary).toEqual(24);
});
});
describe('changeState()', () => {
it('should make an HTTP post query, then call the showSuccess()', () => {
jest.spyOn(controller.vnApp, 'showSuccess').mockReturnThis();
const expectedParams = {id: 1, claimStateFk: 1};
$httpBackend.when('GET', `Claims/1/getSummary`).respond(200, 24);
$httpBackend.expect('PATCH', `Claims/updateClaim/1`, expectedParams).respond(200);
controller.changeState(1);
$httpBackend.flush();
expect(controller.vnApp.showSuccess).toHaveBeenCalled();
});
});
describe('$onChanges()', () => {
it('should call loadData when $onChanges is called', () => {
jest.spyOn(controller, 'loadData');
controller.$onChanges();
expect(controller.loadData).toHaveBeenCalledWith();
});
});
});
});

View File

@ -1,28 +0,0 @@
@import "./variables";
vn-claim-summary {
section.photo {
height: 248px;
}
.photo .image {
border-radius: 3px;
}
vn-textarea *{
height: 80px;
}
.video {
width: 100%;
height: 100%;
object-fit: cover;
cursor: pointer;
box-shadow: 0 2px 2px 0 rgba(0,0,0,.14),
0 3px 1px -2px rgba(0,0,0,.2),
0 1px 5px 0 rgba(0,0,0,.12);
border: 2px solid transparent;
}
.video:hover {
border: 2px solid $color-primary
}
}

View File

@ -81,7 +81,7 @@
</vn-check> </vn-check>
</vn-td> </vn-td>
<vn-td shrink> <vn-td shrink>
<a ui-sref="claim.card.basicData({id: sale.claim.claimFk})"> <a ng-click="$ctrl.goToLilium('basic-data', sale.claim.claimFk)">
<vn-icon icon="icon-claims" <vn-icon icon="icon-claims"
ng-show="sale.claim.claimFk" ng-show="sale.claim.claimFk"
vn-tooltip="{{::$ctrl.$t('Claim')}}: {{::sale.claim.claimFk}}"> vn-tooltip="{{::$ctrl.$t('Claim')}}: {{::sale.claim.claimFk}}">

View File

@ -214,7 +214,7 @@ class Controller extends Section {
const params = {ticketId: this.ticket.id, sales: sales}; const params = {ticketId: this.ticket.id, sales: sales};
this.resetChanges(); this.resetChanges();
this.$http.post(`Claims/createFromSales`, params) this.$http.post(`Claims/createFromSales`, params)
.then(res => this.$state.go('claim.card.basicData', {id: res.data.id})); .then(async res => window.location.href = await this.vnApp.getUrl(`claim/${res.data.id}/basic-data`));
} }
showTransferPopover(event) { showTransferPopover(event) {
@ -558,6 +558,10 @@ class Controller extends Section {
changedModelId: saleId changedModelId: saleId
}); });
} }
async goToLilium(section, id) {
window.location.href = await this.vnApp.getUrl(`claim/${id}/${section}`);
}
} }
ngModule.vnComponent('vnTicketSale', { ngModule.vnComponent('vnTicketSale', {

View File

@ -295,20 +295,26 @@ describe('Ticket', () => {
describe('onCreateClaimAccepted()', () => { describe('onCreateClaimAccepted()', () => {
it('should perform a query and call window open', () => { it('should perform a query and call window open', () => {
jest.spyOn(controller, 'resetChanges').mockReturnThis(); jest.spyOn(controller, 'resetChanges').mockReturnThis();
jest.spyOn(controller.$state, 'go').mockReturnThis(); jest.spyOn(controller.vnApp, 'getUrl').mockReturnThis();
Object.defineProperty(window, 'location', {
value: {
href: () => {}
},
});
jest.spyOn(controller.window.location, 'href');
const newEmptySale = {quantity: 10}; const newEmptySale = {quantity: 10};
controller.sales.push(newEmptySale); controller.sales.push(newEmptySale);
const firstSale = controller.sales[0]; const firstSale = controller.sales[0];
const claimId = 1;
firstSale.checked = true; firstSale.checked = true;
const expectedParams = {ticketId: 1, sales: [firstSale]}; const expectedParams = {ticketId: 1, sales: [firstSale]};
$httpBackend.expect('POST', `Claims/createFromSales`, expectedParams).respond(200, {id: 1}); $httpBackend.expect('POST', `Claims/createFromSales`, expectedParams).respond(200, {id: claimId});
controller.onCreateClaimAccepted(); controller.onCreateClaimAccepted();
$httpBackend.flush(); $httpBackend.flush();
expect(controller.resetChanges).toHaveBeenCalledWith(); expect(controller.resetChanges).toHaveBeenCalledWith();
expect(controller.$state.go).toHaveBeenCalledWith('claim.card.basicData', {id: 1});
}); });
}); });

View File

@ -152,13 +152,13 @@
<vn-tbody> <vn-tbody>
<vn-tr ng-repeat="sale in $ctrl.summary.sales track by sale.id"> <vn-tr ng-repeat="sale in $ctrl.summary.sales track by sale.id">
<vn-td shrink> <vn-td shrink>
<a ui-sref="claim.card.basicData({id: sale.claim.claimFk})"> <a ng-click="$ctrl.goToLilium('basic-data', sale.claim.claimFk)">
<vn-icon icon="icon-claims" <vn-icon icon="icon-claims"
ng-show="sale.claim.claimFk" ng-show="sale.claim.claimFk"
vn-tooltip="{{::$ctrl.$t('Claim')}}: {{::sale.claim.claimFk}}"> vn-tooltip="{{::$ctrl.$t('Claim')}}: {{::sale.claim.claimFk}}">
</vn-icon> </vn-icon>
</a> </a>
<a ui-sref="claim.card.basicData({id: sale.claimBeginning.claimFk})"> <a ng-click="$ctrl.goToLilium('basic-data', sale.claimBeginning.claimFk)">
<vn-icon <vn-icon
ng-show="sale.claimBeginning.claimFk" ng-show="sale.claimBeginning.claimFk"
icon="icon-claims" icon="icon-claims"

View File

@ -83,6 +83,10 @@ class Controller extends Summary {
return 'Acepted'; return 'Acepted';
} }
} }
async goToLilium(section, id) {
window.location.href = await this.vnApp.getUrl(`claim/${id}/${section}`);
}
} }
ngModule.vnComponent('vnTicketSummary', { ngModule.vnComponent('vnTicketSummary', {