ionic-tryton-inventory/inventory-list/inventory-list.ts

592 lines
18 KiB
TypeScript

import { Component, Input, ViewChild, ElementRef, Renderer } from '@angular/core';
import { Locker } from 'angular-safeguard';
import { NavController, NavParams, AlertController,
Platform, LoadingController, Events } from 'ionic-angular';
import {TranslateService} from 'ng2-translate';
import { EncodeJSONRead } from '../../ngx-tryton-json/encode-json-read'
import { EncodeJSONWrite } from '../../ngx-tryton-json/encode-json-write'
import { TrytonProvider } from '../../ngx-tryton-providers/tryton-provider'
import { Products } from '../../ngx-tryton-product-interface/products'
import { Inventory, InventoryLines } from '../../ngx-tryton-stock-interface/inventory'
import { MainMenuPage } from '../../../pages/main-menu/main-menu'
@Component({
selector: 'page-inventory-list',
templateUrl: 'inventory-list.html'
})
export class InventoryListPage {
@Input()
itemInput: string = '';
/**
* Barcode value
* @type {string}
*/
barcode: string = '';
@ViewChild('focusInput2') myInput2: ElementRef;
lastItem: string;
/**
* Items to display
* @type {InventoryLines[]}
*/
item_array: InventoryLines[] = [];
product: Products;
location: Location;
inventory: Inventory;
inventory_line: InventoryLines;
local_storage = this.locker.useDriver(Locker.DRIVERS.LOCAL)
loading: any;
/**
* Enables or disables the save button
* True -> Disabled, False -> Enabled
* @type {boolean}
*/
elementInput: boolean = true;
/**
* Set to true if we are creating a new inventory
* @type {boolean}
*/
new_inventory: boolean = true;
blur_element: boolean;
/**
* Set to true when the inventory has been saved
* @type {boolean}
*/
saved: boolean = false;
not_checking: boolean = true;
inventory_fields: Array<string> = [];
constructor(
public navCtrl: NavController, public navParams: NavParams,
public trytonProvider: TrytonProvider, public locker: Locker,
public alertCtrl: AlertController, public platform: Platform,
public translateService: TranslateService,public rd: Renderer,
public loadingCtrl: LoadingController, public events: Events) {
// Get location
let params = navParams.get('params');
this.new_inventory = params.new_inventory
this.location = params.location
this.blur_element = true;
this.setDefaultFields();
if (this.new_inventory.constructor.name != 'Object' && this.new_inventory == false) {
this.showLoading();
this.not_checking = false;
this.saved = true;
this.inventory = params.inventory
this.fetchInventoryData(this.location, this.inventory)
} else {
// Remove preovious view, this will force the stack to to go back
// to the location-inventory view
//navCtrl.remove(navCtrl.length() - 1)
let current_date = new Date()
this.inventory = {
date: this.format_date(current_date),
location: navParams.get('params').location,
lost_found: navParams.get('params').lost_found || 7,
state: "draft",
id: -1,
lines: []
}
this.save();
// If the user choosed a complete inventory
if (!params.products_inventory) {
this.showLoading();
// Save current inventory
events.subscribe('Save procedure completed', (eventData) => {
this.completeLines();
this.saved = false;
events.unsubscribe('Save procedure completed');
})
}
}
}
/**
* Asks the user if he/she wants to leave the view
* @return {Promise<any>} True or false
*/
ionViewCanLeave(): Promise<any> {
console.log("Saving", this.saved)
if (!this.saved) {
let title_alert: string = null;
let text_alert: string = null;
this.translateService.get('Are you sure you want to leave?').subscribe(
value => {
title_alert = value
}
)
this.translateService.get('All progress will be lost if you do not save').subscribe(
value => {
text_alert = value
}
)
return new Promise((resolve, reject) => {
let confirm = this.alertCtrl.create({
title: title_alert,
message: text_alert,
enableBackdropDismiss: false,
buttons: [{
text: 'OK',
handler: () => {
resolve();
},
}, {
text: 'Cancel',
handler: () => {
reject();
}
}],
});
confirm.present();
})
}
}
/**
* Fallback if the input loses focus
* TODO: Remove
*/
blurInputs(event) {
if (this.blur_element)
document.getElementById('test').focus()
//this.rd.invqokeElementMethod(this.myInput2.nativeElement, 'focus')
this.blur_element = false;
}
/**
* Fetchs the data from the selected inventory
* @param {Location} location Information about the location
* @param {Inventory} inventory The inventory information
*/
private fetchInventoryData(location: Location, inventory: Inventory) {
let json_constructor = new EncodeJSONRead;
let method = "stock.inventory.line";
let fields = this.inventory_fields;
let domain = [json_constructor.createDomain("inventory", "=", inventory.id)];
json_constructor.addNode(method, domain, fields);
let json = json_constructor.createJson();
this.inventory.lines = [];
this.trytonProvider.search(json).subscribe(
data => {
let product_ids = [];
for (let line of data[method]) {
this.product = {
name: line['product.name'],
codes_number: [line['product.code']],
rec_name: line['product.rec_name'],
id: line['product.id']
}
product_ids.push(this.product.id)
this.inventory_line = {
product: this.product,
quantity: line.quantity,
expected_quantity: line.expected_quantity,
id: line.id
}
this.item_array.push(this.inventory_line);
this.inventory.lines.push(this.inventory_line);
}
this.events.publish("Fetch complete")
this.hideLoading();
/* We gather all the data to display it and then we get all the barcodes
* This is so its gonna be faster to givve the user feedback by displaying
* the products than waiting a longer for the barcodes
*/
this.search_code(product_ids);
},
error => {
})
}
/**
* Searchs the given ID and stores its code.
* @param {string} product_ids id of the product
*/
public search_code(product_ids){
let json_constructor = new EncodeJSONRead;
let method = "product.code";
let fields = ["product", "number"];
let domain = [json_constructor.createDomain("product", "in", product_ids)];
json_constructor.addNode(method, domain, fields);
let json = json_constructor.createJson();
this.trytonProvider.search(json).subscribe(
data => {
for (let value of data[method]) {
let res = this.inventory.lines.filter(product => product.product.id == value.product)
let res2 = this.item_array.filter(product => product.product.id == value.product)
if (res !== undefined) {
res[0].product.codes_number.push(value.number);
res2[0].product.codes_number.push(value.number);
}
}
},
error => {
console.log("Error", error)
},
() => {
console.log("Done gathering product numbers", this.item_array)
})
}
/**
* Checks wether or not the given barcode exists in the system and adds
* a quantity to it if it already exists or adds it to the list
* @param {string} data barcode or quantity to add
* @return {boolean} True if completed correctly
*/
public checkInput(event): boolean {
if (this.barcode.length >= 5) {
if (!this.setProductQuantity(this.barcode, 1)){
if (!this.getProduct(this.barcode)){
return false;
}
}
} else if (this.barcode.length < 5) {
// Should never show the alert
if (!this.setProductQuantity(this.lastItem, Number(this.barcode))){
alert('No se ha podido encontrar el producto')
return false;
}
}
return true;
}
/**
* Sets the quantity for a given code
* @param {string} item_code Code of the item to look for.
* If the code is smaller than 100000 its considered
* a quantity for the previous product
* @param {number} set_quantity Quantity to add or to set
* @return {boolean} True if an item was found
*/
private setProductQuantity(item_code: string, set_quantity: number) {
if (set_quantity == NaN) return false;
for (let line of this.item_array) {
if (line.product.codes_number.indexOf(item_code) >= 0) {
if (this.barcode.length > 5) {
line.quantity += set_quantity;
this.lastItem = this.barcode;
}
else
line.quantity = set_quantity
// Set element to first position
let index_array = this.item_array.indexOf(line);
let index_list = this.inventory.lines.indexOf(line);
this.item_array.splice(index_array, 1);
this.inventory.lines.splice(index_list, 1);
this.item_array.unshift(line);
this.inventory.lines.unshift(line);
this.itemInput = '';
this.barcode = '';
this.elementInput = false;
this.saved = false;
return true;
}
}
return false;
}
/**
* Gets the data from the given product barcode
* @param {string} barcode Bar code number of a product
*/
public getProduct(barcode: string) {
let json_constructor = new EncodeJSONRead;
let method = "product.product";
let fields = ["name", "rec_name"];
let domain = [json_constructor.createDomain('code', '=', barcode)];
json_constructor.addNode(method, domain, fields);
let json = json_constructor.createJson();
this.trytonProvider.search(json).subscribe(
data => {
if (data[method].length == 0) {
this.translateService.get('The product does not exists').subscribe(
value => {
let alertTitle = value;
alert(alertTitle);
}
)
return true;
}
this.product = data[method][0];
this.product.codes_number = [barcode];
this.inventory_line = {
product: this.product,
quantity: 1,
id: -1,
};
this.item_array.unshift(this.inventory_line);
this.inventory.lines.unshift(this.inventory_line)
this.lastItem = this.itemInput;
this.elementInput = false;
this.saved = false;
},
error => {
this.translateService.get('The product does not exists').subscribe(
value => {
let alertTitle = value;
alert(alertTitle);
}
)
},
() => {
this.itemInput = '';
this.barcode = '';
});
}
/**
* Sets the line quantity to 0
* @param {any} inventory_line Clicked line
* @return {Null} No return
*/
public setLineZero(inventory_line: any, index) {
this.item_array[index].quantity = 0;
this.saved = false;
this.elementInput = false;
}
/**
* Clears the input
*/
public clearInput(): void{
this.itemInput = '';
this.barcode = '';
}
public setDefaultFields(){
this.inventory_fields = ['product:["rec_name", "name", "code"]', 'quantity', 'expected_quantity'];
}
/**
* Sets the date to a format that tryton understands
* @param {date object} date object containing the full initial and final date
* @return {date object} contains the new date in a format that trytond udersantds
* YYYY-MM-DD HH:mm:ss
*/
private format_date(date) {
let start_year = date.getUTCFullYear();
let start_month = date.getUTCMonth() + 1;
let start_day = date.getUTCDate();
return start_year + '-' + this.pad_with_zeroes(start_month, 2) + '-' +
this.pad_with_zeroes(start_day, 2)
}
/**
* Adds as many '0' at the begining of the string 'number'
* as the value of 'length'
* @param {string} $'8' String of numbers
* @param {int} $2 Length of the new string
* @return {string} $'08'
*/
private pad_with_zeroes(number, length) {
let my_string = '' + number;
while (my_string.length < length) {
my_string = '0' + my_string;
}
return my_string;
}
/**
* Shows a loading component on top of the view
* Only present during the beggining of the view
*/
private showLoading() {
let loading_text;
this.translateService.get('Loading').subscribe(
value => {
loading_text = value
}
)
this.loading = this.loadingCtrl.create({
content: loading_text
})
this.loading.present()
}
/**
* Hides the current loading component on the screen
*/
private hideLoading() {
if (this.loading) {
this.loading.dismiss();
// Send event to cancel timeout
this.events.publish("Loading done")
//this.myInput2.setFocus();
}
}
/**
* Calls complete lines method in tryton. This method will complete
* the inventory with the necessary data
* @param {string = 'True'} fill If set to true the server will create
* new lines, if set to false it will not
* create them, but will calculate them
* @return {boolean} True if succesful
*/
private completeLines(fill: number = 1): Promise<boolean> {
return new Promise((resolve, reject) => {
this.trytonProvider.rpc_call('model.stock.inventory.complete_lines',
[[this.inventory.id], fill]).subscribe(
data => {
if (fill) {
this.fetchInventoryData(this.location, this.inventory);
this.elementInput = false;
// Set amounts to 0
this.events.subscribe('Fetch complete', (eventData) => {
this.saved = false;
for (let line of this.inventory.lines) line.quantity = 0;
this.events.unsubscribe('Fetch complete')
})
}
resolve(data);
},
error => {
console.log("An error occurred", error)
reject(error)
}
)
})
}
/**
* Saves the current inventory into tryton.
* Inventories with no products are not saved
*/
save() {
if (this.new_inventory == false) {
this.update()
return;
}
let json_constructor = new EncodeJSONWrite;
let method = "stock.inventory";
let id = this.inventory.id;
let values = {
company: this.inventory.company,
location: this.inventory.location.id,
lost_found: this.inventory.lost_found.id,
date: this.inventory.date
}
json_constructor.addNode(method, [id, values])
let json = json_constructor.createJSON()
// TODO: Look how its done in the catalogs / shipments in FSSM
// Its shorter and more efficient
// Save the inventory
this.trytonProvider.write(json).subscribe(
data => {
this.inventory.id = data[method][0];
let json_lines = new EncodeJSONWrite;
let inventory_line = "stock.inventory.line"
for (let line of this.inventory.lines) {
id = line.id;
let values = {
inventory: data[method][0],
product: line.product.id,
quantity: line.quantity,
}
json_lines.addNode(inventory_line, [id, values])
}
let lines = json_lines.createJSON()
// Save the lines
this.trytonProvider.write(lines).subscribe(
data => {
this.saved = true;
this.new_inventory = false;
this.events.publish("Save procedure completed")
return true;
},
error => {
console.log("Error", error);
alert(error.messages[0])
})
},
error => {
console.log("Error", error);
alert(error.messages[0])
})
}
/**
* Updates the current inventory with the new values
* TODO: Look how its done in FSSM for catalogs/shipments. Its more efficient
*/
update() {
let json_lines = new EncodeJSONWrite;
let inventory_line = "stock.inventory.line"
for (let line of this.inventory.lines) {
let id = line.id;
let values = {
inventory: this.inventory.id,
product: line.product.id,
quantity: line.quantity,
}
json_lines.addNode(inventory_line, [id, values])
}
let lines = json_lines.createJSON()
this.trytonProvider.write(lines).subscribe(
data => {
this.saved = true;
this.item_array = []
this.fetchInventoryData(this.location, this.inventory);
alert('Inventario actualizado')
},
error => {
console.log("Error", error);
alert(error.messages[0])
})
}
/**
* Confirms the current inventory.
*/
confirm() :void{
let id = this.inventory.id;
// TODO: This is going to be removed later on
this.completeLines(0).then((data) => {
this.trytonProvider.rpc_call("model.stock.inventory.confirm",
[[id]]).subscribe(
data => {
this.navCtrl.setRoot(MainMenuPage);
},
error => {
console.log("An error occurred", error)
// Show an alert when an error occurs
// TODO: Add this as a global function
let error_alert;
this.translateService.get('Generic_Error').subscribe(
value => {
error_alert = value
}
);
alert(error_alert)
})
});
}
public keyboardInput(event: KeyboardEvent) {
if( event.keyCode == 13) {
this.itemInput = this.barcode
}
}
}