diff --git a/db/dump/fixtures.sql b/db/dump/fixtures.sql
index 331d115a3..a7d22a811 100644
--- a/db/dump/fixtures.sql
+++ b/db/dump/fixtures.sql
@@ -101,6 +101,16 @@ INSERT INTO `vn`.`sector`(`id`, `description`, `warehouseFk`, `isPreviousPrepare
(1, 'First sector', 1, 1, 'FIRST', 999, 999),
(2, 'Second sector', 2, 0, 'SECOND', 100, 150);
+INSERT INTO `vn`.`parking` (`id`, `column`, `row`, `sectorFk`, `code`, `pickingOrder`)
+ VALUES
+ ('1', '700', '01', '1', '700-01', '70001'),
+ ('2', '700', '02', '2', '700-02', '70002');
+
+INSERT INTO `vn`.`shelving` (`code`, `parkingFk`, `isPrinted`, `priority`, `parked`, `userFk`)
+ VALUES
+ ('GVC', '1', '0', '1', '0', '106'),
+ ('HEJ', '2', '0', '1', '0', '106');
+
INSERT INTO `vn`.`warehouseAlias`(`id`, `name`)
VALUES
(1, 'Main Warehouse');
@@ -928,6 +938,18 @@ INSERT INTO `vn`.`saleComponent`(`saleFk`, `componentFk`, `value`)
(32, 36, -92.324),
(32, 39, 0.994);
+INSERT INTO `vn`.`itemShelving` (`id`, `itemFk`, `shelvingFk`, `shelve`, `deep`, `quantity`, `visible`, `available`, `grouping`, `packing`, `level`, `userFk`)
+ VALUES
+ ('1', '2', 'GVC', 'A', '0', '1', '1', '1', '1', '1', '1', '106'),
+ ('2', '4', 'HEJ', 'A', '0', '2', '1', '1', '1', '1', '1', '106');
+
+INSERT INTO `vn`.`itemShelvingSale` (`itemShelvingFk`, `saleFk`, `quantity`, `created`, `userFk`)
+ VALUES
+ ('1', '1', '1', '', '106'),
+ ('2', '2', '5', '', '106'),
+ ('1', '7', '1', '', '106'),
+ ('2', '8', '5', '', '106');
+
INSERT INTO `vncontrol`.`accion`(`accion_id`, `accion`)
VALUES
(3, 'ACTION ONE'),
diff --git a/e2e/helpers/selectors.js b/e2e/helpers/selectors.js
index 5469c09df..94d11887c 100644
--- a/e2e/helpers/selectors.js
+++ b/e2e/helpers/selectors.js
@@ -367,6 +367,13 @@ export default {
advancedSearchInvoiceOut: 'vn-ticket-search-panel vn-textfield[ng-model="filter.refFk"]',
newTicketButton: 'vn-ticket-index a',
searchResult: 'vn-ticket-index vn-card > vn-table > div > vn-tbody > a.vn-tr',
+ secondTicketCheckbox: 'vn-ticket-index vn-tbody > a:nth-child(2) > vn-td:nth-child(1) > vn-check',
+ thirdTicketCheckbox: 'vn-ticket-index vn-tbody > a:nth-child(3) > vn-td:nth-child(1) > vn-check',
+ sixthTicketCheckbox: 'vn-ticket-index vn-tbody > a:nth-child(6) > vn-td:nth-child(1) > vn-check',
+ payoutButton: 'vn-ticket-index vn-button[icon="icon-recovery"]',
+ payoutCompany: '.vn-dialog vn-autocomplete[ng-model="$ctrl.receipt.companyFk"]',
+ payoutBank: '.vn-dialog vn-autocomplete[ng-model="$ctrl.receipt.bankFk"]',
+ submitPayout: '.vn-dialog vn-button[label="Save"]',
searchWeeklyResult: 'vn-ticket-weekly-index vn-table vn-tbody > vn-tr',
searchResultDate: 'vn-ticket-summary [label=Landed] span',
topbarSearch: 'vn-searchbar',
diff --git a/e2e/paths/05-ticket/18_index_payout.spec.js b/e2e/paths/05-ticket/18_index_payout.spec.js
new file mode 100644
index 000000000..749428c44
--- /dev/null
+++ b/e2e/paths/05-ticket/18_index_payout.spec.js
@@ -0,0 +1,60 @@
+import selectors from '../../helpers/selectors.js';
+import getBrowser from '../../helpers/puppeteer';
+
+describe('Ticket index payout path', () => {
+ let browser;
+ let page;
+
+ beforeAll(async() => {
+ browser = await getBrowser();
+ page = browser.page;
+ });
+
+ afterAll(async() => {
+ await browser.close();
+ });
+
+ it('should navigate to the ticket index', async() => {
+ await page.loginAndModule('administrative', 'ticket');
+ let url = await page.expectURL('#!/ticket/index');
+
+ expect(url).toBe(true);
+ });
+
+ it('should check three tickets 2 of a clinet and 1 of another', async() => {
+ await page.keyboard.press('Enter');
+ await page.waitToClick(selectors.ticketsIndex.secondTicketCheckbox);
+ await page.waitToClick(selectors.ticketsIndex.sixthTicketCheckbox);
+ await page.waitToClick(selectors.ticketsIndex.payoutButton);
+ const result = await page.waitForLastSnackbar();
+
+ expect(result).toEqual('You cannot make a payment on account from multiple clients');
+ });
+
+ it('should uncheck the sixth ticket result and check the third which is from the same client then open the payout form', async() => {
+ await page.waitToClick(selectors.ticketsIndex.sixthTicketCheckbox);
+ await page.waitToClick(selectors.ticketsIndex.thirdTicketCheckbox);
+ await page.waitToClick(selectors.ticketsIndex.payoutButton);
+
+ await page.waitForSelector(selectors.ticketsIndex.payoutCompany);
+ });
+
+ it('should fill the company and bank to perform a payout', async() => {
+ await page.autocompleteSearch(selectors.ticketsIndex.payoutBank, 'cash');
+ await page.waitToClick(selectors.ticketsIndex.submitPayout);
+ const result = await page.waitForLastSnackbar();
+
+ expect(result).toEqual('Data saved!');
+ });
+
+ it('should navigate to the client balance section and check a new balance line was entered', async() => {
+ await page.waitToClick(selectors.globalItems.homeButton);
+ await page.selectModule('client');
+ await page.accessToSearchResult('101');
+ await page.accessToSection('client.card.balance.index');
+ await page.waitForSelector('vn-client-balance-index vn-tbody > vn-tr');
+ let result = await page.countElement('vn-client-balance-index vn-tbody > vn-tr');
+
+ expect(result).toEqual(4);
+ });
+});
diff --git a/e2e/paths/07-order/02_basic_data.spec.js b/e2e/paths/07-order/02_basic_data.spec.js
index d7bd01208..2c3292b61 100644
--- a/e2e/paths/07-order/02_basic_data.spec.js
+++ b/e2e/paths/07-order/02_basic_data.spec.js
@@ -81,7 +81,7 @@ describe('Order edit basic data path', () => {
await page.waitToClick(selectors.orderBasicData.saveButton);
const result = await page.waitForLastSnackbar();
- expect(result).toEqual('Data saved!');
+ expect(result).toContain('Data saved!');
});
it('should now confirm the client have been edited', async() => {
diff --git a/e2e/paths/07-order/04_catalog.spec.js b/e2e/paths/07-order/04_catalog.spec.js
index 0db313088..34fdbbec0 100644
--- a/e2e/paths/07-order/04_catalog.spec.js
+++ b/e2e/paths/07-order/04_catalog.spec.js
@@ -69,8 +69,7 @@ describe('Order catalog', () => {
});
it('should search for an item by id', async() => {
- await page.write(selectors.orderCatalog.itemId, '2');
- await page.keyboard.press('Enter');
+ await page.accessToSearchResult('2');
await page.waitForNumberOfElements('section.product', 1);
const result = await page.countElement('section.product');
diff --git a/modules/item/back/model-config.json b/modules/item/back/model-config.json
index d8ec5914a..c085e075a 100644
--- a/modules/item/back/model-config.json
+++ b/modules/item/back/model-config.json
@@ -44,6 +44,9 @@
"ItemTypeTag": {
"dataSource": "vn"
},
+ "ItemShelving": {
+ "dataSource": "vn"
+ },
"ItemShelvingSale": {
"dataSource": "vn"
},
diff --git a/modules/item/back/models/item-shelving-sale.json b/modules/item/back/models/item-shelving-sale.json
index 547c882a0..04f505ddd 100644
--- a/modules/item/back/models/item-shelving-sale.json
+++ b/modules/item/back/models/item-shelving-sale.json
@@ -25,6 +25,11 @@
"model": "Sale",
"foreignKey": "saleFk"
},
+ "itemShelving": {
+ "type": "belongsTo",
+ "model": "ItemShelving",
+ "foreignKey": "itemShelvingFk"
+ },
"user": {
"type": "belongsTo",
"model": "Account",
diff --git a/modules/item/back/models/item-shelving.json b/modules/item/back/models/item-shelving.json
new file mode 100644
index 000000000..0fcc00f7e
--- /dev/null
+++ b/modules/item/back/models/item-shelving.json
@@ -0,0 +1,40 @@
+{
+ "name": "ItemShelving",
+ "base": "VnModel",
+ "options": {
+ "mysql": {
+ "table": "itemShelving"
+ }
+ },
+ "properties": {
+ "id": {
+ "type": "number",
+ "id": true,
+ "description": "Identifier"
+ },
+ "shelve": {
+ "type": "string"
+ },
+ "deep": {
+ "type": "number"
+ },
+ "quantity": {
+ "type": "number"
+ },
+ "created": {
+ "type": "Date"
+ }
+ },
+ "relations": {
+ "item": {
+ "type": "belongsTo",
+ "model": "Item",
+ "foreignKey": "itemFk"
+ },
+ "user": {
+ "type": "belongsTo",
+ "model": "Account",
+ "foreignKey": "userFk"
+ }
+ }
+}
diff --git a/modules/order/front/catalog/index.html b/modules/order/front/catalog/index.html
index 4c8786e13..f9c2c40bd 100644
--- a/modules/order/front/catalog/index.html
+++ b/modules/order/front/catalog/index.html
@@ -11,6 +11,13 @@
limit="50"
data="$ctrl.items">
+
+
+
+
@@ -77,14 +84,6 @@
-
-
-
-
-
-
Id: {{$ctrl.itemId}}
+
+ Name
+ : {{$ctrl.itemName}}
+
{
- if (this.$params.itemId)
- this.itemId = parseInt(this.$params.itemId);
-
if (this.$params.categoryId)
this.categoryId = parseInt(this.$params.categoryId);
@@ -111,17 +108,6 @@ class Controller extends Section {
this.applyFilters();
}
- get itemId() {
- return this._itemId;
- }
-
- set itemId(value) {
- this._itemId = value;
-
- this.updateStateParams();
- this.applyFilters();
- }
-
get tags() {
return this._tags;
}
@@ -195,18 +181,6 @@ class Controller extends Section {
this.itemTypes = res.data);
}
- /**
- * Search by item id filter
- * @param {object} event
- */
- onSearchById(event) {
- const value = this.$.itemId.value;
- if (event.key === 'Enter' && value) {
- this.itemId = value;
- this.$.itemId.value = null;
- }
- }
-
/**
* Search by tag value
* @param {object} event
@@ -230,9 +204,19 @@ class Controller extends Section {
this.applyFilters();
}
- applyFilters() {
+ removeItemId() {
+ this.itemId = null;
+ this.applyFilters();
+ }
+
+ removeItemName() {
+ this.itemName = null;
+ this.applyFilters();
+ }
+
+ applyFilters(filter = {}) {
let newParams = {};
- let newFilter = {};
+ let newFilter = Object.assign({}, filter);
const model = this.$.model;
if (this.categoryId)
@@ -241,16 +225,13 @@ class Controller extends Section {
if (this.typeId)
newFilter.typeFk = this.typeId;
- if (this.itemId)
- newFilter = {'i.id': this.itemId};
-
newParams = {
- orderFk: this.order.id,
+ orderFk: this.$params.id,
orderBy: this.getOrderBy(),
tags: this.tags,
};
- model.applyFilter({where: newFilter}, newParams);
+ return model.applyFilter({where: newFilter}, newParams);
}
openPanel(event) {
@@ -282,10 +263,6 @@ class Controller extends Section {
if (this.typeId)
params.typeId = this.typeId;
- params.itemId = undefined;
- if (this.itemId)
- params.itemId = this.itemId;
-
params.tags = undefined;
if (this.tags.length) {
const tags = [];
@@ -344,6 +321,27 @@ class Controller extends Section {
newFilterList = newFilterList.concat(tags);
this.orderFields = newFilterList;
}
+
+ onSearch(params) {
+ if (!params) return;
+
+ this.itemId = null;
+ this.itemName = null;
+
+ if (params.search) {
+ if (/^\d+$/.test(params.search)) {
+ this.itemId = params.search;
+ return this.applyFilters({
+ 'i.id': params.search
+ });
+ } else {
+ this.itemName = params.search;
+ return this.applyFilters({
+ 'i.name': {like: `%${params.search}%`}
+ });
+ }
+ } else return this.applyFilters();
+ }
}
ngModule.component('vnOrderCatalog', {
diff --git a/modules/order/front/catalog/index.spec.js b/modules/order/front/catalog/index.spec.js
index f01712110..f7635665a 100644
--- a/modules/order/front/catalog/index.spec.js
+++ b/modules/order/front/catalog/index.spec.js
@@ -17,12 +17,15 @@ describe('Order', () => {
$scope.search = {};
$scope.itemId = {};
$state = _$state_;
- $state.params.categoryId = 1;
- $state.params.typeId = 2;
$state.current.name = 'my.current.state';
const $element = angular.element('');
controller = $componentController('vnOrderCatalog', {$element, $scope});
controller._order = {id: 4};
+ controller.$params = {
+ categoryId: 1,
+ typeId: 2,
+ id: 4
+ };
}));
describe('order() setter', () => {
@@ -112,18 +115,6 @@ describe('Order', () => {
});
});
- describe('itemId() setter', () => {
- it(`should set itemId property and then call updateStateParams() and applyFilters() methods`, () => {
- jest.spyOn(controller, 'updateStateParams');
- jest.spyOn(controller, 'applyFilters');
-
- controller.itemId = 1;
-
- expect(controller.updateStateParams).toHaveBeenCalledWith();
- expect(controller.applyFilters).toHaveBeenCalledWith();
- });
- });
-
describe('tags() setter', () => {
it(`should set tags property and then call updateStateParams() and applyFilters() methods`, () => {
jest.spyOn(controller, 'updateStateParams');
@@ -158,23 +149,27 @@ describe('Order', () => {
});
});
- describe('onSearchById()', () => {
- it(`should not filter by id if the event key code doesn't equals to 'Enter'`, () => {
+ describe('onSearch()', () => {
+ it(`should apply a filter by item id an then call the applyFilters method`, () => {
jest.spyOn(controller, 'applyFilters');
- controller.$.itemId.value = 1;
- controller.onSearchById({key: 'Tab'});
+ const itemId = 1;
+ controller.onSearch({search: itemId});
- expect(controller.applyFilters).not.toHaveBeenCalledWith();
+ expect(controller.applyFilters).toHaveBeenCalledWith({
+ 'i.id': itemId
+ });
});
- it(`should filter by id if the event key code equals to 'Enter' an then call applyFilters()`, () => {
+ it(`should apply a filter by item name an then call the applyFilters method`, () => {
jest.spyOn(controller, 'applyFilters');
- controller.$.itemId.value = 1;
- controller.onSearchById({key: 'Enter'});
+ const itemName = 'Bow';
+ controller.onSearch({search: itemName});
- expect(controller.applyFilters).toHaveBeenCalledWith();
+ expect(controller.applyFilters).toHaveBeenCalledWith({
+ 'i.name': {like: `%${itemName}%`}
+ });
});
});
@@ -224,7 +219,6 @@ describe('Order', () => {
controller._categoryId = 2;
controller._typeId = 4;
- controller._itemId = 1;
controller._tags = [
{tagFk: 11, value: 'Precission', tagSelection: {name: 'Category'}}
];
@@ -232,7 +226,7 @@ describe('Order', () => {
value: 'Precission',
tagFk: 11, tagSelection: {name: 'Category'}}
]);
- let result = {categoryId: 2, typeId: 4, itemId: 1, tags: tags};
+ let result = {categoryId: 2, typeId: 4, tags: tags};
controller.updateStateParams();
expect(controller.$state.go).toHaveBeenCalledWith('my.current.state', result);
diff --git a/modules/order/front/catalog/locale/es.yml b/modules/order/front/catalog/locale/es.yml
new file mode 100644
index 000000000..27d16fe2d
--- /dev/null
+++ b/modules/order/front/catalog/locale/es.yml
@@ -0,0 +1,2 @@
+Name: Nombre
+Search by item id or name: Buscar por id de artÃculo o nombre
\ No newline at end of file
diff --git a/modules/order/front/routes.json b/modules/order/front/routes.json
index b607aef9d..eec628b89 100644
--- a/modules/order/front/routes.json
+++ b/modules/order/front/routes.json
@@ -41,7 +41,7 @@
"order": "$ctrl.order"
}
}, {
- "url": "/catalog?categoryId&typeId&itemId&tags",
+ "url": "/catalog?q&categoryId&typeId&tags",
"state": "order.card.catalog",
"component": "vn-order-catalog",
"description": "Catalog",
diff --git a/modules/ticket/back/methods/ticket/setDeleted.js b/modules/ticket/back/methods/ticket/setDeleted.js
index 6daad7c39..0f7e0b57f 100644
--- a/modules/ticket/back/methods/ticket/setDeleted.js
+++ b/modules/ticket/back/methods/ticket/setDeleted.js
@@ -23,6 +23,7 @@ module.exports = Self => {
Self.setDeleted = async(ctx, id) => {
const models = Self.app.models;
+ const userId = ctx.req.accessToken.userId;
const isEditable = await Self.isEditable(ctx, id);
const $t = ctx.req.__; // $translate
@@ -30,16 +31,30 @@ module.exports = Self => {
throw new UserError(`The sales of this ticket can't be modified`);
// Check if has sales with shelving
+ const isSalesAssistant = await models.Account.hasRole(userId, 'salesAssistant');
const sales = await models.Sale.find({
- include: {relation: 'itemShelving'},
+ include: {relation: 'itemShelvingSale'},
where: {ticketFk: id}
});
const hasItemShelvingSales = sales.some(sale => {
- return sale.itemShelving();
+ return sale.itemShelvingSale();
});
- if (hasItemShelvingSales)
+
+ if (hasItemShelvingSales && !isSalesAssistant)
throw new UserError(`You cannot delete a ticket that part of it is being prepared`);
+ if (hasItemShelvingSales && isSalesAssistant) {
+ const promises = [];
+ for (let sale of sales) {
+ if (sale.itemShelvingSale()) {
+ const itemShelvingSale = sale.itemShelvingSale();
+ const destroyedShelving = models.ItemShelvingSale.destroyById(itemShelvingSale.id);
+ promises.push(destroyedShelving);
+ }
+ }
+ await Promise.all(promises);
+ }
+
// Check for existing claim
const claimOfATicket = await models.Claim.findOne({where: {ticketFk: id}});
if (claimOfATicket)
diff --git a/modules/ticket/back/methods/ticket/specs/setDeleted.spec.js b/modules/ticket/back/methods/ticket/specs/setDeleted.spec.js
index 890fc6c45..2713bd700 100644
--- a/modules/ticket/back/methods/ticket/specs/setDeleted.spec.js
+++ b/modules/ticket/back/methods/ticket/specs/setDeleted.spec.js
@@ -1,29 +1,33 @@
const app = require('vn-loopback/server/server');
+const models = app.models;
describe('ticket deleted()', () => {
let ticket;
- let ctx;
+ let sale;
beforeAll(async done => {
- let originalTicket = await app.models.Ticket.findOne({where: {id: 16}});
+ let originalTicket = await models.Ticket.findOne({where: {id: 16}});
originalTicket.id = null;
- ticket = await app.models.Ticket.create(originalTicket);
+ ticket = await models.Ticket.create(originalTicket);
+ sale = await models.Sale.create({
+ ticketFk: ticket.id,
+ itemFk: 4,
+ concept: 'Melee weapon',
+ quantity: 10
+ });
- ctx = {
- req: {
- accessToken: {userId: 106},
- headers: {
- origin: 'http://localhost:5000'
- },
- __: () => {}
- }
- };
+ await models.ItemShelvingSale.create({
+ itemShelvingFk: 1,
+ saleFk: sale.id,
+ quantity: 10,
+ userFk: 106
+ });
done();
});
afterAll(async done => {
- await app.models.Ticket.destroyById(ticket.id);
+ await models.Ticket.destroyById(ticket.id);
done();
});
@@ -32,16 +36,62 @@ describe('ticket deleted()', () => {
expect(ticket.isDeleted).toEqual(false);
});
- it('should set a ticket to deleted', async() => {
+ it('should make sure the ticket sale has an item shelving', async() => {
+ const sales = await models.Sale.find({
+ include: {relation: 'itemShelvingSale'},
+ where: {ticketFk: ticket.id}
+ });
+ const hasItemShelvingSales = sales.some(sale => {
+ return sale.itemShelvingSale();
+ });
+
+ expect(hasItemShelvingSales).toEqual(true);
+ });
+
+ it('should set a ticket to deleted and remove all item shelvings', async() => {
+ const salesAssistantId = 21;
+ const ctx = {
+ req: {
+ accessToken: {userId: salesAssistantId},
+ headers: {
+ origin: 'http://localhost:5000'
+ },
+ __: () => {}
+ }
+ };
await app.models.Ticket.setDeleted(ctx, ticket.id);
- let deletedTicket = await app.models.Ticket.findOne({where: {id: ticket.id}, fields: ['isDeleted']});
+ let deletedTicket = await app.models.Ticket.findOne({
+ where: {id: ticket.id},
+ fields: ['isDeleted']
+ });
expect(deletedTicket.isDeleted).toEqual(true);
});
+ it('should not have any item shelving', async() => {
+ const sales = await models.Sale.find({
+ include: {relation: 'itemShelvingSale'},
+ where: {ticketFk: ticket.id}
+ });
+ const hasItemShelvingSales = sales.some(sale => {
+ return sale.itemShelvingSale();
+ });
+
+ expect(hasItemShelvingSales).toEqual(false);
+ });
+
it('should throw an error if the given ticket has a claim', async() => {
- let ticketId = 16;
+ const ticketId = 16;
+ const ctx = {
+ req: {
+ accessToken: {userId: 106},
+ headers: {
+ origin: 'http://localhost:5000'
+ },
+ __: () => {}
+ }
+ };
let error;
try {
diff --git a/modules/ticket/back/models/sale.json b/modules/ticket/back/models/sale.json
index 1f2ea4bbf..767a3e59e 100644
--- a/modules/ticket/back/models/sale.json
+++ b/modules/ticket/back/models/sale.json
@@ -73,7 +73,7 @@
"model": "SaleTracking",
"foreignKey": "saleFk"
},
- "itemShelving": {
+ "itemShelvingSale": {
"type": "hasOne",
"model": "ItemShelvingSale",
"foreignKey": "saleFk"
diff --git a/modules/ticket/front/index/index.js b/modules/ticket/front/index/index.js
index 7d6163086..4803f40ff 100644
--- a/modules/ticket/front/index/index.js
+++ b/modules/ticket/front/index/index.js
@@ -29,7 +29,7 @@ export default class Controller extends Section {
}
get checked() {
- const tickets = this.$.tickets || [];
+ const tickets = this.$.model.data || [];
const checkedLines = [];
for (let ticket of tickets) {
if (ticket.checked)
diff --git a/modules/ticket/front/index/index.spec.js b/modules/ticket/front/index/index.spec.js
index 1cb57926b..10bd88d3d 100644
--- a/modules/ticket/front/index/index.spec.js
+++ b/modules/ticket/front/index/index.spec.js
@@ -88,7 +88,7 @@ describe('Component vnTicketIndex', () => {
controller.$.balanceCreateDialog = {show: () => {}};
jest.spyOn(controller.$.balanceCreateDialog, 'show').mockReturnThis();
- controller.$.tickets = tickets;
+ controller.$.model = {data: tickets};
controller.$.balanceCreateDialog.amountPaid = 0;
controller.openBalanceDialog();
@@ -102,7 +102,7 @@ describe('Component vnTicketIndex', () => {
describe('checked()', () => {
it('should return an array of checked tickets', () => {
- controller.$.tickets = tickets;
+ controller.$.model = {data: tickets};
const result = controller.checked;
const firstRow = result[0];
const secondRow = result[1];
@@ -115,7 +115,7 @@ describe('Component vnTicketIndex', () => {
describe('totalChecked()', () => {
it('should return the total number of checked tickets', () => {
- controller.$.tickets = tickets;
+ controller.$.model = {data: tickets};
const result = controller.checked;
expect(result.length).toEqual(2);