瀏覽代碼

Módulo para dashboard anterior

SEBAS 1 年之前
當前提交
590888bbac
共有 100 個文件被更改,包括 7416 次插入0 次删除
  1. 3 0
      __init__.py
  2. 二進制
      __init__.pyc
  3. 36 0
      __openerp__.py
  4. 1 0
      controllers/__init__.py
  5. 二進制
      controllers/__init__.pyc
  6. 26 0
      controllers/helpers/__init__.py
  7. 二進制
      controllers/helpers/__init__.pyc
  8. 64 0
      controllers/helpers/account_bank_statement.py
  9. 二進制
      controllers/helpers/account_bank_statement.pyc
  10. 62 0
      controllers/helpers/account_bank_statement_line.py
  11. 二進制
      controllers/helpers/account_bank_statement_line.pyc
  12. 53 0
      controllers/helpers/account_invoice.py
  13. 二進制
      controllers/helpers/account_invoice.pyc
  14. 65 0
      controllers/helpers/account_invoice_line.py
  15. 二進制
      controllers/helpers/account_invoice_line.pyc
  16. 54 0
      controllers/helpers/account_invoice_multi_store.py
  17. 二進制
      controllers/helpers/account_invoice_multi_store.pyc
  18. 106 0
      controllers/helpers/account_move_line.py
  19. 二進制
      controllers/helpers/account_move_line.pyc
  20. 55 0
      controllers/helpers/account_voucher_multi_store.py
  21. 二進制
      controllers/helpers/account_voucher_multi_store.pyc
  22. 49 0
      controllers/helpers/company.py
  23. 二進制
      controllers/helpers/company.pyc
  24. 29 0
      controllers/helpers/dashboard_objetive.py
  25. 二進制
      controllers/helpers/dashboard_objetive.pyc
  26. 69 0
      controllers/helpers/hr_employee.py
  27. 二進制
      controllers/helpers/hr_employee.pyc
  28. 17 0
      controllers/helpers/ir_module_module.py
  29. 二進制
      controllers/helpers/ir_module_module.pyc
  30. 27 0
      controllers/helpers/journal.py
  31. 二進制
      controllers/helpers/journal.pyc
  32. 43 0
      controllers/helpers/payments_journals.py
  33. 二進制
      controllers/helpers/payments_journals.pyc
  34. 45 0
      controllers/helpers/pos_order.py
  35. 二進制
      controllers/helpers/pos_order.pyc
  36. 76 0
      controllers/helpers/pos_order_line.py
  37. 二進制
      controllers/helpers/pos_order_line.pyc
  38. 48 0
      controllers/helpers/pos_order_multi_store.py
  39. 二進制
      controllers/helpers/pos_order_multi_store.pyc
  40. 22 0
      controllers/helpers/res_partner.py
  41. 二進制
      controllers/helpers/res_partner.pyc
  42. 18 0
      controllers/helpers/res_store.py
  43. 二進制
      controllers/helpers/res_store.pyc
  44. 22 0
      controllers/helpers/res_user.py
  45. 二進制
      controllers/helpers/res_user.pyc
  46. 55 0
      controllers/helpers/voucher.py
  47. 二進制
      controllers/helpers/voucher.pyc
  48. 27 0
      controllers/helpers/widget_list.py
  49. 二進制
      controllers/helpers/widget_list.pyc
  50. 56 0
      controllers/helpers/widget_stock_valuation.py
  51. 二進制
      controllers/helpers/widget_stock_valuation.pyc
  52. 64 0
      controllers/helpers/widget_stock_valuation_with_variant_cost_price.py
  53. 二進制
      controllers/helpers/widget_stock_valuation_with_variant_cost_price.pyc
  54. 153 0
      controllers/main.py
  55. 二進制
      controllers/main.pyc
  56. 101 0
      data/charts_data.xml
  57. 4 0
      models/__init__.py
  58. 二進制
      models/__init__.pyc
  59. 18 0
      models/chart_config.py
  60. 二進制
      models/chart_config.pyc
  61. 17 0
      models/model.py
  62. 二進制
      models/model.pyc
  63. 101 0
      models/rrhh.py
  64. 二進制
      models/rrhh.pyc
  65. 7 0
      security/ir.model.access.csv
  66. 二進制
      static/description/icon.png
  67. 53 0
      static/src/css/circliful.css
  68. 155 0
      static/src/css/dashboard.css
  69. 412 0
      static/src/js/chart.js
  70. 107 0
      static/src/js/charts/ranking_customer.js
  71. 110 0
      static/src/js/charts/ranking_sale_by_store.js
  72. 105 0
      static/src/js/charts/ranking_salesman.js
  73. 217 0
      static/src/js/dashboard.js
  74. 41 0
      static/src/js/main.js
  75. 60 0
      static/src/js/widget_base.js
  76. 366 0
      static/src/js/widgets/widget_account.js
  77. 269 0
      static/src/js/widgets/widget_balance.js
  78. 209 0
      static/src/js/widgets/widget_bank_cash.js
  79. 288 0
      static/src/js/widgets/widget_financial_state.js
  80. 241 0
      static/src/js/widgets/widget_income_outcome.js
  81. 158 0
      static/src/js/widgets/widget_rrhh.js
  82. 894 0
      static/src/js/widgets/widget_sale_purchase_expense.js
  83. 63 0
      static/src/js/widgets/widget_stock_valuation.js
  84. 78 0
      static/src/js/widgets/widget_stock_valuation_with_variant_cost_price.js
  85. 1053 0
      static/src/lib/chartjs-plugin-datalabels.js
  86. 437 0
      static/src/lib/circliful.js
  87. 29 0
      static/src/lib/jquery.timeago.es.js
  88. 17 0
      static/src/xml/charts/ranking_customer.xml
  89. 17 0
      static/src/xml/charts/ranking_sale_by_store.xml
  90. 17 0
      static/src/xml/charts/ranking_salesman.xml
  91. 9 0
      static/src/xml/dashboard.xml
  92. 31 0
      static/src/xml/modals/bank_cash_modal.xml
  93. 55 0
      static/src/xml/modals/modal_income_outcome.xml
  94. 95 0
      static/src/xml/modals/widget_account_modal.xml
  95. 53 0
      static/src/xml/modals/widget_modal.xml
  96. 11 0
      static/src/xml/widget_base.xml
  97. 37 0
      static/src/xml/widgets/widget_account.xml
  98. 23 0
      static/src/xml/widgets/widget_balance.xml
  99. 29 0
      static/src/xml/widgets/widget_bank_cash.xml
  100. 84 0
      static/src/xml/widgets/widget_financial_state.xml

+ 3 - 0
__init__.py

@@ -0,0 +1,3 @@
+# -*- coding: utf-8 -*-
+import models
+import controllers

二進制
__init__.pyc


+ 36 - 0
__openerp__.py

@@ -0,0 +1,36 @@
+# -*- coding: utf-8 -*-
+{
+    'name': "Dashboard",
+    'author': "Eiru",
+    'website': "https://www.eiru.com.py",
+    'category': 'Uncategorized',
+    'version': '2.0',
+    'depends': [
+        'base',
+        'account',
+        'sale',
+        'purchase',
+        'stock',
+        'eiru_assets',
+        'currency_utility',
+        'multi_store',
+        'multi_store_stock',
+        # 'eiru_sidebar_toggler',
+    ],
+    'data': [
+        'views/chart_config.xml',
+        'views/res_users.xml',
+        'views/actions.xml',
+        'views/menus.xml',
+        'views/objetive.xml',
+        'templates.xml',
+        'security/ir.model.access.csv',
+        'data/charts_data.xml',
+    ],
+    'qweb': [
+        'static/src/xml/*.xml',
+        'static/src/xml/widgets/*.xml',
+        'static/src/xml/charts/*.xml',
+        'static/src/xml/modals/*.xml',
+    ]
+}

+ 1 - 0
controllers/__init__.py

@@ -0,0 +1 @@
+import main

二進制
controllers/__init__.pyc


+ 26 - 0
controllers/helpers/__init__.py

@@ -0,0 +1,26 @@
+# -*- coding: utf-8 -*-
+from journal import get_journals_widget
+from widget_stock_valuation import get_stock_valuation
+from company import get_company_info
+from account_invoice import get_account_invoice_widget
+from ir_module_module import get_ir_module_widget
+from dashboard_objetive import get_dashboard_objetive_widget
+from pos_order import get_pos_order_widget
+from account_invoice import get_account_invoice_widget
+from payments_journals import get_payments_journals_widget
+from voucher import get_account_voucher_widget
+from account_bank_statement_line import get_account_bank_statement_line_widget
+from account_move_line import get_account_move_line_widget
+from account_bank_statement import get_account_bank_statement_widget
+from res_partner import get_customer_info
+from res_user import get_user_info
+from account_invoice_multi_store import get_account_invoice_multi_store_widget
+from pos_order_multi_store import get_pos_order_multi_store_widget
+from res_store import get_res_store_widget
+from account_invoice_line import get_account_invoice_line_widget
+from pos_order_line import get_pos_order_line_widget
+from account_voucher_multi_store import get_account_voucher_multi_store_widget
+from widget_list import get_widget_list
+from hr_employee import get_hr_payslip_widget
+from account_move_line import get_account_move_line_partial_reconcile
+from widget_stock_valuation_with_variant_cost_price import get_stock_valuation_with_variant_cost_price

二進制
controllers/helpers/__init__.pyc


+ 64 - 0
controllers/helpers/account_bank_statement.py

@@ -0,0 +1,64 @@
+# -*- coding: utf-8 -*-
+from openerp.http import request as r
+
+def get_account_bank_statement_widget():
+    user_store = r.env.user.store_id.id
+    company_currency_rate = r.env.user.company_id.currency_id.rate
+    query = '''
+        SELECT
+        	statement.id,
+        	statement.name,
+        	journal.name,
+        	journal.type,
+        	journal.currency,
+        	statement.balance_end,
+        	CASE
+        		WHEN journal.currency IS NULL
+        		THEN statement.balance_end
+        		ELSE statement.balance_end * (%s / (array_agg(rate.rate ORDER BY rate.name DESC))[1])
+        		END AS balance_end_currency,
+            currency.symbol,
+            currency.decimal_places,
+            currency.thousands_separator,
+            currency.decimal_separator
+        FROM account_bank_statement AS statement
+        LEFT JOIN res_store_journal_rel AS journal_rel
+        ON statement.journal_id = journal_rel.journal_id
+        LEFT JOIN account_journal AS journal
+        ON statement.journal_id = journal.id
+        LEFT JOIN res_currency_rate AS rate
+        ON rate.currency_id = journal.currency
+        LEFT JOIN res_currency AS currency
+        ON currency.id = journal.currency
+        WHERE statement.state NOT IN ('confirm')
+        AND journal_rel.store_id = ''' + str(user_store) + '''
+        GROUP BY
+        	statement.id,
+        	statement.name,
+        	journal.name,
+        	journal.type,
+        	journal.currency,
+        	statement.balance_end,
+            currency.symbol,
+            currency.decimal_places,
+            currency.thousands_separator,
+            currency.decimal_separator
+    '''
+
+    r.cr.execute(query,(tuple([company_currency_rate])))
+
+    return [
+        {
+            'id': j[0],
+            'name': j[1],
+            'journal_name': j[2],
+            'journal_type': j[3],
+            'journal_currency': j[4],
+            'balance_end': j[5],
+            'balance_end_currency': j[6],
+            'symbol': j[7],
+            'decimal_places': j[8],
+            'thousands_separator': j[9],
+            'decimal_separator': j[10],
+        } for j in r.cr.fetchall()
+    ]

二進制
controllers/helpers/account_bank_statement.pyc


+ 62 - 0
controllers/helpers/account_bank_statement_line.py

@@ -0,0 +1,62 @@
+# -*- coding: utf-8 -*-
+from openerp.http import request as r
+
+def get_account_bank_statement_line_widget():
+    user_store = r.env.user.store_id.id
+    company_currency_rate = r.env.user.company_id.currency_id.rate
+    validate = '''
+        SELECT EXISTS(
+            SELECT column_name
+            FROM information_schema.columns
+            WHERE table_schema='public'
+                AND table_name='account_bank_statement_line'
+                AND column_name='pos_statement_id')
+    '''
+
+    query = '''
+        SELECT
+        	line.id,
+        	line.journal_id,
+        	journal.currency,
+            line.amount,
+        	CASE
+        		WHEN journal.currency IS NULL
+        		THEN line.amount
+        		ELSE line.amount * (%s / (array_agg(rate.rate ORDER BY rate.name DESC))[1])
+        		END AS amount_currency
+        FROM account_bank_statement_line AS line
+        LEFT JOIN account_bank_statement AS statement
+        ON statement.id = line.statement_id
+        LEFT JOIN res_store_journal_rel AS journal_rel
+        ON journal_rel.journal_id = statement.journal_id
+        LEFT JOIN account_journal AS journal
+        ON journal.id = statement.journal_id
+        LEFT JOIN res_currency_rate AS rate
+        ON rate.currency_id = journal.currency
+        WHERE TO_CHAR(line.date,'YYYY-MM') = TO_CHAR(current_date,'YYYY-MM')
+        AND line.pos_statement_id IS NOT NULL
+        AND journal_rel.store_id = ''' + str(user_store) + '''
+        GROUP BY
+        	line.id,
+        	line.amount,
+        	line.journal_id,
+        	journal.currency
+    '''
+
+    r.cr.execute(validate)
+    for j in r.cr.fetchall():
+        band = j[0]
+
+    if band == True:
+        r.cr.execute(query,(tuple([company_currency_rate])))
+        return [
+            {
+                'id': j[0],
+                'journal_id': j[1],
+                'currency_id': j[2],
+                'amount': j[3],
+                'amount_currency': j[4],
+            } for j in r.cr.fetchall()
+        ]
+    else:
+        return []

二進制
controllers/helpers/account_bank_statement_line.pyc


+ 53 - 0
controllers/helpers/account_invoice.py

@@ -0,0 +1,53 @@
+# -*- coding: utf-8 -*-
+from openerp.http import request as r
+
+def get_account_invoice_widget():
+    user_store = r.env.user.store_id.id
+    company_currency_rate = r.env.user.company_id.currency_id.rate
+    query = '''
+        SELECT
+        	invoice.id,
+        	rate.currency_id,
+        	invoice.date_invoice,
+        	invoice.type,
+        	invoice.origin,
+        	invoice.partner_id,
+        	invoice.user_id,
+        	invoice.amount_total * (%s / (array_agg(rate.rate ORDER BY rate.id DESC))[1]) as invoice_rate
+        FROM account_invoice AS invoice
+        LEFT JOIN res_store_journal_rel AS journal
+        ON journal.journal_id = invoice.journal_id
+        LEFT JOIN res_currency_rate AS rate
+        ON rate.currency_id = invoice.currency_id
+        LEFT JOIN res_company AS company
+        ON company.id = invoice.company_id
+        WHERE invoice.state NOT IN ('draft', 'cancel')
+        AND TO_CHAR(invoice.date_invoice,'YYYY-MM') = TO_CHAR(current_date,'YYYY-MM')
+        AND journal.store_id = ''' + str(user_store) + '''
+        GROUP BY
+        	invoice.id,
+        	rate.currency_id,
+        	invoice.date_invoice,
+        	invoice.type,
+        	invoice.origin,
+        	invoice.amount_total,
+        	invoice.partner_id,
+        	invoice.user_id,
+            invoice.amount_total
+    '''
+
+    r.cr.execute(query,(tuple([company_currency_rate])))
+
+    return [
+        {
+            'invoice_id': j[0],
+            'currency_id': j[1],
+            'date': j[2],
+            'type': j[3],
+            'origin': j[4],
+            'customer_id':j[5],
+            'user_id':j[6],
+            'amount':j[7],
+
+        } for j in r.cr.fetchall()
+    ]

二進制
controllers/helpers/account_invoice.pyc


+ 65 - 0
controllers/helpers/account_invoice_line.py

@@ -0,0 +1,65 @@
+# -*- coding: utf-8 -*-
+from openerp.http import request as r
+
+def get_account_invoice_line_widget():
+    user_store = r.env.user.store_id.id
+    company_currency_rate = r.env.user.company_id.currency_id.rate
+    query = '''
+        SELECT
+        	invoice.id AS invoice_id,
+        	line.id AS invoice_line_id,
+        	invoice.number,
+        	invoice.origin,
+        	invoice.date_invoice,
+        	invoice.type,
+        	product.name_template,
+        	line.price_unit * (%s / (array_agg(rate.rate ORDER BY rate.id DESC))[1]) as price_unit,
+        	line.quantity AS cant,
+        	line.price_subtotal * (%s / (array_agg(rate.rate ORDER BY rate.id DESC))[1]) AS subtotal,
+        	(line.quantity * (line.price_unit * (%s / (array_agg(rate.rate ORDER BY rate.id DESC))[1]))) - (line.price_subtotal * (%s / (array_agg(rate.rate ORDER BY rate.id DESC))[1])) AS impuestos,
+        	(array_agg(history.cost ORDER BY history.id DESC))[1] AS cost
+            FROM account_invoice AS invoice
+            LEFT JOIN account_invoice_line AS line
+            ON line.invoice_id = invoice.id
+            LEFT JOIN product_product AS product
+            ON line.product_id = product.id
+            LEFT JOIN res_store_journal_rel AS journal
+            ON journal.journal_id = invoice.journal_id
+            LEFT JOIN product_price_history AS history
+            ON history.product_template_id = product.product_tmpl_id
+            LEFT JOIN res_currency_rate AS rate
+            ON rate.currency_id = invoice.currency_id
+            WHERE invoice.state NOT IN ('draft', 'cancel')
+            AND TO_CHAR(invoice.date_invoice,'YYYY-MM') = TO_CHAR(current_date,'YYYY-MM')
+            AND journal.store_id = ''' + str(user_store) + '''
+        GROUP BY
+        	invoice.id,
+        	line.id,
+        	invoice.number,
+        	invoice.origin,
+        	invoice.date_invoice,
+        	product.name_template,
+        	line.price_unit,
+        	line.quantity,
+        	line.price_subtotal
+    '''
+
+    r.cr.execute(query,(tuple([company_currency_rate,company_currency_rate,company_currency_rate,company_currency_rate])))
+
+    return [
+        {
+            'invoice_id': j[0],
+            'invoice_line_id': j[1],
+            'number': j[2],
+            'origin': j[3],
+            'date': j[4],
+            'type': j[5],
+            'product_name':j[6],
+            'price_unit':j[7],
+            'quantity':j[8],
+            'subtotal':j[9],
+            'tax': j[10],
+            'cost': j[11],
+            'history_id': [12],
+        } for j in r.cr.fetchall()
+    ]

二進制
controllers/helpers/account_invoice_line.pyc


+ 54 - 0
controllers/helpers/account_invoice_multi_store.py

@@ -0,0 +1,54 @@
+# -*- coding: utf-8 -*-
+from openerp.http import request as r
+
+def get_account_invoice_multi_store_widget():
+    user_store = r.env.user.store_id.id
+    company_currency_rate = r.env.user.company_id.currency_id.rate
+    query = '''
+        SELECT
+            invoice.id,
+            rate.currency_id,
+            invoice.date_invoice,
+            invoice.type,
+            invoice.origin,
+            invoice.partner_id,
+            invoice.user_id,
+            invoice.amount_total * (%s / (array_agg(rate.rate ORDER BY rate.id DESC))[1]),
+            journal.store_id
+        FROM account_invoice AS invoice
+        LEFT JOIN res_store_journal_rel AS journal
+        ON journal.journal_id = invoice.journal_id
+        LEFT JOIN res_currency_rate AS rate
+        ON rate.currency_id = invoice.currency_id
+        LEFT JOIN res_company AS company
+        ON company.id = invoice.company_id
+        WHERE invoice.state NOT IN ('draft', 'cancel')
+        AND TO_CHAR(invoice.date_invoice,'YYYY-MM') = TO_CHAR(current_date,'YYYY-MM')
+        GROUP BY
+            invoice.id,
+            rate.currency_id,
+            invoice.date_invoice,
+            invoice.type,
+            invoice.origin,
+            invoice.amount_total,
+            invoice.partner_id,
+            invoice.user_id,
+            invoice.amount_total,
+            journal.store_id
+    '''
+
+    r.cr.execute(query,(tuple([company_currency_rate])))
+
+    return [
+        {
+            'invoice_id': j[0],
+            'currency_id': j[1],
+            'date': j[2],
+            'type': j[3],
+            'origin': j[4],
+            'customer_id':j[5],
+            'user_id':j[6],
+            'amount':j[7],
+            'store_id':j[8]
+        } for j in r.cr.fetchall()
+    ]

二進制
controllers/helpers/account_invoice_multi_store.pyc


+ 106 - 0
controllers/helpers/account_move_line.py

@@ -0,0 +1,106 @@
+# -*- coding: utf-8 -*-
+from openerp.http import request as r
+
+def get_account_move_line_widget():
+    user_store = r.env.user.store_id.id
+    company_currency_rate = r.env.user.company_id.currency_id.rate
+    query = '''
+        SELECT
+            invoice.number,
+            line.date_maturity,
+            partner.name,
+            partner.ruc,
+            line.credit,
+            line.debit,
+            invoice.id,
+            line.reconcile_ref,
+            line.reconcile_id,
+            line.reconcile_partial_id,
+            invoice.amount_total AS invoice_amount,
+            CASE
+        		WHEN invoice.currency_id IS NULL
+        		THEN invoice.amount_total
+        		ELSE invoice.amount_total * (%s / (array_agg(rate.rate ORDER BY rate.name DESC))[1])
+        		END AS invoice_amount_currency
+        -- INVOICE
+        FROM account_invoice AS invoice
+        -- JOURNAL/STORE
+        LEFT JOIN res_store_journal_rel AS journal_rel
+        ON journal_rel.journal_id = invoice.journal_id
+        -- JOURNAL
+        LEFT JOIN account_journal AS journal
+        ON journal.id = invoice.journal_id
+	-- MOVE
+        LEFT JOIN account_move AS move
+        ON move.name = invoice.number
+	-- MOVE LINE
+        LEFT JOIN account_move_line AS line
+        ON line.move_id = move.id AND line.date_maturity IS NOT NULL
+        -- PARTNER
+        LEFT JOIN res_partner AS partner
+        ON line.partner_id = partner.id
+        -- RATE
+        LEFT JOIN res_currency_rate AS rate
+        ON rate.currency_id = invoice.currency_id
+        --WHERE
+        WHERE invoice.state = 'open'
+        AND journal_rel.store_id = ''' + str(user_store) + '''
+        --AND invoice.id = 20940
+        GROUP BY    invoice.number,
+                    line.date_maturity,
+                    partner.name,
+                    partner.ruc,
+                    line.credit,
+                    line.debit,
+                    invoice.id,
+                    line.reconcile_ref,
+                    line.reconcile_id,
+                    line.reconcile_partial_id,
+                    invoice.amount_total,
+                    journal.currency,
+                    journal.name
+
+    '''
+
+    r.cr.execute(query,(tuple([company_currency_rate])))
+    return [
+        {
+            'number': j[0],
+            'date_maturity': j[1],
+            'partner': j[2],
+            'ruc': j[3],
+            'credit': j[4],
+            'debit': j[5],
+            'invoice_id': j[6],
+            'reconcile_ref': j[7],
+            'reconcile_id': j[8],
+            'reconcile_partial_id': j[9],
+            'amount_total': j[10],
+            'amount_total_currency': j[11],
+        } for j in r.cr.fetchall()
+    ]
+
+def get_account_move_line_partial_reconcile():
+    query = '''
+        SELECT
+        	journal.id,
+        	line.reconcile_partial_id,
+            line.credit,
+            line.debit
+        FROM account_journal AS journal
+        LEFT JOIN account_move_line AS line
+        ON line.journal_id = journal.id
+        WHERE journal.type in ('bank','cash')
+        AND line.reconcile_partial_id IS NOT NULL
+    '''
+
+    r.cr.execute(query)
+
+    return [
+        {
+            'journal_id': j[0],
+            'reconcile_partial_id': j[1],
+            'credit': j[2],
+            'debit': j[3],
+        } for j in r.cr.fetchall()
+    ]

二進制
controllers/helpers/account_move_line.pyc


+ 55 - 0
controllers/helpers/account_voucher_multi_store.py

@@ -0,0 +1,55 @@
+# -*- coding: utf-8 -*-
+from openerp.http import request as r
+
+def get_account_voucher_multi_store_widget():
+    company_currency_rate = r.env.user.company_id.currency_id.rate
+    query = '''
+        SELECT  voucher.id,
+        	voucher.number,
+        	voucher.type,
+        	voucher.date,
+        	journal.currency,
+            voucher.amount,
+        	CASE
+        		WHEN journal.currency IS NULL
+        		THEN voucher.amount
+        		ELSE voucher.amount * (%s / (array_agg(rate.rate ORDER BY rate.name DESC))[1])
+        		END AS amount_currency,
+            voucher.journal_id,
+            voucher.reference
+        FROM account_voucher AS voucher
+        LEFT JOIN res_store_journal_rel AS journal_rel
+        ON voucher.journal_id = journal_rel.journal_id
+        LEFT JOIN account_journal AS journal
+        ON journal.id = voucher.journal_id
+        LEFT JOIN res_currency_rate AS rate
+        ON rate.currency_id = journal.currency
+        LEFT JOIN account_invoice AS invoice
+        ON invoice.number = voucher.reference
+        WHERE voucher.state = 'posted'
+        AND invoice.state = 'open'
+        GROUP BY
+        	voucher.id,
+        	voucher.number,
+        	voucher.type,
+        	voucher.date,
+        	voucher.amount,
+        	journal.name,
+        	journal.currency
+    '''
+
+    r.cr.execute(query,(tuple([company_currency_rate])))
+
+    return [
+        {
+            'id': j[0],
+            'number': j[1],
+            'type': j[2],
+            'date': j[3],
+            'currency_id': j[4],
+            'amount': j[5],
+            'amount_currency': j[6],
+            'journal_id': j[7],
+            'reference': j[8],
+        } for j in r.cr.fetchall()
+    ]

二進制
controllers/helpers/account_voucher_multi_store.pyc


+ 49 - 0
controllers/helpers/company.py

@@ -0,0 +1,49 @@
+# -*- coding: utf-8 -*-
+from openerp.http import request as r
+
+def get_company_info():
+    user_company = r.env.user.company_id.id
+    user_store = r.env.user.store_id.id
+    query = '''
+    SELECT
+        company.id,
+        company.name,
+        currency.id,
+        currency.name,
+        currency.symbol,
+        currency.decimal_places,
+        currency.decimal_separator,
+        currency.thousands_separator,
+        currency.symbol_position,
+        store.name AS store,
+        partner.ruc AS company_ruc
+    FROM res_company AS company
+    LEFT JOIN res_currency AS currency
+    ON company.currency_id = currency.id
+    LEFT JOIN res_store AS store
+    ON store.id = ''' + str(user_store) + '''
+    LEFT JOIN res_partner AS partner
+    ON partner.id = company.partner_id
+    WHERE currency.active = true
+    AND company.id = ''' + str(user_company) + '''
+    '''
+
+    r.cr.execute(query)
+
+    return [
+        {
+            'id': j[0],
+            'name': j[1],
+            'currency_id':{
+                'id':  j[2],
+                'name':  j[3],
+                'symbol':  j[4],
+                'decimal_places':  j[5],
+                'decimal_separator':  j[6],
+                'thousands_separator':  j[7],
+                'symbol_position':  j[8],
+            },
+            'store': j[9],
+            'ruc': j[10],
+        } for j in r.cr.fetchall()
+    ]

二進制
controllers/helpers/company.pyc


+ 29 - 0
controllers/helpers/dashboard_objetive.py

@@ -0,0 +1,29 @@
+# -*- coding: utf-8 -*-
+from openerp.http import request as r
+
+def get_dashboard_objetive_widget():
+    user_store = r.env.user.store_id.id
+    query = '''
+    SELECT  store_id,
+            date,
+            sale_objetive,
+            purchase_limit,
+            expense_limit,
+            payment_objetive
+    FROM dashboard_objetive
+    WHERE TO_CHAR(date,'YYYY-MM') = TO_CHAR(current_date,'YYYY-MM')
+    AND store_id = ''' + str(user_store) + '''
+    '''
+
+    r.cr.execute(query)
+
+    return [
+        {
+            'store_id': j[0],
+            'date': j[1],
+            'sale_objetive': j[2],
+            'purchase_limit': j[3],
+            'expense_limit': j[4],
+            'payment_objetive': j[5]
+        } for j in r.cr.fetchall()
+    ]

二進制
controllers/helpers/dashboard_objetive.pyc


+ 69 - 0
controllers/helpers/hr_employee.py

@@ -0,0 +1,69 @@
+# -*- coding: utf-8 -*-
+from openerp.http import request as r
+
+def get_hr_payslip_widget():
+    user_store = r.env.user.store_id.id
+
+    validate = '''
+        SELECT EXISTS(
+            SELECT table_name
+            FROM information_schema.columns
+            WHERE table_schema='public'
+                AND table_name='hr_employee'
+                AND column_name='store_id')
+    '''
+
+    query = '''
+        SELECT
+        	payslip.id,
+        	payslip.number,
+        	payslip.create_date,
+        	employee.name_related,
+        	payslip_line.code,
+        	CASE
+        		WHEN payslip_line.code = 'NET'
+        		THEN payslip_line.total
+        		END AS NETO,
+        	CASE
+        		WHEN payslip_line.code = 'IPSC'
+        		THEN payslip_line.total
+        		END AS IPSC,
+        	CASE
+        		WHEN payslip_line.code = 'IPSE'
+        		THEN payslip_line.total
+        		END AS IPSE
+        FROM hr_payslip AS payslip
+        LEFT JOIN hr_employee AS employee
+        ON employee.id = payslip.employee_id
+        LEFT JOIN hr_payslip_line AS payslip_line
+        ON payslip_line.slip_id = payslip.id
+        WHERE TO_CHAR(payslip.date_to,'YYYY-MM') = TO_CHAR(current_date,'YYYY-MM')
+        AND payslip.state in ('done','paid')
+        AND payslip_line.code in ('NET','IPSC','IPSE')
+        AND employee.store_id = ''' + str(user_store) + '''
+        GROUP BY
+        	payslip.id,
+        	payslip.number,
+        	employee.name_related,
+        	payslip_line.code,
+        	payslip_line.total
+    '''
+
+    r.cr.execute(validate)
+    for j in r.cr.fetchall():
+        band = j[0]
+
+    if band == True:
+        r.cr.execute(query)
+        return [
+            {
+                'payslip_id': j[0],
+                'number': j[1],
+                'date': j[2],
+                'name': j[3],
+                'code': j[4],
+                'net': j[5],
+                'ipsc': j[6],
+                'ipse': j[7],
+            } for j in r.cr.fetchall()
+        ]

二進制
controllers/helpers/hr_employee.pyc


+ 17 - 0
controllers/helpers/ir_module_module.py

@@ -0,0 +1,17 @@
+# -*- coding: utf-8 -*-
+from openerp.http import request as r
+
+def get_ir_module_widget():
+    query = '''
+    SELECT name
+    FROM ir_module_module
+    WHERE state = 'installed'
+    '''
+
+    r.cr.execute(query)
+
+    return [
+        {
+            'name': j[0],
+        } for j in r.cr.fetchall()
+    ]

二進制
controllers/helpers/ir_module_module.pyc


+ 27 - 0
controllers/helpers/journal.py

@@ -0,0 +1,27 @@
+# -*- coding: utf-8 -*-
+from openerp.http import request as r
+
+def get_journals_widget():
+    user_store = r.env.user.store_id.id
+    query = '''
+    SELECT aj.id, aj.name, aj.code, aj.type, rs.store_id
+    FROM account_journal AS aj
+    LEFT JOIN res_store_journal_rel AS rs
+    ON aj.id = rs.journal_id
+    WHERE aj.active = true
+    AND aj.type in ('sale', 'purchase', 'sale_refund', 'purchase_refund')
+    AND rs.store_id = ''' + str(user_store) + '''
+    ORDER BY aj.id
+    '''
+
+    r.cr.execute(query)
+
+    return [
+        {
+            'id': j[0],
+            'name': j[1],
+            'code': j[2],
+            'type': j[3],
+            'store_id': j[4]
+        } for j in r.cr.fetchall()
+    ]

二進制
controllers/helpers/journal.pyc


+ 43 - 0
controllers/helpers/payments_journals.py

@@ -0,0 +1,43 @@
+# -*- coding: utf-8 -*-
+from openerp.http import request as r
+
+def get_payments_journals_widget():
+    user_store = r.env.user.store_id.id
+    query = '''
+    SELECT
+            journal.id,
+            journal.name,
+            journal.code,
+            journal.type,
+            journal.currency,
+            journal_rel.store_id,
+            currency.symbol,
+            currency.decimal_places,
+            currency.thousands_separator,
+            currency.decimal_separator
+    FROM account_journal AS journal
+    LEFT JOIN res_store_journal_rel AS journal_rel
+    ON journal_rel.journal_id = journal.id
+    LEFT JOIN res_currency AS currency
+    ON currency.id = journal.currency
+    WHERE journal.active = true
+    AND journal.type in ('bank', 'cash')
+    AND journal_rel.store_id = ''' + str(user_store) + '''
+    '''
+
+    r.cr.execute(query)
+
+    return [
+        {
+            'id': j[0],
+            'name': j[1],
+            'code': j[2],
+            'type': j[3],
+            'currency': j[4],
+            'store_id': j[5],
+            'symbol': j[6],
+            'decimal_places': j[7],
+            'thousands_separator': j[8],
+            'decimal_separator': j[9],
+        } for j in r.cr.fetchall()
+    ]

二進制
controllers/helpers/payments_journals.pyc


+ 45 - 0
controllers/helpers/pos_order.py

@@ -0,0 +1,45 @@
+# -*- coding: utf-8 -*-
+from openerp.http import request as r
+
+def get_pos_order_widget():
+    user_store = r.env.user.store_id.id
+
+    validate = '''
+        SELECT EXISTS(
+            SELECT table_name
+            FROM information_schema.columns
+            WHERE table_schema='public'
+                AND table_name='pos_order')
+    '''
+
+    query = '''
+        SELECT pos.create_date, pos.name, pos.partner_id, pos.user_id, SUM(line.price_subtotal_incl) as amount
+        FROM pos_order as pos
+        LEFT JOIN res_store_journal_rel as journal
+        ON journal.journal_id = pos.sale_journal
+        LEFT JOIN pos_order_line AS line
+        ON line.order_id = pos.id
+        --LEFT JOIN res_partner AS partner
+        --ON partner.id = pos.partner_id
+        WHERE TO_CHAR(pos.date_order,'YYYY-MM') = TO_CHAR(current_date,'YYYY-MM')
+        AND journal.store_id = ''' + str(user_store)+ '''
+        GROUP BY pos.create_date, pos.partner_id, pos.user_id, pos.name
+    '''
+
+    r.cr.execute(validate)
+    for j in r.cr.fetchall():
+        band = j[0]
+
+    if band == True:
+        r.cr.execute(query)
+        return [
+            {
+                'date': j[0],
+                'name': j[1],
+                'customer_id': j[2],
+                'user_id': j[3],
+                'amount': j[4],
+            } for j in r.cr.fetchall()
+        ]
+    else:
+        return []

二進制
controllers/helpers/pos_order.pyc


+ 76 - 0
controllers/helpers/pos_order_line.py

@@ -0,0 +1,76 @@
+# -*- coding: utf-8 -*-
+from openerp.http import request as r
+
+def get_pos_order_line_widget():
+    user_store = r.env.user.store_id.id
+    company_currency_rate = r.env.user.company_id.currency_id.rate
+
+    validate = '''
+        SELECT EXISTS(
+            SELECT table_name
+            FROM information_schema.columns
+            WHERE table_schema='public'
+                AND table_name='pos_order')
+    '''
+
+    query = '''
+        SELECT
+        	pos.id,
+        	line.id,
+        	pos.name,
+        	product.name_template,
+        	line.price_unit,
+        	line.qty,
+        	line.price_subtotal,
+        	(line.price_unit * line.qty) - line.price_subtotal AS impuestos,
+        	(array_agg(history.cost ORDER BY history.id DESC))[1] AS cost,
+            history.product_template_id,
+            pos.date_order
+        FROM pos_order AS pos
+        LEFT JOIN pos_order_line AS line
+        ON pos.id = line.order_id
+        LEFT JOIN res_store_journal_rel as journal
+        ON journal.journal_id = pos.sale_journal
+        LEFT JOIN product_product as product
+        ON product.id = line.product_id
+        LEFT JOIN product_price_history AS history
+        ON history.product_template_id = product.product_tmpl_id
+        WHERE TO_CHAR(pos.date_order,'YYYY-MM') = TO_CHAR(current_date,'YYYY-MM')
+        AND journal.store_id = ''' + str(user_store) + '''
+        AND pos.state NOT IN ('draft')
+        GROUP BY
+        	pos.id,
+        	line.id,
+        	pos.name,
+        	product.name_template,
+        	line.price_unit,
+        	line.qty,
+        	line.price_subtotal,
+            history.product_template_id,
+            pos.date_order
+    '''
+
+    r.cr.execute(validate)
+
+    for j in r.cr.fetchall():
+        band = j[0]
+
+    if band == True:
+        r.cr.execute(query,(tuple([company_currency_rate])))
+        return [
+            {
+                'order_id': j[0],
+                'order_line_id': j[1],
+                'name': j[2],
+                'product_name':j[3],
+                'price_unit':j[4],
+                'quantity':j[5],
+                'subtotal':j[6],
+                'tax': j[7],
+                'cost': j[8],
+                'history_id': j[9],
+                'date': j[10],
+            } for j in r.cr.fetchall()
+        ]
+    else:
+        return []

二進制
controllers/helpers/pos_order_line.pyc


+ 48 - 0
controllers/helpers/pos_order_multi_store.py

@@ -0,0 +1,48 @@
+# -*- coding: utf-8 -*-
+from openerp.http import request as r
+
+def get_pos_order_multi_store_widget():
+    user_store = r.env.user.store_id.id
+
+    validate = '''
+        SELECT EXISTS(
+            SELECT table_name
+            FROM information_schema.columns
+            WHERE table_schema='public'
+                AND table_name='pos_order')
+    '''
+
+    query = '''
+        SELECT  pos.create_date,
+                pos.name,
+                pos.partner_id,
+                pos.user_id,
+                SUM(line.price_subtotal_incl) as amount,
+                journal.store_id
+        FROM pos_order as pos
+        LEFT JOIN res_store_journal_rel as journal
+        ON journal.journal_id = pos.sale_journal
+        LEFT JOIN pos_order_line AS line
+        ON line.order_id = pos.id
+        WHERE TO_CHAR(pos.date_order,'YYYY-MM') = TO_CHAR(current_date,'YYYY-MM')
+        GROUP BY pos.create_date, pos.partner_id, pos.user_id, pos.name, journal.store_id
+    '''
+
+    r.cr.execute(validate)
+    for j in r.cr.fetchall():
+        band = j[0]
+
+    if band == True:
+        r.cr.execute(query)
+        return [
+            {
+                'date': j[0],
+                'name': j[1],
+                'customer_id': j[2],
+                'user_id': j[3],
+                'amount': j[4],
+                'store_id': j[5],
+            } for j in r.cr.fetchall()
+        ]
+    else:
+        return []

二進制
controllers/helpers/pos_order_multi_store.pyc


+ 22 - 0
controllers/helpers/res_partner.py

@@ -0,0 +1,22 @@
+# -*- coding: utf-8 -*-
+from openerp.http import request as r
+
+def get_customer_info():
+    user_company = r.env.user.company_id.id
+    query = '''
+    SELECT
+        partner.id,
+        partner.name
+    FROM res_partner AS partner
+    WHERE partner.active = true
+    AND partner.customer = true
+    '''
+
+    r.cr.execute(query)
+
+    return [
+        {
+            'id': j[0],
+            'name': j[1],
+        } for j in r.cr.fetchall()
+    ]

二進制
controllers/helpers/res_partner.pyc


+ 18 - 0
controllers/helpers/res_store.py

@@ -0,0 +1,18 @@
+# -*- coding: utf-8 -*-
+from openerp.http import request as r
+
+def get_res_store_widget():
+    query = '''
+    SELECT  id,
+            name
+    FROM res_store
+    '''
+
+    r.cr.execute(query)
+
+    return [
+        {
+            'id': j[0],
+            'name': j[1],
+        } for j in r.cr.fetchall()
+    ]

二進制
controllers/helpers/res_store.pyc


+ 22 - 0
controllers/helpers/res_user.py

@@ -0,0 +1,22 @@
+# -*- coding: utf-8 -*-
+from openerp.http import request as r
+
+def get_user_info():
+    query = '''
+    SELECT
+        users.id,
+        partner.name
+    FROM res_users AS users
+    LEFT JOIN res_partner AS partner
+    ON partner.id = users.partner_id
+    WHERE users.active = true
+    '''
+
+    r.cr.execute(query)
+
+    return [
+        {
+            'id': j[0],
+            'name': j[1],
+        } for j in r.cr.fetchall()
+    ]

二進制
controllers/helpers/res_user.pyc


+ 55 - 0
controllers/helpers/voucher.py

@@ -0,0 +1,55 @@
+# -*- coding: utf-8 -*-
+from openerp.http import request as r
+
+def get_account_voucher_widget():
+    user_store = r.env.user.store_id.id
+    company_currency_rate = r.env.user.company_id.currency_id.rate
+    query = '''
+    SELECT  voucher.id,
+        	voucher.number,
+        	voucher.type,
+        	voucher.date,
+        	journal.currency,
+            voucher.amount,
+        	CASE
+        		WHEN journal.currency IS NULL
+        		THEN voucher.amount
+        		ELSE voucher.amount * (%s / (array_agg(rate.rate ORDER BY rate.name DESC))[1])
+        		END AS amount_currency,
+            voucher.journal_id,
+            voucher.reference
+        FROM account_voucher AS voucher
+        LEFT JOIN res_store_journal_rel AS journal_rel
+        ON voucher.journal_id = journal_rel.journal_id
+        LEFT JOIN account_journal AS journal
+        ON journal.id = voucher.journal_id
+        LEFT JOIN res_currency_rate AS rate
+        ON rate.currency_id = journal.currency
+        WHERE voucher.state = 'posted'
+        AND TO_CHAR(voucher.date,'YYYY-MM') = TO_CHAR(current_date,'YYYY-MM')
+        AND journal_rel.store_id = ''' + str(user_store) + '''
+        GROUP BY
+        	voucher.id,
+        	voucher.number,
+        	voucher.type,
+        	voucher.date,
+        	voucher.amount,
+        	journal.name,
+        	journal.currency
+    '''
+
+    r.cr.execute(query,(tuple([company_currency_rate])))
+
+    return [
+        {
+            'id': j[0],
+            'number': j[1],
+            'type': j[2],
+            'date': j[3],
+            'currency_id': j[4],
+            'amount': j[5],
+            'amount_currency': j[6],
+            'journal_id': j[7],
+            'reference': j[8],
+        } for j in r.cr.fetchall()
+    ]

二進制
controllers/helpers/voucher.pyc


+ 27 - 0
controllers/helpers/widget_list.py

@@ -0,0 +1,27 @@
+# -*- coding: utf-8 -*-
+from openerp.http import request as r
+
+def get_widget_list():
+    user_id = r.env.user.id
+    query = '''
+        SELECT
+        	--users.id,
+        	--partner.name,
+        	chart.name
+        FROM res_users AS users
+        --LEFT JOIN res_partner AS partner
+        --ON partner.id = users.partner_id
+        LEFT JOIN chart_list_users_rel AS chart_rel
+        ON chart_rel.user_id = users.id
+        LEFT JOIN chart_list AS chart
+        ON chart.id = chart_rel.chart_id
+        WHERE users.id = ''' + str(user_id) + '''
+    '''
+
+    r.cr.execute(query)
+
+    return [
+        {
+            'name': j[0],
+        } for j in r.cr.fetchall()
+    ]

二進制
controllers/helpers/widget_list.pyc


+ 56 - 0
controllers/helpers/widget_stock_valuation.py

@@ -0,0 +1,56 @@
+# -*- coding: utf-8 -*-
+from openerp.http import request as r
+
+def get_stock_valuation():
+    user_store = r.env.user.store_id.id
+    query = '''
+        SELECT
+        	location.id,
+        	quant.id,
+        	quant.qty,
+        	quant.product_id,
+        	(array_agg(history.cost ORDER BY history.id DESC))[1] * quant.qty as cost
+        FROM stock_location AS location
+        LEFT JOIN stock_quant AS quant
+        ON quant.location_id = location.id
+        LEFT JOIN product_product AS product
+        ON product.id = quant.product_id
+        LEFT JOIN product_price_history AS history
+        ON history.product_template_id = product.product_tmpl_id
+        WHERE location.usage = 'internal'
+        AND location.active = TRUE
+        AND product.active = TRUE
+        AND location.store_id = ''' + str(user_store) + '''
+        GROUP BY
+        	location.id,
+        	quant.id,
+        	quant.qty,
+        	quant.product_id
+    '''
+
+    r.cr.execute(query)
+
+    data = [
+        {
+            'location_id': j[0],
+            'quant_id': j[1],
+            'qty': j[2],
+            'product_id': j[3],
+            'cost': j[4],
+        } for j in r.cr.fetchall()
+    ]
+
+    values = []
+    valuation = 0
+    quantity = 0
+
+    for item in data:
+        valuation += item['cost']
+        quantity += item['qty']
+
+    values.append({
+        'valuation': valuation,
+        'quantity': quantity,
+    })
+
+    return values

二進制
controllers/helpers/widget_stock_valuation.pyc


+ 64 - 0
controllers/helpers/widget_stock_valuation_with_variant_cost_price.py

@@ -0,0 +1,64 @@
+# -*- coding: utf-8 -*-
+from openerp.http import request as r
+
+def get_stock_valuation_with_variant_cost_price():
+    user_store = r.env.user.store_id.id
+    query = '''
+        SELECT
+        	location.id,
+        	quant.id,
+        	quant.qty,
+        	quant.product_id,
+        	(array_agg(variant_cost_history.cost ORDER BY variant_cost_history.id DESC))[1] * quant.qty as variant_cost,
+        	(array_agg(template_cost_history.cost ORDER BY template_cost_history.id DESC))[1] * quant.qty as template_cost
+        FROM stock_location AS location
+        LEFT JOIN stock_quant AS quant
+        ON quant.location_id = location.id
+        LEFT JOIN product_product AS product
+        ON product.id = quant.product_id
+        LEFT JOIN product_price_history_product AS variant_cost_history
+        ON variant_cost_history.product_id = product.id
+        LEFT JOIN product_price_history AS template_cost_history
+        ON template_cost_history.product_template_id = product.product_tmpl_id
+        WHERE location.usage = 'internal'
+        AND location.active = TRUE
+        --AND product.active = TRUE
+        GROUP BY
+        	location.id,
+        	quant.id,
+        	quant.qty,
+        	quant.product_id
+    '''
+
+    r.cr.execute(query)
+
+    data = [
+        {
+            'location_id': j[0],
+            'quant_id': j[1],
+            'qty': j[2],
+            'product_id': j[3],
+            'variant_cost': j[4],
+            'template_cost': j[5],
+        } for j in r.cr.fetchall()
+    ]
+
+    # values = []
+    # valuation = 0
+    # quantity = 0
+    # value = 0;
+    #
+    # #
+    # for item in data:
+    #     value = item['cost']
+    #     valuation += value
+    #
+    #     # quantity += item['qty']
+    # print('*************************', valuation)
+    #
+    # # values.append({
+    # #     'valuation': valuation,
+    # #     'quantity': quantity,
+    # # })
+    return data
+    # return values

二進制
controllers/helpers/widget_stock_valuation_with_variant_cost_price.pyc


+ 153 - 0
controllers/main.py

@@ -0,0 +1,153 @@
+# -*- coding: utf-8 -*-
+from openerp import http
+from werkzeug.wrappers import Response
+from werkzeug.datastructures import Headers
+from gzip import GzipFile
+from StringIO import StringIO as IO
+import simplejson as json
+import helpers as hp
+import logging
+
+LOGGER = logging.getLogger(__name__)
+GZIP_COMPRESSION_LEVEL = 9
+
+def make_gzip_response(data=None, status=200):
+    gzip_buffer = IO()
+
+    with GzipFile(mode='wb', compresslevel=GZIP_COMPRESSION_LEVEL, fileobj=gzip_buffer) as gzip_file:
+        gzip_file.write(json.dumps(data))
+
+    value = gzip_buffer.getvalue()
+    gzip_buffer.close()
+
+    headers = Headers()
+    headers.add('Content-Encoding', 'gzip')
+    headers.add('Vary', 'Accept-Encoding')
+    headers.add('Content-Length', len(value))
+
+    return Response(value, status=status, headers=headers, content_type='application/json')
+
+class DashboardController(http.Controller):
+
+    @http.route('/dashboard-widgets', auth='user', methods=['GET', 'POST'])
+    def getWidgetList(self, **kw):
+        return make_gzip_response({
+            'widgets': hp.get_widget_list(),
+        })
+
+    @http.route('/dashboard-StockValuation', auth='user', methods=['GET', 'POST'])
+    def getStockValuation(self, **kw):
+
+        return make_gzip_response({
+            'valuation': hp.get_stock_valuation(),
+            'company': hp.get_company_info(),
+        })
+
+    @http.route('/dashboard-StockValuationWithVariantCostPrice', auth='user', methods=['GET', 'POST'])
+    def getStockValuationWithVariantCostPrice(self, **kw):
+
+        return make_gzip_response({
+            'valuation': hp.get_stock_valuation_with_variant_cost_price(),
+            'company': hp.get_company_info(),
+        })
+
+    @http.route('/dashboard-SalePurchaseExpense', auth='user', methods=['GET', 'POST'])
+    def getSalePurchaseExpense(self, **kw):
+
+        return make_gzip_response({
+            'company': hp.get_company_info(),
+            'modules': hp.get_ir_module_widget(),
+            'objetives':hp.get_dashboard_objetive_widget(),
+            'orders':hp.get_pos_order_widget(),
+            'invoices':hp.get_account_invoice_widget(),
+            'payslips': hp.get_hr_payslip_widget(),
+            'voucher': hp.get_account_voucher_widget(),
+        })
+
+    @http.route('/dashboard-Balance', auth='user', methods=['GET', 'POST'])
+    def getBalance(self, **kw):
+
+        return make_gzip_response({
+            'company': hp.get_company_info(),
+            'orders':hp.get_pos_order_widget(),
+            'invoices':hp.get_account_invoice_widget(),
+            'payslips': hp.get_hr_payslip_widget(),
+        })
+
+    @http.route('/dashboard-IncomeOutcome', auth='user', methods=['GET', 'POST'])
+    def getIncomeOutcome(self, **kw):
+        return make_gzip_response({
+            'company': hp.get_company_info(),
+            'journals':hp.get_payments_journals_widget(),
+            'vouchers':hp.get_account_voucher_widget(),
+            'statement_lines':hp.get_account_bank_statement_line_widget(),
+        })
+
+    @http.route('/dashboard-Account', auth='user', methods=['GET', 'POST'])
+    def getAccount(self, **kw):
+        return make_gzip_response({
+            'company': hp.get_company_info(),
+            'move_lines': hp.get_account_move_line_widget(),
+            'vouchers':hp.get_account_voucher_multi_store_widget(),
+            'reconcile_partials':hp.get_account_move_line_partial_reconcile(),
+        })
+
+    @http.route('/dashboard-BankCash', auth='user', methods=['GET', 'POST'])
+    def getBankCash(self, **kw):
+        return make_gzip_response({
+            'company': hp.get_company_info(),
+            'statements': hp.get_account_bank_statement_widget(),
+        })
+
+    @http.route('/dashboard-RankingCustomer', auth='user', methods=['GET', 'POST'])
+    def getRankingCustomer(self, **kw):
+
+        return make_gzip_response({
+            'company': hp.get_company_info(),
+            'partners': hp.get_customer_info(),
+            'orders':hp.get_pos_order_widget(),
+            'invoices':hp.get_account_invoice_widget(),
+        })
+
+    @http.route('/dashboard-RankingSalesman', auth='user', methods=['GET', 'POST'])
+    def getRankingSalesman(self, **kw):
+
+        return make_gzip_response({
+            'company': hp.get_company_info(),
+            'users': hp.get_user_info(),
+            'orders':hp.get_pos_order_widget(),
+            'invoices':hp.get_account_invoice_widget(),
+        })
+
+    @http.route('/dashboard-RankingSaleByStore', auth='user', methods=['GET', 'POST'])
+    def getRankingSaleByStore(self, **kw):
+
+        return make_gzip_response({
+            'company': hp.get_company_info(),
+            'users': hp.get_user_info(),
+            'orders':hp.get_pos_order_multi_store_widget(),
+            'invoices':hp.get_account_invoice_multi_store_widget(),
+            'stores':hp.get_res_store_widget(),
+        })
+
+    @http.route('/dashboard-FinancialState', auth='user', methods=['GET', 'POST'])
+    def getFinancialState(self, **kw):
+
+        return make_gzip_response({
+            'company': hp.get_company_info(),
+            'invoice_lines': hp.get_account_invoice_line_widget(),
+            'order_lines': hp.get_pos_order_line_widget(),
+            'payslips': hp.get_hr_payslip_widget(),
+        })
+
+    @http.route('/dashboard-rrhh', auth='user', methods=['GET', 'POST'])
+    def getRRHH(self, **kw):
+
+        return make_gzip_response({
+            'company': hp.get_company_info(),
+            'payslips': hp.get_hr_payslip_widget(),
+            'modules': hp.get_ir_module_widget(),
+            # 'orders':hp.get_pos_order_multi_store_widget(),
+            # 'invoices':hp.get_account_invoice_multi_store_widget(),
+            # 'stores':hp.get_res_store_widget(),
+        })

二進制
controllers/main.pyc


+ 101 - 0
data/charts_data.xml

@@ -0,0 +1,101 @@
+<?xml version="1.0" encoding="utf-8"?>
+<openerp>
+    <data>
+        <!--
+        =============================================================
+            VENTAS / COMPRAS / GASTOS
+        =============================================================
+        -->
+        <record model="chart.list" id="widget_sale_purchase_expense">
+            <field name="name">WidgetSalePurchaseExpense</field>
+        </record>
+        <!--
+        =============================================================
+            INGRESOS / EGRESOS
+        =============================================================
+        -->
+        <record model="chart.list" id="widget_income_outcome">
+            <field name="name">WidgetIncomeOutcome</field>
+        </record>
+        <!--
+        =============================================================
+            BALANCE
+        =============================================================
+        -->
+        <record model="chart.list" id="widget_balance">
+            <field name="name">WidgetBalance</field>
+        </record>
+        <!--
+        =======================================================
+            STOCK
+        =======================================================
+        -->
+        <record model="chart.list" id="widget_stock_valuation">
+            <field name="name">WidgetStockValuation</field>
+        </record>
+        <!--
+        =======================================================
+            STOCK WITH VARIANT COST PRICE
+        =======================================================
+        -->
+        <record model="chart.list" id="widget_stock_valuation_with_variant_cost_price">
+            <field name="name">WidgetStockValuationVariantCostPrice</field>
+        </record>
+        <!--
+        =======================================================
+            CUENTAS A COBRAR / A PAGAR
+        =======================================================
+        -->
+        <record model="chart.list" id="widget_account">
+            <field name="name">WidgetAccount</field>
+        </record>
+        <!--
+        =======================================================
+            BANCOS Y CAJA
+        =======================================================
+        -->
+        <record model="chart.list" id="widget_bank_cash">
+            <field name="name">WidgetBankCash</field>
+        </record>
+        <!--
+        =======================================================
+            RRHH
+        =======================================================
+        -->
+        <record model="chart.list" id="widget_rrhh">
+            <field name="name">WidgetRRHH</field>
+        </record>
+        <!--
+        =======================================================
+            RANKING DE CLIENTES
+        =======================================================
+        -->
+        <record model="chart.list" id="ranking_customer">
+            <field name="name">RankingCustomer</field>
+        </record>
+        <!--
+        =======================================================
+            RANKING DE VENDEDORES
+        =======================================================
+        -->
+        <record model="chart.list" id="ranking_salesman">
+            <field name="name">RankingSalesman</field>
+        </record>
+        <!--
+        =======================================================
+            RANKING DE VENTAS POR SUCURSAL
+        =======================================================
+        -->
+        <record model="chart.list" id="ranking_saleByStore">
+            <field name="name">RankingSaleByStore</field>
+        </record>
+        <!--
+        =======================================================
+            ESTADO FINANCIERO
+        =======================================================
+        -->
+        <record model="chart.list" id="widget_financial_state">
+            <field name="name">WidgetFinancialState</field>
+        </record>
+    </data>
+</openerp>

+ 4 - 0
models/__init__.py

@@ -0,0 +1,4 @@
+# -*- coding: utf-8 -*-
+import chart_config
+import model
+import rrhh

二進制
models/__init__.pyc


+ 18 - 0
models/chart_config.py

@@ -0,0 +1,18 @@
+# -*- encoding: utf-8 -*-
+from openerp import models, fields, api
+
+class ChartList(models.Model):
+    _name = 'chart.list'
+
+    name = fields.Char('Chart Name', required=True)
+    description = fields.Char('Description')
+
+
+class ResUsers(models.Model):
+	_inherit = 'res.users'
+
+	chart_ids = fields.Many2many('chart.list', 'chart_list_users_rel', 'user_id', 'chart_id', 'Charts')
+
+	@api.model
+	def get_user(self):
+		return self.env.user.id

二進制
models/chart_config.pyc


+ 17 - 0
models/model.py

@@ -0,0 +1,17 @@
+# -*- coding: utf-8 -*-
+from openerp import models, fields, api
+
+class DashboardObejetive(models.Model):
+    _name = 'dashboard.objetive'
+
+    store_id = fields.Many2one(
+        'res.store',
+        string='Sucursal',
+        ondelete='restrict'
+    )
+
+    date = fields.Date("Fecha")
+    sale_objetive = fields.Float(string="Objetivo de Ventas")
+    purchase_limit = fields.Float(string="Limite de Compras")
+    expense_limit = fields.Float(string="Limite de Gastos")
+    payment_objetive = fields.Float(string="Objetivo de Cobros")

二進制
models/model.pyc


+ 101 - 0
models/rrhh.py

@@ -0,0 +1,101 @@
+# -*- coding: utf-8 -*-
+
+from openerp import models, fields, api
+from datetime import datetime
+from pytz import timezone
+DATE_FORMAT = '%Y-%m'
+
+class HrEmployee(models.Model):
+	_inherit = 'hr.employee'
+
+	def get_timezone(self):
+		tz_name = self._context.get('tz') or self.env.user.tz
+		return timezone(tz_name)
+
+	def get_server_datetime(self):
+		return datetime.now(self.get_timezone()).strftime(DATE_FORMAT)
+
+	@api.model
+	def getHHRRWidget(self,domain):
+		user_store = self.env.user.store_id.id
+		domain.append(('store_id','=',user_store))
+		HrEmployee = self.env['hr.employee'].search(domain)
+		date_server = self.get_server_datetime()
+		values = []
+		employee_ids = []
+		for employee in HrEmployee:
+			total = 0
+			ipse = 0
+			amortized = 0
+			faltas = 0
+			residual = 0
+			HrPayslip = self.env['hr.payslip'].search([('employee_id','=',employee.id),('state','in',['done','paid']),('date_to','like',date_server)])
+			for payslip in HrPayslip:
+				HrPayslipLine = self.env['hr.payslip.line'].search([('slip_id','=',payslip.id),])
+				for line in HrPayslipLine:
+					if(line.code in ['BASIC','CMS']):
+						total += line.total
+					if(line.code in ['IPSE']):
+						ipse += (line.total*-1)
+					if(line.code in ['ADPER']):
+						amortized += (line.total*-1)
+					if(line.code in ['FALTAS']):
+						faltas += (line.total*-1)
+					if(line.code in ['NET']):
+						residual += line.total
+
+			values.append({
+				'id': employee.id,
+				'name': employee.name,
+				'identification_id': employee.identification_id,
+				'payslip': total - ipse - faltas,
+				'amortized': amortized,
+				'residual': residual,
+			})
+
+		return values
+
+	def get_account_account_ipsc (self, code):
+		return self.env['account.account'].search([('code', '=', code)])
+
+	@api.model
+	def getPayslipTotalWidget(self,domain):
+		HrPayslip = self.env['hr.payslip'].search(domain)
+		values = []
+		total = 0
+		ipse = 0
+		ipsc = 0
+		faltas = 0
+		amortized = 0
+		residual = 0
+		for payslip in HrPayslip:
+			HrPayslipLine = self.env['hr.payslip.line'].search([('slip_id','=',payslip.id)])
+			accountIpsC = self.get_account_account_ipsc('245000')
+			accountId= accountIpsC.id
+			for line in HrPayslipLine:
+				if(line.code in ['BASIC','CMS']):
+					total += line.total
+				if(line.code in ['FALTAS']):
+					faltas += (line.total*-1)
+				if(line.code in ['IPSE']):
+					AccountMoveLine = self.env['account.move.line'].search([('move_id','=',payslip.move_id.id),('partner_id', '=', payslip.employee_id.address_home_id.id),('reconcile_id','=',False),('credit','>',0)])
+					if(len(AccountMoveLine) > 0):
+						ipse += (line.total*-1)
+				if(line.code in ['IPSC']):
+					AccountMoveLine = self.env['account.move.line'].search([('move_id','=',payslip.move_id.id),('ref', '=', payslip.number),('reconcile_id', '=',False),('account_id', '=', accountId)])
+					if(len(AccountMoveLine) > 0):
+						ipsc += line.total
+				if(payslip.state == 'done'):
+					if(line.code in ['NET']):
+						residual += line.total
+		amortized = total - ipse - faltas - residual
+
+		values.append({
+			'payslip': total - ipse - faltas,
+			'amortized': amortized,
+			'residual': residual,
+			'ipse': ipse,
+			'ipsc': ipsc,
+		})
+
+		return values

二進制
models/rrhh.pyc


+ 7 - 0
security/ir.model.access.csv

@@ -0,0 +1,7 @@
+"id","name","model_id:id","group_id:id","perm_read","perm_write","perm_create","perm_unlink"
+
+"access_chart_config","chart.list","model_chart_list","base.group_partner_manager",1,1,1,1
+"access_chart_list_public","chart.list.public","model_chart_list",,1,0,0,0
+
+"access_dashboard_objetive_manager","dashboard.objetive","model_dashboard_objetive","base.group_partner_manager",1,1,1,1
+"access_dashboard_objetive_public","dashboard.objetive.public","model_dashboard_objetive",,1,0,0,0

二進制
static/description/icon.png


+ 53 - 0
static/src/css/circliful.css

@@ -0,0 +1,53 @@
+.circliful .outer {
+    fill: transparent;
+    stroke: #333;
+    stroke-width: 19.8;
+    stroke-dasharray: 534;
+    transition: stroke-dashoffset 1s;
+    -webkit-animation-play-state: running;
+    /* firefox bug fix - won't rotate at 90deg angles */
+    -moz-transform: rotate(-89deg) translateX(-190px);
+}
+
+/* full circle 25 empty 534 */
+.circliful .inner {
+    fill: transparent;
+    stroke: orange;
+    stroke-width: 20;
+    stroke-dasharray: 534;
+    transition: stroke-dashoffset 1s;
+    -webkit-animation-play-state: running;
+    /* firefox bug fix - won't rotate at 90deg angles */
+    -moz-transform: rotate(-89deg) translateX(-190px);
+    stroke-dashoffset: 0;
+}
+
+.circliful {
+    overflow: visible !important;
+
+}
+
+.svg-container {
+    width: 100%;
+    margin: 0 auto;
+    overflow: visible;
+    position: relative;
+}
+
+svg .icon {
+    font-family: FontAwesome;
+}
+
+.legend-line {
+    white-space: nowrap;
+}
+
+.color-box {
+    width: 15px;
+    height: 15px;
+    border-radius: 2px;
+    display: inline-block;
+    float: left;
+    padding-top: 3px;
+    margin: 2px 5px 0 0;
+}

+ 155 - 0
static/src/css/dashboard.css

@@ -0,0 +1,155 @@
+.dashboard {
+    width: 100%;
+    height: 100%;
+    padding: 10px 2px;
+    background: #f5f5f5;
+}
+.grid-stack.reporting-dashboard {
+    width: 100%;
+    height: 100%;
+}
+.grid-stack-item-content.reporting-dashboard {
+    overflow-y: hidden !important;
+}
+/*
+===================================================
+    GREEN
+===================================================
+*/
+.grid-stack-item-content.reporting-dashboard.green {
+    color: #43a047;
+    background: #fff;
+    padding: 5px;
+    border-bottom: 2px solid #43a047;
+}
+.grid-stack-item-content.reporting-dashboard.green > .widget-content > .row > .col-xs-4 > a {
+    color: #43a047;
+    font-size: 12pt;
+}
+.widget-green{
+    border-right: 2px solid #f5f5f5;
+    text-align: center;
+    color: #43a047;
+}
+.widget-green > a {
+    color: #43a047 !important;
+}
+
+.widget-yellow{
+    border-right: 2px solid #f5f5f5;
+    text-align: center;
+    color: #f9a825;
+}
+.widget-yellow > a {
+    color: #f9a825 !important;
+}
+.widget-orange{
+    text-align: center;
+    color: #ef6c00;
+}
+
+.widget-orange > a {
+    color: #ef6c00 !important;
+}
+
+.widget-blue{
+    text-align: center;
+    color: #0288d1;
+}
+
+.widget-blue > a {
+    color: #0288d1 !important;
+}
+/*
+===================================================
+    YELLOW
+===================================================
+*/
+.grid-stack-item-content.reporting-dashboard.yellow {
+    color: #f9a825;
+    background: #fff;
+    padding: 5px;
+    border-bottom: 2px solid #f9a825;
+}
+.grid-stack-item-content.reporting-dashboard.yellow > .widget-content > .row > .col-xs-4 > a {
+    color: #f9a825;
+    font-size: 15pt;
+}
+/*
+===================================================
+    ORANGE
+===================================================
+*/
+.grid-stack-item-content.reporting-dashboard.orange {
+    color: #ef6c00;
+    background: #fff;
+    padding: 5px;
+    border-bottom: 2px solid #ef6c00;
+}
+.grid-stack-item-content.reporting-dashboard.orange > .widget-content > .row > .col-xs-4 > a {
+    color: #ef6c00;
+    font-size: 15pt;
+}
+/*
+===================================================
+    BLUE
+===================================================
+*/
+.grid-stack-item-content.reporting-dashboard.blue {
+    color:#0288d1;
+    background: #fff;
+    padding: 5px;
+    border-bottom: 2px solid #0288d1;
+}
+.grid-stack-item-content.reporting-dashboard.blue > .widget-content > .row > .col-xs-4 > a {
+    color: #0288d1;
+    font-size: 15pt;
+}
+/*
+===================================================
+    UCRM
+===================================================
+*/
+.grid-stack-item-content.reporting-dashboard.ucrm {
+    color:#0288d1;
+    background: #fff;
+    padding: 5px;
+    border: 1px solid #e0e0e0;
+}
+.grid-stack-item-content.reporting-dashboard.ucrm > .widget-content > .row > .col-xs-4 > a {
+
+    font-size: 15pt;
+}
+
+.ui-state-disabled {
+    opacity: 1 !important;
+}
+
+/*
+===================================================
+    STATEMENT BODY
+===================================================
+*/
+
+.statement-body {
+    border:1px solid #e0e0e0;
+    height:5cm;
+    margin:10px;
+    font-size: 10pt;
+    text-align: center;
+    color: #43a047;
+}
+
+.statement-body > h3 > a{
+    color: #43a047 !important;
+}
+
+
+/* Columnas metas */
+.objectives{
+  font-size: 18px;
+  width: 25%;
+  position: absolute;
+  left: 50%;
+  line-height: 0.9;
+}

+ 412 - 0
static/src/js/chart.js

@@ -0,0 +1,412 @@
+function DashboardChart(widget) {
+    "use strict";
+
+    var model = openerp;
+
+    widget.DashboardChartWidget = widget.Base.extend({
+        BuildLineChart: function (name,label,data,CurrencyBase,linecolor,backgroundColor) {
+            var self = this;
+            Chart.scaleService.updateScaleDefaults('linear', {
+                ticks: {
+                    callback: function(tick) {
+                        return tick.toLocaleString('de-DE');
+                    }
+                }
+            });
+            Chart.defaults.global.tooltips.callbacks.label = function(tooltipItem, data) {
+                var dataset = data.datasets[tooltipItem.datasetIndex];
+                var datasetLabel = dataset.label || '';
+                return datasetLabel +  dataset.data[tooltipItem.index].toLocaleString('de-DE');
+            };
+            var chart = new Chart($(name), {
+                type: 'line',
+                data: {
+                    labels: label,
+                    datasets: [
+                        {
+                            label: false,
+                            data: data,
+                            backgroundColor: backgroundColor,
+                            borderColor: linecolor,
+                            borderWidth: 2,
+                            fill: false,
+                        }
+                    ]
+                },
+                options: {
+                    responsive: true,
+                    responsiveAnimationDuration:10,
+                    maintainAspectRatio:false,
+                    title: {
+                        display: false,
+                    },
+                    hover: {
+                        mode: 'nearest',
+                        intersect: true
+                    },
+                    legend: {
+                       display: false,
+                    },
+                    layout: {
+                        padding: {
+                            top: 0,
+                            bottom: 0,
+                            left : 0,
+                            rigth: 0,
+                        }
+                    },
+                    events: ['click'],
+                    tooltips: {
+                        callbacks: {
+                            label: function(tooltipItem, data) {
+                                var label = data.datasets[tooltipItem.datasetIndex].label || '';
+
+                                if (label) {
+                                    label += ': ';
+                                }
+                                label += accounting.formatMoney(tooltipItem.yLabel, CurrencyBase.symbol, CurrencyBase.decimal_places, CurrencyBase.thousands_separator, CurrencyBase.decimal_separator);
+                                return label;
+                            }
+                        }
+                    }
+                }
+            });
+        },
+
+        BuildBalanceLineChart: function (name,label,bodySale,bodyPurchase,bodyExpense,CurrencyBase) {
+            var self = this;
+            Chart.scaleService.updateScaleDefaults('linear', {
+                ticks: {
+                    callback: function(tick) {
+                        return tick.toLocaleString('de-DE');
+                    }
+                }
+            });
+            Chart.defaults.global.tooltips.callbacks.label = function(tooltipItem, data) {
+                var dataset = data.datasets[tooltipItem.datasetIndex];
+                var datasetLabel = dataset.label || '';
+                return datasetLabel +  dataset.data[tooltipItem.index].toLocaleString('de-DE');
+            };
+            var chartData = {
+    			labels: label,
+    			datasets: [ {
+    				type: 'bar',
+    				label: 'Ventas',
+                    borderColor: '#43a047',
+                    backgroundColor: '#43a047',
+    				borderWidth: 2,
+    				data: bodySale,
+                    fill: false,
+    			}, {
+    				type: 'bar',
+    				label: 'Compras',
+                    borderColor: '#f9a825',
+    				backgroundColor: '#f9a825',
+    				data: bodyPurchase,
+    				borderWidth: 2,
+                    fill: false,
+    			}, {
+    				type: 'bar',
+    				label: 'Gastos',
+                    borderColor: '#ef6c00',
+    				backgroundColor: '#ef6c00',
+    				data: bodyExpense,
+    				borderWidth: 2,
+                    fill: false,
+    			}]
+    		};
+            var chart = new Chart($(name),{
+                type: 'bar',
+                data: chartData,
+                options: {
+                    responsive: true,
+                    responsiveAnimationDuration:10,
+                    maintainAspectRatio:false,
+                    title: {
+                        display: false,
+                    },
+                    hover: {
+                        mode: 'nearest',
+                        intersect: true
+                    },
+                    legend: {
+                       display: true,
+                    },
+                    layout: {
+                        padding: {
+                            top: 0,
+                            bottom: 0,
+                            left : 0,
+                            rigth: 0,
+                        }
+                    },
+                    // events: ['click'],
+                    tooltips: {
+                        callbacks: {
+                            label: function(tooltipItem, data) {
+                                var label = data.datasets[tooltipItem.datasetIndex].label || '';
+
+                                if (label) {
+                                    label += ': ';
+                                }
+                                label += accounting.formatMoney(tooltipItem.yLabel, CurrencyBase.symbol, CurrencyBase.decimal_places, CurrencyBase.thousands_separator, CurrencyBase.decimal_separator);
+                                return label;
+                            }
+                        }
+                    }
+                }
+            });
+        },
+
+        BuildDoughnutChart: function (name,label,data,CurrencyBase) {
+            var self = this;
+            Chart.scaleService.updateScaleDefaults('linear', {
+                ticks: {
+                    callback: function(tick) {
+                        return tick.toLocaleString('de-DE');
+                    }
+                }
+            });
+            Chart.defaults.global.tooltips.callbacks.label = function(tooltipItem, data) {
+                var dataset = data.datasets[tooltipItem.datasetIndex];
+                var datasetLabel = dataset.label || '';
+                return datasetLabel +  dataset.data[tooltipItem.index].toLocaleString('de-DE');
+            };
+            var color = [];
+            color.push('#BCCEF4');
+            color.push('#A9E5E3');
+            color.push('#A0D995');
+            color.push('#C5D084');
+            color.push('#FAE187');
+            color.push('#FFBD82');
+            color.push('#FFA8B8');
+            color.push('#E5BEDD');
+            color.push('#C4ABFE');
+            color.push('#D8D8D8');
+            var myBar = new Chart($(name), {
+                type: 'doughnut',
+                data: {
+                    labels: label,
+                    datasets: [{
+                        backgroundColor: color,
+                        fill: true,
+                        label:false,
+                        data: data,
+                    }],
+                },
+                options: {
+                    responsive: true,
+                    responsiveAnimationDuration:10,
+                    maintainAspectRatio:false,
+                    hover: {
+                       mode: 'nearest',
+                       intersect: true
+                    },
+                    title: {
+                        display: false,
+                    },
+                    legend: {
+                        display: false,
+                        position: 'right',
+                    },
+                    layout: {
+                        padding: {
+                            top: 0,
+                            bottom: 0,
+                            left : 0,
+                            rigth: 0,
+                        }
+                    },
+                    tooltips: {
+                        callbacks: {
+                            label: function(tooltipItem, data) {
+                                var label = data.labels[tooltipItem.index] || '';
+                                if (label) {
+                                    label += ': ';
+                                }
+                                label += accounting.formatMoney(data.datasets[0].data[tooltipItem.index],{
+                                    symbol: CurrencyBase.symbol,
+                                    format: "%s%v",
+                                    precision: CurrencyBase.decimal_places,
+                                    thousand: CurrencyBase.thousands_separator,
+                                    decimal: CurrencyBase.decimal_separator,
+                                });
+                                return label;
+                            }
+                        }
+                    },
+                },
+            });
+        },
+    		// Chart.plugins.register({
+    		// 	afterDatasetsDraw: function(chart) {
+            //         var ctx = document.getElementById("myChart2").getContext("2d");
+            //         chart.data.datasets.forEach(function(dataset, i) {
+    		// 			var meta = chart.getDatasetMeta(i);
+    		// 			if (!meta.hidden) {
+    		// 				meta.data.forEach(function(element, index) {
+    		// 					ctx.fillStyle = '#fff';
+    		// 					var fontSize = 15;
+    		// 					var fontStyle = 'normal';
+    		// 					var fontFamily = 'Arial, Helvetica, sans-serif';
+    		// 					ctx.font = Chart.helpers.fontString(fontSize, fontStyle, fontFamily);
+    		// 					var dataString = dataset.data[index].toString();
+    		// 					ctx.textAlign = 'center';
+    		// 					ctx.textBaseline = 'middle';
+    		// 					var padding = 5;
+    		// 					var position = element.tooltipPosition();
+    		// 					ctx.fillText(dataString, position.x, position.y - (fontSize / 2) - padding);
+    		// 				});
+    		// 			}
+    		// 		});
+    		// 	}
+    		// });
+
+        BuildHorizontalBarChart: function (name,CurrencyBase,label,body) {
+            var self = this;
+            Chart.scaleService.updateScaleDefaults('linear', {
+                ticks: {
+                    callback: function(tick) {
+                        return tick.toLocaleString('de-DE');
+                    }
+                }
+            });
+            Chart.defaults.global.tooltips.callbacks.label = function(tooltipItem, data) {
+                var dataset = data.datasets[tooltipItem.datasetIndex];
+                var datasetLabel = dataset.label || '';
+                return datasetLabel +  dataset.data[tooltipItem.index].toLocaleString('de-DE');
+            };
+
+            // var color = [];
+            // color.push('#BCCEF4');
+            // color.push('#A9E5E3');
+            // color.push('#A0D995');
+            // color.push('#C5D084');
+            // color.push('#FAE187');
+            // color.push('#FFBD82');
+            // color.push('#FFA8B8');
+            // color.push('#E5BEDD');
+            // color.push('#C4ABFE');
+            // color.push('#D8D8D8');
+
+            var chart = new Chart($(name), {
+                type: 'horizontalBar',
+                data: {
+                    labels: label,
+                    datasets: [
+                        {
+                            data: body,
+                            // backgroundColor: '#43a047',
+                            backgroundColor: '#bbdefb',
+                            fill: true,
+                        }
+                    ]
+                },
+                options: {
+                    responsive: true,
+                    responsiveAnimationDuration:10,
+                    maintainAspectRatio:false,
+                    title: {
+                        display: false,
+                    },
+                    hover: {
+                        mode: 'nearest',
+                        intersect: true
+                    },
+                    legend: {
+                       display: false,
+                    },
+                    layout: {
+                        padding: {
+                            top: 0,
+                            bottom: 0,
+                            left : 0,
+                            rigth: 0,
+                        }
+                    },
+                    tooltips: {
+                        callbacks: {
+                            label: function(tooltipItem, data) {
+                                var label = data.datasets[tooltipItem.datasetIndex].label || '';
+
+                                if (label) {
+                                    label += ': ';
+                                }
+                                label += accounting.formatMoney(tooltipItem.xLabel, CurrencyBase.symbol, CurrencyBase.decimal_places, CurrencyBase.thousands_separator, CurrencyBase.decimal_separator);
+                                return label;
+                            }
+                        }
+                    },
+                    // events: ['click'],
+                }
+            });
+        },
+
+        BuildBarChart: function (name,CurrencyBase,label,body) {
+            var self = this;
+            Chart.scaleService.updateScaleDefaults('linear', {
+                ticks: {
+                    callback: function(tick) {
+                        return tick.toLocaleString('de-DE');
+                    }
+                }
+            });
+            Chart.defaults.global.tooltips.callbacks.label = function(tooltipItem, data) {
+                var dataset = data.datasets[tooltipItem.datasetIndex];
+                var datasetLabel = dataset.label || '';
+                return datasetLabel +  dataset.data[tooltipItem.index].toLocaleString('de-DE');
+            };
+            var chart = new Chart($(name), {
+                type: 'bar',
+                data: {
+                    labels: label,
+                    datasets: [
+                        {
+                            data: body,
+                            backgroundColor: '#bbdefb',
+                            fill: true,
+                        }
+                    ]
+                },
+                options: {
+                    responsive: true,
+                    responsiveAnimationDuration:10,
+                    maintainAspectRatio:false,
+                    title: {
+                        display: false,
+                    },
+                    hover: {
+                        mode: 'nearest',
+                        intersect: true
+                    },
+                    legend: {
+                       display: false,
+                    },
+                    layout: {
+                        padding: {
+                            top: 0,
+                            bottom: 0,
+                            left : 0,
+                            rigth: 0,
+                        }
+                    },
+                    tooltips: {
+                        callbacks: {
+                            label: function(tooltipItem, data) {
+                                var label = data.datasets[tooltipItem.datasetIndex].label || '';
+
+                                if (label) {
+                                    label += ': ';
+                                }
+                                label += accounting.formatMoney(tooltipItem.yLabel, CurrencyBase.symbol, CurrencyBase.decimal_places, CurrencyBase.thousands_separator, CurrencyBase.decimal_separator);
+                                return label;
+                            }
+                        }
+                    },
+                    // events: ['click'],
+                }
+            });
+        },
+
+    });
+}

+ 107 - 0
static/src/js/charts/ranking_customer.js

@@ -0,0 +1,107 @@
+function ranking_customer(widget) {
+    "use strict";
+
+    var model = openerp;
+    var Qweb = openerp.web.qweb;
+
+    widget.RankingCustomer = widget.Base.extend({
+        template: 'RankingCustomer',
+        init: function (parent) {
+            this._super(parent, {
+                width: 6,
+                height: 4
+            });
+        },
+        start: function () {
+            var self = this;
+            self.fetchInitial();
+        },
+        fetchInitial: function(){
+            var self = this;
+            self.fetchDataSQL().then(function (DataSQL) {
+                return DataSQL;
+            }).then(function(DataSQL) {
+                self.ResCompany = DataSQL.company;
+                self.AccountInvoice = DataSQL.invoices;
+                self.PosOrder = DataSQL.orders;
+                self.ResPartner = DataSQL.partners;
+                return self.CustomerRanking();
+            });
+        },
+        fetchDataSQL: function() {
+            var data = $.get('/dashboard-RankingCustomer');
+            return data;
+        },
+        getPosOrder:function(id) {
+            var self = this;
+            return _.flatten(_.filter(self.PosOrder,function (inv) {
+                return inv.customer_id === id;
+            }));
+        },
+        getAccountInvoice:function(id, type) {
+            var self = this;
+            return _.flatten(_.filter(self.AccountInvoice,function (inv) {
+                return inv.customer_id === id && inv.type == type;
+            }));
+        },
+
+        CustomerRanking: function() {
+            var self = this;
+            var ranking = [];
+            var ResPartner = self.ResPartner;
+            _.each(ResPartner, function (item) {
+                var PosOrder = self.getPosOrder(item.id);
+                var AccountInvoice = self.getAccountInvoice(item.id,'out_invoice');
+                var AccountInvoiceRefund = self.getAccountInvoice(item.id,'out_refund');
+                var pos_order_amount = _.reduce(_.map(PosOrder,function(item) {
+                    return item.amount;
+                }),function(memo, num) {
+                    return memo + num;
+                },0);
+                var account_invoice_amount = _.reduce(_.map(AccountInvoice,function(item) {
+                    return item.amount;
+                }),function(memo, num) {
+                    return memo + num;
+                },0);
+                var account_invoice_refund_amount = _.reduce(_.map(AccountInvoiceRefund,function(item) {
+                    return item.amount;
+                }),function(memo, num) {
+                    return memo + num;
+                },0);
+                var amount = (pos_order_amount + account_invoice_amount) - account_invoice_refund_amount;
+                if(amount > 0 ){
+                   ranking.push({
+                       name: item.name,
+                       amount: amount,
+                   });
+                }
+            });
+            ranking.sort(function(a, b) {
+                return b.amount - a.amount
+            });
+            self.BuildChart(ranking);
+        },
+        BuildChart: function (ranking){
+            var self = this;
+            var label = [];
+            var body = [];
+            var rank = 10;
+            var item;
+            var CurrencyBase = self.ResCompany[0].currency_id;
+            for (var i = 0; i < rank; i++) {
+                if (ranking[i]){
+                    item = ranking[i];
+                }else{
+                    item = {};
+                    item.name = "N/A";
+                    item.amount = 0;
+                }
+                label.push(item.name)
+                body.push(item.amount);
+            }
+            var name = '.ranking-horizontalBar-chart';
+            var chart = new widget.DashboardChartWidget(self);
+            chart.BuildHorizontalBarChart(name,CurrencyBase,label,body);
+        }
+    });
+}

+ 110 - 0
static/src/js/charts/ranking_sale_by_store.js

@@ -0,0 +1,110 @@
+function ranking_sale_by_store(widget) {
+    "use strict";
+
+    var model = openerp;
+    var Qweb = openerp.web.qweb;
+
+    widget.RankingSaleByStore = widget.Base.extend({
+        template: 'RankingSaleByStore',
+        init: function (parent) {
+            this._super(parent, {
+                width: 6,
+                height: 4
+            });
+        },
+        start: function () {
+            var self = this;
+            self.fetchInitial();
+        },
+        fetchInitial: function(){
+            var self = this;
+            self.fetchDataSQL().then(function (DataSQL) {
+                return DataSQL;
+            }).then(function(DataSQL) {
+                self.ResCompany = DataSQL.company;
+                self.AccountInvoice = DataSQL.invoices;
+                self.PosOrder = DataSQL.orders;
+                self.ResStore = DataSQL.stores;
+                return self.StoreRanking();
+            });
+        },
+        fetchDataSQL: function() {
+            var data = $.get('/dashboard-RankingSaleByStore');
+            return data;
+        },
+        getPosOrder:function(id) {
+            var self = this;
+            return _.flatten(_.filter(self.PosOrder,function (inv) {
+                return inv.store_id === id;
+            }));
+        },
+        getAccountInvoice:function(id) {
+            var self = this;
+            return _.flatten(_.filter(self.AccountInvoice,function (inv) {
+                return inv.store_id === id && inv.type == 'out_invoice' && inv.origin.match(/SO/) != null;
+            }));
+        },
+        getAccountInvoiceRefund:function(id) {
+            var self = this;
+            return _.flatten(_.filter(self.AccountInvoice,function (inv) {
+                return inv.store_id === id && inv.type == 'out_refund';
+            }));
+        },
+        StoreRanking: function() {
+            var self = this;
+            var ranking = [];
+            _.each(self.ResStore, function (item) {
+                var PosOrder = self.getPosOrder(item.id);
+                var AccountInvoice = self.getAccountInvoice(item.id);
+                var AccountInvoiceRefund = self.getAccountInvoiceRefund(item.id);
+                var pos_order_amount = _.reduce(_.map(PosOrder,function(item) {
+                    var utc = moment.utc(item.date,'YYYY-MM-DD h:mm:ss A');
+                    utc = moment(utc._d).format('YYYY-MM-DD');
+                    var value = 0;
+                    if(moment(utc).format('YYYY-MM') == moment().format('YYYY-MM')){
+                        value = item.amount;
+                    }
+                    return value;
+                }),function(memo, num) {
+                    return memo + num;
+                },0);
+                var account_invoice_amount = _.reduce(_.map(AccountInvoice,function(item) {
+                    return item.amount;
+                }),function(memo, num) {
+                    return memo + num;
+                },0);
+                var account_invoice_refund_amount = _.reduce(_.map(AccountInvoiceRefund,function(item) {
+                    return item.amount;
+                }),function(memo, num) {
+                    return memo + num;
+                },0);
+                var amount = (pos_order_amount + account_invoice_amount) - account_invoice_refund_amount;
+                if(amount > 0 ){
+                    ranking.push({
+                        name: item.name,
+                        amount: amount,
+                    });
+                }
+            });
+            ranking.sort(function(a, b) {
+                return b.amount - a.amount;
+            });
+            self.BuildChart(ranking);
+        },
+        BuildChart: function (ranking){
+            var self = this;
+            var label = [];
+            var body = [];
+            var item;
+            var CurrencyBase = self.ResCompany[0].currency_id;
+            for (var i = 0; i < ranking.length; i++) {
+                item = ranking[i];
+                label.push(item.name);
+                body.push(item.amount);
+            }
+            var name = '.ranking-sale-by-store-chart';
+            var chart = new widget.DashboardChartWidget(self);
+            chart.BuildHorizontalBarChart(name,CurrencyBase,label,body);
+        }
+    });
+}

+ 105 - 0
static/src/js/charts/ranking_salesman.js

@@ -0,0 +1,105 @@
+function ranking_salesman(widget) {
+    "use strict";
+
+    var model = openerp;
+    var Qweb = openerp.web.qweb;
+
+    widget.RankingSalesman = widget.Base.extend({
+        template: 'RankingSalesman',
+        init: function (parent) {
+            this._super(parent, {
+                width: 6,
+                height: 4
+            });
+        },
+        start: function () {
+            var self = this;
+            self.fetchInitial();
+        },
+        fetchInitial: function(){
+            var self = this;
+            self.fetchDataSQL().then(function (DataSQL) {
+                return DataSQL;
+            }).then(function(DataSQL) {
+                self.ResCompany = DataSQL.company;
+                self.AccountInvoice = DataSQL.invoices;
+                self.PosOrder = DataSQL.orders;
+                self.ResUser = DataSQL.users;
+                return self.SalesmanRanking();
+            });
+        },
+        fetchDataSQL: function() {
+            var data = $.get('/dashboard-RankingSalesman');
+            return data;
+        },
+        getPosOrder:function(id) {
+            var self = this;
+            return _.flatten(_.filter(self.PosOrder,function (inv) {
+                return inv.user_id === id;
+            }));
+        },
+        getAccountInvoice:function(id) {
+            var self = this;
+            return _.flatten(_.filter(self.AccountInvoice,function (inv) {
+                return inv.user_id === id && inv.type == 'out_invoice' && inv.origin.match(/SO/) != null;
+            }));
+        },
+        getAccountInvoiceRefund:function(id) {
+            var self = this;
+            return _.flatten(_.filter(self.AccountInvoice,function (inv) {
+                return inv.user_id === id && inv.type == 'out_refund';
+            }));
+        },
+        SalesmanRanking: function() {
+            var self = this;
+            var ranking = [];
+            var ResUser = self.ResUser;
+            _.each(ResUser, function (item) {
+                var PosOrder = self.getPosOrder(item.id);
+                var AccountInvoice = self.getAccountInvoice(item.id);
+                var AccountInvoiceRefund = self.getAccountInvoiceRefund(item.id);
+                var pos_order_amount = _.reduce(_.map(PosOrder,function(item) {
+                    return item.amount;
+                }),function(memo, num) {
+                    return memo + num;
+                },0);
+                var account_invoice_amount = _.reduce(_.map(AccountInvoice,function(item) {
+                    return item.amount;
+                }),function(memo, num) {
+                    return memo + num;
+                },0);
+                var account_invoice_refund_amount = _.reduce(_.map(AccountInvoiceRefund,function(item) {
+                    return item.amount;
+                }),function(memo, num) {
+                    return memo + num;
+                },0);
+                var amount = (pos_order_amount + account_invoice_amount) - account_invoice_refund_amount;
+                if(amount > 0 ){
+                   ranking.push({
+                       name: item.name,
+                       amount: amount,
+                   });
+                }
+            });
+            ranking.sort(function(a, b) {
+                return b.amount - a.amount
+            });
+            self.BuildChart(ranking);
+        },
+        BuildChart: function (ranking){
+            var self = this;
+            var label = [];
+            var body = [];
+            var item;
+            var CurrencyBase = self.ResCompany[0].currency_id;
+            for (var i = 0; i < ranking.length; i++) {
+                item = ranking[i];
+                label.push(item.name)
+                body.push(item.amount);
+            }
+            var name = '.ranking-salesman-chart';
+            var chart = new widget.DashboardChartWidget(self);
+            chart.BuildHorizontalBarChart(name,CurrencyBase,label,body);
+        }
+    });
+}

+ 217 - 0
static/src/js/dashboard.js

@@ -0,0 +1,217 @@
+function dashboard_reporting_widget (instance, widget) {
+    "use strict";
+
+    var widgets = widget;
+    var InstalledModule = 0;
+
+    widget.DashboardReportingWidget = instance.Widget.extend({
+        template: 'DashboardReportingTemplate',
+        widgets: [],
+        grid: {},
+
+        start: function () {
+            // instance.eiru_sidebar_toggler.sidebar_fold();
+            this.$el.find('.grid-stack.reporting-dashboard').gridstack({
+                animate: true,
+                disableResize: true,
+                disableDrag: true,
+            });
+            this.grid = this.$el.find('.grid-stack.reporting-dashboard').data('gridstack');
+            this.fetchInitial();
+        },
+
+        fetchInitial: function(){
+            var self = this;
+            self.fetchDataSQL().then(function (DataSQL) {
+                return DataSQL;
+            }).then(function(DataSQL) {
+                self.Widgets = DataSQL.widgets;
+                return self.RenderWidgets();
+            });
+        },
+
+        fetchDataSQL: function() {
+            var self = this;
+            var data = $.get('/dashboard-widgets');
+            return data;
+        },
+
+        RenderWidgets: function () {
+            var self = this;
+            var ChartList = self.Widgets;
+
+            /*
+            ==========================================================
+                VENTAS / COMPRAS / GASTOS
+            ==========================================================
+            */
+            var chart =  _.flatten(_.filter(ChartList,function (inv) {
+                return inv.name == 'WidgetSalePurchaseExpense';
+            }));
+            if(chart.length > 0){
+                var WidgetSalePurchaseExpense = new widgets.WidgetSalePurchaseExpense(self);
+                WidgetSalePurchaseExpense.renderElement();
+                WidgetSalePurchaseExpense.start();
+                self.grid.addWidget(WidgetSalePurchaseExpense.$el, 0, 0,WidgetSalePurchaseExpense.size.width,WidgetSalePurchaseExpense.size.height,true);
+            }
+
+
+            /*
+            ==========================================================
+                INGRESO / EGRESO
+            ==========================================================
+            */
+            var chart =  _.flatten(_.filter(ChartList,function (inv) {
+                return inv.name == 'WidgetIncomeOutcome';
+            }));
+            if(chart.length > 0){
+                var WidgetIncomeOutcome = new widgets.WidgetIncomeOutcome(self);
+                WidgetIncomeOutcome.renderElement();
+                WidgetIncomeOutcome.start();
+                self.grid.addWidget(WidgetIncomeOutcome.$el, 0, 0,WidgetIncomeOutcome.size.width,WidgetIncomeOutcome.size.height,true);
+            }
+            /*
+            ==========================================================
+                ANALISIS DE VENTAS
+            ==========================================================
+            */
+            var chart =  _.flatten(_.filter(ChartList,function (inv) {
+                return inv.name == 'WidgetFinancialState';
+            }));
+            if(chart.length > 0){
+
+                var WidgetFinancialState = new widgets.WidgetFinancialState(self);
+                WidgetFinancialState.renderElement();
+                WidgetFinancialState.start();
+                self.grid.addWidget(WidgetFinancialState.$el, 0, 0,WidgetFinancialState.size.width,WidgetFinancialState.size.height,true);
+            };
+
+            /*
+            ==========================================================
+                BALANCE
+            ==========================================================
+            */
+            var chart =  _.flatten(_.filter(ChartList,function (inv) {
+                return inv.name == 'WidgetBalance';
+            }));
+            if(chart.length > 0){
+                var WidgetBalance = new widgets.WidgetBalance(self);
+                WidgetBalance.renderElement();
+                WidgetBalance.start();
+                self.grid.addWidget(WidgetBalance.$el, 0, 0,WidgetBalance.size.width,WidgetBalance.size.height,true);
+            }
+            /*
+            ==========================================================
+                RANKING DE CLIENTES
+            ==========================================================
+            */
+            var chart =  _.flatten(_.filter(ChartList,function (inv) {
+                return inv.name == 'RankingCustomer';
+            }));
+            if(chart.length > 0){
+                var RankingCustomer = new widgets.RankingCustomer(self);
+                RankingCustomer.renderElement();
+                RankingCustomer.start();
+                self.grid.addWidget(RankingCustomer.$el, 0, 0,RankingCustomer.size.width,RankingCustomer.size.height,true);
+            }
+            /*
+            ==========================================================
+                RANKING DE VENDEDORES
+            ==========================================================
+            */
+            var chart =  _.flatten(_.filter(ChartList,function (inv) {
+                return inv.name == 'RankingSalesman';
+            }));
+            if(chart.length > 0){
+                var RankingSalesman = new widgets.RankingSalesman(self);
+                RankingSalesman.renderElement();
+                RankingSalesman.start();
+                self.grid.addWidget(RankingSalesman.$el, 0, 0,RankingSalesman.size.width,RankingSalesman.size.height,true);
+            }
+            /*
+            ==========================================================
+                VENTAS POR SURCURSAL
+            ==========================================================
+            */
+            var chart =  _.flatten(_.filter(ChartList,function (inv) {
+                return inv.name == 'RankingSaleByStore';
+            }));
+            if(chart.length > 0){
+                var RankingSaleByStore = new widgets.RankingSaleByStore(self);
+                RankingSaleByStore.renderElement();
+                RankingSaleByStore.start();
+                self.grid.addWidget(RankingSaleByStore.$el, 0, 0,RankingSaleByStore.size.width,RankingSaleByStore.size.height,true);
+            };
+            /*
+            ==========================================================
+                STOCK
+            ==========================================================
+            */
+            var chart =  _.flatten(_.filter(ChartList,function (inv) {
+                return inv.name == 'WidgetStockValuation';
+            }));
+            if(chart.length > 0){
+                var WidgetStockValuation = new widgets.WidgetStockValuation(self);
+                WidgetStockValuation.renderElement();
+                WidgetStockValuation.start();
+                self.grid.addWidget(WidgetStockValuation.$el, 0, 0,WidgetStockValuation.size.width,WidgetStockValuation.size.height,true);
+            }
+            /*
+            ==========================================================
+                STOCK WITH VARIANT COST PRICE
+            ==========================================================
+            */
+            var chart =  _.flatten(_.filter(ChartList,function (inv) {
+                return inv.name == 'WidgetStockValuationVariantCostPrice';
+            }));
+            if(chart.length > 0){
+                var WidgetStockValuationWithVariantCostPrice = new widgets.WidgetStockValuationVariantCostPrice(self);
+                WidgetStockValuationWithVariantCostPrice.renderElement();
+                WidgetStockValuationWithVariantCostPrice.start();
+                self.grid.addWidget(WidgetStockValuationWithVariantCostPrice.$el, 0, 0,WidgetStockValuationWithVariantCostPrice.size.width,WidgetStockValuationWithVariantCostPrice.size.height,true);
+            }
+            /*
+            ==========================================================
+                CUENTAS A COBRAR / A PAGAR
+            ==========================================================
+            */
+            var chart =  _.flatten(_.filter(ChartList,function (inv) {
+                return inv.name == 'WidgetAccount';
+            }));
+            if(chart.length > 0){
+                var WidgetAccount = new widgets.WidgetAccount(self);
+                WidgetAccount.renderElement();
+                WidgetAccount.start();
+                self.grid.addWidget(WidgetAccount.$el, 0, 0,WidgetAccount.size.width,WidgetAccount.size.height,true);
+            };
+            /*
+            ==========================================================
+                BANCO Y CAJA
+            ==========================================================
+            */
+            var chart =  _.flatten(_.filter(ChartList,function (inv) {
+                return inv.name == 'WidgetBankCash';
+            }));
+            if(chart.length > 0){
+                var WidgetBankCash = new widgets.WidgetBankCash(self);
+                WidgetBankCash.renderElement();
+                WidgetBankCash.start();
+                self.grid.addWidget(WidgetBankCash.$el, 0, 0,WidgetBankCash.size.width,WidgetBankCash.size.height,true);
+            };
+            /*
+            ==========================================================
+                RRHH
+            ==========================================================
+            */
+            var chart =  _.flatten(_.filter(ChartList,function (inv) {
+                return inv.name == 'WidgetRRHH';
+            }));
+            if(chart.length > 0){
+                var WidgetRRHH = new widgets.WidgetRRHH(self);
+                WidgetRRHH.renderElement();
+                WidgetRRHH.start();
+                self.grid.addWidget(WidgetRRHH.$el, 0, 0,WidgetRRHH.size.width,WidgetRRHH.size.height,true);
+            };
+        }
+    });
+}

+ 41 - 0
static/src/js/main.js

@@ -0,0 +1,41 @@
+openerp.eiru_reports_dashboard = function (instance) {
+    "use strict";
+
+    instance.eiru_reports_dashboard = {};
+    instance.eiru_reports_dashboard.reports = {};
+
+    var dashboard = instance.eiru_reports_dashboard;
+
+    widget_reporting_base(instance, dashboard);
+    dashboard_reporting_widget(instance, dashboard);
+
+    try{
+
+        DashboardChart(dashboard);
+
+        /*===========
+            WIDGETS
+        ===========*/
+        widget_sale_purchase_expense(dashboard);
+        widget_rrhh(dashboard);
+        widget_income_outcome(dashboard);
+        widget_balance(dashboard);
+        widget_stock_valuation(dashboard);
+        widget_stock_valuation_with_variant_cost_price(dashboard);
+        widget_account(dashboard);
+        widget_bank_cash(dashboard);
+        widget_financial_state(dashboard);
+        /*===========
+            CHARTS
+        ===========*/
+        ranking_customer(dashboard);
+        ranking_salesman(dashboard);
+        ranking_sale_by_store(dashboard);
+
+    }catch(e){
+        // error
+    }
+
+    // Action
+    instance.web.client_actions.add('eiru_reports_dashboard.action_dashboard', 'instance.eiru_reports_dashboard.DashboardReportingWidget');
+}

+ 60 - 0
static/src/js/widget_base.js

@@ -0,0 +1,60 @@
+function widget_reporting_base (instance, widget) {
+    "use strict";
+
+    widget.Base = instance.Widget.extend({
+        size: {
+            width: 0,
+            height: 0
+        },
+        position: {
+            x: 0,
+            y: 0
+        },
+        init: function (parent, size, position) {
+            this._super(parent);
+            this.size = size || this.size;
+            this.position = position || this.position;
+        },
+        start: function () {
+            console.log('Widget started');
+        },
+        getSize: function () {
+            return this.size;
+        },
+        getWidth: function () {
+            return this.getSize().width;
+        },
+        getHeight: function () {
+            return this.getSize().height;
+        },
+        getPosition: function () {
+            return this.position;
+        },
+        getPositionX: function () {
+            return this.getPosition().x;
+        },
+        getPositionY: function () {
+            return this.getPosition().y;
+        },
+        setSize: function (width, height) {
+            this.size.width = width;
+            this.size.height = height;
+        },
+        setWidth: function (width) {
+            this.size.width = width;
+        },
+        setHeight: function (height) {
+            this.size.height = height;
+        },
+        setPosition: function (x, y) {
+            this.position.x = x;
+            this.position.y = y;
+        },
+        setPositionX: function (x) {
+            this.position.x = x;
+        },
+        setPositionY: function (y) {
+            this.position.y = y;
+        }
+    });
+}

+ 366 - 0
static/src/js/widgets/widget_account.js

@@ -0,0 +1,366 @@
+function widget_account(widget) {
+    "use strict";
+
+    var model = openerp;
+    var Qweb = openerp.web.qweb;
+
+    widget.WidgetAccount = widget.Base.extend({
+        template: 'WidgetAccount',
+        modelId: [],
+        data: [],
+
+        events: {
+            'click .toReceibe': 'showModal',
+            'click .toPay': 'showModal',
+        },
+
+        init: function (parent) {
+            this._super(parent, {
+                width: 6,
+                height: 3
+            });
+        },
+
+        start: function () {
+            var self = this;
+            self.fetchInitial();
+        },
+
+        fetchInitial: function(){
+            var self = this;
+            self.fetchDataSQL().then(function (DataSQL) {
+                return DataSQL;
+            }).then(function(DataSQL) {
+                self.ResCompany = DataSQL.company;
+                self.AccountMoveLine = DataSQL.move_lines;
+                self.AccountVoucher = DataSQL.vouchers;
+                self.AccountMoveLinePartialReconcile = DataSQL.reconcile_partials;
+                return self.fetchCustomerModel();
+            }).then(function(CustomerModel) {
+                self.CustomerModel = CustomerModel;
+                return self.fetchSupplierModel();
+            }).then(function(SupplierModel) {
+                self.SupplierModel = SupplierModel;
+                return self.show();
+            });
+        },
+
+        fetchDataSQL: function() {
+            var self = this;
+            var data = $.get('/dashboard-Account');
+            return data;
+        },
+
+        fetchCustomerModel: function() {
+            var self = this;
+            var defer = $.Deferred();
+            var irModelData = new model.web.Model('ir.model.data');
+            var getObtjectReference = irModelData.get_func('get_object_reference');
+            this.alive(getObtjectReference('account', 'invoice_form')).then(function(results) {
+                defer.resolve(results);
+            });
+            return defer;
+        },
+
+        fetchSupplierModel: function() {
+            var self = this;
+            var defer = $.Deferred();
+            var irModelData = new model.web.Model('ir.model.data');
+            var getObtjectReference = irModelData.get_func('get_object_reference');
+            this.alive(getObtjectReference('account', 'invoice_supplier_form')).then(function(results) {
+                defer.resolve(results);
+            });
+            return defer;
+        },
+
+        getAccountMoveLinePartialReconcile: function (id) {
+            var self = this;
+            return _.filter(self.AccountMoveLinePartialReconcile,function (item) {
+                return item.reconcile_partial_id == id;
+            });
+        },
+
+        getAccountInvoice: function () {
+            var self = this;
+            var invoice_numbers = _.flatten(_.map(self.AccountMoveLine, function (item) {
+                return item.number;
+            }));
+            return _.uniq(invoice_numbers);
+        },
+
+        getAccountMoveLine: function (number) {
+            var self = this;
+            var lines = _.filter(self.AccountMoveLine,function (item) {
+                return item.number == number;
+            });
+            var x = lines.length;
+            var i = 1;
+            var content =_.map(lines, function(item){
+                item.description = 'Cuota ' + i +' / ' + x;
+                i++;
+                return item;
+            });
+            return content;
+        },
+
+        getAccountVoucher: function (number) {
+            var self = this;
+            var valor = _.reduce(_.map(self.AccountVoucher, function (map) {
+                if(map.reference == number){
+                    return map.amount_currency;
+                }else{
+                    return 0;
+                }
+            }), function (memo, num) {
+                return memo + num;
+            });
+            return valor;
+        },
+
+        getContent: function (type) {
+            var self = this;
+            var data = [];
+            var CurrencyBase = self.ResCompany[0].currency_id;
+            var AccountInvoice = self.getAccountInvoice();
+            _.each(AccountInvoice,function(index) {
+                var AccountMoveLine = self.getAccountMoveLine(index);
+                _.each(AccountMoveLine,function(item) {
+                    var residual = 0;
+                    var reconcile = 0;
+                    if(item.debit > 0 && item.date_maturity != false && type == 'toReceibe'){
+                        residual = 0;
+                        if(item.reconcile_id != null){
+                            return;
+                        }
+                        if(item.reconcile_partial_id == null){
+                            residual = item.debit;
+                        }else{
+                            reconcile = self.getAccountMoveLinePartialReconcile(item.reconcile_partial_id);
+                            reconcile = _.reduce(_.map(reconcile, function (map) {
+                                return map.credit;
+                            }), function (memo, num) {
+                                return memo + num;
+                            },0);
+                            residual = item.debit - reconcile;
+                        }
+                        data.push({
+                            'invoice_id': item.invoice_id,
+                            'month': moment(item.date_maturity).format('YYYY-MM'),
+                            'date': item.date_maturity,
+                            'ruc': item.ruc,
+                            'partner': item.partner,
+                            'number': item.number,
+                            'description': item.description,
+                            'date_maturity': moment(item.date_maturity).format('DD/MM/YYYY'),
+                            'residual': accounting.formatMoney(residual, CurrencyBase.symbol, CurrencyBase.decimal_places, CurrencyBase.thousands_separator, CurrencyBase.decimal_separator),
+                            'value': item.debit,
+                        });
+                    }else{
+                        if(item.credit > 0 && item.date_maturity != false && type == 'toPay'){
+                            residual = 0;
+                            if(item.reconcile_id != null){
+                                return;
+                            }
+                            if(item.reconcile_partial_id == null){
+                                residual = item.credit;
+                            }else{
+                                reconcile = self.getAccountMoveLinePartialReconcile(item.reconcile_partial_id);
+                                reconcile = _.reduce(_.map(reconcile, function (map) {
+                                    return map.debit;
+                                }), function (memo, num) {
+                                    return memo + num;
+                                },0);
+                                residual = item.credit - reconcile;
+                            }
+                            data.push({
+                                'invoice_id': item.invoice_id,
+                                'month': moment(item.date_maturity).format('YYYY-MM'),
+                                'date': item.date_maturity,
+                                'ruc': item.ruc,
+                                'partner': item.partner,
+                                'number': item.number,
+                                'description': item.description,
+                                'date_maturity': moment(item.date_maturity).format('DD/MM/YYYY'),
+                                'residual': accounting.formatMoney(residual, CurrencyBase.symbol, CurrencyBase.decimal_places, CurrencyBase.thousands_separator, CurrencyBase.decimal_separator),
+                                'value': item.credit,
+                            });
+                        }
+                    }
+                });
+            });
+            data.sort(function (a, b) {
+                var x = a.date;
+                var y = b.date;
+                if (x > y) {
+                    return -1;
+                }
+                if (x < y) {
+                    return 1;
+                }
+                return 0;
+            });
+            return data;
+        },
+
+        show: function () {
+            var self = this;
+            var toReceibe = 0;
+            var toPay = 0;
+            var CurrencyBase = self.ResCompany[0].currency_id;
+            var AccountInvoice = self.getAccountInvoice();
+            _.each(AccountInvoice,function(index) {
+                var AccountMoveLine = self.getAccountMoveLine(index);
+                _.each(AccountMoveLine,function(item) {
+                    var residual = 0;
+                    var reconcile = 0;
+                    if(item.debit > 0 && item.date_maturity != false){
+                        residual = 0;
+                        if(item.reconcile_id != null){
+                            return;
+                        }
+                        if(item.reconcile_partial_id == null){
+                            residual = item.debit;
+                        }else{
+                            /*
+                                Si es una factura de cliente, el monto de la conciliacion
+                                parcial esta guardado en credito
+                            */
+                            reconcile = self.getAccountMoveLinePartialReconcile(item.reconcile_partial_id);
+                            reconcile = _.reduce(_.map(reconcile, function (map) {
+                                return map.credit;
+                            }), function (memo, num) {
+                                return memo + num;
+                            },0);
+                            residual = item.debit - reconcile;
+                        }
+                        toReceibe += residual;
+                    }
+                    if(item.credit > 0 && item.date_maturity != false){
+                        residual = 0;
+                        if(item.reconcile_id != null){
+                            return;
+                        }
+                        if(item.reconcile_partial_id == null){
+                            residual = item.credit;
+                        }else{
+                            /*
+                                Si es una factura de proveedor, el monto de la conciliacion
+                                parcial esta guardado en debito
+                            */
+                            reconcile = self.getAccountMoveLinePartialReconcile(item.reconcile_partial_id);
+                            reconcile = _.reduce(_.map(reconcile, function (map) {
+                                return map.debit;
+                            }), function (memo, num) {
+                                return memo + num;
+                            },0);
+                            residual = item.credit - reconcile;
+                        }
+                        toPay += residual;
+                    }
+                });
+            });
+            self.$el.find('.widget-content').find('#acobrar').text(accounting.formatMoney(toReceibe, CurrencyBase.symbol, CurrencyBase.decimal_places, CurrencyBase.thousands_separator, CurrencyBase.decimal_separator));
+            self.$el.find('.widget-content').find('#apagar').text(accounting.formatMoney(toPay, CurrencyBase.symbol, CurrencyBase.decimal_places, CurrencyBase.thousands_separator, CurrencyBase.decimal_separator));
+        },
+
+        RedirectToReceibe: function() {
+            var self = this;
+            self.do_action({
+                type: 'ir.actions.client',
+                tag: "eiru_reports.dues_analysis_action",
+                target: 'current',
+                params: {},
+                context: self.session.user_context,
+            });
+            self.removeModal();
+        },
+
+        loadTable:function(rowsTable){
+            var self = this;
+            self.rowsData = rowsTable;
+            var table = $('.account-table');
+            table.bootstrapTable('load', rowsTable);
+        },
+
+        showModal: function (e){
+            var self = this;
+            var titleData = [];
+            var CurrencyBase = self.ResCompany[0].currency_id;
+            var type = $(e.target).closest('a')[0].className;
+            
+            var data = self.getContent(type);
+            if(type == 'toReceibe'){
+                titleData.push({
+                    'title': 'Cuentas a cobrar'
+                });
+            }else{
+                titleData.push({
+                    'title': 'Cuentas a pagar'
+                });
+            }
+            var modal = Qweb.render('WidgetAccountModal', {
+                modalTitle: titleData,
+            });
+            $('.openerp_webclient_container').after(modal);
+            $('.dashboard-account-modal').modal();
+            $('.dashboard-account-modal').on('hidden.bs.modal', function (e) {
+                self.removeModal(e);
+            });
+            var contenido = $('.dashboard-account-modal').find('.redirect-to-report');
+            contenido.click(function (e) {
+                if(type == 'toReceibe'){
+                    self.RedirectToReceibe();
+                }
+            });
+            var table = $('.dashboard-account-modal').find('.account-table');
+            table.click(function (e) {
+                $(table).find('tr').removeClass('table-row-select');
+                $(e.target).closest('tr').addClass('table-row-select');
+                var id = $(e.target).closest('tr').children()[0].textContent;
+                if(type == 'toReceibe'){
+                    self.RenderCustomerForm(id);
+                }else{
+                    self.RenderSupplierForm(id);
+                }
+            });
+            self.loadTable(data);
+        },
+
+        removeModal: function (e) {
+            $('.dashboard-account-modal').remove();
+            $('.modal-backdrop').remove();
+        },
+
+        RenderCustomerForm: function(id){
+            var self = this;
+            id = parseInt(id);
+            this.do_action({
+                type: "ir.actions.act_window",
+                res_model: "account.invoice",
+                views: [[self.CustomerModel[1], 'form']],
+                target: 'current',
+                domain: [['id', '=', id]],
+                context: {},
+                res_id: id,
+            }).then(function() {
+                self.removeModal();
+            });
+        },
+
+        RenderSupplierForm: function(id){
+            var self = this;
+            id = parseInt(id);
+            this.do_action({
+                type: "ir.actions.act_window",
+                res_model: "account.invoice",
+                views: [[self.SupplierModel[1], 'form']],
+                target: 'current',
+                domain: [['id', '=', id]],
+                context: {},
+                res_id: id,
+            }).then(function() {
+                self.removeModal();
+            });
+        }
+    });
+}

+ 269 - 0
static/src/js/widgets/widget_balance.js

@@ -0,0 +1,269 @@
+function widget_balance(widget) {
+    "use strict";
+
+    var model = openerp;
+    var Qweb = openerp.web.qweb;
+
+    widget.WidgetBalance = widget.Base.extend({
+        template: 'WidgetBalance',
+        ContentSale: [],
+        ContentPurchase: [],
+        ContentExpense: [],
+
+        init: function (parent) {
+            this._super(parent, {
+                width: 12,
+                height: 5
+            });
+        },
+
+        start: function () {
+            var self = this;
+            self.fetchInitial();
+        },
+
+        fetchInitial: function(){
+            var self = this;
+            self.fetchDataSQL().then(function (DataSQL) {
+                return DataSQL;
+            }).then(function(DataSQL) {
+                self.ResCompany = DataSQL.company;
+                self.PosOrder = DataSQL.orders;
+                self.AccountInvoice = DataSQL.invoices;
+                self.HrPayslip = DataSQL.payslips;
+                return self.showThisMonth();
+            });
+        },
+
+        fetchDataSQL: function() {
+            var self = this;
+            var data = $.get('/dashboard-Balance');
+            return data;
+        },
+
+        getContentByDate:function(date, content) {
+            var self = this;
+            return _.flatten(_.filter(content,function (inv) {
+                return moment(inv.date).format('YYYY-MM-DD') === date;
+            }));
+        },
+
+        showThisMonth: function () {
+            var self = this;
+            var ContentSale = [];
+            var ContentPurchase = [];
+            var ContentExpense = [];
+
+            /*
+            ==============
+                VENTAS
+            ==============
+            */
+            _.each(self.PosOrder,function(item){
+                var utc = moment.utc(item.date,'YYYY-MM-DD h:mm:ss A');
+                ContentSale.push({
+                    date: moment(utc._d).format('YYYY-MM-DD'),
+                    amount: item.amount,
+                });
+            });
+            _.each(self.AccountInvoice,function(item){
+                if(item.type == "out_invoice" && item.origin.match(/SO/) != null){
+                    ContentSale.push({
+                        date: item.date,
+                        amount: item.amount,
+                    });
+                }
+                if(item.type == "out_refund"){
+                    ContentSale.push({
+                        date: item.date,
+                        amount: item.amount * -1,
+                    });
+                }
+            });
+            /*
+            ==============
+                COMPRAS
+            ==============
+            */
+            _.each(self.AccountInvoice,function(item){
+                var origin = item.origin;
+                if(item.type == "in_invoice" && item.origin != null){
+                    if(origin.match(/PO/) != null){
+                        ContentPurchase.push({
+                            date: item.date,
+                            amount: item.amount,
+                        });
+                    }
+                }
+            });
+            /*
+            ==============
+                GASTOS
+            ==============
+            */
+            _.each(self.AccountInvoice,function(item){
+                var origin = item.origin;
+                if(item.type == "in_invoice"){
+                    if(origin == null){
+                        ContentExpense.push({
+                            date: item.date,
+                            amount: item.amount,
+                        });
+                    }else{
+                        if(origin != null){
+                            if(origin.match(/PO/) == null){
+                                ContentExpense.push({
+                                    date: item.date,
+                                    amount: item.amount,
+                                });
+                            }
+                        }
+                    }
+                }
+            });
+
+            /*
+            =====================
+                RECURSOS HUMANOS
+            =====================
+            */
+
+            var net = _.reduce(_.map(self.HrPayslip,function(item) {
+                ContentExpense.push({
+                    date: item.date,
+                    amount: item.net,
+                });
+                return item.net;
+            }),function(memo, num) {
+                return memo + num;
+            },0);
+
+            var ipsc = _.reduce(_.map(self.HrPayslip,function(item) {
+                if(item.code == 'IPSC' && item.ipsc != null){
+                    ContentExpense.push({
+                        date: item.date,
+                        amount: item.ipsc,
+                    });
+                    return item.ipsc;
+                }else{
+                    return 0;
+                }
+            }),function(memo, num) {
+                return memo + num;
+            },0);
+
+            var ipse = _.reduce(_.map(self.HrPayslip,function(item) {
+                if(item.code == 'IPSE' && item.ipse != null){
+                    ContentExpense.push({
+                        date: item.date,
+                        amount: item.ipse * -1,
+                    });
+                    return item.ipse * -1;
+                }else{
+                    return 0;
+                }
+            }),function(memo, num) {
+                return memo + num;
+            },0);
+
+
+            self.ContentSale = ContentSale;
+            self.ContentPurchase = ContentPurchase;
+            self.ContentExpense = ContentExpense;
+            self.BuildChartMonth();
+        },
+        /*
+        =====================================================
+            BUILD CHART - MONTH
+        =====================================================
+        */
+        BuildChartMonth: function(){
+            var self = this;
+            var data = [];
+            var CurrencyBase = self.ResCompany[0].currency_id;
+            var label = [];
+            var bodySale = [];
+            var bodyPurchase = [];
+            var bodyExpense = [];
+            var name = '.month-dashboard-chart';
+            var date_start = moment().format('YYYY-MM-01');
+            var date_stop = moment().add(1,'months').format('YYYY-MM-01');
+            var date = date_start;
+            var total, i;
+            /*
+            ===================
+                VENTAS
+            ===================
+            */
+            for (i = 0; i < 32; i++) {
+                total = 0;
+                if(i > 0){
+                    date = moment(date).add(1,'days').format('YYYY-MM-DD');
+                }
+                if(date == date_stop){
+                    break;
+                }
+                data = self.getContentByDate(date, self.ContentSale);
+                if(data.length > 0){
+                    total = _.reduce(_.map(data,function(item) {
+                        return item.amount;
+                    }),function(memo, num) {
+                        return memo + num;
+                    },0);
+                }
+                label.push(moment(date).format('DD'));
+                bodySale.push(total);
+            }
+            /*
+            ===================
+                COMPRAS
+            ===================
+            */
+            date = date_start;
+            for (i = 0; i < 32; i++) {
+                total = 0;
+                if(i > 0){
+                    date = moment(date).add(1,'days').format('YYYY-MM-DD');
+                }
+                if(date == date_stop){
+                    break;
+                }
+                data = self.getContentByDate(date, self.ContentPurchase);
+                if(data.length > 0){
+                    total = _.reduce(_.map(data,function(item) {
+                        return item.amount;
+                    }),function(memo, num) {
+                        return memo + num;
+                    },0);
+                }
+                bodyPurchase.push(total);
+            }
+            /*
+            ===================
+                GASTOS
+            ===================
+            */
+            date = date_start;
+            for (i = 0; i < 32; i++) {
+                total = 0;
+                if(i > 0){
+                    date = moment(date).add(1,'days').format('YYYY-MM-DD');
+                }
+                if(date == date_stop){
+                    break;
+                }
+                data = self.getContentByDate(date, self.ContentExpense);
+                if(data.length > 0){
+                    total = _.reduce(_.map(data,function(item) {
+                        return item.amount;
+                    }),function(memo, num) {
+                        return memo + num;
+                    },0);
+                }
+                bodyExpense.push(total);
+            }
+            var chart = new widget.DashboardChartWidget(self);
+            chart.BuildBalanceLineChart(name,label,bodySale,bodyPurchase,bodyExpense,CurrencyBase);
+        },
+    });
+}

+ 209 - 0
static/src/js/widgets/widget_bank_cash.js

@@ -0,0 +1,209 @@
+function widget_bank_cash(widget) {
+    "use strict";
+
+    var model = openerp;
+    var Qweb = openerp.web.qweb;
+
+    widget.WidgetBankCash = widget.Base.extend({
+        template: 'WidgetBankCash',
+        ModelBank: [],
+        ModelCash: [],
+        DataBank: [],
+        DataCash: [],
+        events: {
+            'click .bank': 'showModal',
+            'click .cash': 'showModal',
+        },
+        init: function (parent) {
+            this._super(parent, {
+                width: 6,
+                height: 3
+            });
+        },
+        start: function () {
+            var self = this;
+            self.fetchInitial();
+        },
+        fetchInitial: function(){
+            var self = this;
+            self.fetchDataSQL().then(function (DataSQL) {
+                return DataSQL;
+            }).then(function(DataSQL) {
+                self.ResCompany = DataSQL.company;
+                self.AccountBankStatement = DataSQL.statements;
+                return self.fetchGetModelBank();
+            }).then(function(ModelBank) {
+                self.ModelBank= ModelBank;
+                return self.fetchGetModelCash();
+            }).then(function(ModelCash) {
+                self.ModelCash= ModelCash;
+                return self.show();
+            });
+        },
+        fetchDataSQL: function() {
+            var self = this;
+            var data = $.get('/dashboard-BankCash');
+            return data;
+        },
+        fetchGetModelBank: function() {
+            var self = this;
+            var defer = $.Deferred();
+            var irModelData = new model.web.Model('ir.model.data');
+            var getObtjectReference = irModelData.get_func('get_object_reference');
+            this.alive(getObtjectReference('account', 'view_bank_statement_form')).then(function(results) {
+                defer.resolve(results);
+            });
+            return defer;
+        },
+        fetchGetModelCash: function() {
+            var self = this;
+            var defer = $.Deferred();
+            var irModelData = new model.web.Model('ir.model.data');
+            var getObtjectReference = irModelData.get_func('get_object_reference');
+            this.alive(getObtjectReference('account', 'view_bank_statement_form2')).then(function(results) {
+                defer.resolve(results);
+            });
+            return defer;
+        },
+        show: function () {
+            var self = this;
+            var bank = 0;
+            var cash = 0;
+            var DataCash = [];
+            var DataBank = [];
+            var AccountBankStatement = self.AccountBankStatement;
+            var CurrencyBase = self.ResCompany[0].currency_id;
+            if(AccountBankStatement.length > 0){
+                bank = _.reduce(_.map(AccountBankStatement, function (map) {
+                    if(map.journal_type == 'bank'){
+                        if(map.journal_currency != null){
+                            var balance_end = accounting.formatMoney(map.balance_end, map.symbol, map.decimal_places, map.thousands_separator, map.decimal_separator);
+                        }else{
+                            var balance_end = accounting.formatMoney(map.balance_end, CurrencyBase.symbol, CurrencyBase.decimal_places, CurrencyBase.thousands_separator, CurrencyBase.decimal_separator)
+                        }
+                        DataBank.push({
+                            'id':map.id,
+                            'name':map.name,
+                            'journal_name':map.journal_name,
+                            'balance_end':balance_end,
+                            'balance_end_currency':accounting.formatMoney(map.balance_end_currency, CurrencyBase.symbol, CurrencyBase.decimal_places, CurrencyBase.thousands_separator, CurrencyBase.decimal_separator)
+                        });
+                        return map.balance_end_currency;
+                    }else{
+                        return 0;
+                    }
+                }), function (memo, num) {
+                    return memo + num;
+                });
+
+                cash = _.reduce(_.map(AccountBankStatement, function (map) {
+                    if(map.journal_type == 'cash'){
+                        if(map.journal_currency != null){
+                            var balance_end = accounting.formatMoney(map.balance_end, map.symbol, map.decimal_places, map.thousands_separator, map.decimal_separator);
+                        }else{
+                            var balance_end = accounting.formatMoney(map.balance_end, CurrencyBase.symbol, CurrencyBase.decimal_places, CurrencyBase.thousands_separator, CurrencyBase.decimal_separator)
+                        }
+                        DataCash.push({
+                            'id':map.id,
+                            'name':map.name,
+                            'journal_name':map.journal_name,
+                            'balance_end':balance_end,
+                            'balance_end_currency':accounting.formatMoney(map.balance_end_currency, CurrencyBase.symbol, CurrencyBase.decimal_places, CurrencyBase.thousands_separator, CurrencyBase.decimal_separator)
+                        });
+                        return map.balance_end_currency;
+                    }else{
+                        return 0;
+                    }
+                }), function (memo, num) {
+                    return memo + num;
+                });
+            };
+            self.DataBank = DataBank;
+            self.DataCash = DataCash;
+            self.$el.find('.widget-content').find('#bank').text(accounting.formatMoney(bank, CurrencyBase.symbol, CurrencyBase.decimal_places, CurrencyBase.thousands_separator, CurrencyBase.decimal_separator))
+            self.$el.find('.widget-content').find('#cash').text(accounting.formatMoney(cash, CurrencyBase.symbol, CurrencyBase.decimal_places, CurrencyBase.thousands_separator, CurrencyBase.decimal_separator))
+        },
+        showModal: function (e){
+            var self = this;
+            var titleData = [];
+            var data = []
+            var CurrencyBase = self.ResCompany[0].currency_id;
+            var type = $(e.target).closest('a')[0].className;
+            if(type == 'bank'){
+                if (self.DataBank <= 0) {
+                    model.web.notification.do_warn("Atención","Sin datos");
+                    return
+                }
+                data = self.DataBank;
+                titleData.push({
+                    title: 'Extratos Bancarios Abiertos',
+                });
+            };
+            if(type == 'cash'){
+                if (self.DataCash <= 0) {
+                    model.web.notification.do_warn("Atención","Sin datos");
+                    return
+                }
+                data = self.DataCash;
+                titleData.push({
+                    title: 'Registros de caja abiertos',
+                });
+            };
+            var modal = Qweb.render('BankCashModal', {
+                modalTitle: titleData,
+                data: data,
+            });
+            $('.openerp_webclient_container').after(modal);
+            $('.dashboard-bank-cash-modal').modal();
+            $('.dashboard-bank-cash-modal').on('hidden.bs.modal', function (e) {
+                self.removeModal(e);
+            });
+            var contenido = $('.dashboard-bank-cash-modal').find('.statement-name');
+            contenido.click(function (e) {
+                var name = $(e.target).closest('.statement-name')[0].text;
+                var statement =  _.flatten(_.filter(self.AccountBankStatement,function (inv) {
+                    return inv.name == name;
+                }));
+                if(statement[0].journal_type == 'bank'){
+                    self.renderBankForm(statement[0].id);
+                }else{
+                    self.renderCashForm(statement[0].id);
+                };
+           });
+        },
+        removeModal: function (e) {
+            $('.dashboard-bank-cash-modal').remove();
+            $('.modal-backdrop').remove();
+        },
+        renderBankForm: function(id){
+            var self = this;
+            id = parseInt(id);
+            this.do_action({
+                type: "ir.actions.act_window",
+                res_model: "account.bank.statement",
+                views: [[self.ModelBank[1], 'form']],
+                target: 'current',
+                domain: [['id', '=', id]],
+                context: {},
+                res_id: id,
+            }).then(function() {
+                self.removeModal();
+            });
+        },
+        renderCashForm: function(id){
+            var self = this;
+            id = parseInt(id);
+            this.do_action({
+                type: "ir.actions.act_window",
+                res_model: "account.bank.statement",
+                views: [[self.ModelCash[1], 'form']],
+                target: 'current',
+                domain: [['id', '=', id]],
+                context: {},
+                res_id: id,
+            }).then(function() {
+                self.removeModal();
+            });
+        },
+    });
+}

+ 288 - 0
static/src/js/widgets/widget_financial_state.js

@@ -0,0 +1,288 @@
+function widget_financial_state(widget) {
+    "use strict";
+
+    var model = openerp;
+
+    widget.WidgetFinancialState = widget.Base.extend({
+        template: 'WidgetFinancialState',
+        content: [],
+
+        events: {
+
+        },
+
+        init: function (parent) {
+            this._super(parent, {
+                width: 12,
+                height: 5
+            });
+        },
+
+        start: function () {
+            var self = this;
+            self.fetchInitial();
+        },
+
+        fetchInitial: function(){
+            var self = this;
+            self.fetchDataSQL().then(function (DataSQL) {
+                return DataSQL;
+            }).then(function(DataSQL) {
+                self.ResCompany = DataSQL.company;
+                self.AccountInvoiceLine = DataSQL.invoice_lines;
+                self.PosOrderLine = DataSQL.order_lines;
+                self.HrPayslip = DataSQL.payslips;
+                return self.Show();
+            });
+        },
+
+        fetchDataSQL: function() {
+            var self = this;
+            var data = $.get('/dashboard-FinancialState');
+            return data;
+        },
+
+        Show: function () {
+            var self = this;
+            var label = ['Total','Sub-Total','Impuestos','Coste','Utilidad'];
+            var body = [];
+            var name = '.financial-chart';
+            var sale = [];
+            var expense = [];
+            var CurrencyBase = self.ResCompany[0].currency_id;
+
+            /*
+            ==================================
+                SALE
+            ==================================
+            */
+
+            var SaleSubtotal = _.reduce(_.map(self.AccountInvoiceLine,function(item) {
+                if(item.type == "out_invoice" && item.origin.match(/SO/) != null){
+                    return item.subtotal;
+                }else{
+                    return 0;
+                }
+            }),function(memo, num) {
+                return memo + num;
+            },0);
+            var SaleTax = _.reduce(_.map(self.AccountInvoiceLine,function(item) {
+                if(item.type == "out_invoice" && item.origin.match(/SO/) != null){
+                    return item.tax;
+                }else{
+                    return 0;
+                }
+            }),function(memo, num) {
+                return memo + num;
+            },0);
+            var SaleCost = _.reduce(_.map(self.AccountInvoiceLine,function(item) {
+                if(item.type == "out_invoice" && item.origin.match(/SO/) != null){
+                    return item.cost * item.quantity;
+                }else{
+                    return 0;
+                }
+            }),function(memo, num) {
+                return memo + num;
+            },0);
+
+            /*
+            ==================================
+                REFUND
+            ==================================
+            */
+
+            var SaleRefundSubtotal = _.reduce(_.map(self.AccountInvoiceLine,function(item) {
+                if(item.type == "out_refund"){
+                    return item.subtotal;
+                }else{
+                    return 0;
+                }
+            }),function(memo, num) {
+                return memo + num;
+            },0);
+            var SaleRefundTax = _.reduce(_.map(self.AccountInvoiceLine,function(item) {
+                if(item.type == "out_refund"){
+                    return item.tax;
+                }else{
+                    return 0;
+                }
+            }),function(memo, num) {
+                return memo + num;
+            },0);
+            var SaleRefundCost = _.reduce(_.map(self.AccountInvoiceLine,function(item) {
+                if(item.type == "out_refund"){
+                    return item.cost * item.quantity;
+                }else{
+                    return 0;
+                }
+            }),function(memo, num) {
+                return memo + num;
+            },0);
+
+            /*
+            ==================================
+                ORDER
+            ==================================
+            */
+
+            var OrderSubtotal = _.reduce(_.map(self.PosOrderLine,function(item) {
+                var utc = moment.utc(item.date,'YYYY-MM-DD h:mm:ss A');
+                utc = moment(utc._d).format('YYYY-MM-DD');
+                var value = 0;
+                if(moment(utc).format('YYYY-MM') == moment().format('YYYY-MM')){
+                    value = item.subtotal;
+                }
+                return value;
+            }),function(memo, num) {
+                return memo + num;
+            },0);
+
+            var OrderTax = _.reduce(_.map(self.PosOrderLine,function(item) {
+                var utc = moment.utc(item.date,'YYYY-MM-DD h:mm:ss A');
+                utc = moment(utc._d).format('YYYY-MM-DD');
+                var value = 0;
+                if(moment(utc).format('YYYY-MM') == moment().format('YYYY-MM')){
+                    value = item.tax;
+                }
+                return value;
+            }),function(memo, num) {
+                return memo + num;
+            },0);
+
+            var OrderCost = _.reduce(_.map(self.PosOrderLine,function(item) {
+                var utc = moment.utc(item.date,'YYYY-MM-DD h:mm:ss A');
+                utc = moment(utc._d).format('YYYY-MM-DD');
+                var value = 0;
+                if(moment(utc).format('YYYY-MM') == moment().format('YYYY-MM')){
+                    value = item.cost * item.quantity;
+                }
+                return value;
+            }),function(memo, num) {
+                return memo + num;
+            },0);
+
+            var subtotal = SaleSubtotal + OrderSubtotal - SaleRefundSubtotal;
+            var tax = SaleTax + OrderTax - SaleRefundTax;
+            var cost = SaleCost + OrderCost - SaleRefundCost;
+            var ganancia = subtotal - cost;
+            var Rentabilidad = (ganancia / cost) * 100;
+            var utilidad = (ganancia / subtotal) * 100;
+
+            body.push(Math.round(subtotal + tax));
+            body.push(subtotal);
+            body.push(tax);
+            body.push(cost);
+            body.push(ganancia);
+
+            sale.push({
+                subtotal: accounting.formatMoney(subtotal, CurrencyBase.symbol, CurrencyBase.decimal_places, CurrencyBase.thousands_separator, CurrencyBase.decimal_separator),
+                tax: accounting.formatMoney(tax, CurrencyBase.symbol, CurrencyBase.decimal_places, CurrencyBase.thousands_separator, CurrencyBase.decimal_separator),
+                cost: accounting.formatMoney(cost, CurrencyBase.symbol, CurrencyBase.decimal_places, CurrencyBase.thousands_separator, CurrencyBase.decimal_separator),
+                margin : accounting.formatNumber(utilidad,2,".",",") + '%',
+                total: accounting.formatMoney(subtotal + tax, CurrencyBase.symbol, CurrencyBase.decimal_places, CurrencyBase.thousands_separator, CurrencyBase.decimal_separator),
+                utility: accounting.formatMoney(ganancia, CurrencyBase.symbol, CurrencyBase.decimal_places, CurrencyBase.thousands_separator, CurrencyBase.decimal_separator),
+                profitability: accounting.formatNumber(Rentabilidad,2,".",",") + '%',
+            });
+
+            // GASTOS
+            var ExpenseSubtotal = _.reduce(_.map(self.AccountInvoiceLine,function(item) {
+                if(item.type == "in_invoice"){
+                    if(item.origin == null){
+                        return item.subtotal;
+                    }else{
+                        var subtotal = 0;
+                        if(item.origin != null){
+                            if(item.origin.match(/PO/) == null){
+                                subtotal = item.subtotal;
+                            }
+                        }
+                        return subtotal;
+                    }
+                }else{
+                    return 0;
+                }
+            }),function(memo, num) {
+                return memo + num;
+            },0);
+
+            var ExpenseTax = _.reduce(_.map(self.AccountInvoiceLine,function(item) {
+                if(item.type == "in_invoice"){
+                    if(item.origin == null){
+                        return item.tax;
+                    }else{
+                        var tax = 0;
+                        if(item.origin != null){
+                            if(item.origin.match(/PO/) == null){
+                                subtotal = item.tax;
+                            }
+                        }
+                        return tax;
+                    }
+                }else{
+                    return 0;
+                }
+            }),function(memo, num) {
+                return memo + num;
+            },0);
+
+            //RRHH
+
+            var net = _.reduce(_.map(self.HrPayslip,function(item) {
+                return item.net;
+            }),function(memo, num) {
+                return memo + num;
+            },0);
+
+            var ipsc = _.reduce(_.map(self.HrPayslip,function(item) {
+                if(item.code == 'IPSC' && item.ipsc != null){
+                    return item.ipsc;
+                }else{
+                    return 0;
+                }
+            }),function(memo, num) {
+                return memo + num;
+            },0);
+
+            var ipse = _.reduce(_.map(self.HrPayslip,function(item) {
+                if(item.code == 'IPSE' && item.ipse != null){
+                    return item.ipse * -1;
+                }else{
+                    return 0;
+                }
+            }),function(memo, num) {
+                return memo + num;
+            },0);
+
+            ExpenseSubtotal = ExpenseSubtotal + net + ipse + ipsc;
+
+            expense.push({
+                subtotal: accounting.formatMoney(ExpenseSubtotal, CurrencyBase.symbol, CurrencyBase.decimal_places, CurrencyBase.thousands_separator, CurrencyBase.decimal_separator),
+                tax: accounting.formatMoney(ExpenseTax, CurrencyBase.symbol, CurrencyBase.decimal_places, CurrencyBase.thousands_separator, CurrencyBase.decimal_separator),
+                total: accounting.formatMoney(ExpenseSubtotal + ExpenseTax, CurrencyBase.symbol, CurrencyBase.decimal_places, CurrencyBase.thousands_separator, CurrencyBase.decimal_separator),
+            });
+
+            self.$el.find('.widget-content').find('.company').text(self.ResCompany[0].name);
+            self.$el.find('.widget-content').find('.store').text(self.ResCompany[0].store);
+            self.$el.find('.widget-content').find('.ruc').text(self.ResCompany[0].ruc);
+            self.$el.find('.widget-content').find('.balance-end').text(accounting.formatMoney(ganancia - ExpenseSubtotal, CurrencyBase.symbol, CurrencyBase.decimal_places, CurrencyBase.thousands_separator, CurrencyBase.decimal_separator));
+            self.loadTable(sale);
+            self.loadExpenseTable(expense);
+            var chart = new widget.DashboardChartWidget(self);
+            chart.BuildBarChart(name,CurrencyBase,label,body);
+        },
+
+        loadTable:function(rowsTable){
+            var self = this;
+            self.rowsData = rowsTable;
+            var table = this.$el.find('.financial-state-sale-table');
+            table.bootstrapTable('load', rowsTable);
+        },
+
+        loadExpenseTable:function(rowsTable){
+            var self = this;
+            self.rowsData = rowsTable;
+            var table = this.$el.find('.financial-state-expense-table');
+            table.bootstrapTable('load', rowsTable);
+        },
+    });
+}

+ 241 - 0
static/src/js/widgets/widget_income_outcome.js

@@ -0,0 +1,241 @@
+function widget_income_outcome(widget) {
+    "use strict";
+
+    var model = openerp;
+    var Qweb = openerp.web.qweb;
+
+    widget.WidgetIncomeOutcome = widget.Base.extend({
+        template: 'WidgetIncomeOutcome',
+        rowsData :[],
+        content: [],
+        label: [],
+        body: [],
+        Income: '',
+        Outcome: '',
+        modules: ['point_of_sale'],
+        events: {
+            'click .number_income': 'showModal',
+            'click .number_outcome': 'showModal',
+        },
+        init: function (parent) {
+            this._super(parent, {
+                width: 6,
+                height: 3
+            });
+        },
+        start: function () {
+            var self = this;
+            self.fetchInitial();
+        },
+        checkModel : function(model){
+            var self = this;
+            return _.filter(self.IrModuleModule,function(item){
+                return item.name === model;
+            });
+        },
+        fetchInitial: function(){
+            var self = this;
+            self.fetchDataSQL().then(function (DataSQL) {
+                return DataSQL;
+            }).then(function(DataSQL) {
+                self.ResCompany = DataSQL.company;
+                self.AccountJournal = DataSQL.journals;
+                self.AccountVoucher = DataSQL.vouchers;
+                self.AccountBankStatementLine = DataSQL.statement_lines;
+                return self.showThisMonth();
+            });
+        },
+        fetchDataSQL: function() {
+            var self = this;
+            var data = $.get('/dashboard-IncomeOutcome');
+            return data;
+        },
+        getAccountVoucher: function (id, type) {
+            var self = this;
+            return _.filter(self.AccountVoucher,function (item) {
+                return item.journal_id === id && item.type == type;
+            });
+        },
+        getAccountAccountBankStatementLine: function (id) {
+            var self = this;
+            return _.filter(self.AccountBankStatementLine,function (item) {
+                return item.journal_id === id;
+            });
+        },
+        showThisMonth: function () {
+            var self = this;
+            var AccountVoucher = _.reduce(_.map(self.AccountVoucher,function(item) {
+                if(item.type == 'receipt'){
+                    return item.amount_currency;
+                }else{
+                    return 0;
+                }
+            }),function(memo, num) {
+                return memo + num;
+            },0);
+            var AccountBankStatementLine = _.reduce(_.map(self.AccountBankStatementLine,function(item) {
+                return item.amount;
+            }),function(memo, num) {
+                return memo + num;
+            },0);
+            var AccountVoucherOut = _.reduce(_.map(self.AccountVoucher,function(item) {
+                if(item.type == 'payment'){
+                    return item.amount_currency;
+                }else{
+                    return 0;
+                }
+            }),function(memo, num) {
+                return memo + num;
+            },0);
+            var amount = AccountVoucher + AccountBankStatementLine;
+            var CurrencyBase = self.ResCompany[0].currency_id;
+            self.$el.find('.widget-content').find('#number_income').text(accounting.formatMoney(amount, CurrencyBase.symbol, CurrencyBase.decimal_places, CurrencyBase.thousands_separator, CurrencyBase.decimal_separator));
+            self.$el.find('.widget-content').find('#number_outcome').text(accounting.formatMoney(AccountVoucherOut, CurrencyBase.symbol, CurrencyBase.decimal_places, CurrencyBase.thousands_separator, CurrencyBase.decimal_separator));
+            self.Income =accounting.formatMoney(amount, CurrencyBase.symbol, CurrencyBase.decimal_places, CurrencyBase.thousands_separator, CurrencyBase.decimal_separator);
+            self.Outcome = accounting.formatMoney(AccountVoucherOut, CurrencyBase.symbol, CurrencyBase.decimal_places, CurrencyBase.thousands_separator, CurrencyBase.decimal_separator);
+        },
+        RedirectIncome: function() {
+            var self = this;
+            self.do_action({
+                type: 'ir.actions.client',
+                tag: "eiru_reports.income_summary_action",
+                target: 'current',
+                params: {},
+                context: self.session.user_context,
+            });
+            self.removeModal();
+        },
+        RedirectOutcome: function() {
+            var self = this;
+            self.do_action({
+                type: 'ir.actions.client',
+                tag: "eiru_reports.outcome_summary_action",
+                target: 'current',
+                params: {},
+                context: self.session.user_context,
+            });
+            self.removeModal();
+        },
+        ShowByPaymentType: function (type){
+            var self = this;
+            var data = [];
+            var label = [];
+            var body = [];
+            var amount = 0;
+            var amount_currency = 0;
+            var voucher = 0;
+            var voucher_currency = 0;
+            var line = 0;
+            var line_currency = 0;
+            var CurrencyBase = self.ResCompany[0].currency_id;
+            _.each(self.AccountJournal,function(item) {
+                var AccountVoucher = self.getAccountVoucher(item.id, type);
+                var AccountBankStatementLine = 0;
+                if(type == 'receipt'){
+                    AccountBankStatementLine = self.getAccountAccountBankStatementLine(item.id);
+                }
+
+                voucher = 0;
+                voucher_currency = 0;
+                _.each(AccountVoucher,function(index) {
+                    if(index.currency_id != null){
+                        voucher += index.amount;
+                    }
+                    voucher_currency += index.amount_currency;
+                });
+
+                line = 0;
+                line_currency = 0;
+                _.each(AccountBankStatementLine,function(index) {
+                    if(index.currency_id != null){
+                        line += index.amount;
+                    }
+                    line_currency += index.amount_currency;
+                });
+
+                amount_currency = voucher_currency + line_currency;
+                amount = voucher + line;
+
+                var value;
+                if(item.currency == null){
+                    value = accounting.formatMoney(amount_currency,CurrencyBase.symbol,CurrencyBase.decimal_places, CurrencyBase.thousands_separator, CurrencyBase.decimal_separator);
+                }else{
+                    value = accounting.formatMoney(amount_currency,CurrencyBase.symbol,CurrencyBase.decimal_places, CurrencyBase.thousands_separator, CurrencyBase.decimal_separator) + ' ( ' + accounting.formatMoney(amount,item.symbol,item.decimal_places, item.thousands_separator, item.decimal_separator) + ' )';
+                }
+
+                if(amount_currency > 0){
+                    data.push({
+                        'name':item.name,
+                        'amount': amount_currency,
+                        'value_amount': value,
+                    });
+                    label.push(item.name);
+                    body.push(amount_currency);
+                }
+            });
+            data.sort(function(a, b) {
+                return b.amount - a.amount;
+            });
+            self.content = data;
+            self.label = label;
+            self.body = body;
+        },
+
+        loadTable:function(rowsTable){
+            var self = this;
+            self.rowsData = rowsTable;
+            var table = $('.Income-Outcome-table');
+            table.bootstrapTable('load', rowsTable);
+        },
+        BuildChart: function(label,body,CurrencyBase){
+            var self = this;
+            var name = '.dashboard-income-outcome-chart';
+            var chart = new widget.DashboardChartWidget(self);
+            chart.BuildDoughnutChart(name,label,body,CurrencyBase);
+        },
+        showModal: function (e){
+            var self = this;
+            var titleData = [];
+            var CurrencyBase = self.ResCompany[0].currency_id;
+            var type = $(e.target).closest('a')[0].className;
+            var amount = '';
+            if(type == 'number_income'){
+                type = 'receipt';
+                amount = self.Income;
+                titleData.push({
+                    'title': 'Resumen de Ingresos'
+                });
+            }else{
+                type = 'payment';
+                amount = self.Outcome;
+                titleData.push({
+                    'title': 'Resumen de Egresos'
+                });
+            }
+            self.ShowByPaymentType(type);
+            var modal = Qweb.render('DashboardIncomeOutcomeModal', {
+                modalTitle: titleData,
+                amount: amount
+            });
+            $('.openerp_webclient_container').after(modal);
+            $('.dashboard-income-outcome-modal').modal();
+            $('.dashboard-income-outcome-modal').on('hidden.bs.modal', function (e) {
+                self.removeModal(e);
+            });
+            var contenido = $('.dashboard-income-outcome-modal').find('.redirect-to-report');
+            contenido.click(function (e) {
+                if(type == 'receipt'){
+                    self.RedirectIncome();
+                }else{
+                    self.RedirectOutcome();
+                }
+            });
+            self.loadTable(self.content);
+            self.BuildChart(self.label,self.body,CurrencyBase);
+        },
+        removeModal: function (e) {
+            $('.dashboard-income-outcome-modal').remove();
+            $('.modal-backdrop').remove();
+        },
+    });
+}

+ 158 - 0
static/src/js/widgets/widget_rrhh.js

@@ -0,0 +1,158 @@
+function widget_rrhh(widget) {
+    "use strict";
+
+    var model = openerp;
+    var Qweb = openerp.web.qweb;
+
+    widget.WidgetRRHH = widget.Base.extend({
+        template: 'WidgetRRHH',
+        content: [],
+        modules: ['eiru_reports_rrhh'],
+        month: 0,
+        week: 0,
+        today: 0,
+        objective: 0,
+        percentage: 0,
+
+        events: {
+            'click .number': 'showModal',
+        },
+
+        init: function (parent) {
+            this._super(parent, {
+                width: 12,
+                height: 6
+            });
+        },
+
+        start: function () {
+            var self = this;
+            var table = this.$el.find('#table');
+            table.bootstrapTable({data : self.rowsData});
+            self.fetchInitial();
+        },
+
+        checkModel : function(model){
+            var self = this;
+            return _.filter(self.IrModuleModule,function(item){
+                return item.name === model;
+            });
+        },
+
+        valorNull:function(dato){
+            var valor = "";
+            if (dato){
+                valor = dato;
+            }
+            return valor;
+        },
+
+        fetchInitial: function(){
+            var self = this;
+            self.fetchDataSQL().then(function (DataSQL) {
+                return DataSQL;
+            }).then(function(DataSQL) {
+                self.ResCompany = DataSQL.company;
+                self.IrModuleModule = DataSQL.modules;
+                return self.fetchHrPayslipTotal();
+            }).then(function(HrPayslipTotal) {
+                self.HrPayslipTotal = HrPayslipTotal;
+                return self.fetchHHRR();
+            }).then(function(HHRR) {
+                self.HHRR = HHRR;
+                return self.showThisMonth();
+            });
+        },
+
+        fetchDataSQL: function() {
+            var self = this;
+            var data = $.get('/dashboard-rrhh');
+            return data;
+        },
+
+        /*
+        =====================================================
+            HR PAYSLIP TOTAL
+        =====================================================
+        */
+        fetchHrPayslipTotal: function(){
+            var self = this;
+            var date = moment().format('YYYY-MM');
+            var modules = self.checkModel('eiru_reports_rrhh');
+            if (modules.length > 0){
+                var domain = [
+                    ['date_to','like',date],
+                    ['state','in',['done','paid']],
+                ];
+                var HrPayslipTotal = new model.web.Model('hr.employee');
+                return HrPayslipTotal.call('getPayslipTotalWidget',[domain], {
+                    context: new model.web.CompoundContext()
+                });
+            }else{
+                return [];
+            }
+        },
+        /*
+        =====================================================
+            HH RR
+        =====================================================
+        */
+        fetchHHRR: function(){
+            var self = this;
+            var modules = self.checkModel('eiru_reports_rrhh');
+            if (modules.length > 0){
+                var domain = [
+                    ['active', '=', true],
+                    ['address_home_id', '!=', false],
+                ];
+                var HHRR = new model.web.Model('hr.employee');
+                return HHRR.call('getHHRRWidget',[domain], {
+                    context: new model.web.CompoundContext()
+                });
+            }else{
+                return [];
+            }
+        },
+        showThisMonth: function () {
+            var self = this;
+
+            var data = [];
+            var label = [];
+            var body = [];
+            var CurrencyBase = self.ResCompany[0].currency_id;
+            _.each(self.HHRR, function(item){
+                if(item.payslip > 0){
+                    label.push(item.name);
+                    body.push(item.payslip);
+                }
+                data.push({
+                    'identification_id': self.valorNull(item.identification_id),
+                    'name': item.name,
+                    'payslip': accounting.formatMoney(item.payslip, CurrencyBase.symbol, CurrencyBase.decimal_places, CurrencyBase.thousands_separator, CurrencyBase.decimal_separator),
+                    'amortized': accounting.formatMoney(item.amortized, CurrencyBase.symbol, CurrencyBase.decimal_places, CurrencyBase.thousands_separator, CurrencyBase.decimal_separator),
+                    'residual': accounting.formatMoney(item.residual, CurrencyBase.symbol, CurrencyBase.decimal_places, CurrencyBase.thousands_separator, CurrencyBase.decimal_separator),
+                });
+            });
+            if(self.HrPayslipTotal.length > 0){
+                self.$el.find('.widget-content').find('.pending').text(accounting.formatMoney(self.HrPayslipTotal[0].residual, CurrencyBase.symbol, CurrencyBase.decimal_places, CurrencyBase.thousands_separator, CurrencyBase.decimal_separator));
+                self.$el.find('.widget-content').find('.amortizado').text(accounting.formatMoney(self.HrPayslipTotal[0].amortized, CurrencyBase.symbol, CurrencyBase.decimal_places, CurrencyBase.thousands_separator, CurrencyBase.decimal_separator));
+                self.$el.find('.widget-content').find('.ips').text(accounting.formatMoney(self.HrPayslipTotal[0].ipse + self.HrPayslipTotal[0].ipsc, CurrencyBase.symbol, CurrencyBase.decimal_places, CurrencyBase.thousands_separator, CurrencyBase.decimal_separator));
+                self.$el.find('.widget-content').find('.total').text(accounting.formatMoney(self.HrPayslipTotal[0].payslip, CurrencyBase.symbol, CurrencyBase.decimal_places, CurrencyBase.thousands_separator, CurrencyBase.decimal_separator));
+            }
+            self.loadTable(data);
+            self.BuildChart(label,body,CurrencyBase);
+        },
+        BuildChart: function(label,body,CurrencyBase){
+            var self = this;
+            var name = '.doughnut-dashboard-chart';
+            var chart = new widget.DashboardChartWidget(self);
+            chart.BuildDoughnutChart(name,label,body,CurrencyBase);
+        },
+        loadTable:function(rowsTable){
+            var self = this;
+            self.rowsData = rowsTable;
+            var table = this.$el.find('#table');
+            table.bootstrapTable('load', rowsTable);
+        },
+    });
+}

+ 894 - 0
static/src/js/widgets/widget_sale_purchase_expense.js

@@ -0,0 +1,894 @@
+function widget_sale_purchase_expense(widget) {
+    "use strict";
+
+    var model = openerp;
+    var Qweb = openerp.web.qweb;
+
+    widget.WidgetSalePurchaseExpense = widget.Base.extend({
+        template: 'WidgetSalePurchaseExpense',
+        modules: ['point_of_sale'],
+        /*
+        ========================
+            OBJETIVOS
+        ========================
+        */
+        SaleObjetiveAmount: 0,
+        PurchaseLimitAmount: 0,
+        ExpenseLimitAmount: 0,
+        PaymentObjetiveAmount:0,
+        /*
+        ========================
+            MES
+        ========================
+        */
+        SaleMonth: 0,
+        PurchaseMonth: 0,
+        ExpenseMonth: 0,
+        PaymentMonth:0,
+        /*
+        ========================
+            PORCENTAJES
+        ========================
+        */
+        SalePercentage: 0,
+        PurchasePercentage: 0,
+        ExpensePercentage: 0,
+        PaymentPercentage:0,
+        content: [],
+
+        events: {
+            'click .number_sale': 'showModal',
+            'click .number_purchase': 'showModal',
+            'click .number_expense': 'showModal',
+            'click .number_payment': 'showModal',
+        },
+
+        init: function (parent) {
+            this._super(parent, {
+                width: 6,
+                height: 3
+            });
+        },
+
+        fetchUTC: function(){
+            var self = this;
+            var date = moment().format('YYYY-MM-01 00:00:00');
+            var utc = moment.utc(date,'YYYY-MM-DD h:mm:ss A');
+            var first = moment('00:00:00','HH:mm:ss');
+            var second = moment(moment(utc._d,'HH:mm:ss'));
+            var data = moment.duration(first - second);
+            return data._data.hours;
+       },
+
+        start: function () {
+            var self = this;
+            self.fetchInitial();
+        },
+
+        checkModel : function(model){
+            var self = this;
+            return _.filter(self.IrModuleModule,function(item){
+                return item.name === model;
+            });
+        },
+
+        fetchInitial: function(){
+            var self = this;
+            self.fetchDataSQL().then(function (DataSQL) {
+                return DataSQL;
+            }).then(function(DataSQL) {
+                self.IrModuleModule = DataSQL.modules;
+                self.ResCompany = DataSQL.company;
+                self.DashboardObjetive = DataSQL.objetives;
+                self.PosOrder = DataSQL.orders;
+                self.AccountInvoice = DataSQL.invoices;
+                self.HrPayslip = DataSQL.payslips;
+                self.AccountVoucher = DataSQL.voucher;
+                return self.fetchMonth();
+            });
+        },
+
+        fetchDataSQL: function() {
+            var self = this;
+            var data = $.get('/dashboard-SalePurchaseExpense');
+            return data;
+        },
+        /*
+        =====================================================
+            DATA BY DAY
+        =====================================================
+        */
+        getContent:function(day) {
+            var self = this;
+            var week = moment().week();
+            return _.flatten(_.filter(self.content,function (inv) {
+                return moment(inv.date).week() === week & moment(inv.date).weekday() === day;
+            }));
+        },
+        /*
+        =====================================================
+            GET PERCENTAGE
+        =====================================================
+        */
+        getPercentage: function(amount, objetive){
+            var self = this;
+            var percentage = 0;
+            if(objetive > 0){
+                percentage = (amount * 100)/objetive;
+            }
+            return percentage;
+        },
+        /*
+        =====================================================
+            SHOW TODAY
+        =====================================================
+        */
+        fetchToday: function (type) {
+            var self = this;
+            var amount = 0;
+            /*
+            =========================================================================================
+                VENTAS
+            =========================================================================================
+            */
+            if(type == "sale"){
+                // console.log("ESTOY");
+                var today = moment().format('YYYY-MM-DD');
+                var PosOrder = _.reduce(_.map(self.PosOrder,function(item) {
+                    var utc = moment.utc(item.date,'YYYY-MM-DD h:mm:ss A');
+                    utc = moment(utc._d).format('YYYY-MM-DD');
+                    if(utc == today){
+                        return item.amount;
+                    }else{
+                        return 0;
+                    }
+                }),function(memo, num) {
+                    return memo + num;
+                },0);
+                var AccountInvoice = _.reduce(_.map(self.AccountInvoice,function(item) {
+                    if(item.date ==  moment().format('YYYY-MM-DD') && item.type == "out_invoice"){
+                        var origin = 0;
+
+                        if(item.origin == '%SO%'){
+                            origin = item.amount;
+                        }
+                        return origin;
+                    }else{
+                        return 0;
+                    }
+
+                    // if(item.date ==  moment().format('YYYY-MM-DD') && item.type == "out_invoice" && item.origin <> '%INTEREST%'){
+                    //     return item.amount;
+                    // }else{
+                    //     return 0;
+                    // }
+                }),function(memo, num) {
+                    return memo + num;
+                },0);
+                var AccountInvoiceRefund = _.reduce(_.map(self.AccountInvoice,function(item) {
+                    if(item.date ==  moment().format('YYYY-MM-DD') && item.type == "out_refund" && item.origin != '%INTEREST%'){
+                        return item.amount;
+                    }else{
+                        return 0;
+                    }
+                }),function(memo, num) {
+                    return memo + num;
+                },0);
+                amount = PosOrder + AccountInvoice - AccountInvoiceRefund;
+            }
+            /*
+            =========================================================================================
+                COMPRAS
+            =========================================================================================
+            */
+            if(type == "purchase"){
+                var purchase = _.reduce(_.map(self.AccountInvoice,function(item) {
+                    if(item.type == 'in_invoice' && item.origin != null && item.date ==  moment().format('YYYY-MM-DD')){
+                        var origin = 0;
+                        if(item.origin.match(/PO/) != null){
+                            origin = item.amount;
+                        }
+                        return origin;
+                    }else{
+                        return 0;
+                    }
+                }),function(memo, num) {
+                    return memo + num;
+                },0);
+                amount = purchase;
+            }
+            /*
+            =========================================================================================
+                GASTOS
+            =========================================================================================
+            */
+            if(type == "expense"){
+                var expense = _.reduce(_.map(self.AccountInvoice,function(item) {
+                    if(item.type == 'in_invoice' && item.date ==  moment().format('YYYY-MM-DD')){
+                        if(item.origin == null){
+                            return item.amount;
+                        }else{
+                            var origin = 0;
+                            if(item.origin != null){
+                                if(item.origin.match(/PO/) == null){
+                                    origin = item.amount;
+                                }
+                            }
+                            return origin;
+                        }
+                    }else{
+                        return 0;
+                    }
+                }),function(memo, num) {
+                    return memo + num;
+                },0);
+
+                // RRHH
+
+                var net = _.reduce(_.map(self.HrPayslip,function(item) {
+                    if(moment(item.date).format('YYYY-MM-DD') ==  moment().format('YYYY-MM-DD')){
+                        return item.net;
+                    }else{
+                        return 0;
+                    }
+                }),function(memo, num) {
+                    return memo + num;
+                },0);
+
+                var ipsc = _.reduce(_.map(self.HrPayslip,function(item) {
+                    if(item.code == 'IPSC' && item.ipsc != null && moment(item.date).format('YYYY-MM-DD') == moment().format('YYYY-MM-DD')){
+                        return item.ipsc;
+                    }else{
+                        return 0;
+                    }
+                }),function(memo, num) {
+                    return memo + num;
+                },0);
+
+                var ipse = _.reduce(_.map(self.HrPayslip,function(item) {
+                    if(item.code == 'IPSE' && item.ipse != null && moment(item.date).format('YYYY-MM-DD') == moment().format('YYYY-MM-DD')){
+                        return item.ipse * -1;
+                    }else{
+                        return 0;
+                    }
+                }),function(memo, num) {
+                    return memo + num;
+                },0);
+
+                amount = expense + net + ipse + ipsc;
+            }
+
+            if(type == "payment"){
+                var payment = _.reduce(_.map(self.AccountVoucher,function(item) {
+                    if(item.type == 'receipt' && item.date ==  moment().format('YYYY-MM-DD')){
+                        return item.amount;
+                    }else{
+                        return 0;
+                    }
+                }),function(memo, num) {
+                    return memo + num;
+                },0);
+                amount = payment;
+            }
+            self.today = amount;
+        },
+        /*
+        =====================================================
+            SHOW WEEK
+        =====================================================
+        */
+        fetchWeek: function (type) {
+            var self = this;
+            var amount = 0;
+            var content = [];
+            /*
+            =========================================================================================
+                VENTAS
+            =========================================================================================
+            */
+            if(type == "sale"){
+                var utc = self.fetchUTC();
+                var week_from = moment().weekday(0).format('YYYY-MM-DD 00:00:00');
+                week_from = moment(week_from).add(utc,'hours').format('YYYY-MM-DD HH:mm:ss');
+                var week_to = moment(week_from).weekday(7).format('YYYY-MM-DD HH:mm:ss');
+                var PosOrder = _.reduce(_.map(self.PosOrder,function(item) {
+                    if(item.date >= week_from && item.date < week_to){
+                        var utc = moment.utc(item.date,'YYYY-MM-DD h:mm:ss A');
+                        content.push({
+                            date: moment(utc._d).format('YYYY-MM-DD'),
+                            amount: item.amount,
+                        });
+                        return item.amount;
+                    }else{
+                        return 0;
+                    }
+                }),function(memo, num) {
+                    return memo + num;
+                },0);
+                var AccountInvoice = _.reduce(_.map(self.AccountInvoice,function(item) {
+                    if(item.date >= moment().weekday(0).format('YYYY-MM-DD') && item.date < moment().weekday(7).format('YYYY-MM-DD') && item.type == "out_invoice" && item.origin == '%SO%'){
+                        content.push({
+                            date: item.date,
+                            amount: item.amount,
+                        });
+                        return item.amount;
+                    }else{
+                        return 0;
+                    }
+                }),function(memo, num) {
+                    return memo + num;
+                },0);
+                var AccountInvoiceRefund = _.reduce(_.map(self.AccountInvoiceRefund,function(item) {
+                    if(item.date >= moment().weekday(0).format('YYYY-MM-DD') && item.date < moment().weekday(7).format('YYYY-MM-DD') && item.type == "out_refund"){
+                        content.push({
+                            date: item.date,
+                            amount: item.amount * -1,
+                        });
+                        return item.amount;
+                    }else{
+                        return 0;
+                    }
+                }),function(memo, num) {
+                    return memo + num;
+                },0);
+                amount = PosOrder + AccountInvoice - AccountInvoiceRefund;
+            }
+            /*
+            =========================================================================================
+                COMPRAS
+            =========================================================================================
+            */
+            if(type == "purchase"){
+                var purchase = _.reduce(_.map(self.AccountInvoice,function(item) {
+                    /*
+                    ============================
+                        CONTENIDO DE LA SEMANA
+                    ============================
+                    */
+                    if(item.type == 'in_invoice' && item.origin != null && item.date >= moment().weekday(0).format('YYYY-MM-DD') && item.date < moment().weekday(7).format('YYYY-MM-DD')){
+                        var origin = 0;
+                        if(item.origin.match(/PO/) != null){
+                            content.push({
+                                date: item.date,
+                                amount: item.amount,
+                            });
+                            origin = item.amount;
+                        }
+                        return origin;
+                    }else{
+                        return 0;
+                    }
+                }),function(memo, num) {
+                    return memo + num;
+                },0);
+                amount = purchase;
+            }
+            /*
+            =========================================================================================
+                GASTOS
+            =========================================================================================
+            */
+            if(type == "expense"){
+                var expense = _.reduce(_.map(self.AccountInvoice,function(item) {
+                    /*
+                    ============================
+                        CONTENIDO DE LA SEMANA
+                    ============================
+                    */
+                    if(item.type == 'in_invoice' && item.date >= moment().weekday(0).format('YYYY-MM-DD') && item.date < moment().weekday(7).format('YYYY-MM-DD')){
+                        if(item.origin == null){
+                            content.push({
+                                date: item.date,
+                                amount: item.amount,
+                            });
+                            return item.amount;
+                        }else{
+                            var origin = 0;
+                            if(item.origin != null){
+                                if(item.origin.match(/PO/) == null){
+                                    content.push({
+                                        date: item.date,
+                                        amount: item.amount,
+                                    });
+                                    origin = item.amount;
+                                }
+                            }
+                            return origin;
+                        }
+                    }else{
+                        return 0;
+                    }
+                }),function(memo, num) {
+                    return memo + num;
+                },0);
+
+                var net = _.reduce(_.map(self.HrPayslip,function(item) {
+                    if(item.date >= moment().weekday(0).format('YYYY-MM-DD') && item.date < moment().weekday(7).format('YYYY-MM-DD')){
+                        content.push({
+                            date: item.date,
+                            amount: item.net,
+                        });
+                        return item.net;
+                    }else{
+                        return 0;
+                    }
+                }),function(memo, num) {
+                    return memo + num;
+                },0);
+
+                var ipsc = _.reduce(_.map(self.HrPayslip,function(item) {
+                    if(item.code == 'IPSC' && item.ipsc != null && item.date >= moment().weekday(0).format('YYYY-MM-DD') && item.date < moment().weekday(7).format('YYYY-MM-DD')){
+                        content.push({
+                            date: item.date,
+                            amount: item.ipsc,
+                        });
+                        return item.ipsc;
+                    }else{
+                        return 0;
+                    }
+                }),function(memo, num) {
+                    return memo + num;
+                },0);
+
+                var ipse = _.reduce(_.map(self.HrPayslip,function(item) {
+                    if(item.code == 'IPSE' && item.ipse != null && item.date >= moment().weekday(0).format('YYYY-MM-DD') && item.date < moment().weekday(7).format('YYYY-MM-DD')){
+                        content.push({
+                            date: item.date,
+                            amount: item.ipse * -1,
+                        });
+                        return item.ipse * -1;
+                    }else{
+                        return 0;
+                    }
+                }),function(memo, num) {
+                    return memo + num;
+                },0);
+                amount = expense + net + ipse + ipsc;
+            }
+
+            if(type == "payment"){
+                var payment = _.reduce(_.map(self.AccountVoucher,function(item) {
+                    /*
+                    ============================
+                        CONTENIDO DE LA SEMANA
+                    ============================
+                    */
+                    if(item.type == 'receipt' && item.date >= moment().weekday(0).format('YYYY-MM-DD') && item.date < moment().weekday(7).format('YYYY-MM-DD')){
+                      content.push({
+                          date: item.date,
+                          amount: item.amount,
+                      });
+                        return item.amount;
+                    }else{
+                        return 0;
+                    }
+
+                }),function(memo, num) {
+                    return memo + num;
+                },0);
+                amount = payment;
+            }
+            self.week = amount;
+            self.content = content;
+        },
+        /*
+        =====================================================
+            SHOW MONTH
+        =====================================================
+        */
+        fetchMonth: function () {
+            var self = this;
+            var content = [];
+            var CurrencyBase = self.ResCompany[0].currency_id;
+            /*
+            =======================
+                VENTAS
+            =======================
+            */
+            var PosOrder = _.reduce(_.map(self.PosOrder,function(item) {
+                var utc = moment.utc(item.date,'YYYY-MM-DD h:mm:ss A');
+                utc = moment(utc._d).format('YYYY-MM-DD');
+                var value = 0;
+                if(moment(utc).format('YYYY-MM') == moment().format('YYYY-MM')){
+                    value = item.amount;
+                }
+                return value;
+            }),function(memo, num) {
+                return memo + num;
+            },0);
+            // && item.origin == '%SO%'
+            var AccountInvoice = _.reduce(_.map(self.AccountInvoice,function(item) {
+            //     if(item.type == 'out_invoice' && item.origin == '%SO%'){
+            //         return item.amount;
+            //     }else{
+            //         return 0;
+            //     }
+            // }),function(memo, num) {
+            //     return memo + num;
+            // },0);
+            if(item.type == 'out_invoice'){
+                var origin = 0;
+                if(item.origin.match(/SO/) != null){
+                    origin = item.amount;
+                }
+                return origin;
+              }else{
+                  return 0;
+              }
+            }),function(memo, num) {
+                return memo + num;
+            },0);
+            var AccountInvoiceRefund = _.reduce(_.map(self.AccountInvoice,function(item) {
+                if(item.type == 'out_refund'){
+                    return item.amount;
+                }else{
+                    return 0;
+                }
+            }),function(memo, num) {
+                return memo + num;
+            },0);
+            var sale = PosOrder + AccountInvoice - AccountInvoiceRefund;
+            /*
+            =======================
+                COMPRAS
+            =======================
+            */
+            var purchase = _.reduce(_.map(self.AccountInvoice,function(item) {
+                if(item.type == 'in_invoice' && item.origin != null){
+                    var origin = 0;
+                    if(item.origin.match(/PO/) != null){
+                        origin = item.amount;
+                    }
+                    return origin;
+                }else{
+                    return 0;
+                }
+            }),function(memo, num) {
+                return memo + num;
+            },0);
+            /*
+            =======================
+                GASTOS
+            =======================
+            */
+            var expense = _.reduce(_.map(self.AccountInvoice,function(item) {
+                if(item.type == 'in_invoice'){
+                    if(item.origin == null){
+                        return item.amount;
+                    }else{
+                        var origin = 0;
+                        if(item.origin != null){
+                            if(item.origin.match(/PO/) == null){
+                                origin = item.amount;
+                            }
+                        }
+                        return origin;
+                    }
+                }else{
+                    return 0;
+                }
+            }),function(memo, num) {
+                return memo + num;
+            },0);
+
+            /*
+            =======================
+                COBROS
+            =======================
+            */
+
+            var payment = _.reduce(_.map(self.AccountVoucher,function(item) {
+              if(item.type == 'receipt'){
+                  return item.amount;
+              }else{
+                  return 0;
+              }
+            }),function(memo, num) {
+                return memo + num;
+            },0);
+
+            /*
+            =======================
+                RECURSOS HUMANOS
+            =======================
+            */
+
+            var net = _.reduce(_.map(self.HrPayslip,function(item) {
+                return item.net;
+            }),function(memo, num) {
+                return memo + num;
+            },0);
+
+            var ipsc = _.reduce(_.map(self.HrPayslip,function(item) {
+                if(item.code == 'IPSC' && item.ipsc != null){
+                    return item.ipsc;
+                }else{
+                    return 0;
+                }
+            }),function(memo, num) {
+                return memo + num;
+            },0);
+
+            var ipse = _.reduce(_.map(self.HrPayslip,function(item) {
+                if(item.code == 'IPSE' && item.ipse != null){
+                    return item.ipse * -1;
+                }else{
+                    return 0;
+                }
+            }),function(memo, num) {
+                return memo + num;
+            },0);
+
+            expense = expense + net + ipse + ipsc;
+
+            self.$el.find('.widget-content').find('#number_sale').text(accounting.formatMoney(sale, CurrencyBase.symbol, CurrencyBase.decimal_places, CurrencyBase.thousands_separator, CurrencyBase.decimal_separator));
+            self.$el.find('.widget-content').find('#number_purchase').text(accounting.formatMoney(purchase, CurrencyBase.symbol, CurrencyBase.decimal_places, CurrencyBase.thousands_separator, CurrencyBase.decimal_separator));
+            self.$el.find('.widget-content').find('#number_expense').text(accounting.formatMoney(expense, CurrencyBase.symbol, CurrencyBase.decimal_places, CurrencyBase.thousands_separator, CurrencyBase.decimal_separator));
+            self.$el.find('.widget-content').find('#number_payment').text(accounting.formatMoney(payment, CurrencyBase.symbol, CurrencyBase.decimal_places, CurrencyBase.thousands_separator, CurrencyBase.decimal_separator));
+
+            self.SaleMonth = sale;
+            self.PurchaseMonth = purchase;
+            self.ExpenseMonth = expense;
+            self.PaymentMonth = payment;
+            self.BuildWidget(sale,purchase,expense,payment);
+        },
+
+        BuildWidget: function(sale,purchase,expense,payment){
+            var self = this;
+            var SaleObjetive = 0;
+            var PurchaseLimit = 0;
+            var ExpenseLimit = 0;
+            var PaymentObjetive = 0;
+
+            if(self.DashboardObjetive.length > 0){
+                self.SaleObjetiveAmount = self.DashboardObjetive[0].sale_objetive;
+                self.PurchaseLimitAmount = self.DashboardObjetive[0].purchase_limit;
+                self.ExpenseLimitAmount = self.DashboardObjetive[0].expense_limit;
+                self.PaymentObjetiveAmount = self.DashboardObjetive[0].payment_objetive;
+
+                SaleObjetive = self.getPercentage(sale, self.SaleObjetiveAmount);
+                PurchaseLimit = self.getPercentage(purchase, self.PurchaseLimitAmount);
+                ExpenseLimit = self.getPercentage(expense, self.ExpenseLimitAmount);
+                PaymentObjetive = self.getPercentage(payment, self.PaymentObjetiveAmount)
+            }
+
+            $("#circle-sale").circliful({
+                animationStep: 10,
+                foregroundBorderWidth: 12,
+                backgroundBorderWidth: 1,
+                percent: SaleObjetive,
+                fontColor: '#43a047',
+                foregroundColor: '#43a047',
+                percentageTextSize: 35,
+            });
+
+            $("#circle-purchase").circliful({
+                animationStep: 10,
+                foregroundBorderWidth: 12,
+                backgroundBorderWidth: 1,
+                percent: PurchaseLimit,
+                fontColor: '#f9a825',
+                foregroundColor: '#f9a825',
+                percentageTextSize: 35,
+            });
+
+            $("#circle-expense").circliful({
+                animationStep: 10,
+                foregroundBorderWidth: 12,
+                backgroundBorderWidth: 1,
+                percent: ExpenseLimit,
+                fontColor: '#ef6c00',
+                foregroundColor: '#ef6c00',
+                percentageTextSize: 35,
+            });
+
+            $("#circle-payment").circliful({
+                animationStep: 10,
+                foregroundBorderWidth: 12,
+                backgroundBorderWidth: 1,
+                percent: PaymentObjetive,
+                fontColor: '#0288d1',
+                foregroundColor: '#0288d1',
+                percentageTextSize: 35,
+            });
+
+            self.SalePercentage = SaleObjetive;
+            self.PurchasePercentage = PurchaseLimit;
+            self.ExpensePercentage = ExpenseLimit;
+            self.PaymentPercentage = PaymentObjetive;
+        },
+
+        BuildChartWeek: function(ChartColor){
+            var self = this;
+            var data = [];
+            var CurrencyBase = self.ResCompany[0].currency_id;
+            var label = ['Domingo','Lunes', 'Martes', 'Miercoles','Jueves','Viernes','Sabado'];
+            var body = [];
+            var name = '.week-dashboard-chart';
+            for (var i = 0; i < 7; i++) {
+                var content = self.getContent(i);
+                var amount = _.reduce(_.map(content,function(item) {
+                    return item.amount;
+                }),function(memo, num) {
+                    return memo + num;
+                },0);
+                body.push(amount);
+            }
+            var chart = new widget.DashboardChartWidget(self);
+            chart.BuildLineChart(name,label,body,CurrencyBase,ChartColor,ChartColor);
+        },
+        RedirectSale: function() {
+            var self = this;
+            self.do_action({
+                type: 'ir.actions.client',
+                tag: "eiru_reports.sale_action",
+                target: 'current',
+                params: {},
+                context: self.session.user_context,
+            });
+            self.removeModal();
+        },
+        RedirectPurchase: function() {
+            var self = this;
+            self.do_action({
+                type: 'ir.actions.client',
+                tag: "eiru_reports.purchase_action",
+                target: 'current',
+                params: {},
+                context: self.session.user_context,
+            });
+            self.removeModal();
+        },
+        RedirectExpense: function() {
+            var self = this;
+            self.do_action({
+                type: 'ir.actions.client',
+                tag: "eiru_reports.expense_action",
+                target: 'current',
+                params: {},
+                context: self.session.user_context,
+            });
+            self.removeModal();
+        },
+
+        RedirectPayment: function() {
+            var self = this;
+            self.do_action({
+                type: 'ir.actions.client',
+                tag: "eiru_reports.customer_payment_action",
+                target: 'current',
+                params: {},
+                context: self.session.user_context,
+            });
+            self.removeModal();
+        },
+        /*
+        =====================================================
+            MODAL
+        =====================================================
+        */
+        showModal: function (e){
+            var self = this;
+            var titleData = [];
+            var ObjectiveTitle;
+            var ObjectivePendingTitle;
+            var ObjectivePending;
+            var Objective;
+            var Today;
+            var Week;
+            var Month;
+            var ObjectivePercentage;
+            var ChartColor;
+            var CurrencyBase = self.ResCompany[0].currency_id;
+            var type = $(e.target).closest('a')[0].className;
+            if(type == 'number_sale'){
+                self.fetchToday('sale');
+                self.fetchWeek('sale');
+                ChartColor = '#43a047';
+                ObjectivePercentage = self.SalePercentage;
+                titleData.push({
+                    title: 'Ventas',
+                });
+                ObjectiveTitle = "Meta del mes";
+                ObjectivePendingTitle = "Faltante para logar la meta del mes";
+                ObjectivePending = accounting.formatMoney(self.SaleObjetiveAmount - self.SaleMonth, CurrencyBase.symbol, CurrencyBase.decimal_places, CurrencyBase.thousands_separator, CurrencyBase.decimal_separator);
+                Objective = accounting.formatMoney(self.SaleObjetiveAmount, CurrencyBase.symbol, CurrencyBase.decimal_places, CurrencyBase.thousands_separator, CurrencyBase.decimal_separator);
+                Today = accounting.formatMoney(self.today, CurrencyBase.symbol, CurrencyBase.decimal_places, CurrencyBase.thousands_separator, CurrencyBase.decimal_separator);
+                Week = accounting.formatMoney(self.week, CurrencyBase.symbol, CurrencyBase.decimal_places, CurrencyBase.thousands_separator, CurrencyBase.decimal_separator);
+                Month = accounting.formatMoney(self.SaleMonth, CurrencyBase.symbol, CurrencyBase.decimal_places, CurrencyBase.thousands_separator, CurrencyBase.decimal_separator);
+            }
+            if(type == 'number_purchase'){
+                self.fetchToday('purchase');
+                self.fetchWeek('purchase');
+                ChartColor = '#f9a825';
+                ObjectivePercentage = self.PurchasePercentage;
+                titleData.push({
+                    title: 'Compras',
+                });
+                ObjectiveTitle = "Limite de compras del mes";
+                ObjectivePendingTitle = "Sobrante del presupuesto del mes";
+                ObjectivePending = accounting.formatMoney(self.PurchaseLimitAmount - self.PurchaseMonth, CurrencyBase.symbol, CurrencyBase.decimal_places, CurrencyBase.thousands_separator, CurrencyBase.decimal_separator);
+                Objective = accounting.formatMoney(self.PurchaseLimitAmount, CurrencyBase.symbol, CurrencyBase.decimal_places, CurrencyBase.thousands_separator, CurrencyBase.decimal_separator);
+                Today = accounting.formatMoney(self.today, CurrencyBase.symbol, CurrencyBase.decimal_places, CurrencyBase.thousands_separator, CurrencyBase.decimal_separator);
+                Week = accounting.formatMoney(self.week, CurrencyBase.symbol, CurrencyBase.decimal_places, CurrencyBase.thousands_separator, CurrencyBase.decimal_separator);
+                Month = accounting.formatMoney(self.PurchaseMonth, CurrencyBase.symbol, CurrencyBase.decimal_places, CurrencyBase.thousands_separator, CurrencyBase.decimal_separator);
+            }
+            if(type == 'number_expense'){
+                self.fetchToday('expense');
+                self.fetchWeek('expense');
+                ChartColor = '#ef6c00';
+                ObjectivePercentage = self.ExpensePercentage;
+                titleData.push({
+                    title: 'Gastos',
+                });
+                ObjectiveTitle = "Limite de gastos del mes";
+                ObjectivePendingTitle = "Sobrante del presupuesto del mes";
+                ObjectivePending = accounting.formatMoney(self.ExpenseLimitAmount - self.ExpenseMonth, CurrencyBase.symbol, CurrencyBase.decimal_places, CurrencyBase.thousands_separator, CurrencyBase.decimal_separator);
+                Objective = accounting.formatMoney(self.ExpenseLimitAmount, CurrencyBase.symbol, CurrencyBase.decimal_places, CurrencyBase.thousands_separator, CurrencyBase.decimal_separator);
+                Today = accounting.formatMoney(self.today, CurrencyBase.symbol, CurrencyBase.decimal_places, CurrencyBase.thousands_separator, CurrencyBase.decimal_separator);
+                Week = accounting.formatMoney(self.week, CurrencyBase.symbol, CurrencyBase.decimal_places, CurrencyBase.thousands_separator, CurrencyBase.decimal_separator);
+                Month = accounting.formatMoney(self.ExpenseMonth, CurrencyBase.symbol, CurrencyBase.decimal_places, CurrencyBase.thousands_separator, CurrencyBase.decimal_separator);
+            }
+            if(type == 'number_payment'){
+                self.fetchToday('payment');
+                self.fetchWeek('payment');
+                ChartColor = '#0288d1';
+                ObjectivePercentage = self.PaymentPercentage;
+                titleData.push({
+                    title: 'Cobros',
+                });
+                ObjectiveTitle = "Objetivo de cobros del mes";
+                ObjectivePendingTitle = "Faltante del mes";
+                ObjectivePending = accounting.formatMoney(self.PaymentObjetiveAmount - self.PaymentMonth, CurrencyBase.symbol, CurrencyBase.decimal_places, CurrencyBase.thousands_separator, CurrencyBase.decimal_separator);
+                Objective = accounting.formatMoney(self.PaymentObjetiveAmount, CurrencyBase.symbol, CurrencyBase.decimal_places, CurrencyBase.thousands_separator, CurrencyBase.decimal_separator);
+                Today = accounting.formatMoney(self.today, CurrencyBase.symbol, CurrencyBase.decimal_places, CurrencyBase.thousands_separator, CurrencyBase.decimal_separator);
+                Week = accounting.formatMoney(self.week, CurrencyBase.symbol, CurrencyBase.decimal_places, CurrencyBase.thousands_separator, CurrencyBase.decimal_separator);
+                Month = accounting.formatMoney(self.PaymentMonth, CurrencyBase.symbol, CurrencyBase.decimal_places, CurrencyBase.thousands_separator, CurrencyBase.decimal_separator);
+            }
+            var modal = Qweb.render('WidgetModal', {
+                modalTitle: titleData,
+                objectiveTitle: ObjectiveTitle,
+                objectivePending: ObjectivePendingTitle,
+                pending: ObjectivePending,
+                objective: Objective,
+                today: Today,
+                week: Week,
+                month: Month,
+            });
+            $('.openerp_webclient_container').after(modal);
+            $('.widget-modal').modal();
+            $('.widget-modal').on('hidden.bs.modal', function (e) {
+                self.removeModal(e);
+            });
+            var contenido = $('.widget-modal').find('.redirect-to-report');
+            contenido.click(function (e) {
+                if(type == 'number_sale'){
+                    self.RedirectSale();
+                }
+                if(type == 'number_purchase'){
+                  self.RedirectPurchase();
+                }
+                if(type = 'number_payment'){
+                  self.RedirectPayment();
+                }else{
+                  self.RedirectExpense();
+                }
+
+            });
+            $("#myItemModal").circliful({
+                animationStep: 10,
+                foregroundBorderWidth: 12,
+                backgroundBorderWidth: 1,
+                percent: ObjectivePercentage,
+                fontColor: ChartColor,
+                foregroundColor: ChartColor,
+                percentageTextSize: 35,
+            });
+            self.BuildChartWeek(ChartColor);
+        },
+        removeModal: function (e) {
+            $('.widget-modal').remove();
+            $('.modal-backdrop').remove();
+        },
+    });
+}

+ 63 - 0
static/src/js/widgets/widget_stock_valuation.js

@@ -0,0 +1,63 @@
+function widget_stock_valuation(widget) {
+    "use strict";
+
+    var model = openerp;
+
+    widget.WidgetStockValuation = widget.Base.extend({
+        template: 'WidgetStockValuation',
+        content: [],
+
+        events: {
+            'click .valuation': 'Redirect',
+            'click .quantity': 'Redirect',
+        },
+
+        init: function (parent) {
+            this._super(parent, {
+                width: 6,
+                height: 3
+            });
+        },
+
+        start: function () {
+            var self = this;
+            self.fetchInitial();
+        },
+
+        fetchInitial: function(){
+            var self = this;
+            self.fetchStockValuationSQL().then(function (StockValuationSQL) {
+                return StockValuationSQL;
+            }).then(function(StockValuationSQL) {
+                self.Valuation = StockValuationSQL.valuation;
+                self.ResCompany = StockValuationSQL.company;
+                return self.ShowValuation();
+            });
+        },
+
+        fetchStockValuationSQL: function() {
+            var self = this;
+            var data = $.get('/dashboard-StockValuation');
+            return data;
+        },
+
+        ShowValuation: function () {
+            var self = this;
+
+            var CurrencyBase = self.ResCompany[0].currency_id;
+            self.$el.find('.widget-content').find('#valuation').text(accounting.formatMoney(self.Valuation[0].valuation, CurrencyBase.symbol, CurrencyBase.decimal_places, CurrencyBase.thousands_separator, CurrencyBase.decimal_separator));
+            self.$el.find('.widget-content').find('#quantity').text(accounting.formatNumber(self.Valuation[0].quantity,2,'.',','));
+        },
+
+        Redirect: function() {
+            var self = this;
+            self.do_action({
+                type: 'ir.actions.client',
+                tag: "eiru_reports.stock_valuation_action",
+                target: 'current',
+                params: {},
+                context: self.session.user_context,
+            });
+        },
+    });
+}

+ 78 - 0
static/src/js/widgets/widget_stock_valuation_with_variant_cost_price.js

@@ -0,0 +1,78 @@
+function widget_stock_valuation_with_variant_cost_price(widget) {
+    "use strict";
+
+    var model = openerp;
+
+    widget.WidgetStockValuationVariantCostPrice = widget.Base.extend({
+        template: 'WidgetStockValuationVariantCostPrice',
+        content: [],
+
+        events: {
+            'click .valuation': 'Redirect',
+            'click .quantity': 'Redirect',
+        },
+
+        init: function (parent) {
+            this._super(parent, {
+                width: 6,
+                height: 3
+            });
+        },
+
+        start: function () {
+            var self = this;
+            self.fetchInitial();
+        },
+
+        fetchInitial: function(){
+            var self = this;
+            self.fetchStockValuationSQL().then(function (StockValuationSQL) {
+                return StockValuationSQL;
+            }).then(function(StockValuationSQL) {
+                self.Valuation = StockValuationSQL.valuation;
+                self.ResCompany = StockValuationSQL.company;
+                return self.ShowValuation();
+            });
+        },
+
+        fetchStockValuationSQL: function() {
+            var self = this;
+            var data = $.get('/dashboard-StockValuationWithVariantCostPrice');
+            return data;
+        },
+
+        ShowValuation: function () {
+            var self = this;
+
+            var CurrencyBase = self.ResCompany[0].currency_id;
+            var contador = 0;
+            var value = _.reduce(_.map(self.Valuation, function (map) {
+                if(map.variant_cost == null){
+                    return map.template_cost;
+                }else{
+                    return map.variant_cost;
+                }
+            }), function (meno,num) {
+                return meno + num;
+            }, 0);
+            var quantity = _.reduce(_.map(self.Valuation, function (map) {
+                return map.qty;
+            }), function (meno,num) {
+                return meno + num;
+            }, 0);
+            self.$el.find('.widget-content').find('#valuation').text(accounting.formatMoney(value, CurrencyBase.symbol, CurrencyBase.decimal_places, CurrencyBase.thousands_separator, CurrencyBase.decimal_separator));
+            self.$el.find('.widget-content').find('#quantity').text(accounting.formatNumber(quantity,2,'.',','));
+        },
+
+        Redirect: function() {
+            var self = this;
+            self.do_action({
+                type: 'ir.actions.client',
+                tag: "eiru_reports.stock_valuation_action",
+                target: 'current',
+                params: {},
+                context: self.session.user_context,
+            });
+        },
+    });
+}

+ 1053 - 0
static/src/lib/chartjs-plugin-datalabels.js

@@ -0,0 +1,1053 @@
+/*!
+ * @license
+ * chartjs-plugin-datalabels
+ * http://chartjs.org/
+ * Version: 0.4.0
+ *
+ * Copyright 2018 Chart.js Contributors
+ * Released under the MIT license
+ * https://github.com/chartjs/chartjs-plugin-datalabels/blob/master/LICENSE.md
+ */
+(function (global, factory) {
+	typeof exports === 'object' && typeof module !== 'undefined' ? factory(require('chart.js')) :
+	typeof define === 'function' && define.amd ? define(['chart.js'], factory) :
+	(factory(global.Chart));
+}(this, (function (Chart) { 'use strict';
+
+Chart = Chart && Chart.hasOwnProperty('default') ? Chart['default'] : Chart;
+
+'use strict';
+
+var helpers$2 = Chart.helpers;
+
+var HitBox = function() {
+	this._rect = null;
+	this._rotation = 0;
+};
+
+helpers$2.extend(HitBox.prototype, {
+	update: function(center, rect, rotation) {
+		var margin = 1;
+		var cx = center.x;
+		var cy = center.y;
+		var x = cx + rect.x;
+		var y = cy + rect.y;
+
+		this._rotation = rotation;
+		this._rect = {
+			x0: x - margin,
+			y0: y - margin,
+			x1: x + rect.w + margin * 2,
+			y1: y + rect.h + margin * 2,
+			cx: cx,
+			cy: cy,
+		};
+	},
+
+	contains: function(x, y) {
+		var me = this;
+		var rect = me._rect;
+		var cx, cy, r, rx, ry;
+
+		if (!rect) {
+			return false;
+		}
+
+		cx = rect.cx;
+		cy = rect.cy;
+		r = me._rotation;
+		rx = cx + (x - cx) * Math.cos(r) + (y - cy) * Math.sin(r);
+		ry = cy - (x - cx) * Math.sin(r) + (y - cy) * Math.cos(r);
+
+		return !(rx < rect.x0
+			|| ry < rect.y0
+			|| rx > rect.x1
+			|| ry > rect.y1);
+	}
+});
+
+'use strict';
+
+var helpers$3 = Chart.helpers;
+
+var devicePixelRatio = typeof window !== 'undefined'
+	? window.devicePixelRatio
+	: 1;
+
+var utils = {
+	// @todo move this in Chart.helpers.toTextLines
+	toTextLines: function(inputs) {
+		var lines = [];
+		var input;
+
+		inputs = [].concat(inputs);
+		while (inputs.length) {
+			input = inputs.pop();
+			if (typeof input === 'string') {
+				lines.unshift.apply(lines, input.split('\n'));
+			} else if (Array.isArray(input)) {
+				inputs.push.apply(inputs, input);
+			} else if (!helpers$3.isNullOrUndef(inputs)) {
+				lines.unshift('' + input);
+			}
+		}
+
+		return lines;
+	},
+
+	// @todo move this method in Chart.helpers.canvas.toFont (deprecates helpers.fontString)
+	// @see https://developer.mozilla.org/en-US/docs/Web/CSS/font
+	toFontString: function(font) {
+		if (!font || helpers$3.isNullOrUndef(font.size) || helpers$3.isNullOrUndef(font.family)) {
+			return null;
+		}
+
+		return (font.style ? font.style + ' ' : '')
+			+ (font.weight ? font.weight + ' ' : '')
+			+ font.size + 'px '
+			+ font.family;
+	},
+
+	// @todo move this in Chart.helpers.canvas.textSize
+	// @todo cache calls of measureText if font doesn't change?!
+	textSize: function(ctx, lines, font) {
+		var items = [].concat(lines);
+		var ilen = items.length;
+		var prev = ctx.font;
+		var width = 0;
+		var i;
+
+		ctx.font = font.string;
+
+		for (i = 0; i < ilen; ++i) {
+			width = Math.max(ctx.measureText(items[i]).width, width);
+		}
+
+		ctx.font = prev;
+
+		return {
+			height: ilen * font.lineHeight,
+			width: width
+		};
+	},
+
+	// @todo move this method in Chart.helpers.options.toFont
+	parseFont: function(value) {
+		var global = Chart.defaults.global;
+		var size = helpers$3.valueOrDefault(value.size, global.defaultFontSize);
+		var font = {
+			family: helpers$3.valueOrDefault(value.family, global.defaultFontFamily),
+			lineHeight: helpers$3.options.toLineHeight(value.lineHeight, size),
+			size: size,
+			style: helpers$3.valueOrDefault(value.style, global.defaultFontStyle),
+			weight: helpers$3.valueOrDefault(value.weight, null),
+			string: ''
+		};
+
+		font.string = utils.toFontString(font);
+		return font;
+	},
+
+	/**
+	 * Returns value bounded by min and max. This is equivalent to max(min, min(value, max)).
+	 * @todo move this method in Chart.helpers.bound
+	 * https://doc.qt.io/qt-5/qtglobal.html#qBound
+	 */
+	bound: function(min, value, max) {
+		return Math.max(min, Math.min(value, max));
+	},
+
+	/**
+	 * Returns an array of pair [value, state] where state is:
+	 * * -1: value is only in a0 (removed)
+	 * *  1: value is only in a1 (added)
+	 */
+	arrayDiff: function(a0, a1) {
+		var prev = a0.slice();
+		var updates = [];
+		var i, j, ilen, v;
+
+		for (i = 0, ilen = a1.length; i < ilen; ++i) {
+			v = a1[i];
+			j = prev.indexOf(v);
+
+			if (j === -1) {
+				updates.push([v, 1]);
+			} else {
+				prev.splice(j, 1);
+			}
+		}
+
+		for (i = 0, ilen = prev.length; i < ilen; ++i) {
+			updates.push([prev[i], -1]);
+		}
+
+		return updates;
+	},
+
+	/**
+	 * https://github.com/chartjs/chartjs-plugin-datalabels/issues/70
+	 */
+	rasterize: function(v) {
+		return Math.round(v * devicePixelRatio) / devicePixelRatio;
+	}
+};
+
+'use strict';
+
+function orient(point, origin) {
+	var x0 = origin.x;
+	var y0 = origin.y;
+
+	if (x0 === null) {
+		return {x: 0, y: -1};
+	}
+	if (y0 === null) {
+		return {x: 1, y: 0};
+	}
+
+	var dx = point.x - x0;
+	var dy = point.y - y0;
+	var ln = Math.sqrt(dx * dx + dy * dy);
+	return {
+		x: ln ? dx / ln : 0,
+		y: ln ? dy / ln : -1
+	};
+}
+
+function aligned(x, y, vx, vy, align) {
+	switch (align) {
+	case 'center':
+		vx = vy = 0;
+		break;
+	case 'bottom':
+		vx = 0;
+		vy = 1;
+		break;
+	case 'right':
+		vx = 1;
+		vy = 0;
+		break;
+	case 'left':
+		vx = -1;
+		vy = 0;
+		break;
+	case 'top':
+		vx = 0;
+		vy = -1;
+		break;
+	case 'start':
+		vx = -vx;
+		vy = -vy;
+		break;
+	case 'end':
+		// keep the natural orientation
+		break;
+	default:
+		// clockwise rotation (in degree)
+		align *= (Math.PI / 180);
+		vx = Math.cos(align);
+		vy = Math.sin(align);
+		break;
+	}
+
+	return {
+		x: x,
+		y: y,
+		vx: vx,
+		vy: vy
+	};
+}
+
+var positioners = {
+	arc: function(vm, anchor, align) {
+		var angle = (vm.startAngle + vm.endAngle) / 2;
+		var vx = Math.cos(angle);
+		var vy = Math.sin(angle);
+		var r0 = vm.innerRadius;
+		var r1 = vm.outerRadius;
+		var d;
+
+		if (anchor === 'start') {
+			d = r0;
+		} else if (anchor === 'end') {
+			d = r1;
+		} else {
+			d = (r0 + r1) / 2;
+		}
+
+		return aligned(
+			vm.x + vx * d,
+			vm.y + vy * d,
+			vx,
+			vy,
+			align);
+	},
+
+	point: function(vm, anchor, align, origin) {
+		var v = orient(vm, origin);
+		var r = vm.radius;
+		var d = 0;
+
+		if (anchor === 'start') {
+			d = -r;
+		} else if (anchor === 'end') {
+			d = r;
+		}
+
+		return aligned(
+			vm.x + v.x * d,
+			vm.y + v.y * d,
+			v.x,
+			v.y,
+			align);
+	},
+
+	rect: function(vm, anchor, align, origin) {
+		var horizontal = vm.horizontal;
+		var size = Math.abs(vm.base - (horizontal ? vm.x : vm.y));
+		var x = horizontal ? Math.min(vm.x, vm.base) : vm.x;
+		var y = horizontal ? vm.y : Math.min(vm.y, vm.base);
+		var v = orient(vm, origin);
+
+		if (anchor === 'center') {
+			if (horizontal) {
+				x += size / 2;
+			} else {
+				y += size / 2;
+			}
+		} else if (anchor === 'start' && !horizontal) {
+			y += size;
+		} else if (anchor === 'end' && horizontal) {
+			x += size;
+		}
+
+		return aligned(
+			x,
+			y,
+			v.x,
+			v.y,
+			align);
+	},
+
+	fallback: function(vm, anchor, align, origin) {
+		var v = orient(vm, origin);
+		return aligned(
+			vm.x,
+			vm.y,
+			v.x,
+			v.y,
+			align);
+	}
+};
+
+'use strict';
+
+var helpers$1 = Chart.helpers;
+var rasterize = utils.rasterize;
+
+function boundingRects(size, padding) {
+	var th = size.height;
+	var tw = size.width;
+	var tx = -tw / 2;
+	var ty = -th / 2;
+
+	return {
+		frame: {
+			x: tx - padding.left,
+			y: ty - padding.top,
+			w: tw + padding.width,
+			h: th + padding.height,
+		},
+		text: {
+			x: tx,
+			y: ty,
+			w: tw,
+			h: th
+		}
+	};
+}
+
+function getScaleOrigin(el) {
+	var horizontal = el._model.horizontal;
+	var scale = el._scale || (horizontal && el._xScale) || el._yScale;
+
+	if (!scale) {
+		return null;
+	}
+
+	if (scale.xCenter !== undefined && scale.yCenter !== undefined) {
+		return {x: scale.xCenter, y: scale.yCenter};
+	}
+
+	var pixel = scale.getBasePixel();
+	return horizontal ?
+		{x: pixel, y: null} :
+		{x: null, y: pixel};
+}
+
+function getPositioner(el) {
+	if (el instanceof Chart.elements.Arc) {
+		return positioners.arc;
+	}
+	if (el instanceof Chart.elements.Point) {
+		return positioners.point;
+	}
+	if (el instanceof Chart.elements.Rectangle) {
+		return positioners.rect;
+	}
+	return positioners.fallback;
+}
+
+function coordinates(el, model, rect) {
+	var point = model.positioner(el._view, model.anchor, model.align, model.origin);
+	var vx = point.vx;
+	var vy = point.vy;
+
+	if (!vx && !vy) {
+		// if aligned center, we don't want to offset the center point
+		return {x: point.x, y: point.y};
+	}
+
+	// include borders to the bounding rect
+	var borderWidth = model.borderWidth || 0;
+	var w = (rect.w + borderWidth * 2);
+	var h = (rect.h + borderWidth * 2);
+
+	// take in account the label rotation
+	var rotation = model.rotation;
+	var dx = Math.abs(w / 2 * Math.cos(rotation)) + Math.abs(h / 2 * Math.sin(rotation));
+	var dy = Math.abs(w / 2 * Math.sin(rotation)) + Math.abs(h / 2 * Math.cos(rotation));
+
+	// scale the unit vector (vx, vy) to get at least dx or dy equal to w or h respectively
+	// (else we would calculate the distance to the ellipse inscribed in the bounding rect)
+	var vs = 1 / Math.max(Math.abs(vx), Math.abs(vy));
+	dx *= vx * vs;
+	dy *= vy * vs;
+
+	// finally, include the explicit offset
+	dx += model.offset * vx;
+	dy += model.offset * vy;
+
+	return {
+		x: point.x + dx,
+		y: point.y + dy
+	};
+}
+
+function drawFrame(ctx, rect, model) {
+	var bgColor = model.backgroundColor;
+	var borderColor = model.borderColor;
+	var borderWidth = model.borderWidth;
+
+	if (!bgColor && (!borderColor || !borderWidth)) {
+		return;
+	}
+
+	ctx.beginPath();
+
+	helpers$1.canvas.roundedRect(
+		ctx,
+		rasterize(rect.x) - borderWidth / 2,
+		rasterize(rect.y) - borderWidth / 2,
+		rasterize(rect.w) + borderWidth,
+		rasterize(rect.h) + borderWidth,
+		model.borderRadius);
+
+	ctx.closePath();
+
+	if (bgColor) {
+		ctx.fillStyle = bgColor;
+		ctx.fill();
+	}
+
+	if (borderColor && borderWidth) {
+		ctx.strokeStyle = borderColor;
+		ctx.lineWidth = borderWidth;
+		ctx.lineJoin = 'miter';
+		ctx.stroke();
+	}
+}
+
+function drawText(ctx, lines, rect, model) {
+	var align = model.textAlign;
+	var font = model.font;
+	var lh = font.lineHeight;
+	var color = model.color;
+	var ilen = lines.length;
+	var x, y, i;
+
+	if (!ilen || !color) {
+		return;
+	}
+
+	x = rect.x;
+	y = rect.y + lh / 2;
+
+	if (align === 'center') {
+		x += rect.w / 2;
+	} else if (align === 'end' || align === 'right') {
+		x += rect.w;
+	}
+
+	ctx.font = model.font.string;
+	ctx.fillStyle = color;
+	ctx.textAlign = align;
+	ctx.textBaseline = 'middle';
+
+	for (i = 0; i < ilen; ++i) {
+		ctx.fillText(
+			lines[i],
+			rasterize(x),
+			rasterize(y),
+			rasterize(rect.w));
+
+		y += lh;
+	}
+}
+
+var Label = function(config, ctx, el, index) {
+	var me = this;
+
+	me._hitbox = new HitBox();
+	me._config = config;
+	me._index = index;
+	me._model = null;
+	me._ctx = ctx;
+	me._el = el;
+};
+
+helpers$1.extend(Label.prototype, {
+	/**
+	 * @private
+	 */
+	_modelize: function(lines, config, context) {
+		var me = this;
+		var index = me._index;
+		var resolve = helpers$1.options.resolve;
+		var font = utils.parseFont(resolve([config.font, {}], context, index));
+
+		return {
+			align: resolve([config.align, 'center'], context, index),
+			anchor: resolve([config.anchor, 'center'], context, index),
+			backgroundColor: resolve([config.backgroundColor, null], context, index),
+			borderColor: resolve([config.borderColor, null], context, index),
+			borderRadius: resolve([config.borderRadius, 0], context, index),
+			borderWidth: resolve([config.borderWidth, 0], context, index),
+			clip: resolve([config.clip, false], context, index),
+			color: resolve([config.color, Chart.defaults.global.defaultFontColor], context, index),
+			font: font,
+			lines: lines,
+			offset: resolve([config.offset, 0], context, index),
+			opacity: resolve([config.opacity, 1], context, index),
+			origin: getScaleOrigin(me._el),
+			padding: helpers$1.options.toPadding(resolve([config.padding, 0], context, index)),
+			positioner: getPositioner(me._el),
+			rotation: resolve([config.rotation, 0], context, index) * (Math.PI / 180),
+			size: utils.textSize(me._ctx, lines, font),
+			textAlign: resolve([config.textAlign, 'start'], context, index)
+		};
+	},
+
+	update: function(context) {
+		var me = this;
+		var model = null;
+		var index = me._index;
+		var config = me._config;
+		var value, label, lines;
+
+		if (helpers$1.options.resolve([config.display, true], context, index)) {
+			value = context.dataset.data[index];
+			label = helpers$1.valueOrDefault(helpers$1.callback(config.formatter, [value, context]), value);
+			lines = helpers$1.isNullOrUndef(label) ? [] : utils.toTextLines(label);
+			model = lines.length ? me._modelize(lines, config, context) : null;
+		}
+
+		me._model = model;
+	},
+
+	draw: function(chart) {
+		var me = this;
+		var ctx = chart.ctx;
+		var model = me._model;
+		var rects, center, area;
+
+		if (!model || !model.opacity) {
+			return;
+		}
+
+		rects = boundingRects(model.size, model.padding);
+		center = coordinates(me._el, model, rects.frame);
+		me._hitbox.update(center, rects.frame, model.rotation);
+
+		ctx.save();
+
+		if (model.clip) {
+			area = chart.chartArea;
+			ctx.beginPath();
+			ctx.rect(
+				area.left,
+				area.top,
+				area.right - area.left,
+				area.bottom - area.top);
+			ctx.clip();
+		}
+
+		ctx.globalAlpha = utils.bound(0, model.opacity, 1);
+		ctx.translate(rasterize(center.x), rasterize(center.y));
+		ctx.rotate(model.rotation);
+
+		drawFrame(ctx, rects.frame, model);
+		drawText(ctx, model.lines, rects.text, model);
+
+		ctx.restore();
+	},
+
+	contains: function(x, y) {
+		return this._hitbox.contains(x, y);
+	}
+});
+
+/**
+ * @module Options
+ */
+
+'use strict';
+
+var helpers$4 = Chart.helpers;
+
+var defaults = {
+	/**
+	 * The label box alignment relative to `anchor` that can be expressed either by a number
+	 * representing the clockwise angle (in degree) or by one of the following string presets:
+	 * - 'start': before the anchor point, following the same direction
+	 * - 'end': after the anchor point, following the same direction
+	 * - 'center': centered on the anchor point
+	 * - 'right': 0°
+	 * - 'bottom': 90°
+	 * - 'left': 180°
+	 * - 'top': 270°
+	 * @member {String|Number|Array|Function}
+	 * @default 'center'
+	 */
+	align: 'center',
+
+	/**
+	 * The label box alignment relative to the element ('start'|'center'|'end')
+	 * @member {String|Array|Function}
+	 * @default 'center'
+	 */
+	anchor: 'center',
+
+	/**
+	 * The color used to draw the background of the surrounding frame.
+	 * @member {String|Array|Function|null}
+	 * @default null (no background)
+	 */
+	backgroundColor: null,
+
+	/**
+	 * The color used to draw the border of the surrounding frame.
+	 * @member {String|Array|Function|null}
+	 * @default null (no border)
+	 */
+	borderColor: null,
+
+	/**
+	 * The border radius used to add rounded corners to the surrounding frame.
+	 * @member {Number|Array|Function}
+	 * @default 0 (not rounded)
+	 */
+	borderRadius: 0,
+
+	/**
+	 * The border width of the surrounding frame.
+	 * @member {Number|Array|Function}
+	 * @default 0 (no border)
+	 */
+	borderWidth: 0,
+
+	/**
+	 * Clip the label drawing to the chart area.
+	 * @member {Boolean|Array|Function}
+	 * @default false (no clipping)
+	 */
+	clip: false,
+
+	/**
+	 * The color used to draw the label text.
+	 * @member {String|Array|Function}
+	 * @default undefined (use Chart.defaults.global.defaultFontColor)
+	 */
+	color: undefined,
+
+	/**
+	 * Whether to display labels global (boolean) or per data (function)
+	 * @member {Boolean|Array|Function}
+	 * @default true
+	 */
+	display: true,
+
+	/**
+	 * The font options used to draw the label text.
+	 * @member {Object|Array|Function}
+	 * @prop {String} font.family - defaults to Chart.defaults.global.defaultFontFamily
+	 * @prop {Number} font.lineHeight - defaults to 1.2
+	 * @prop {Number} font.size - defaults to Chart.defaults.global.defaultFontSize
+	 * @prop {String} font.style - defaults to Chart.defaults.global.defaultFontStyle
+	 * @prop {Number} font.weight - defaults to 'normal'
+	 * @default Chart.defaults.global.defaultFont.*
+	 */
+	font: {
+		family: undefined,
+		lineHeight: 1.2,
+		size: undefined,
+		style: undefined,
+		weight: null
+	},
+
+	/**
+	 * The distance (in pixels) to pull the label away from the anchor point, the direction
+	 * being determined by the `align` value (only applicable if `align` is `start` or `end`).
+	 * @member {Number|Array|Function}
+	 * @default 4
+	 */
+	offset: 4,
+
+	/**
+	 * The label global opacity, including the text, background, borders, etc., specified as
+	 * a number between 0.0 (fully transparent) and 1.0 (fully opaque).
+	 * https://developer.mozilla.org/en-US/docs/Web/API/CanvasRenderingContext2D/globalAlpha
+	 * @member {Number|Array|Function}
+	 * @default 1
+	 */
+	opacity: 1,
+
+	/**
+	 * The padding (in pixels) to apply between the text and the surrounding frame.
+	 * @member {Number|Object|Array|Function}
+	 * @prop {Number} padding.top - Space above the text.
+	 * @prop {Number} padding.right - Space on the right of the text.
+	 * @prop {Number} padding.bottom - Space below the text.
+	 * @prop {Number} padding.left - Space on the left of the text.
+	 * @default 4 (all values)
+	 */
+	padding: {
+		top: 4,
+		right: 4,
+		bottom: 4,
+		left: 4
+	},
+
+	/**
+	 * Clockwise rotation of the label relative to its center.
+	 * @member {Number|Array|Function}
+	 * @default 0
+	 */
+	rotation: 0,
+
+	/**
+	 * Text alignment for multi-lines labels ('left'|'right'|'start'|'center'|'end').
+	 * @member {String|Array|Function}
+	 * @default 'start'
+	 */
+	textAlign: 'start',
+
+	/**
+	 * Allows to customize the label text by transforming input data.
+	 * @member {Function|null}
+	 * @prop {*} value - The data value
+	 * @prop {Object} context - The function unique argument:
+	 * @prop {Chart} context.chart - The current chart
+	 * @prop {Number} context.dataIndex - Index of the current data
+	 * @prop {Object} context.dataset - The current dataset
+	 * @prop {Number} context.datasetIndex - Index of the current dataset
+	 * @default data[index]
+	 */
+	formatter: function(value) {
+		if (helpers$4.isNullOrUndef(value)) {
+			return null;
+		}
+
+		var label = value;
+		var keys, klen, k;
+		if (helpers$4.isObject(value)) {
+			if (!helpers$4.isNullOrUndef(value.label)) {
+				label = value.label;
+			} else if (!helpers$4.isNullOrUndef(value.r)) {
+				label = value.r;
+			} else {
+				label = '';
+				keys = Object.keys(value);
+				for (k = 0, klen = keys.length; k < klen; ++k) {
+					label += (k !== 0 ? ', ' : '') + keys[k] + ': ' + value[keys[k]];
+				}
+			}
+		}
+		return '' + label;
+	},
+
+	/**
+	 * Event listeners, where the property is the type of the event to listen and the value
+	 * a callback with a unique `context` argument containing the same information as for
+	 * scriptable options. If a callback explicitly returns `true`, the label is updated
+	 * with the current context and the chart re-rendered. This allows to implement visual
+	 * interactions with labels such as highlight, selection, etc.
+	 *
+	 * Event currently supported are:
+	 * - 'click': a mouse click is detected within a label
+	 * - 'enter': the mouse enters a label
+	 * -' leave': the mouse leaves a label
+	 *
+	 * @member {Object}
+	 * @default {}
+	 */
+	listeners: {}
+};
+
+/**
+ * @see https://github.com/chartjs/Chart.js/issues/4176
+ */
+
+'use strict';
+
+var helpers = Chart.helpers;
+var EXPANDO_KEY = '$datalabels';
+
+Chart.defaults.global.plugins.datalabels = defaults;
+
+function configure(dataset, options) {
+	var override = dataset.datalabels;
+	var config = {};
+
+	if (override === false) {
+		return null;
+	}
+	if (override === true) {
+		override = {};
+	}
+
+	return helpers.merge(config, [options, override]);
+}
+
+function drawLabels(chart, datasetIndex) {
+	var meta = chart.getDatasetMeta(datasetIndex);
+	var elements = meta.data || [];
+	var ilen = elements.length;
+	var i, el, label;
+
+	for (i = 0; i < ilen; ++i) {
+		el = elements[i];
+		label = el[EXPANDO_KEY];
+		if (label) {
+			label.draw(chart);
+		}
+	}
+}
+
+function labelAtXY(chart, x, y) {
+	var items = chart[EXPANDO_KEY].labels;
+	var i, j, labels, label;
+
+	// Until we support z-index, let's hit test in the drawing reverse order
+	for (i = items.length - 1; i >= 0; --i) {
+		labels = items[i] || [];
+		for (j = labels.length - 1; j >= 0; --j) {
+			label = labels[j];
+			if (label.contains(x, y)) {
+				return {dataset: i, label: label};
+			}
+		}
+	}
+
+	return null;
+}
+
+function dispatchEvent(chart, listeners, target) {
+	var callback = listeners && listeners[target.dataset];
+	if (!callback) {
+		return;
+	}
+
+	var label = target.label;
+	var context = label.$context;
+
+	if (helpers.callback(callback, [context]) === true) {
+		// Users are allowed to tweak the given context by injecting values that can be
+		// used in scriptable options to display labels differently based on the current
+		// event (e.g. highlight an hovered label). That's why we update the label with
+		// the output context and schedule a new chart render by setting it dirty.
+		chart[EXPANDO_KEY].dirty = true;
+		label.update(context);
+	}
+}
+
+function dispatchMoveEvents(chart, listeners, previous, target) {
+	var enter, leave;
+
+	if (!previous && !target) {
+		return;
+	}
+
+	if (!previous) {
+		enter = true;
+	} else if (!target) {
+		leave = true;
+	} else if (previous.label !== target.label) {
+		leave = enter = true;
+	}
+
+	if (leave) {
+		dispatchEvent(chart, listeners.leave, previous);
+	}
+	if (enter) {
+		dispatchEvent(chart, listeners.enter, target);
+	}
+}
+
+function handleMoveEvents(chart, event) {
+	var expando = chart[EXPANDO_KEY];
+	var listeners = expando.listeners;
+	var previous, target;
+
+	if (!listeners.enter && !listeners.leave) {
+		return;
+	}
+
+	if (event.type === 'mousemove') {
+		target = labelAtXY(chart, event.x, event.y);
+	} else if (event.type !== 'mouseout') {
+		return;
+	}
+
+	previous = expando.hovered;
+	expando.hovered = target;
+	dispatchMoveEvents(chart, listeners, previous, target);
+}
+
+function handleClickEvents(chart, event) {
+	var handlers = chart[EXPANDO_KEY].listeners.click;
+	var target = handlers && labelAtXY(chart, event.x, event.y);
+	if (target) {
+		dispatchEvent(chart, handlers, target);
+	}
+}
+
+Chart.defaults.global.plugins.datalabels = defaults;
+
+Chart.plugins.register({
+	id: 'datalabels',
+
+	beforeInit: function(chart) {
+		chart[EXPANDO_KEY] = {
+			actives: []
+		};
+	},
+
+	beforeUpdate: function(chart) {
+		var expando = chart[EXPANDO_KEY];
+		expando.listened = false;
+		expando.listeners = {};    // {event-type: {dataset-index: function}}
+		expando.labels = [];       // [dataset-index: [labels]]
+	},
+
+	afterDatasetUpdate: function(chart, args, options) {
+		var datasetIndex = args.index;
+		var expando = chart[EXPANDO_KEY];
+		var labels = expando.labels[datasetIndex] = [];
+		var visible = chart.isDatasetVisible(datasetIndex);
+		var dataset = chart.data.datasets[datasetIndex];
+		var config = configure(dataset, options);
+		var elements = args.meta.data || [];
+		var ilen = elements.length;
+		var ctx = chart.ctx;
+		var i, el, label;
+
+		ctx.save();
+
+		for (i = 0; i < ilen; ++i) {
+			el = elements[i];
+
+			if (visible && el && !el.hidden && !el._model.skip) {
+				labels.push(label = new Label(config, ctx, el, i));
+				label.update(label.$context = {
+					active: false,
+					chart: chart,
+					dataIndex: i,
+					dataset: dataset,
+					datasetIndex: datasetIndex
+				});
+			} else {
+				label = null;
+			}
+
+			el[EXPANDO_KEY] = label;
+		}
+
+		ctx.restore();
+
+		// Store listeners at the chart level and per event type to optimize
+		// cases where no listeners are registered for a specific event
+		helpers.merge(expando.listeners, config.listeners || {}, {
+			merger: function(key, target, source) {
+				target[key] = target[key] || {};
+				target[key][args.index] = source[key];
+				expando.listened = true;
+			}
+		});
+	},
+
+	// Draw labels on top of all dataset elements
+	// https://github.com/chartjs/chartjs-plugin-datalabels/issues/29
+	// https://github.com/chartjs/chartjs-plugin-datalabels/issues/32
+	afterDatasetsDraw: function(chart) {
+		for (var i = 0, ilen = chart.data.datasets.length; i < ilen; ++i) {
+			drawLabels(chart, i);
+		}
+	},
+
+	beforeEvent: function(chart, event) {
+		// If there is no listener registered for this chart, `listened` will be false,
+		// meaning we can immediately ignore the incoming event and avoid useless extra
+		// computation for users who don't implement label interactions.
+		if (chart[EXPANDO_KEY].listened) {
+			switch (event.type) {
+			case 'mousemove':
+			case 'mouseout':
+				handleMoveEvents(chart, event);
+				break;
+			case 'click':
+				handleClickEvents(chart, event);
+				break;
+			default:
+			}
+		}
+	},
+
+	afterEvent: function(chart) {
+		var expando = chart[EXPANDO_KEY];
+		var previous = expando.actives;
+		var actives = expando.actives = chart.lastActive || [];  // public API?!
+		var updates = utils.arrayDiff(previous, actives);
+		var i, ilen, update, label;
+
+		for (i = 0, ilen = updates.length; i < ilen; ++i) {
+			update = updates[i];
+			if (update[1]) {
+				label = update[0][EXPANDO_KEY];
+				if (label) {
+					label.$context.active = (update[1] === 1);
+					label.update(label.$context);
+				}
+			}
+		}
+
+		if ((expando.dirty || updates.length) && !chart.animating) {
+			chart.render();
+		}
+
+		delete expando.dirty;
+	}
+});
+
+})));

+ 437 - 0
static/src/lib/circliful.js

@@ -0,0 +1,437 @@
+"use strict";
+
+(function ($) {
+
+    $.fn.circliful = function (options, callback) {
+
+        var settings = $.extend({
+            // These are the defaults.
+            foregroundColor: "#3498DB",
+            backgroundColor: "#ccc",
+            pointColor: "none",
+            fillColor: 'none',
+            foregroundBorderWidth: 15,
+            backgroundBorderWidth: 15,
+            pointSize: 28.5,
+            fontColor: '#aaa',
+            percent: 75,
+            animation: 1,
+            animationStep: 5,
+            icon: 'none',
+            iconSize: '30',
+            iconColor: '#ccc',
+            iconPosition: 'top',
+            iconDecoration: true,
+            target: 0,
+            start: 0,
+            showPercent: 1,
+            percentageTextSize: 22,
+            percentageX: 100,
+            percentageY: 113,
+            textAdditionalCss: '',
+            targetPercent: 0,
+            targetTextSize: 17,
+            targetColor: '#2980B9',
+            text: null,
+            textStyle: null,
+            textColor: '#666',
+            textY: null,
+            textX: null,
+            multiPercentage: 0,
+            percentages: [],
+            multiPercentageLegend: 0,
+            textBelow: false,
+            noPercentageSign: false,
+            replacePercentageByText: null,
+            halfCircle: false,
+            animateInView: false,
+            decimals: 0,
+            alwaysDecimals: false,
+            title: 'Circle Chart',
+            description: '',
+            progressColor: null
+        }, options);
+
+        return this.each(function () {
+            var circleContainer = $(this);
+
+            mergeDataAttributes(settings, circleContainer.data());
+
+            var percent = settings.percent;
+            var iconY = 83;
+            var iconX = 100;
+            var percentageY = settings.percentageY;
+            var percentageX = settings.percentageX;
+            var additionalCss;
+            var elements;
+            var icon;
+            var backgroundBorderWidth = settings.backgroundBorderWidth;
+            var progressColor = settings.progressColor
+
+            if (settings.halfCircle) {
+                if (settings.iconPosition === 'left') {
+                    iconX = 80;
+                    iconY = 100;
+                    percentageX = 117;
+                    percentageY = 100;
+                } else if (settings.halfCircle) {
+                    iconY = 80;
+                    percentageY = 100;
+                }
+            } else {
+                if (settings.iconPosition === 'bottom') {
+                    iconY = 124;
+                    percentageY = 95;
+                } else if (settings.iconPosition === 'left') {
+                    iconX = 80;
+                    iconY = 110;
+                    percentageX = 117;
+                } else if (settings.iconPosition === 'middle') {
+                    if (settings.multiPercentage !== 1) {
+                        if (settings.iconDecoration) {
+                          elements = '<g stroke="' + (settings.backgroundColor !== 'none' ? settings.backgroundColor : '#ccc') + '" ><line x1="133" y1="50" x2="140" y2="40" stroke-width="2"  /></g>';
+                          elements += '<g stroke="' + (settings.backgroundColor !== 'none' ? settings.backgroundColor : '#ccc') + '" ><line x1="140" y1="40" x2="200" y2="40" stroke-width="2"  /></g>';
+                        }
+                        percentageX = 170; // To center the percentage exactly in the center.
+                        percentageY = 35;
+                    }
+                    iconY = 110;
+                } else if (settings.iconPosition === 'right') {
+                    iconX = 120;
+                    iconY = 110;
+                    percentageX = 80;
+                } else if (settings.iconPosition === 'top' && settings.icon !== 'none') {
+                    percentageY = 120;
+                }
+            }
+
+            if (settings.targetPercent > 0 && settings.halfCircle !== true) {
+                percentageY = 95;
+                elements = '<g stroke="' + (settings.backgroundColor !== 'none' ? settings.backgroundColor : '#ccc') + '" ><line x1="75" y1="101" x2="125" y2="101" stroke-width="1"  /></g>';
+                elements += '<text text-anchor="middle" x="' + percentageX + '" y="120" style="font-size: ' + settings.targetTextSize + 'px;" fill="' + settings.targetColor + '">' + settings.targetPercent + (settings.noPercentageSign && settings.replacePercentageByText === null ? '' : '%') + '</text>';
+                elements += '<circle cx="100" cy="100" r="69" fill="none" stroke="' + settings.backgroundColor + '" stroke-width="3" stroke-dasharray="450" transform="rotate(-90,100,100)" />';
+                elements += '<circle cx="100" cy="100" r="69" fill="none" stroke="' + settings.targetColor + '" stroke-width="3" stroke-dasharray="' + (435 / 100 * settings.targetPercent) + ', 20000" transform="rotate(-90,100,100)" />';
+            }
+
+            if (settings.text !== null) {
+                if (settings.halfCircle) {
+                    if (settings.textBelow) {
+                        elements += '<text text-anchor="middle" x="' + (settings.textX !== null ? settings.textX : '100') + '" y="' + (settings.textY !== null ? settings.textY : '64%') + '" style="' + settings.textStyle + '" fill="' + settings.textColor + '">' + settings.text + '</text>';
+                    }
+                    else {
+                        elements += '<text text-anchor="middle" x="' + (settings.textX !== null ? settings.textX : '100' ) + '" y="' + (settings.textY !== null ? settings.textY : '115') + '" style="' + settings.textStyle + '" fill="' + settings.textColor + '">' + settings.text + '</text>';
+                    }
+                } else {
+                    if (settings.textBelow) {
+                        elements += '<text text-anchor="middle" x="' + (settings.textX !== null ? settings.textX : '100' ) + '" y="' + (settings.textY !== null ? settings.textY : '99%') + '" style="' + settings.textStyle + '" fill="' + settings.textColor + '">' + settings.text + '</text>';
+                    }
+                    else {
+                        elements += '<text text-anchor="middle" x="' + (settings.textX !== null ? settings.textX : '100' ) + '" y="' + (settings.textY !== null ? settings.textY : '115') + '" style="' + settings.textStyle + '" fill="' + settings.textColor + '">' + settings.text + '</text>';
+                    }
+                }
+            }
+
+            if (settings.icon !== 'none') {
+                icon = '<text text-anchor="middle" x="' + iconX + '" y="' + iconY + '" class="icon" style="font-size: ' + settings.iconSize + 'px" fill="' + settings.iconColor + '">&#x' + settings.icon + '</text>';
+            }
+
+            if (settings.halfCircle) {
+                var rotate = 'transform="rotate(-180,100,100)"';
+                circleContainer
+                    .addClass('svg-container')
+                    .append(
+                        $('<svg xmlns="http://www.w3.org/2000/svg" version="1.1" viewBox="0 0 194 186" class="circliful">' +
+                            (typeof elements !== 'undefined' ? elements : '') +
+                            '<clipPath id="cut-off-bottom"> <rect x="100" y="0" width="100" height="200" /> </clipPath>' +
+                            '<circle cx="100" cy="100" r="57" class="border" fill="' + settings.fillColor + '" stroke="' + settings.backgroundColor + '" stroke-width="' + backgroundBorderWidth + '" stroke-dasharray="360" clip-path="url(#cut-off-bottom)" transform="rotate(-90,100,100)" />' +
+                            '<circle class="circle" cx="100" cy="100" r="57" class="border" fill="none" stroke="' + settings.foregroundColor + '" stroke-width="' + settings.foregroundBorderWidth + '" stroke-dasharray="0,20000" ' + rotate + ' />' +
+                            '<circle cx="100" cy="100" r="' + settings.pointSize + '" fill="' + settings.pointColor + '" clip-path="url(#cut-off-bottom)" transform="rotate(-90,100,100)" />' +
+                            icon +
+                            '<text class="timer" text-anchor="middle" x="' + percentageX + '" y="' + percentageY + '" style="font-size: ' + settings.percentageTextSize + 'px; ' + additionalCss + ';' + settings.textAdditionalCss + '" fill="' + settings.fontColor + '"><tspan class="number">' + (settings.replacePercentageByText === null ? 0 : settings.replacePercentageByText) + '</tspan><tspan class="percent">' + (settings.noPercentageSign || settings.replacePercentageByText !== null ? '' : '%') + '</tspan></text>')
+                    );
+            } else {
+                drawCircles();
+            }
+
+            var circle = circleContainer.find('.circle');
+            var myTimer = circleContainer.find('.timer');
+            var interval = 30;
+            var angle = 0;
+            var angleIncrement = settings.animationStep;
+            var last = 0;
+            var summary = 0;
+            var oneStep = 0;
+            var text = percent;
+            var calculateFill = (360 / 100 * percent);
+
+            if (settings.halfCircle) {
+                calculateFill = (360 / 100 * percent) / 2;
+            }
+
+            if (settings.replacePercentageByText !== null) {
+                text = settings.replacePercentageByText;
+            }
+
+            if (settings.start > 0 && settings.target > 0) {
+                percent = settings.start / (settings.target / 100);
+                oneStep = settings.target / 100;
+            }
+
+            if (settings.animation === 1) {
+                if (settings.animateInView) {
+                    $(window).scroll(function () {
+                        checkAnimation();
+                    });
+                } else {
+                    animate();
+                }
+            } else {
+                if (settings.multiPercentage !== 1) {
+                    circle
+                        .attr("stroke-dasharray", calculateFill + ", 20000");
+
+                    if (settings.showPercent === 1) {
+                        myTimer
+                            .find('.number')
+                            .text(text);
+                    } else {
+                        myTimer
+                            .find('.number')
+                            .text(settings.target);
+                        myTimer
+                            .find('.percent')
+                            .text('');
+                    }
+                } else {
+                    if (settings.replacePercentageByText !== null) {
+                        myTimer
+                            .find('.number')
+                            .text(settings.replacePercentageByText);
+                        myTimer
+                            .find('.percent')
+                            .text('');
+                    }
+                }
+            }
+
+            function animate() {
+                var currentCircle = circle;
+                var currentCalculateFill = calculateFill;
+
+                if (settings.multiPercentage === 1) {
+                    var index;
+                    var percentages = settings.percentages;
+                    var circleRadius = 360;
+                    for (index = 0; index < percentages.length; ++index) {
+                        percent = percentages[index].percent;
+                        currentCalculateFill = (circleRadius / 100 * percent);
+                        currentCircle = circleContainer.find('#circle' + (index + 1));
+
+                        if (index > 0) {
+                            circleRadius = circleRadius + 62.5;
+                            currentCalculateFill = (circleRadius / 100 * percent);
+                        }
+
+                        animateCircle(currentCircle, currentCalculateFill, circleRadius, percent);
+                    }
+                } else {
+                    animateCircle(currentCircle, currentCalculateFill, 360, percent);
+                }
+            }
+
+            function animateCircle(currentCircle, currentCalculateFill, circleRadius, percent) {
+                var timer = window.setInterval(function () {
+                    if ((angle) >= currentCalculateFill) {
+                        window.clearInterval(timer);
+                        last = 1;
+                        if (typeof callback === 'function') {
+                            callback.call(this);
+                        }
+                    } else {
+                        angle += angleIncrement;
+                        summary += oneStep;
+                    }
+                    if (settings.halfCircle) {
+                        if (angle * 2 / (circleRadius / 100) >= percent && last === 1) {
+                            angle = ((circleRadius / 100) * percent) / 2
+                        }
+                    } else {
+                        if (angle / (circleRadius / 100) >= percent && last === 1) {
+                            angle = (circleRadius / 100) * percent;
+                        }
+                    }
+
+                    if (summary > settings.target && last === 1) {
+                        summary = settings.target;
+                    }
+
+                    if (settings.replacePercentageByText === null) {
+                        if (settings.halfCircle) {
+                            text = parseFloat((100 * angle / circleRadius) * 2);
+                        } else {
+                            text = parseFloat((100 * angle / circleRadius));
+                        }
+                        text = Math.floor(text);
+                        if (!settings.alwaysDecimals && (percent === 0 || (percent > 1 && last !== 1))) {
+                            text = parseInt(text);
+                        }
+                    }
+
+                    currentCircle
+                        .attr("stroke-dasharray", angle + ", 20000");
+
+                    if (settings.multiPercentage !== 1) {
+                        if (settings.showPercent === 1) {
+                            myTimer
+                                .find('.number')
+                                .text(text);
+                        } else {
+
+                            myTimer
+                                .find('.number')
+                                .text(summary);
+                            myTimer
+                                .find('.percent')
+                                .text('');
+                        }
+                    } else {
+                        myTimer
+                            .find('.number')
+                            .text('');
+                        myTimer
+                            .find('.percent')
+                            .text('');
+                    }
+
+                    if (progressColor !== null) {
+                        $.each(progressColor, function (key, color) {
+                            if (settings.halfCircle) {
+                                key /= 2
+                            }
+                            if (angle >= key * (circleRadius / 100)) {
+                                currentCircle.css({
+                                    stroke: color,
+                                    transition: 'stroke 0.1s linear'
+                                });
+                            }
+                        });
+                    }
+                }.bind(currentCircle), interval);
+            }
+
+            function isElementInViewport() {
+                // Get the scroll position of the page.
+                var scrollElem = ((navigator.userAgent.toLowerCase().indexOf('webkit') !== -1) ? 'body' : 'html');
+                var viewportTop = $(scrollElem).scrollTop();
+                var viewportBottom = viewportTop + $(window).height();
+
+                // Get the position of the element on the page.
+                var elemTop = Math.round(circle.offset().top);
+                var elemBottom = elemTop + circle.height();
+
+                return ((elemTop < viewportBottom) && (elemBottom > viewportTop));
+            }
+
+            function checkAnimation() {
+                // If the animation has already been started
+                if (circle.hasClass('start')) return;
+
+                if (isElementInViewport(circle)) {
+                    // Start the animation
+                    circle.addClass('start');
+                    setTimeout(animate, 250)
+                }
+            }
+
+            function mergeDataAttributes(settings, dataAttributes) {
+                $.each(settings, function (key, value) {
+                    if (key.toLowerCase() in dataAttributes) {
+                        settings[key] = dataAttributes[key.toLowerCase()];
+                    }
+                });
+            }
+
+            /**
+             * Draws the initial circles before animate gets called
+             */
+            function drawCircles() {
+                if (settings.multiPercentage === 1) {
+                    var index, calculateFillMulti, percent, color, circles;
+                    var percentages = settings.percentages;
+                    var radius = 47;
+                    var circleRadius = 360;
+                    var rotate = -90;
+                    for (index = 0; index < percentages.length; ++index) {
+                        percent = percentages[index].percent;
+                        color = percentages[index].color;
+                        calculateFillMulti = (circleRadius / 100 * percent);
+                        if (index > 0) {
+                            circleRadius = circleRadius + 62.5;
+                            calculateFillMulti = (circleRadius / 100 * percent);
+                        }
+                        radius += 10;
+                        circles += '<circle cx="100" cy="100" r="' + radius + '" class="border" fill="' + settings.fillColor + '" stroke="' + settings.backgroundColor + '" stroke-width="' + backgroundBorderWidth + '" stroke-dasharray="' + circleRadius + '" transform="rotate(' + rotate + ',100,100)" />' +
+                            '<circle class="circle" id="circle' + (index + 1) + '" data-percent="' + percent + '" cx="100" cy="100" r="' + radius + '" class="border" fill="none" stroke="' + color + '" stroke-width="' + settings.foregroundBorderWidth + '" stroke-dasharray="' + calculateFillMulti + ',20000" transform="rotate(' + rotate + ',100,100)" />';
+                    }
+
+                    circleContainer
+                        .addClass('svg-container')
+                        .append(
+                            $('<svg xmlns="http://www.w3.org/2000/svg" version="1.1" viewBox="0 0 194 186" class="circliful">' +
+                                (typeof elements !== 'undefined' ? elements : '') +
+                                circles +
+                                icon +
+                                '<text class="timer" text-anchor="middle" x="' + percentageX + '" y="' + percentageY + '" style="font-size: ' + settings.percentageTextSize + 'px; ' + additionalCss + ';' + settings.textAdditionalCss + '" fill="' + settings.fontColor + '">' +
+                                '<tspan class="number">' + (settings.replacePercentageByText === null ? 0 : settings.replacePercentageByText) + '</tspan>' +
+                                '<tspan class="percent">' + (settings.noPercentageSign || settings.replacePercentageByText !== null ? '' : '%') + '</tspan>' +
+                                '</text>')
+                        );
+
+                    if (settings.multiPercentageLegend === 1) {
+                        showLegend();
+                    }
+                } else {
+                    circleContainer
+                        .addClass('svg-container')
+                        .append(
+                            $('<svg xmlns="http://www.w3.org/2000/svg" version="1.1" viewBox="0 0 194 186" class="circliful">' +
+                                (typeof elements !== 'undefined' ? elements : '') +
+                                '<circle cx="100" cy="100" r="57" class="border" fill="' + settings.fillColor + '" stroke="' + settings.backgroundColor + '" stroke-width="' + backgroundBorderWidth + '" stroke-dasharray="360" transform="rotate(-90,100,100)" />' +
+                                '<circle class="circle" cx="100" cy="100" r="57" class="border" fill="none" stroke="' + settings.foregroundColor + '" stroke-width="' + settings.foregroundBorderWidth + '" stroke-dasharray="0,20000" transform="rotate(-90,100,100)" />' +
+                                '<circle cx="100" cy="100" r="' + settings.pointSize + '" fill="' + settings.pointColor + '" />' +
+                                icon +
+                                '<text class="timer" text-anchor="middle" x="' + percentageX + '" y="' + percentageY + '" style="font-size: ' + settings.percentageTextSize + 'px; ' + additionalCss + ';' + settings.textAdditionalCss + '" fill="' + settings.fontColor + '">' +
+                                '<tspan class="number">' + (settings.replacePercentageByText === null ? 0 : settings.replacePercentageByText) + '</tspan>' +
+                                '<tspan class="percent">' + (settings.noPercentageSign || settings.replacePercentageByText !== null ? '' : '%') + '</tspan>' +
+                                '</text>')
+                        );
+                }
+            }
+
+            /**
+             * Show the legend only for multi percentage circles
+             */
+            function showLegend() {
+                var height = circleContainer.height();
+                var width = circleContainer.width();
+                var percentages = settings.percentages;
+                var index;
+                var lines = '';
+                for (index = 0; index < percentages.length; ++index) {
+                    var title = percentages[index].title;
+                    var color = percentages[index].color;
+                    var percent = percentages[index].percent;
+
+                    lines += '<div><span class="color-box" style="background: ' + color + '"></span>' + title + ', ' + percent + '%</div>';
+                }
+
+                circleContainer.append(
+                    $('<div/>')
+                        .append(lines)
+                        .attr('style', 'position:absolute;top:' + height / 3 + 'px;left:' + (width + 20) + 'px')
+                        .attr('class', 'legend-line')
+                );
+            }
+        });
+    }
+}(jQuery));

+ 29 - 0
static/src/lib/jquery.timeago.es.js

@@ -0,0 +1,29 @@
+(function (factory) {
+  if (typeof define === 'function' && define.amd) {
+    define(['jquery'], factory);
+  } else if (typeof module === 'object' && typeof module.exports === 'object') {
+    factory(require('jquery'));
+  } else {
+    factory(jQuery);
+  }
+}(function (jQuery) {
+  // Spanish
+  jQuery.timeago.settings.strings = {
+     prefixAgo: "hace",
+     prefixFromNow: "dentro de",
+     suffixAgo: "",
+     suffixFromNow: "",
+     seconds: "menos de un minuto",
+     minute: "un minuto",
+     minutes: "unos %d minutos",
+     hour: "una hora",
+     hours: "%d horas",
+     day: "un día",
+     days: "%d días",
+     month: "un mes",
+     months: "%d meses",
+     year: "un año",
+     years: "%d años"
+  };
+}));
+

+ 17 - 0
static/src/xml/charts/ranking_customer.xml

@@ -0,0 +1,17 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<template xml:space="preserve">
+    <t t-name="RankingCustomer">
+        <div>
+            <div class="grid-stack-item-content reporting-dashboard ucrm">
+                <div class="widget-content">
+                    <div class="row">
+                        <h2 style="margin-top:0px;margin-left:10px;" class="text-center"><small> Ranking de clientes </small></h2>
+                    </div>
+                    <div class="chart-container center-block" style="padding:10px;height:6.5cm;">
+                        <canvas class="ranking-horizontalBar-chart"></canvas>
+                    </div>
+                </div>
+            </div>
+        </div>
+    </t>
+</template>

+ 17 - 0
static/src/xml/charts/ranking_sale_by_store.xml

@@ -0,0 +1,17 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<template xml:space="preserve">
+    <t t-name="RankingSaleByStore">
+        <div>
+            <div class="grid-stack-item-content reporting-dashboard ucrm">
+                <div class="widget-content">
+                    <div class="row">
+                        <h2 style="margin-top:0px;margin-left:10px;" class="text-center"><small> Ventas por surcursal </small></h2>
+                    </div>
+                    <div class="chart-container center-block" style="padding:10px;height:6.5cm;">
+                        <canvas class="ranking-sale-by-store-chart"></canvas>
+                    </div>
+                </div>
+            </div>
+        </div>
+    </t>
+</template>

+ 17 - 0
static/src/xml/charts/ranking_salesman.xml

@@ -0,0 +1,17 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<template xml:space="preserve">
+    <t t-name="RankingSalesman">
+        <div>
+            <div class="grid-stack-item-content reporting-dashboard ucrm">
+                <div class="widget-content">
+                    <div class="row">
+                        <h2 style="margin-top:0px;margin-left:10px;" class="text-center"><small> Ranking de Vendedores </small></h2>
+                    </div>
+                    <div class="chart-container center-block" style="padding:10px;height:6.5cm;">
+                        <canvas class="ranking-salesman-chart"></canvas>
+                    </div>
+                </div>
+            </div>
+        </div>
+    </t>
+</template>

+ 9 - 0
static/src/xml/dashboard.xml

@@ -0,0 +1,9 @@
+<?xml version="1.0" encoding="UTF-8"?>
+
+<template xml:space="preserve">
+    <t t-name="DashboardReportingTemplate">
+        <div class="container dashboard">
+            <div class="grid-stack reporting-dashboard"></div>
+        </div>
+    </t>
+</template>

+ 31 - 0
static/src/xml/modals/bank_cash_modal.xml

@@ -0,0 +1,31 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<template xml:space="preserve">
+    <t t-name="BankCashModal">
+        <div class="modal in dashboard-bank-cash-modal" tabindex="-1" role="dialog">
+            <div class="modal-dialog modal-lg" role="document">
+                <div class="modal-content openerp">
+                    <div class="modal-header">
+                        <button type="button" class="close" data-dismiss="modal" aria-label="Close" aria-hidden="true">x</button>
+                        <h3 class="modal-title" t-foreach="modalTitle" t-as="title">
+                            <t t-esc="title_value.title"/>
+                        </h3>
+                    </div>
+                    <div class="container-fluid" style="padding:30px;">
+                        <div class="row">
+                            <t t-foreach="data" t-as="field">
+                                <div class="col-xs-12 col-sm-6 col-md-4 col-lg-4">
+                                    <div class="statement-body">
+                                        <h3><a class="statement-name"><t t-esc="field_value.name"/></a></h3>
+                                        <i class="fa fa-archive fa-5x" aria-hidden="true"></i>
+                                        <h6><t t-esc="field_value.journal_name"/></h6>
+                                        <h3><t t-esc="field_value.balance_end"/></h3>
+                                    </div>
+                                </div>
+                            </t>
+                        </div>
+                    </div>
+                </div>
+            </div>
+        </div>
+    </t>
+</template>

+ 55 - 0
static/src/xml/modals/modal_income_outcome.xml

@@ -0,0 +1,55 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<template xml:space="preserve">
+    <t t-name="DashboardIncomeOutcomeModal">
+        <div class="modal in dashboard-income-outcome-modal" tabindex="-1" role="dialog">
+            <div class="modal-dialog" role="document">
+                <div class="modal-content openerp">
+                    <div class="modal-header">
+                        <button type="button" class="close" data-dismiss="modal" aria-label="Close" aria-hidden="true">x</button>
+                        <h3 class="modal-title" t-foreach="modalTitle" t-as="title">
+                            <t t-esc="title_value.title"/>
+                        </h3>
+                    </div>
+                    <div class="modal-body dashboard-modal">
+                        <div class="row" style="border-bottom:1px solid #e0e0e0;">
+                            <div class="col-md-12 col-lg-12 text-center">
+                                <div class="">
+                                    <canvas class="dashboard-income-outcome-chart"></canvas>
+                                </div>
+                            </div>
+                            <div class="col-md-12 col-lg-12">
+                                <table id="#Income-Outcome-table"
+                                    class="Income-Outcome-table"
+                                    data-classes="table table-condensed table-no-bordered"
+                                    data-undefined-text=" "
+                                    >
+                                    <thead style="background:none;">
+                                        <tr>
+                                            <th data-field="name" data-align="left" data-footer-formatter="Total">Metodo de pago</th>
+                                            <th data-field="value_amount" data-align="right">Valor</th>
+                                        </tr>
+                                    </thead>
+                                </table>
+                                <br/>
+                            </div>
+                        </div>
+                        <div class="row">
+                            <div class="center-block" style="padding:5px;font-size:10pt;">
+                                <div class="col-xs-6" style="color:#777;">
+                                    <span><i class="fa fa-circle-o fa-lg" style="padding:10px"></i>Total: <t t-esc="amount"/></span>
+                                </div>
+                                <div class="col-xs-6 text-right">
+                                    <a class="redirect-to-report"><i class="fa fa-external-link-square fa-lg" style="padding:10px"></i>Ir a informe</a>
+                                </div>
+                            </div>
+                        </div>
+                    </div>
+                </div>
+            </div>
+            <script>
+                var table = $('.Income-Outcome-table');
+                table.bootstrapTable({data : self.rowsData});
+            </script>
+        </div>
+    </t>
+</template>

+ 95 - 0
static/src/xml/modals/widget_account_modal.xml

@@ -0,0 +1,95 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<template xml:space="preserve">
+    <t t-name="WidgetAccountModal">
+        <div class="modal in dashboard-account-modal" tabindex="-1" role="dialog">
+            <div class="modal-dialog modal-lg" role="document">
+                <div class="modal-content openerp">
+                    <div class="modal-header">
+                        <button type="button" class="close" data-dismiss="modal" aria-label="Close" aria-hidden="true">x</button>
+                        <h3 class="modal-title" t-foreach="modalTitle" t-as="title">
+                            <t t-esc="title_value.title"/>
+                        </h3>
+                    </div>
+                    <div class="modal-body dashboard-modal">
+                        <div id="toolbar">
+                            <div class="btn-group">
+                                <a type="button" class="btn btn-default">Filtrar</a>
+                                <a type="button" class="btn btn-default dropdown-toggle" data-toggle="dropdown" aria-haspopup="true" aria-expanded="false">
+                                    <span class="caret"></span>
+                                    <span class="sr-only">Toggle Dropdown</span>
+                                </a>
+                                <ul class="dropdown-menu">
+                                    <li><a class="today">Hoy</a></li>
+                                    <li><a class="this-month">Mes Actual</a></li>
+                                    <li><a class="last-month">Mes Pasado</a></li>
+                                    <li role="separator" class="divider"></li>
+                                    <li><a class="unfiltered">Sin Filtro</a></li>
+                                </ul>
+                            </div>
+                        </div>
+                        <table id="#account-table"
+                            class="account-table"
+                            data-search="true"
+                            data-height="450"
+                            data-classes="table table-condensed table-hover"
+                            data-undefined-text=" "
+                            data-buttons-class="oe_button myButton"
+                            data-toolbar="#toolbar"
+                            >
+                             <!-- table-no-bordered -->
+                            <thead style="background:none;">
+                                <tr>
+                                    <th data-field="invoice_id" data-align="center" data-width="5%">ID</th>
+                                    <th data-field="month" data-align="center" data-visible="false"></th>
+                                    <th data-field="date" data-align="center" data-visible="false"></th>
+
+                                    <th data-field="ruc" data-align="left" data-width="10%">RUC</th>
+                                    <th data-field="partner" data-align="left" data-width="40%">Cliente</th>
+                                    <th data-field="number" data-align="left">Factura</th>
+                                    <th data-field="description" data-align="left">Descripción</th>
+                                    <th data-field="date_maturity" data-align="center">Vencimiento</th>
+                                    <th data-field="residual" data-align="right"> Cuota </th>
+                                    <th data-field="value" data-align="right" data-visible="false"></th>
+                                </tr>
+                            </thead>
+                        </table>
+                        <div class="text-right">
+                            <a class="redirect-to-report"><i class="fa fa-external-link-square fa-lg" style="padding:10px"></i>Ir a informe</a>
+                        </div>
+                    </div>
+                </div>
+            </div>
+            <script>
+
+                var table = $('.account-table');
+                var Today = $('.today');
+                var ThisMonth = $('.this-month');
+                var LastMonth = $('.last-month');
+                var Unfiltered = $('.unfiltered');
+
+                table.bootstrapTable({data : self.rowsData});
+                Today.click(function () {
+                    var date = moment().format('YYYY-MM-DD');
+                    table.bootstrapTable('filterBy', {
+                        datex: [date]
+                    });
+                });
+                ThisMonth.click(function () {
+                    var date = moment().format('YYYY-MM');
+                    table.bootstrapTable('filterBy', {
+                        month: [date]
+                    });
+                });
+                LastMonth.click(function () {
+                    var date = moment().add(-1,'months').format('YYYY-MM');
+                    table.bootstrapTable('filterBy', {
+                        month: [date]
+                    });
+                });
+                Unfiltered.click(function () {
+                    table.bootstrapTable('filterBy', {});
+                });
+            </script>
+        </div>
+    </t>
+</template>

+ 53 - 0
static/src/xml/modals/widget_modal.xml

@@ -0,0 +1,53 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<template xml:space="preserve">
+    <t t-name="WidgetModal">
+        <div class="modal in widget-modal" tabindex="-1" role="dialog">
+            <div class="modal-dialog modal-lg" role="document">
+                <div class="modal-content">
+                    <div class="modal-header">
+                        <button type="button" class="close" data-dismiss="modal" aria-label="Close" aria-hidden="true">x</button>
+                        <h3 class="modal-title" t-foreach="modalTitle" t-as="title">
+                            <t t-esc="title_value.title"/>
+                        </h3>
+                    </div>
+                    <div class="modal-body dashboard-modal">
+                        <div class="row" style="border-bottom:1px solid #e0e0e0;">
+                            <div class="col-md-4 col-lg-4 text-center">
+                                <div
+                                    id="myItemModal"
+                                    data-preset="circle"
+                                    class="ldBar label-center"
+                                    style="width:auto;font-family:Arial; font-size: 4.0em; margin:auto"
+                                    data-stroke-width="10"
+                                ></div>
+                            </div>
+                            <div class="col-md-8 col-lg-8" style="height:250px;">
+                                <canvas class="week-dashboard-chart"></canvas>
+                            </div>
+                        </div>
+                        <div class="row">
+                            <div class="center-block" style="padding:5px;font-size:10pt;">
+                                <div class="col-xs-6" style="color:#777;">
+                                    <span><i class="fa fa-circle-o fa-lg" style="padding:10px"></i><t t-esc="objectiveTitle"/>: <t t-esc="objective"/></span>
+                                    <br/>
+                                    <span><i class="fa fa-circle-o fa-lg" style="padding:10px"></i><t t-esc="objectivePending"/>: <t t-esc="pending"/></span>
+                                </div>
+                                <div class="col-xs-6" style="color:#777;">
+                                    <span><i class="fa fa-money fa-lg" style="padding:10px"></i>Facturación del dia: <t t-esc="today"/></span>
+                                    <br/>
+                                    <span><i class="fa fa-money fa-lg" style="padding:10px"></i>Facturación de la semana: <t t-esc="week"/></span>
+                                    <br/>
+                                    <span><i class="fa fa-money fa-lg" style="padding:10px"></i>Facturación del mes: <t t-esc="month"/></span>
+                                    <br/>
+                                    <div class="text-right">
+                                        <span><a class="redirect-to-report"><i class="fa fa-external-link-square fa-lg" style="padding:10px"></i>Ir a informe</a></span>
+                                    </div>
+                                </div>
+                            </div>
+                        </div>
+                    </div>
+                </div>
+            </div>
+        </div>
+    </t>
+</template>

+ 11 - 0
static/src/xml/widget_base.xml

@@ -0,0 +1,11 @@
+<?xml version="1.0" encoding="UTF-8"?>
+
+<template xml:space="preserve">
+    <t t-name="WidgetReportingBaseTemplate">
+        <div>
+            <div class="grid-stack-item-content reporting-dashboard">
+                <t t-raw="0" />
+            </div>
+        </div>
+    </t>
+</template>

+ 37 - 0
static/src/xml/widgets/widget_account.xml

@@ -0,0 +1,37 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<template xml:space="preserve">
+    <t t-name="WidgetAccount">
+        <div>
+            <div class="grid-stack-item-content reporting-dashboard ucrm">
+                <div class="widget-content">
+                    <div class="row">
+                        <div class="col-xs-6 widget-green">
+                            <a class="toReceibe">
+                            <h2><small> Cuentas a cobrar </small></h2>
+                            <i class="fa fa-folder-open-o fa-5x" aria-hidden="true" style="padding:10px"></i>
+                            <br/>
+                            <span id="acobrar" style="font-size: 15pt;"/>
+                            <!-- <br/><br/> -->
+                            <!-- <div class="text-left" style="color:#777;">
+                                - Hoy <span class="toReceibe" style="font-size: 10pt;"></span>
+                            </div> -->
+                          </a>
+                        </div>
+                        <div class="col-xs-6 widget-orange">
+                          <a class="toPay">
+                            <h2><small> Cuentas a pagar </small></h2>
+                            <i class="fa fa-folder-open-o fa-5x" aria-hidden="true" style="padding:10px"></i>
+                            <br/>
+                            <span id="apagar" style="font-size: 15pt;"/>
+                            <!-- <br/><br/> -->
+                            <!-- <div class="text-left" style="color:#777;">
+                                - Hoy <span class="toPay" style="font-size: 10pt;"></span>
+                            </div> -->
+                          </a>
+                        </div>
+                    </div>
+                </div>
+            </div>
+        </div>
+    </t>
+</template>

+ 23 - 0
static/src/xml/widgets/widget_balance.xml

@@ -0,0 +1,23 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<template xml:space="preserve">
+    <t t-name="WidgetBalance">
+        <div>
+            <div class="grid-stack-item-content reporting-dashboard ucrm">
+                <div class="widget-content">
+                    <h2 style="margin-top:0px;margin-left:10px"><small> Histórico de facturación del mes actual</small></h2>
+                    <div class="chart-container center-block" style="padding:5px;height:330px;">
+                        <canvas class="month-dashboard-chart"></canvas>
+                    </div>
+                    <!-- <div class="center-block" style="width:95%;padding:5px;font-size:12pt;">
+                        <div class="col-xs-8" style="color:#777;">
+                            <span><i class="fa fa-bar-chart fa-lg" style="padding:10px"></i> Balance Actual: <a class="number" style="color:#0288d1;"></a></span>
+                        </div>
+                        <div class="col-xs-4" style="color:#777;">
+                            <span><i class="fa fa-line-chart fa-lg" style="padding:10px"></i> Porcentaje Logrado: <a class="percentage" style="color:#0288d1;"></a></span>
+                        </div>
+                    </div> -->
+                </div>
+            </div>
+        </div>
+    </t>
+</template>

+ 29 - 0
static/src/xml/widgets/widget_bank_cash.xml

@@ -0,0 +1,29 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<template xml:space="preserve">
+    <t t-name="WidgetBankCash">
+        <div>
+            <div class="grid-stack-item-content reporting-dashboard ucrm">
+                <div class="widget-content">
+                    <div class="row">
+                        <div class="col-xs-6 widget-green">
+                          <a class="bank">
+                            <h2><small> Banco </small></h2>
+                            <i class="fa fa-university fa-5x" aria-hidden="true" style="padding:10px"></i>
+                            <br/>
+                            <span id="bank" style="font-size: 15pt;"/>
+                          </a>
+                        </div>
+                        <div class="col-xs-6 widget-green">
+                          <a class="cash">
+                            <h2><small> Caja </small></h2>
+                            <i class="fa fa-money fa-5x" aria-hidden="true" style="padding:10px"></i>
+                            <br/>
+                            <span id="cash" style="font-size: 15pt;"/>
+                          </a>
+                        </div>
+                    </div>
+                </div>
+            </div>
+        </div>
+    </t>
+</template>

+ 84 - 0
static/src/xml/widgets/widget_financial_state.xml

@@ -0,0 +1,84 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<template xml:space="preserve">
+    <t t-name="WidgetFinancialState">
+        <div>
+            <div class="grid-stack-item-content reporting-dashboard ucrm">
+                <div class="widget-content">
+                    <div class="row">
+                        <div class="text-center" style="font-size:15pt;color:#777 !important;">
+                            Análisis de Ventas
+                        </div>
+                        <br/>
+                        <div class="col-xs-6" style="color:#777;">
+                            <table id="#financial-state-sale-table"
+                                class="financial-state-sale-table"
+                                data-classes="table table-condensed"
+                                data-undefined-text=" "
+                                >
+                                <thead style="background:none;">
+                                    <tr>
+                                        <th colspan="7" data-align="center">VENTAS</th>
+                                    </tr>
+                                    <tr>
+                                        <th data-field="total">Total</th>
+                                        <th data-field="subtotal">Subtotal</th>
+                                        <th data-field="tax">Impuestos</th>
+                                        <th data-field="cost">Coste</th>
+                                        <th data-field="margin" data-align="center">Margen</th>
+                                        <th data-field="profitability" data-align="center" data-visible="false">Rentabilidad</th>
+                                        <th data-field="utility">Utilidad</th>
+                                    </tr>
+                                </thead>
+                            </table>
+                            <br/>
+                            <table id="#financial-state-expense-table"
+                                class="financial-state-expense-table"
+                                data-classes="table table-condensed"
+                                data-undefined-text=" "
+                                >
+                                <thead style="background:none;">
+                                    <tr>
+                                        <th colspan="3" data-align="center">GASTOS</th>
+                                    </tr>
+                                    <tr>
+                                        <th data-field="total">Total</th>
+                                        <th data-field="subtotal">Subtotal</th>
+                                        <th data-field="tax">Impuestos</th>
+                                    </tr>
+                                </thead>
+                            </table>
+                        </div>
+                        <div class="col-xs-6 col-md-6 col-lg-6">
+                            <div class="text-center" style="font-size:12pt;color:#777 !important;">
+                                Gráfico de Ventas
+                            </div>
+                            <div class="chart-container center-block" style="padding:10px;height:6cm;">
+                                <canvas class="financial-chart"></canvas>
+                            </div>
+                        </div>
+                    </div>
+                    <div class="row" style="font-size:10pt;color:#777;border-top:1px solid #f5f5f5;padding-top:5px;">
+                        <div class="col-xs-6">
+                            <span><i class="fa fa-circle-o fa-lg" style="padding:10px"></i> Empresa: <a class="company" style="color:#777;"></a></span>
+                            <br/>
+                            <span><i class="fa fa-circle-o fa-lg" style="padding:10px"></i> Sucursal: <a class="store" style="color:#777;"></a></span>
+                            <br/>
+                            <!-- <span><i class="fa fa-circle-o fa-lg" style="padding:10px"></i> RUC: <a class="ruc" style="color:#777;"></a></span> -->
+                        </div>
+                        <div class="col-xs-6">
+                            <span><i class="fa fa-line-chart fa-lg" style="padding:10px"></i> Utilidad Neta: <a class="balance-end" style="color:#777;"></a></span>
+                        </div>
+
+                    </div>
+                </div>
+            </div>
+            <script>
+                var tablex = $('.financial-state-sale-table');
+                tablex.bootstrapTable({data : self.rowsData});
+
+                var tabley = $('.financial-state-expense-table');
+                tabley.bootstrapTable({data : self.rowsData});
+            </script>
+        </div>
+    </t>
+</template>

Some files were not shown because too many files changed in this diff