Browse Source

módulo de clientes implementado

robert2206 8 years ago
parent
commit
edf0570859

+ 1 - 1
src/app/app.component.ts

@@ -45,7 +45,7 @@ export class OdooMobileApp {
                         component: CustomerListPage
                     },
                     {
-                        visible: true,
+                        visible: false,
                         title: "Presupuestos",
                         icon: "basket",
                         component: BudgetListPage 

+ 7 - 0
src/defaults/default-listable.ts

@@ -62,6 +62,13 @@ export abstract class DefaultListable<T> implements IListable<T> {
         return this._visibleElements;
     }
 
+    /**
+     *
+     */
+    hasVisibleElements(): boolean {
+        return this._visibleElements.length != 0;
+    }
+
     /**
      *
      */

+ 2 - 0
src/models/base.model.ts

@@ -3,6 +3,8 @@ export class BaseModel {
     public id: string;
     public remote_id: number;
     public doc_state: string;
+    public create_date: string;
+    public write_date: string;
 
     constructor() {
         this.id = null;

+ 45 - 0
src/models/sale.order.line.ts

@@ -0,0 +1,45 @@
+import { BaseModel } from "./base.model";
+
+export class SaleOrderLine extends BaseModel {
+
+    public discount: number;
+    public display_name: string;
+    public invoiced: boolean;
+    public invoice_lines: Array<any>;
+    public name: string;
+    public order_id: number;
+    public order_partner_id: number;
+    public price_reduce: number;
+    public price_subtotal: number;
+    public price_unit: number;
+    public product_id: number;
+    public product_tmpl_id: number;
+    public product_uom: number;
+    public product_uom_qty: number;
+    public product_uos: number;
+    public product_uos_qty: number;
+    public salesman_id: number;
+    public tax_id: number;
+
+    constructor() {
+        super();
+
+        this.discount = 0;
+        this.display_name = null;
+        this.invoiced = false;
+        this.invoice_lines = [];
+        this.name = null;
+        this.order_id = 0;
+        this.order_partner_id = 0;
+        this.price_reduce = 0;
+        this.price_subtotal = 0;
+        this.price_unit = 1;
+        this.product_id = 0;
+        this.product_tmpl_id = 0;
+        this.product_uom = 0;
+        this.product_uom_qty = 0;
+        this.product_uos = 0;
+        this.salesman_id = 0;
+        this.tax_id = 0;
+    }
+}

+ 43 - 0
src/models/sale.order.ts

@@ -0,0 +1,43 @@
+import { BaseModel } from "./base.model";
+
+export class SaleOrder extends BaseModel {
+
+    public amount_tax: number;
+    public amount_total: number;
+    public amount_untaxed: number;
+    public date_confirm: string;
+    public date_order: string;
+    public display_name: string;
+    public invoiced: boolean;
+    public invoiced_rate: number;
+    public invoide_exist: boolean;
+    public name: string;
+    public order_line: Array<any>;
+    public partner_id: number;
+    public pricelist_id: number;
+    public product_id: number;
+    public state: number;
+    public user_id: number;
+    public warehouse_id: number;
+
+    constructor() { 
+        super();
+
+        this.amount_tax = 0;
+        this.amount_total = 0;
+        this.amount_untaxed = 0;
+        this.date_confirm = null;
+        this.date_order = null;
+        this.display_name = null;
+        this.invoiced = false;
+        this.invoiced_rate = 0;
+        this.invoide_exist = false;
+        this.name = null;
+        this.order_line = [];
+        this.partner_id = 0;
+        this.product_id = 0;
+        this.state = null;
+        this.user_id = 0;
+        this.warehouse_id = 0;
+    }
+}

+ 28 - 1
src/pages/budget-list/budget-list.html

@@ -10,9 +10,36 @@
 
 </ion-header>
 
+<ion-toolbar color="primary" *ngIf="isSearchMode()">
+    <ion-searchbar placeholder="Buscar" debounce="1000" (ionInput)="search($event)"></ion-searchbar>
+    <ion-buttons end>
+        <button ion-button (click)="toggleSearch()">
+            <ion-icon name="arrow-forward"></ion-icon>
+        </button>
+    </ion-buttons>
+</ion-toolbar>
+
 <ion-content>
 
-     <ion-fab right bottom>
+    <ion-card *ngFor="let b of elements; trackBy:trackByElements">
+        <ion-item>
+            <h2>{{ b.name }}</h2>
+
+            <button ion-button primary clear item-right (click)="showOptions($event, b)">
+                <ion-icon name="more"></ion-icon>
+            </button>
+        </ion-item>
+    </ion-card>
+
+    <div class="no-element" *ngIf="!hasVisibleElements()">
+        <h2>Sin elementos</h2>
+    </div>
+
+    <ion-infinite-scroll (ionInfinite)="seek($event)">
+        <ion-infinite-scroll-content></ion-infinite-scroll-content>
+    </ion-infinite-scroll>
+
+    <ion-fab right bottom>
         <button ion-fab color="yellow" (click)="goToPage(null)">
             <ion-icon name="add" color="light"></ion-icon>
         </button>

+ 55 - 11
src/pages/budget-list/budget-list.ts

@@ -1,24 +1,68 @@
 import { Component } from '@angular/core';
-import { NavController } from 'ionic-angular';
+import { NavController, LoadingController, ToastController } from 'ionic-angular';
 import { INavigable } from "../../interfaces/navigable-interface";
+import { DataProvider } from "../../providers/data-provider";
+import { DefaultListable } from "../../defaults/default-listable";
+import { SaleOrder } from "../../models/sale.order";
+import { BudgetDetailsPage } from "../budget-details/budget-details";
 
 @Component({
     selector: 'page-budget-list',
     templateUrl: 'budget-list.html'
 })
-export class BudgetListPage implements INavigable {
+export class BudgetListPage extends DefaultListable<SaleOrder> implements INavigable {
 
-  constructor(public navCtrl: NavController) {}
-
-  ionViewDidLoad() {
-    console.log('Hello BudgetListPage Page');
+  constructor(
+    public navCtrl: NavController,
+    public loadingCtrl: LoadingController,
+    public toastCtrl: ToastController,
+    public db: DataProvider
+  ) { 
+      super(loadingCtrl);
   }
 
-  /**
-   *
-   */
-  goToPage(page: any) {
+    ionViewDidLoad() {
+        this.initialize();
+    }
 
-  }
+    /**
+    *
+    */
+    initialize(): void {
+        let loader = this.loadingCtrl.create({
+            content: "Cargando presupuestos, espere..."
+        });
+        loader.present();
+
+        this.db.getAll(DataProvider.DOCS.SALE_ORDER).then(orders => {
+            this.elements = orders.filter(item => {
+                return item.state === "draft" && item.doc_state !== "deleted"; 
+            });
+
+            loader.dismiss();
+        }, e => { 
+            console.log(e);
+
+            loader.dismiss();
+
+            this.toastCtrl.create({
+                message: "No se ha podido cargar los presupuestos",
+                duration: 3000
+            }).present();
+        });
+    }
+
+    /**
+    *
+    */
+    goToPage(page: any) {
+        this.navCtrl.push(BudgetDetailsPage, this);
+    }
 
+    /**
+     *
+     */
+    showOptions(): void {
+        
+    }
 }

+ 63 - 19
src/pages/customer-details/customer-details.ts

@@ -2,14 +2,13 @@ import { Component } from '@angular/core';
 import { NavController, NavParams, ActionSheetController, ToastController, AlertController } from 'ionic-angular';
 import { CustomerListPage } from "../customer-list/customer-list";
 import { Partner } from "../../models/partner";
-import { CameraProvider } from "../../providers/camera-provider";
 import { DataProvider } from "../../providers/data-provider";
 import { PhoneProvider } from "../../providers/phone-provider";
 
 @Component({
     selector: 'page-customer-details',
     templateUrl: 'customer-details.html',
-    providers: [ Partner, CameraProvider, PhoneProvider ]
+    providers: [ Partner, PhoneProvider ]
 })
 export class CustomerDetailsPage {
 
@@ -19,7 +18,6 @@ export class CustomerDetailsPage {
         public actionSheetCtrl: ActionSheetController,
         public toastCtrl: ToastController,
         public alertCtrl: AlertController,
-        public camera: CameraProvider,
         public phone: PhoneProvider,
         public customer: Partner,
         public db: DataProvider
@@ -34,11 +32,7 @@ export class CustomerDetailsPage {
      *
      */
     ionViewDidLoad() {
-        this.phone.enableShakeWatcher().subscribe(() => { 
-            this.askIfGetCurrentPosition();
-        }, e => { 
-            console.log(e);
-        });
+        this.phone.enableShakeWatcher().subscribe(() => this.askIfGetCurrentPosition(), e => console.log(e));
     }
 
     /**
@@ -51,27 +45,72 @@ export class CustomerDetailsPage {
     /**
      *
      */
-    private askIfGetCurrentPosition() {
+    private askIfGetCurrentPosition(): void {
         this.alertCtrl.create({
             title: "Confirmar",
             message: "Desea obtener la posición por GPS?",
             buttons: [
                 {
                     text: "Cancelar",
-                    handler: () => {
-                        console.log("Canceled");
-                    }
+                    handler: () => console.log("Canceled")
                 },
                 {
                     text: "Aceptar",
-                    handler: () => {
-                        
-                    }
+                    handler: () => this.checkGpsStatus()
                 }
             ]
         }).present();
     }
 
+    /**
+     *
+     */
+    private checkGpsStatus(): void {
+        this.phone.getGpsStatus().subscribe(status => { 
+            if (status) {
+                this.getCurrentPosition();
+
+            } else {
+                this.alertCtrl.create({
+                    title: "Confirmar",
+                    message: "El GPS se encuentra desactivado, desea activarlo?",
+                    buttons: [
+                        {
+                            text: "Cancelar",
+                            handler: () => console.log("Canceled")
+                        },
+                        {
+                            text: "Aceptar",
+                            handler: () => this.phone.switchToGpsSettings()
+                        }
+                    ]
+                }).present();
+            }
+        });
+    }
+
+    /**
+     *
+     */
+    private getCurrentPosition(): void {
+        this.phone.getCurrentPosition().subscribe(position => { 
+            this.customer.partner_latitude = position.coords.latitude;
+            this.customer.partner_longitude = position.coords.longitude;
+
+            this.toastCtrl.create({
+                message: "Posición GPS obtenida",
+                duration: 3000
+            }).present();
+        }, e => { 
+            console.log(e);
+            
+            this.toastCtrl.create({
+                message: "No se ha podido obtener la posición",
+                duration: 3000
+            }).present();
+        });
+    }
+
     /**
      *
      */
@@ -116,11 +155,16 @@ export class CustomerDetailsPage {
      *
      */
     private takePicture(source: string): void {
-        this.camera.getPicture(source).then(i => {
-            this.customer.image_medium = i;
-            this.customer.image_small = i;
-        }).catch(e => {
+        this.phone.openCamera(source).subscribe(image => { 
+            this.customer.image_medium = image;
+            this.customer.image_small = image;
+        }, e => { 
             console.log(e);
+            
+            this.toastCtrl.create({
+                message: "No se ha podido tomar la foto",
+                duration: 3000
+            }).present();
         });
     }
 

+ 8 - 4
src/pages/customer-list/customer-list.html

@@ -55,22 +55,22 @@
             </button>
         </ion-item>
 
-        <ion-row>
-            <ion-col>
+        <ion-row center>
+            <ion-col width-33>
                 <button ion-button primary clear small>
                     <ion-icon name="cash"></ion-icon>
                     <div>{{ c.sales_order_count || 0}} Pedidos</div>
                 </button>
             </ion-col>
 
-            <ion-col>
+            <ion-col width-33>
                 <button ion-button primary clear small>
                     <ion-icon name="document"></ion-icon>
                     <div>{{ c.invoice_count || 0}} Facturas</div>
                 </button>
             </ion-col>
 
-            <ion-col>
+            <ion-col width-33>
                 <button ion-button primary clear small>
                     <ion-icon name="flag"></ion-icon>
                     <div>{{ c.opportunity_count || 0}} Iniciativas</div>
@@ -79,6 +79,10 @@
         </ion-row>
     </ion-card>
 
+    <div class="no-element" *ngIf="!hasVisibleElements()">
+        <h2>Sin elementos</h2>
+    </div>
+
     <ion-infinite-scroll (ionInfinite)="seek($event)">
         <ion-infinite-scroll-content></ion-infinite-scroll-content>
     </ion-infinite-scroll>

+ 8 - 30
src/pages/customer-list/customer-list.ts

@@ -57,7 +57,6 @@ export class CustomerListPage extends DefaultListable<Partner> implements INavig
 
             loader.dismiss();
             
-
             this.toastCtrl.create({
                 message: "No se ha podido cargar los clientes",
                 duration: 3000
@@ -131,16 +130,11 @@ export class CustomerListPage extends DefaultListable<Partner> implements INavig
             buttons: [
                 {
                     text: "Cancelar",
-                    handler: () => {
-                        console.log("Canceled");
-                        
-                    }
+                    handler: () => console.log("Canceled")
                 },
                 {
                     text: "Aceptar",
-                    handler: () => {
-                        this.removeItem(item);
-                    }
+                    handler: () => this.removeItem(item)
                 }
             ]
         }).present();
@@ -152,9 +146,7 @@ export class CustomerListPage extends DefaultListable<Partner> implements INavig
     removeItem(item: any) {
         item.doc_state = "deleted";
         
-        this.db.delete(DataProvider.DOCS.PRODUCT_TEMPLATE, item).then(result => {
-            this.remove(item);
-        }).catch(e => {
+        this.db.delete(DataProvider.DOCS.PRODUCT_TEMPLATE, item).then(result => this.remove(item)).catch(e => {
             console.log(e);
             
             this.toastCtrl.create({
@@ -201,11 +193,7 @@ export class CustomerListPage extends DefaultListable<Partner> implements INavig
      *
      */
     call(item: Partner): void {
-        this.phone.call(item.mobile).subscribe(() => { 
-            console.log("Dial opened");
-        }, e => {
-            console.log(e);
-        });
+        this.phone.call(item.mobile).subscribe(() => console.log("Dial opened"), e => console.log(e));
     }
 
     /**
@@ -227,15 +215,11 @@ export class CustomerListPage extends DefaultListable<Partner> implements INavig
             buttons: [
                 {
                     text: "Cancelar",
-                    handler: () => {
-                        console.log("Canceled");
-                    }
+                    handler: () => console.log("Canceled")
                 },
                 {
                     text: "Aceptar",
-                    handler: () => {
-                        this.navigate(item);
-                    }
+                    handler: () => this.navigate(item)
                 }
             ]
         }).present();
@@ -279,15 +263,11 @@ export class CustomerListPage extends DefaultListable<Partner> implements INavig
             buttons: [
                 {
                     text: "Cancelar",
-                    handler: () => {
-                        console.log("Canceled");
-                    }
+                    handler: () => console.log("Canceled")
                 },
                 {
                     text: "Aceptar",
-                    handler: () => {
-                        this.saveContact(item);
-                    }
+                    handler: () => this.saveContact(item)
                 }
             ]
         }).present();
@@ -297,8 +277,6 @@ export class CustomerListPage extends DefaultListable<Partner> implements INavig
      *
      */
     saveContact(item: Partner): void {
-        console.log(item);
-        
         this.phone.saveContact(item.name, [item.mobile]).subscribe(() => {
             this.toastCtrl.create({
                 message: "Contacto guardado en el teléfono",

+ 55 - 29
src/pages/product-details/product-details.ts

@@ -1,15 +1,14 @@
 import { Component } from '@angular/core';
-import { NavController, NavParams, ActionSheetController, ToastController, LoadingController } from 'ionic-angular';
-import { Shake, BarcodeScanner } from 'ionic-native';
+import { NavController, NavParams, ActionSheetController, ToastController, LoadingController, AlertController } from 'ionic-angular';
 import { Product } from '../../models/product';
 import { ProductListPage } from '../product-list/product-list';
 import { DataProvider } from '../../providers/data-provider'
-import { CameraProvider } from '../../providers/camera-provider';
+import { PhoneProvider } from '../../providers/phone-provider';
 import { PreferencesProvider } from '../../providers/preferences-provider';
 
 @Component({
     selector: 'page-product-details',
-    providers: [ Product, CameraProvider],
+    providers: [ Product, PhoneProvider ],
     templateUrl: 'product-details.html'
 })
 export class ProductDetailsPage {
@@ -28,7 +27,8 @@ export class ProductDetailsPage {
         public toastCtrl: ToastController,
         public actionSheetCtrl: ActionSheetController,
         public loadingCtrl: LoadingController,
-        public cameraProvider: CameraProvider,
+        public alertCtrl: AlertController,
+        public phone: PhoneProvider,
         public preferencesPreferences: PreferencesProvider
     ) { 
         if (!(this.params.data instanceof ProductListPage)) {
@@ -41,25 +41,50 @@ export class ProductDetailsPage {
      *
      */
     ionViewDidLoad() {
-        this.watcher = Shake.startWatch(40).subscribe(() => { 
-            BarcodeScanner.scan().then(bar => { 
-                this.product.ean13 = bar.cancelled ? null : bar.text;
-            }).catch(e => { 
-                this.toastCtrl.create({
-                    message: 'No se ha podido leer el código',
-                    duration: 3000
-                }).present();
-            });
-        });
+        this.phone.enableShakeWatcher().subscribe(() => this.askIfScanBarcode(), e => console.log(e));
     }
 
     /**
      *
      */
     ionViewDidLeave() {
-        if (this.watcher) {
-            this.watcher.unsubscribe();
-        }
+        this.phone.disableShakeWatcher();
+    }
+
+    /**
+     *
+     */
+    askIfScanBarcode(): void {
+        this.alertCtrl.create({
+            title: "Confirmar",
+            message: "Desea escanear código de barras?",
+            buttons: [
+                {
+                    text: "Cancelar",
+                    handler: () => console.log("Canceled")
+                },
+                {
+                    text: "Aceptar",
+                    handler: () => this.scanBarcode()
+                }
+            ]
+        }).present();
+    }
+
+    /**
+     *
+     */
+    scanBarcode(): void {
+        this.phone.scanBarcode().subscribe(barcode => { 
+            this.product.ean13 = barcode.cancelled ? null : barcode.text;
+        }, e => {
+            console.log(e);
+        
+            this.toastCtrl.create({
+                message: 'No se ha podido leer el código de barras',
+                duration: 3000
+            }).present();
+        });
     }
 
     /**
@@ -83,16 +108,12 @@ export class ProductDetailsPage {
                 {
                     text: "Desde la cámara",
                     icon: "camera",
-                    handler: () => {
-                        this.takePicture("camera");
-                    }
+                    handler: () => this.takePicture("camera")
                 },
                 {
                     text: "Desde la galería",
                     icon: "images",
-                    handler: () => {
-                        this.takePicture("album");
-                    }
+                    handler: () => this.takePicture("album")
                 },
                 {
                     text: "Cancelar",
@@ -106,11 +127,16 @@ export class ProductDetailsPage {
      *
      */
     takePicture(source: string): void {
-        this.cameraProvider.getPicture(source).then(i => {
-            this.product.image_medium = i;
-            this.product.image_small = i;
-        }).catch(e => {
-            console.log(e);                    
+        this.phone.openCamera(source).subscribe(image => { 
+            this.product.image_medium = image;
+            this.product.image_small = image;
+        }, e => { 
+            console.log(e);
+            
+            this.toastCtrl.create({
+                message: 'No se ha podido tomar la foto',
+                duration: 3000
+            }).present();
         });
     }
 

+ 4 - 0
src/pages/product-list/product-list.html

@@ -61,6 +61,10 @@
         </ion-row>
     </ion-card>
 
+    <div class="no-element" *ngIf="!hasVisibleElements()">
+        <h2>Sin elementos</h2>
+    </div>
+
     <ion-infinite-scroll (ionInfinite)="seek($event)">
         <ion-infinite-scroll-content></ion-infinite-scroll-content>
     </ion-infinite-scroll>

+ 0 - 30
src/providers/camera-provider.ts

@@ -1,30 +0,0 @@
-import { Injectable } from '@angular/core';
-import { Camera } from 'ionic-native';
-
-@Injectable()
-export class CameraProvider {
-    
-    options: any;
-
-    constructor() { 
-        this.options = {
-            sourceType: Camera.PictureSourceType.CAMERA,
-            destinationType: Camera.DestinationType.DATA_URL,
-            correctOrientation: true,
-            saveToPhotoAlbum: false,
-            targetHeight: 300,
-            targetWidth: 300
-        }
-    }    
-    
-    /**
-     *
-     */
-    getPicture(source: string): Promise<any> {
-        if (source == "album") {
-            this.options.sourceType = Camera.PictureSourceType.SAVEDPHOTOALBUM;
-        }
-        
-        return Camera.getPicture(this.options);
-    }
-}

+ 22 - 1
src/providers/data-provider.ts

@@ -13,11 +13,16 @@ export class DataProvider {
 
     public static readonly DOCS = {
         PARTNER: 'partner',
+        USER: 'user',
+        SALE_ORDER: 'sale.order',
+        SALE_ORDER_LINE: 'sale.order.line',
         PRODUCT_TEMPLATE: 'product.template',
         PRODUCT_ATTRIBUTE: 'product.attribute',
         PRODUCT_ATTRIBUTE_LINE: 'product.attribute.line',
         PRODUCT_ATTRIBUTE_VALUE: 'product.attribute.value',
-        PRODUCT_PRODUCT: 'product.product'
+        PRODUCT_PRODUCT: 'product.product',
+        PRODUCT_PRICELIST: 'product.pricelist',
+        STOCK_WAREHOUSE: 'stock.warehouse'
     };
 
     db: any;
@@ -30,6 +35,14 @@ export class DataProvider {
             singular: 'user',
             plural: 'users'
         },
+        {
+            singular: 'sale.order',
+            plural: 'sale.orders'
+        },
+        {
+            singular: 'sale.order.line',
+            plural: 'sale.order.lines'
+        },
         {
             singular: 'currency',
             plural: 'currencies'
@@ -69,6 +82,14 @@ export class DataProvider {
         {
             singular: 'product',
             plural: 'products'
+        },
+        {
+            singular: 'product.pricelist',
+            plural: 'product.pricelist'
+        },
+        {
+            singular: 'stock.warehouse',
+            plural: 'stock.warehouses'
         }
     ];
 

+ 65 - 18
src/providers/phone-provider.ts

@@ -2,15 +2,50 @@ import { Injectable } from "@angular/core";
 import { Observable } from "rxjs/Observable";
 import { Observer } from "rxjs/Observer";
 import { Subscription } from "rxjs/Subscription";
-import { Contact, Contacts, ContactName, ContactField, ContactAddress, CallNumber, Geolocation, LaunchNavigator, LaunchNavigatorOptions, Shake } from "ionic-native";
+import { Diagnostic, Camera, BarcodeScanner, Contact, Contacts, ContactName, ContactField, ContactAddress, CallNumber, Geolocation, LaunchNavigator, LaunchNavigatorOptions, Shake, Vibration } from "ionic-native";
 
 @Injectable()
 export class PhoneProvider {
 
+    private cameraOptions = {
+        sourceType: Camera.PictureSourceType.CAMERA,
+        destinationType: Camera.DestinationType.DATA_URL,
+        correctOrientation: true,
+        saveToPhotoAlbum: false,
+        targetHeight: 300,
+        targetWidth: 300
+    };
+
     watcher: Subscription;
 
     constructor() { }
-    
+
+    /**
+     *
+     */
+    openCamera(from: string): Observable<any> {
+        this.cameraOptions.sourceType = from == "album" ? Camera.PictureSourceType.SAVEDPHOTOALBUM : this.cameraOptions.sourceType;
+
+        return Observable.create((observer: Observer<any>) => {
+            Camera.getPicture(this.cameraOptions).then(image => { 
+                observer.next(image);
+                observer.complete();
+            }).catch(e => observer.error(e));
+        });
+    }
+
+    /**
+     *
+     */
+    scanBarcode(): Observable<any> {
+        return Observable.create((observer: Observer<any>) => {
+            BarcodeScanner.scan().then(barcode => {
+                observer.next(barcode);
+                observer.complete();
+            }).catch(e => observer.error(e));
+        });
+    }
+
     /**
      *
      */
@@ -33,13 +68,9 @@ export class PhoneProvider {
                 }
             }
 
-            console.log(contact);
-            
             contact.save().then(() => { 
                 observer.complete();
-            }, e => {
-                observer.error(e);
-            });
+            }, e => observer.error(e));
         });
     }   
 
@@ -48,14 +79,29 @@ export class PhoneProvider {
      */
     call(numberToCall: string): Observable<any> {
         return Observable.create((observer: Observer<any>) => { 
-            CallNumber.callNumber(numberToCall, true).then(() => { 
+            CallNumber.callNumber(numberToCall, true).then(() => observer.complete()).catch(e => observer.error(e));
+        });
+    }
+
+    /**
+     *
+     */
+    getGpsStatus(): Observable<any> {
+        return Observable.create((observer: Observer<any>) => { 
+            Diagnostic.isGpsLocationEnabled().then(status => {
+                observer.next(status);
                 observer.complete();
-            }).catch(e => { 
-                observer.error(e);
             });
         });
     }
 
+    /**
+     *
+     */
+    switchToGpsSettings(): void {
+        Diagnostic.switchToLocationSettings();
+    }
+
     /**
      *
      */
@@ -64,9 +110,7 @@ export class PhoneProvider {
             Geolocation.getCurrentPosition().then(position => {
                 observer.next(position);
                 observer.complete();
-            }).catch(e => { 
-                observer.error(e);
-            });
+            }).catch(e => observer.error(e));
         });
     }
 
@@ -78,9 +122,7 @@ export class PhoneProvider {
             LaunchNavigator.navigate([latitude, longitude]).then(s => { 
                 observer.next(s);
                 observer.complete();
-            }).catch(e => {
-                observer.error(e);
-            });
+            }).catch(e => observer.error(e));
         });
     }
 
@@ -88,8 +130,6 @@ export class PhoneProvider {
      *
      */
     enableShakeWatcher(): Observable<any> {
-        console.log("Watch");
-        
         this.disableShakeWatcher();
 
         return Observable.create((observer: Observer<any>) => {
@@ -107,4 +147,11 @@ export class PhoneProvider {
             this.watcher.unsubscribe();
         }
     }
+
+    /**
+     *
+     */
+    vibrate(): void {
+        Vibration.vibrate(500);
+    }
 }

+ 14 - 0
src/theme/odoo.scss

@@ -24,6 +24,7 @@ ion-label h2 {
 
 ion-card ion-row ion-col button {
     text-transform: none !important;
+    white-space: normal !important;
 }
 
 ion-card ion-row ion-col button ion-icon {
@@ -38,3 +39,16 @@ ion-card ion-row ion-col button ion-icon {
 .popover-viewport ion-list {
     margin: -1px 0 5px 10px !important;
 }
+
+.no-element {
+    width: 100%;
+    height: 100%;
+    display: flex;
+    align-items: center;
+    justify-content: center;  
+}
+
+.no-element > h2 {
+    font-size: 15pt;
+    color: #d3d3d3;
+}