Commit 32052b99 by aa.gusti

village

1 parent d1b50cc4
...@@ -4,65 +4,60 @@ Odoo Accounting ...@@ -4,65 +4,60 @@ Odoo Accounting
The Odoo <a href="https://www.odoo.com/page/accounting">Open Source Accounting</a> app allows a better way to The Odoo <a href="https://www.odoo.com/page/accounting">Open Source Accounting</a> app allows a better way to
collaborate with your accountants, your customers and control your suppliers. collaborate with your accountants, your customers and control your suppliers.
Activate features on demand, from integrated analytic accounting to budget, Activate features on demand, from integrated analytic accounting to budget, assets and multiple companies consolidation.
assets and multiple companies consolidation.
A Smart User Interface A Smart User Interface
---------------------- ----------------------
Record transactions in a few clicks and easily manage all financial activities Record transactions in a few clicks and easily manage all financial activities in one place. Odoo's user interface is
in one place. Odoo's user interface is designed with productivity in mind. designed with productivity in mind.
A Better Way To Work – Together A Better Way To Work – Together
------------------------------- -------------------------------
Share access to your latest business numbers with your team and your accountant Share access to your latest business numbers with your team and your accountant – so everyone is up to speed. From work,
– so everyone is up to speed. From work, home or on the go. home or on the go.
Connect Your Bank Accounts Connect Your Bank Accounts
-------------------------- --------------------------
Import your bank statements and reconcile them in just a few clicks. Prepare Import your bank statements and reconcile them in just a few clicks. Prepare payment orders based on your supplier
payment orders based on your supplier invoices and payment terms. invoices and payment terms.
Electronic invoicing and automated follow-ups Electronic invoicing and automated follow-ups
--------------------------------------------- ---------------------------------------------
Create and send professional invoices & get paid online. Get rid of the stress Create and send professional invoices & get paid online. Get rid of the stress of having to constantly remind your
of having to constantly remind your debtors. Simply set-up and automate debtors. Simply set-up and automate follow-ups to get paid quickly.
follow-ups to get paid quickly.
Sales Integration Sales Integration
----------------- -----------------
Automatically create invoices from sales orders, delivery orders or base them Automatically create invoices from sales orders, delivery orders or base them on time and material. Re-invoice expenses
on time and material. Re-invoice expenses on projects to your customer in just on projects to your customer in just a few clicks.
a few clicks.
Purchase Integration Purchase Integration
-------------------- --------------------
Control supplier invocies based on purchase orders. Get real-time inventory Control supplier invocies based on purchase orders. Get real-time inventory valuation reports automatically posted in
valuation reports automatically posted in your accounts. your accounts.
Multi-Level Analytic Accounting Multi-Level Analytic Accounting
------------------------------- -------------------------------
Integrate your analytic accounting operations with timesheets, projects, Integrate your analytic accounting operations with timesheets, projects, invoices, expenses, etc. No need to record
invoices, expenses, etc. No need to record transactions, all analytic entries transactions, all analytic entries are posted automatically following your business rules.
are posted automatically following your business rules.
Everything you need to grow Everything you need to grow
--------------------------- ---------------------------
Manage your assets, track expenses, control budgets, multi-level analytic Manage your assets, track expenses, control budgets, multi-level analytic accounting; Odoo has all the features you need
accounting; Odoo has all the features you need to sustain all your business to sustain all your business activities.
activities.
Scale With Your Organization Scale With Your Organization
---------------------------- ----------------------------
Odoo supports multiple currencies, multiple users with different access rights, Odoo supports multiple currencies, multiple users with different access rights, multiple companies with real time
multiple companies with real time consolidation and unlimited analytic plans. consolidation and unlimited analytic plans.
...@@ -23,8 +23,8 @@ Menydiakan module untuk followup Wajib Pajak/Retribusi. ...@@ -23,8 +23,8 @@ Menydiakan module untuk followup Wajib Pajak/Retribusi.
'views/view_config.xml', 'views/view_config.xml',
'views/objek_pajak.xml', 'views/objek_pajak.xml',
'views/district.xml', 'views/district.xml',
# 'views/sub_district.xml', 'views/sub_district.xml',
# 'views/village.xml', 'views/village.xml',
'views/sudut_pandang.xml', 'views/sudut_pandang.xml',
'views/pdl_kab_menus.xml', 'views/pdl_kab_menus.xml',
'security/account_security.xml', 'security/account_security.xml',
......
...@@ -30,7 +30,8 @@ class PortalAccount(CustomerPortal): ...@@ -30,7 +30,8 @@ class PortalAccount(CustomerPortal):
return self._get_page_view_values(invoice, access_token, values, 'my_pad_history', False, **kwargs) return self._get_page_view_values(invoice, access_token, values, 'my_pad_history', False, **kwargs)
def _get_pad_domain(self): def _get_pad_domain(self):
return [('move_type', 'in', ('out_invoice', 'out_refund', 'in_invoice', 'in_refund', 'out_receipt', 'in_receipt'))] return [
('move_type', 'in', ('out_invoice', 'out_refund', 'in_invoice', 'in_refund', 'out_receipt', 'in_receipt'))]
@http.route(['/my/pad', '/my/pad/page/<int:page>'], type='http', auth="user", website=True) @http.route(['/my/pad', '/my/pad/page/<int:page>'], type='http', auth="user", website=True)
def portal_my_pad(self, page=1, date_begin=None, date_end=None, sortby=None, filterby=None, **kw): def portal_my_pad(self, page=1, date_begin=None, date_end=None, sortby=None, filterby=None, **kw):
...@@ -86,7 +87,7 @@ class PortalAccount(CustomerPortal): ...@@ -86,7 +87,7 @@ class PortalAccount(CustomerPortal):
'searchbar_sortings': searchbar_sortings, 'searchbar_sortings': searchbar_sortings,
'sortby': sortby, 'sortby': sortby,
'searchbar_filters': OrderedDict(sorted(searchbar_filters.items())), 'searchbar_filters': OrderedDict(sorted(searchbar_filters.items())),
'filterby':filterby, 'filterby': filterby,
}) })
return request.render("account.portal_my_pad", values) return request.render("account.portal_my_pad", values)
...@@ -98,13 +99,15 @@ class PortalAccount(CustomerPortal): ...@@ -98,13 +99,15 @@ class PortalAccount(CustomerPortal):
return request.redirect('/my') return request.redirect('/my')
if report_type in ('html', 'pdf', 'text'): if report_type in ('html', 'pdf', 'text'):
return self._show_report(model=invoice_sudo, report_type=report_type, report_ref='account.account_pad', download=download) return self._show_report(model=invoice_sudo, report_type=report_type, report_ref='account.account_pad',
download=download)
values = self._invoice_get_page_view_values(invoice_sudo, access_token, **kw) values = self._invoice_get_page_view_values(invoice_sudo, access_token, **kw)
acquirers = values.get('acquirers') acquirers = values.get('acquirers')
if acquirers: if acquirers:
country_id = values.get('partner_id') and values.get('partner_id')[0].country_id.id country_id = values.get('partner_id') and values.get('partner_id')[0].country_id.id
values['acq_extra_fees'] = acquirers.get_acquirer_extra_fees(invoice_sudo.amount_residual, invoice_sudo.currency_id, country_id) values['acq_extra_fees'] = acquirers.get_acquirer_extra_fees(invoice_sudo.amount_residual,
invoice_sudo.currency_id, country_id)
return request.render("account.portal_invoice_page", values) return request.render("account.portal_invoice_page", values)
...@@ -119,11 +122,16 @@ class PortalAccount(CustomerPortal): ...@@ -119,11 +122,16 @@ class PortalAccount(CustomerPortal):
if not partner.can_edit_vat(): if not partner.can_edit_vat():
if 'vat' in data and (data['vat'] or False) != (partner.vat or False): if 'vat' in data and (data['vat'] or False) != (partner.vat or False):
error['vat'] = 'error' error['vat'] = 'error'
error_message.append(_('Changing VAT number is not allowed once pad have been issued for your account. Please contact us directly for this operation.')) error_message.append(
_('Changing VAT number is not allowed once pad have been issued for your account. Please contact '
'us directly for this operation.'))
if 'name' in data and (data['name'] or False) != (partner.name or False): if 'name' in data and (data['name'] or False) != (partner.name or False):
error['name'] = 'error' error['name'] = 'error'
error_message.append(_('Changing your name is not allowed once pad have been issued for your account. Please contact us directly for this operation.')) error_message.append(
_('Changing your name is not allowed once pad have been issued for your account. Please contact '
'us directly for this operation.'))
if 'company_name' in data and (data['company_name'] or False) != (partner.company_name or False): if 'company_name' in data and (data['company_name'] or False) != (partner.company_name or False):
error['company_name'] = 'error' error['company_name'] = 'error'
error_message.append(_('Changing your company name is not allowed once pad have been issued for your account. Please contact us directly for this operation.')) error_message.append(
_('Changing your company name is not allowed once pad have been issued for your account. Please contact us directly for this operation.'))
return error, error_message return error, error_message
...@@ -8,11 +8,19 @@ class District(models.Model): ...@@ -8,11 +8,19 @@ class District(models.Model):
_name = 'res.district' _name = 'res.district'
_description = 'Kota/Kabupaten' _description = 'Kota/Kabupaten'
state_id = fields.Many2one('res.country.state', string='Provinsi', required=True) state_id = fields.Many2one('res.country.state', string='Provinsi', required=True)
typ = fields.Selection([
('kab', 'Kabupaten'),
('kabtif', 'Kabupaten Administratif'),
('kota', 'Kota'),
('kotif', 'Kota Administratif')],
string='Jenis')
code = fields.Char(string="Kode Kota/Kabupaten") code = fields.Char(string="Kode Kota/Kabupaten")
name = fields.Char(string="Nama Kota/Kabupaten", index=True) name = fields.Char(string="Nama Kota/Kabupaten", index=True)
display_code = fields.Char(index=True) display_code = fields.Char(index=True)
# display_code = fields.Char(compute='_compute_display_code', store=True, index=True) # display_code = fields.Char(compute='_compute_display_code') #, store=True, index=True)
# sub_district_ids = fields.One2many('res.district.sub', 'district_id', string='Kecamatan') display_name = fields.Char(index=True)
# display_name = fields.Char(compute='_compute_display_name') #, store=True, index=True)
sub_district_ids = fields.One2many('res.district.sub', 'district_id', string='Kecamatan')
# address_view_id = fields.Many2one( # address_view_id = fields.Many2one(
# comodel_name='ir.ui.view', string="Input View", # comodel_name='ir.ui.view', string="Input View",
# domain=[('model', '=', 'res.partner'), ('type', '=', 'form')], # domain=[('model', '=', 'res.partner'), ('type', '=', 'form')],
...@@ -25,49 +33,55 @@ class District(models.Model): ...@@ -25,49 +33,55 @@ class District(models.Model):
# compute="_compute_image_url", string="Flag", # compute="_compute_image_url", string="Flag",
# help="Url of static flag image", # help="Url of static flag image",
# ) # )
_sql_constraints = [
('code_uniq', 'unique (state_id,code)', 'Kode Kabupaten/Kota Harus Unik !'),
('name_uniq', 'unique (state_id,typ,name)', 'Nama Kabupaten/Kota Harus Unik !'),
]
@api.model
def _name_search(self, name, args=None, operator='ilike', limit=100, name_get_uid=None):
args = args or []
if self.env.context.get('state_id'):
args = expression.AND([args, [('state_id', '=', self.env.context.get('state_id'))]])
if operator == 'ilike' and not (name or '').strip():
first_domain = []
domain = []
else:
first_domain = [('code', '=ilike', name)]
domain = [('name', operator, name)]
first_district_ids = self._search(expression.AND([first_domain, args]), limit=limit,
access_rights_uid=name_get_uid) if first_domain else []
return list(first_district_ids) + [
state_id
for state_id in self._search(expression.AND([domain, args]),
limit=limit, access_rights_uid=name_get_uid)
if state_id not in first_district_ids
]
def name_get(self):
result = []
for record in self:
result.append((record.id, "{} {} ({})".format(record.typ, record.name, record.state_id.code)))
return result
def code_get(self):
result = []
for record in self:
result.append((record.id, "{}.{}".format(record.state_id.code, record.code)))
return result
@api.depends('name')
def _compute_display_name(self):
pass
# self.display_name = "{} {}".format(self.typ, self.name)
@api.depends('code')
def _compute_display_code(self):
pass
# self.display_code = "{}.{}".format(self.state_id.code, self.code)
# @api.model
# def _name_search(self, name, args=None, operator='ilike', limit=100, name_get_uid=None):
# args = args or []
# if self.env.context.get('state_id'):
# args = expression.AND([args, [('state_id', '=', self.env.context.get('state_id'))]])
#
# if operator == 'ilike' and not (name or '').strip():
# first_domain = []
# domain = []
# else:
# first_domain = [('code', '=ilike', name)]
# domain = [('name', operator, name)]
#
# first_district_ids = self._search(expression.AND([first_domain, args]), limit=limit,
# access_rights_uid=name_get_uid) if first_domain else []
# return list(first_district_ids) + [
# state_id
# for state_id in self._search(expression.AND([domain, args]),
# limit=limit, access_rights_uid=name_get_uid)
# if state_id not in first_district_ids
# ]
#
# def name_get(self):
# result = []
# for record in self:
# result.append((record.id, "{} ({})".format(record.name, record.state_id.code)))
# return result
#
# def code_get(self):
# result = []
# for record in self:
# result.append((record.id, "{} ({})".format(record.state_id.code, record.code)))
# return result
#
# @api.depends('code')
# def _compute_display_code(self):
# return self.code_get()
# # result = []
# # for record in self:
# # result.append((record.id, "{} ({})".format(record.state_id.code, record.code)))
# # return result
#
# @api.model_create_multi # @api.model_create_multi
# def create(self, vals_list): # def create(self, vals_list):
# for vals in vals_list: # for vals in vals_list:
...@@ -112,11 +126,15 @@ class Village(models.Model): ...@@ -112,11 +126,15 @@ class Village(models.Model):
_name = 'res.district.village' _name = 'res.district.village'
_description = "Desa/Keurahan" _description = "Desa/Keurahan"
sub_district_id = fields.Many2one('res.district.sub', string='Kecamatan', required=True) sub_district_id = fields.Many2one('res.district.sub', string='Kecamatan', required=True)
typ = fields.Selection([
('desa', 'Desa'),
('kelurahan', 'Kelurahan')],
string='Jenis')
code = fields.Char(string="Kode Desa/Kelurahan") code = fields.Char(string="Kode Desa/Kelurahan")
name = fields.Char(string="Nama Desa/Kelurahan", index=True) name = fields.Char(string="Nama Desa/Kelurahan", index=True)
display_code = fields.Char(index=True) # compute='_compute_display_code', store=True, display_code = fields.Char(index=True) # compute='_compute_display_code', store=True,
display_name = fields.Char(index=True) # compute='_compute_display_name', store=True, display_name = fields.Char(index=True) # compute='_compute_display_name', store=True,
_sql_constraints = [ _sql_constraints = [
('village_code_uniq', 'unique (sub_district_id,code)', 'Kode Kelurahan/Desa Harus Unik !'), ('village_code_uniq', 'unique (sub_district_id,code)', 'Kode Kelurahan/Desa Harus Unik !'),
('village_name_uniq', 'unique (sub_district_id,name)', 'Nama Kelurahan/Desa Harus Unik !'), ('village_name_uniq', 'unique (sub_district_id,typ,name)', 'Nama Kelurahan/Desa Harus Unik !'),
] ]
import time from odoo import fields, models
import logging
from psycopg2 import sql, DatabaseError
from odoo import api, fields, models, _
class PdlSudutPandang(models.Model): class PdlSudutPandang(models.Model):
_name = 'pdl.sudut.pandang' _name = 'pdl.sudut.pandang'
_description = 'Sudut Pandang' _description = 'Sudut Pandang'
code = fields.Char(String='Kode', width=32) code = fields.Char(string='Kode', size=32)
name = fields.Char(index=True, String='Nama', width=64) name = fields.Char(index=True, string='Nama', size=64)
value = fields.Float(String='Nilai') value = fields.Float(string='Nilai')
_sql_constraints = [ _sql_constraints = [
('code_uniq', 'unique (code)', 'Kode harus unik') ('code_uniq', 'unique (code)', 'Kode harus unik')
......
<?xml version="1.0" encoding="utf-8"?> <?xml version="1.0" encoding="utf-8"?>
<odoo> <odoo>
<data noupdate="0"> <data noupdate="0">
<record model="ir.module.category" id="pdl_kab"> <record model="ir.module.category" id="pdl_kab">
<field name="name">PDL</field> <field name="name">PDL</field>
<field name="description">PDL</field> <field name="description">PDL</field>
......
"id","name","model_id:id","group_id:id","perm_read","perm_write","perm_create","perm_unlink" "id","name","model_id:id","group_id:id","perm_read","perm_write","perm_create","perm_unlink"
"access_sudut_pandang_config","access.sudut.pandang.config","model_pdl_sudut_pandang","base.group_system",1,1,1,1 "access_sudut_pandang_admin","access.sudut.pandang.admin","model_pdl_sudut_pandang","base.group_system",1,1,1,1
"access_sudut_pandang_config_pdl_admin","access.sudut.pandang.config.pdl.admin","model_pdl_sudut_pandang","group_pdl_konfigurasi",1,1,1,1 "access_sudut_pandang_admin_pdl","access.sudut.pandang.admin.pdl","model_pdl_sudut_pandang","group_pdl_konfigurasi",1,1,1,1
"access_district_admin","access.district.admin","model_res_district","base.group_system",1,1,1,1
"access_district_admin_pdl","access.district.admin.pdl","model_res_district","group_pdl_konfigurasi",1,1,1,1
"access_district_sub_admin","access.district.sub.admin","model_res_district_sub","base.group_system",1,1,1,1
"access_district_sub_admin_pdl","access.district.sub.admin.pdl","model_res_district_sub","group_pdl_konfigurasi",1,1,1,1
"access_village_admin","access.village.admin","model_res_district_village","base.group_system",1,1,1,1
"access_village_admin_pdl","access.village.admin.pdl","model_res_district_village","group_pdl_konfigurasi",1,1,1,1
.openerp div.oe_account_help { .openerp div.oe_account_help {
background : #D6EBFF; background: #D6EBFF;
width: 100%; width: 100%;
padding: 10px; padding: 10px;
border: 3px solid #C1D4E6; border: 3px solid #C1D4E6;
} }
.openerp p.oe_account_font_help{ .openerp p.oe_account_font_help {
text-align: left; text-align: left;
font-weight: bold; font-weight: bold;
margin: 0px; margin: 0px;
font-size: 14px; font-size: 14px;
} }
.openerp p.oe_account_font_content{ .openerp p.oe_account_font_content {
margin-left: 30px; margin-left: 30px;
font-size: 14px; font-size: 14px;
} }
.openerp p.oe_account_font_title{ .openerp p.oe_account_font_title {
margin-top: 7px; margin-top: 7px;
font-size: 15px; font-size: 15px;
font-style: italic; font-style: italic;
......
.openerp .oe_force_bold { .openerp .oe_force_bold {
font-weight: bold !important; font-weight: bold !important;
} }
.openerp label.oe_open_balance{
.openerp label.oe_open_balance {
margin-right: -18px; margin-right: -18px;
} }
.openerp label.oe_subtotal_footer_separator{
float:right; .openerp label.oe_subtotal_footer_separator {
float: right;
width: 184px !important; width: 184px !important;
} }
.openerp label.oe_mini_subtotal_footer_separator{
.openerp label.oe_mini_subtotal_footer_separator {
margin-right: -14px; margin-right: -14px;
} }
.openerp .oe_account_total, .openerp .oe_pos_total { .openerp .oe_account_total, .openerp .oe_pos_total {
margin-left: -2px; margin-left: -2px;
} }
.openerp label.oe_real_closing_balance{
.openerp label.oe_real_closing_balance {
min-width: 184px !important; min-width: 184px !important;
} }
.openerp label.oe_difference, .openerp label.oe_pos_difference { .openerp label.oe_difference, .openerp label.oe_pos_difference {
margin-right: -10px; margin-right: -10px;
padding-left: 10px !important; padding-left: 10px !important;
min-width: 195px !important; min-width: 195px !important;
} }
.openerp .oe_opening_total{
margin-right: 4px; .openerp .oe_opening_total {
margin-right: 4px;
} }
.o_payment_label{ .o_payment_label {
padding-right: 20px; padding-right: 20px;
} }
\ No newline at end of file \ No newline at end of file
...@@ -12,14 +12,14 @@ ...@@ -12,14 +12,14 @@
} }
.oe_tax_group_name { .oe_tax_group_name {
font-weight: bold; font-weight: bold;
min-width: 150px; min-width: 150px;
text-align: right; text-align: right;
padding-right: 20px; padding-right: 20px;
} }
.oe_tax_group_editable .oe_tax_group_amount_value input { .oe_tax_group_editable .oe_tax_group_amount_value input {
width: 65%; width: 65%;
float: right; float: right;
text-align: right; text-align: right;
} }
odoo.define('account.AccountPortalSidebar', function (require) { odoo.define('account.AccountPortalSidebar', function (require) {
'use strict'; 'use strict';
const dom = require('web.dom'); const dom = require('web.dom');
var publicWidget = require('web.public.widget'); var publicWidget = require('web.public.widget');
var PortalSidebar = require('portal.PortalSidebar'); var PortalSidebar = require('portal.PortalSidebar');
var utils = require('web.utils'); var utils = require('web.utils');
publicWidget.registry.AccountPortalSidebar = PortalSidebar.extend({ publicWidget.registry.AccountPortalSidebar = PortalSidebar.extend({
selector: '.o_portal_invoice_sidebar', selector: '.o_portal_invoice_sidebar',
events: { events: {
'click .o_portal_invoice_print': '_onPrintInvoice', 'click .o_portal_invoice_print': '_onPrintInvoice',
}, },
/** /**
* @override * @override
*/ */
start: function () { start: function () {
var def = this._super.apply(this, arguments); var def = this._super.apply(this, arguments);
var $invoiceHtml = this.$el.find('iframe#invoice_html'); var $invoiceHtml = this.$el.find('iframe#invoice_html');
var updateIframeSize = this._updateIframeSize.bind(this, $invoiceHtml); var updateIframeSize = this._updateIframeSize.bind(this, $invoiceHtml);
$(window).on('resize', updateIframeSize); $(window).on('resize', updateIframeSize);
var iframeDoc = $invoiceHtml[0].contentDocument || $invoiceHtml[0].contentWindow.document; var iframeDoc = $invoiceHtml[0].contentDocument || $invoiceHtml[0].contentWindow.document;
if (iframeDoc.readyState === 'complete') { if (iframeDoc.readyState === 'complete') {
updateIframeSize(); updateIframeSize();
} else { } else {
$invoiceHtml.on('load', updateIframeSize); $invoiceHtml.on('load', updateIframeSize);
} }
return def; return def;
}, },
//-------------------------------------------------------------------------- //--------------------------------------------------------------------------
// Handlers // Handlers
//-------------------------------------------------------------------------- //--------------------------------------------------------------------------
/** /**
* Called when the iframe is loaded or the window is resized on customer portal. * Called when the iframe is loaded or the window is resized on customer portal.
* The goal is to expand the iframe height to display the full report without scrollbar. * The goal is to expand the iframe height to display the full report without scrollbar.
* *
* @private * @private
* @param {object} $el: the iframe * @param {object} $el: the iframe
*/ */
_updateIframeSize: function ($el) { _updateIframeSize: function ($el) {
var $wrapwrap = $el.contents().find('div#wrapwrap'); var $wrapwrap = $el.contents().find('div#wrapwrap');
// Set it to 0 first to handle the case where scrollHeight is too big for its content. // Set it to 0 first to handle the case where scrollHeight is too big for its content.
$el.height(0); $el.height(0);
$el.height($wrapwrap[0].scrollHeight); $el.height($wrapwrap[0].scrollHeight);
// scroll to the right place after iframe resize // scroll to the right place after iframe resize
if (!utils.isValidAnchor(window.location.hash)) { if (!utils.isValidAnchor(window.location.hash)) {
return; return;
} }
var $target = $(window.location.hash); var $target = $(window.location.hash);
if (!$target.length) { if (!$target.length) {
return; return;
} }
dom.scrollTo($target[0], {duration: 0}); dom.scrollTo($target[0], {duration: 0});
}, },
/** /**
* @private * @private
* @param {MouseEvent} ev * @param {MouseEvent} ev
*/ */
_onPrintInvoice: function (ev) { _onPrintInvoice: function (ev) {
ev.preventDefault(); ev.preventDefault();
var href = $(ev.currentTarget).attr('href'); var href = $(ev.currentTarget).attr('href');
this._printIframeContent(href); this._printIframeContent(href);
}, },
}); });
}); });
odoo.define('account.ShowResequenceRenderer', function (require) { odoo.define('account.ShowResequenceRenderer', function (require) {
"use strict"; "use strict";
const { Component } = owl; const {Component} = owl;
const { useState } = owl.hooks; const {useState} = owl.hooks;
const AbstractFieldOwl = require('web.AbstractFieldOwl'); const AbstractFieldOwl = require('web.AbstractFieldOwl');
const field_registry = require('web.field_registry_owl'); const field_registry = require('web.field_registry_owl');
class ChangeLine extends Component { } class ChangeLine extends Component {
ChangeLine.template = 'account.ResequenceChangeLine'; }
ChangeLine.props = ["changeLine", 'ordering'];
ChangeLine.template = 'account.ResequenceChangeLine';
ChangeLine.props = ["changeLine", 'ordering'];
class ShowResequenceRenderer extends AbstractFieldOwl {
constructor(...args) { class ShowResequenceRenderer extends AbstractFieldOwl {
super(...args); constructor(...args) {
this.data = this.value ? JSON.parse(this.value) : { super(...args);
changeLines: [], this.data = this.value ? JSON.parse(this.value) : {
ordering: 'date', changeLines: [],
}; ordering: 'date',
} };
async willUpdateProps(nextProps) { }
await super.willUpdateProps(nextProps);
Object.assign(this.data, JSON.parse(this.value)); async willUpdateProps(nextProps) {
await super.willUpdateProps(nextProps);
Object.assign(this.data, JSON.parse(this.value));
}
} }
}
ShowResequenceRenderer.template = 'account.ResequenceRenderer';
ShowResequenceRenderer.components = { ChangeLine }
field_registry.add('account_resequence_widget', ShowResequenceRenderer); ShowResequenceRenderer.template = 'account.ResequenceRenderer';
return ShowResequenceRenderer; ShowResequenceRenderer.components = {ChangeLine}
field_registry.add('account_resequence_widget', ShowResequenceRenderer);
return ShowResequenceRenderer;
}); });
odoo.define('account.hierarchy.selection', function (require) { odoo.define('account.hierarchy.selection', function (require) {
"use strict"; "use strict";
var core = require('web.core'); var core = require('web.core');
var relational_fields = require('web.relational_fields'); var relational_fields = require('web.relational_fields');
...@@ -23,30 +23,48 @@ odoo.define('account.hierarchy.selection', function (require) { ...@@ -23,30 +23,48 @@ odoo.define('account.hierarchy.selection', function (require) {
domain: [], domain: [],
fields: ['id', 'internal_group', 'display_name'], fields: ['id', 'internal_group', 'display_name'],
}, },
}).then(function(arg) { }).then(function (arg) {
self.values = _.map(arg, v => [v['id'], v['display_name']]) self.values = _.map(arg, v => [v['id'], v['display_name']])
self.hierarchy_groups = [ self.hierarchy_groups = [
{ {
'name': _t('Balance Sheet'), 'name': _t('Balance Sheet'),
'children': [ 'children': [
{'name': _t('Assets'), 'ids': _.map(_.filter(arg, v => v['internal_group'] == 'asset'), v => v['id'])}, {
{'name': _t('Liabilities'), 'ids': _.map(_.filter(arg, v => v['internal_group'] == 'liability'), v => v['id'])}, 'name': _t('Assets'),
{'name': _t('Equity'), 'ids': _.map(_.filter(arg, v => v['internal_group'] == 'equity'), v => v['id'])}, 'ids': _.map(_.filter(arg, v => v['internal_group'] == 'asset'), v => v['id'])
},
{
'name': _t('Liabilities'),
'ids': _.map(_.filter(arg, v => v['internal_group'] == 'liability'), v => v['id'])
},
{
'name': _t('Equity'),
'ids': _.map(_.filter(arg, v => v['internal_group'] == 'equity'), v => v['id'])
},
], ],
}, },
{ {
'name': _t('Profit & Loss'), 'name': _t('Profit & Loss'),
'children': [ 'children': [
{'name': _t('Income'), 'ids': _.map(_.filter(arg, v => v['internal_group'] == 'income'), v => v['id'])}, {
{'name': _t('Expense'), 'ids': _.map(_.filter(arg, v => v['internal_group'] == 'expense'), v => v['id'])}, 'name': _t('Income'),
'ids': _.map(_.filter(arg, v => v['internal_group'] == 'income'), v => v['id'])
},
{
'name': _t('Expense'),
'ids': _.map(_.filter(arg, v => v['internal_group'] == 'expense'), v => v['id'])
},
], ],
}, },
{'name': _t('Other'), 'ids': _.map(_.filter(arg, v => !['asset', 'liability', 'equity', 'income', 'expense'].includes(v['internal_group'])), v => v['id'])}, {
'name': _t('Other'),
'ids': _.map(_.filter(arg, v => !['asset', 'liability', 'equity', 'income', 'expense'].includes(v['internal_group'])), v => v['id'])
},
] ]
}); });
} }
Promise.resolve(prom).then(function() { Promise.resolve(prom).then(function () {
self.$el.empty(); self.$el.empty();
self._addHierarchy(self.$el, self.hierarchy_groups, 0); self._addHierarchy(self.$el, self.hierarchy_groups, 0);
var value = self.value; var value = self.value;
...@@ -56,13 +74,13 @@ odoo.define('account.hierarchy.selection', function (require) { ...@@ -56,13 +74,13 @@ odoo.define('account.hierarchy.selection', function (require) {
self.$el.val(JSON.stringify(value)); self.$el.val(JSON.stringify(value));
}); });
}, },
_addHierarchy: function(el, group, level) { _addHierarchy: function (el, group, level) {
var self = this; var self = this;
_.each(group, function(item) { _.each(group, function (item) {
var optgroup = $('<optgroup/>').attr(({ var optgroup = $('<optgroup/>').attr(({
'label': $('<div/>').html('&nbsp;'.repeat(6 * level) + item['name']).text(), 'label': $('<div/>').html('&nbsp;'.repeat(6 * level) + item['name']).text(),
})) }))
_.each(item['ids'], function(id) { _.each(item['ids'], function (id) {
var value = _.find(self.values, v => v[0] == id) var value = _.find(self.values, v => v[0] == id)
optgroup.append($('<option/>', { optgroup.append($('<option/>', {
value: JSON.stringify(value[0]), value: JSON.stringify(value[0]),
......
odoo.define('account.bank_statement', function(require) { odoo.define('account.bank_statement', function (require) {
"use strict"; "use strict";
var KanbanController = require("web.KanbanController"); var KanbanController = require("web.KanbanController");
......
odoo.define('account.upload.bill.mixin', function (require) { odoo.define('account.upload.bill.mixin', function (require) {
"use strict"; "use strict";
var core = require('web.core'); var core = require('web.core');
var _t = core._t; var _t = core._t;
...@@ -29,7 +29,7 @@ odoo.define('account.upload.bill.mixin', function (require) { ...@@ -29,7 +29,7 @@ odoo.define('account.upload.bill.mixin', function (require) {
var self = this; var self = this;
var attachments = Array.prototype.slice.call(arguments, 1); var attachments = Array.prototype.slice.call(arguments, 1);
// Get id from result // Get id from result
var attachent_ids = attachments.reduce(function(filtered, record) { var attachent_ids = attachments.reduce(function (filtered, record) {
if (record.id) { if (record.id) {
filtered.push(record.id); filtered.push(record.id);
} }
...@@ -40,7 +40,7 @@ odoo.define('account.upload.bill.mixin', function (require) { ...@@ -40,7 +40,7 @@ odoo.define('account.upload.bill.mixin', function (require) {
method: 'create_invoice_from_attachment', method: 'create_invoice_from_attachment',
args: ["", attachent_ids], args: ["", attachent_ids],
context: this.initialState.context, context: this.initialState.context,
}).then(function(result) { }).then(function (result) {
self.do_action(result); self.do_action(result);
}); });
}, },
...@@ -62,7 +62,7 @@ odoo.define('account.upload.bill.mixin', function (require) { ...@@ -62,7 +62,7 @@ odoo.define('account.upload.bill.mixin', function (require) {
odoo.define('account.bills.tree', function (require) { odoo.define('account.bills.tree', function (require) {
"use strict"; "use strict";
var core = require('web.core'); var core = require('web.core');
var ListController = require('web.ListController'); var ListController = require('web.ListController');
var ListView = require('web.ListView'); var ListView = require('web.ListView');
...@@ -87,7 +87,7 @@ odoo.define('account.bills.tree', function (require) { ...@@ -87,7 +87,7 @@ odoo.define('account.bills.tree', function (require) {
}); });
odoo.define('account.dashboard.kanban', function (require) { odoo.define('account.dashboard.kanban', function (require) {
"use strict"; "use strict";
var core = require('web.core'); var core = require('web.core');
var KanbanController = require('web.KanbanController'); var KanbanController = require('web.KanbanController');
var KanbanView = require('web.KanbanView'); var KanbanView = require('web.KanbanView');
......
odoo.define('account.ShowGroupedList', function (require) { odoo.define('account.ShowGroupedList', function (require) {
"use strict"; "use strict";
const { Component } = owl; const {Component} = owl;
const { useState } = owl.hooks; const {useState} = owl.hooks;
const AbstractFieldOwl = require('web.AbstractFieldOwl'); const AbstractFieldOwl = require('web.AbstractFieldOwl');
const field_registry = require('web.field_registry_owl'); const field_registry = require('web.field_registry_owl');
class ListItem extends Component { } class ListItem extends Component {
ListItem.template = 'account.GroupedItemTemplate'; }
ListItem.props = ["item_vals", "options"];
ListItem.template = 'account.GroupedItemTemplate';
class ListGroup extends Component { } ListItem.props = ["item_vals", "options"];
ListGroup.template = 'account.GroupedItemsTemplate';
ListGroup.components = { ListItem } class ListGroup extends Component {
ListGroup.props = ["group_vals", "options"];
class ShowGroupedList extends AbstractFieldOwl {
constructor(...args) {
super(...args);
this.data = this.value ? JSON.parse(this.value) : {
groups_vals: [],
options: {
discarded_number: '',
columns: [],
},
};
} }
async willUpdateProps(nextProps) {
await super.willUpdateProps(nextProps); ListGroup.template = 'account.GroupedItemsTemplate';
Object.assign(this.data, JSON.parse(this.value)); ListGroup.components = {ListItem}
ListGroup.props = ["group_vals", "options"];
class ShowGroupedList extends AbstractFieldOwl {
constructor(...args) {
super(...args);
this.data = this.value ? JSON.parse(this.value) : {
groups_vals: [],
options: {
discarded_number: '',
columns: [],
},
};
}
async willUpdateProps(nextProps) {
await super.willUpdateProps(nextProps);
Object.assign(this.data, JSON.parse(this.value));
}
} }
}
ShowGroupedList.template = 'account.GroupedListTemplate';
ShowGroupedList.components = { ListGroup }
field_registry.add('grouped_view_widget', ShowGroupedList); ShowGroupedList.template = 'account.GroupedListTemplate';
return ShowGroupedList; ShowGroupedList.components = {ListGroup}
field_registry.add('grouped_view_widget', ShowGroupedList);
return ShowGroupedList;
}); });
odoo.define('account.activity', function (require) { odoo.define('account.activity', function (require) {
"use strict"; "use strict";
var AbstractField = require('web.AbstractField'); var AbstractField = require('web.AbstractField');
var core = require('web.core'); var core = require('web.core');
var field_registry = require('web.field_registry'); var field_registry = require('web.field_registry');
var QWeb = core.qweb; var QWeb = core.qweb;
var _t = core._t; var _t = core._t;
var VatActivity = AbstractField.extend({ var VatActivity = AbstractField.extend({
className: 'o_journal_activity_kanban', className: 'o_journal_activity_kanban',
events: { events: {
'click .see_all_activities': '_onOpenAll', 'click .see_all_activities': '_onOpenAll',
'click .see_activity': '_onOpenActivity', 'click .see_activity': '_onOpenActivity',
}, },
init: function () { init: function () {
this.MAX_ACTIVITY_DISPLAY = 5; this.MAX_ACTIVITY_DISPLAY = 5;
this._super.apply(this, arguments); this._super.apply(this, arguments);
}, },
//------------------------------------------------------------ //------------------------------------------------------------
// Private // Private
//------------------------------------------------------------ //------------------------------------------------------------
_render: function () { _render: function () {
var self = this; var self = this;
var info = JSON.parse(this.value); var info = JSON.parse(this.value);
if (!info) { if (!info) {
this.$el.html(''); this.$el.html('');
return; return;
} }
info.more_activities = false; info.more_activities = false;
if (info.activities.length > this.MAX_ACTIVITY_DISPLAY) { if (info.activities.length > this.MAX_ACTIVITY_DISPLAY) {
info.more_activities = true; info.more_activities = true;
info.activities = info.activities.slice(0, this.MAX_ACTIVITY_DISPLAY); info.activities = info.activities.slice(0, this.MAX_ACTIVITY_DISPLAY);
} }
this.$el.html(QWeb.render('accountJournalDashboardActivity', info)); this.$el.html(QWeb.render('accountJournalDashboardActivity', info));
}, },
_onOpenActivity: function(e) { _onOpenActivity: function (e) {
e.preventDefault(); e.preventDefault();
var self = this; var self = this;
self.do_action({ self.do_action({
type: 'ir.actions.act_window', type: 'ir.actions.act_window',
name: _t('Journal Entry'), name: _t('Journal Entry'),
target: 'current', target: 'current',
res_id: $(e.target).data('resId'), res_id: $(e.target).data('resId'),
res_model: 'account.move', res_model: 'account.move',
views: [[false, 'form']], views: [[false, 'form']],
}); });
}, },
_onOpenAll: function(e) { _onOpenAll: function (e) {
e.preventDefault(); e.preventDefault();
var self = this; var self = this;
self.do_action({ self.do_action({
type: 'ir.actions.act_window', type: 'ir.actions.act_window',
name: _t('Journal Entries'), name: _t('Journal Entries'),
res_model: 'account.move', res_model: 'account.move',
views: [[false, 'kanban'], [false, 'form']], views: [[false, 'kanban'], [false, 'form']],
search_view_id: [false], search_view_id: [false],
domain: [['journal_id', '=', self.res_id], ['activity_ids', '!=', false]], domain: [['journal_id', '=', self.res_id], ['activity_ids', '!=', false]],
}); });
} }
}) })
field_registry.add('kanban_vat_activity', VatActivity); field_registry.add('kanban_vat_activity', VatActivity);
return VatActivity; return VatActivity;
}); });
odoo.define('account.section_and_note_backend', function (require) { odoo.define('account.section_and_note_backend', function (require) {
// The goal of this file is to contain JS hacks related to allowing // The goal of this file is to contain JS hacks related to allowing
// section and note on sale order and invoice. // section and note on sale order and invoice.
// [UPDATED] now also allows configuring products on sale order. // [UPDATED] now also allows configuring products on sale order.
"use strict"; "use strict";
var FieldChar = require('web.basic_fields').FieldChar; var FieldChar = require('web.basic_fields').FieldChar;
var FieldOne2Many = require('web.relational_fields').FieldOne2Many; var FieldOne2Many = require('web.relational_fields').FieldOne2Many;
var fieldRegistry = require('web.field_registry'); var fieldRegistry = require('web.field_registry');
var ListFieldText = require('web.basic_fields').ListFieldText; var ListFieldText = require('web.basic_fields').ListFieldText;
var ListRenderer = require('web.ListRenderer'); var ListRenderer = require('web.ListRenderer');
var SectionAndNoteListRenderer = ListRenderer.extend({ var SectionAndNoteListRenderer = ListRenderer.extend({
/** /**
* We want section and note to take the whole line (except handle and trash) * We want section and note to take the whole line (except handle and trash)
* to look better and to hide the unnecessary fields. * to look better and to hide the unnecessary fields.
* *
* @override * @override
*/ */
_renderBodyCell: function (record, node, index, options) { _renderBodyCell: function (record, node, index, options) {
var $cell = this._super.apply(this, arguments); var $cell = this._super.apply(this, arguments);
var isSection = record.data.display_type === 'line_section'; var isSection = record.data.display_type === 'line_section';
var isNote = record.data.display_type === 'line_note'; var isNote = record.data.display_type === 'line_note';
if (isSection || isNote) { if (isSection || isNote) {
if (node.attrs.widget === "handle") { if (node.attrs.widget === "handle") {
return $cell; return $cell;
} else if (node.attrs.name === "name") { } else if (node.attrs.name === "name") {
var nbrColumns = this._getNumberOfCols(); var nbrColumns = this._getNumberOfCols();
if (this.handleField) { if (this.handleField) {
nbrColumns--; nbrColumns--;
} }
if (this.addTrashIcon) { if (this.addTrashIcon) {
nbrColumns--; nbrColumns--;
}
$cell.attr('colspan', nbrColumns);
} else {
$cell.removeClass('o_invisible_modifier');
return $cell.addClass('o_hidden');
} }
$cell.attr('colspan', nbrColumns);
} else {
$cell.removeClass('o_invisible_modifier');
return $cell.addClass('o_hidden');
} }
}
return $cell; return $cell;
}, },
/** /**
* We add the o_is_{display_type} class to allow custom behaviour both in JS and CSS. * We add the o_is_{display_type} class to allow custom behaviour both in JS and CSS.
* *
* @override * @override
*/ */
_renderRow: function (record, index) { _renderRow: function (record, index) {
var $row = this._super.apply(this, arguments); var $row = this._super.apply(this, arguments);
if (record.data.display_type) { if (record.data.display_type) {
$row.addClass('o_is_' + record.data.display_type); $row.addClass('o_is_' + record.data.display_type);
} }
return $row; return $row;
}, },
/** /**
* We want to add .o_section_and_note_list_view on the table to have stronger CSS. * We want to add .o_section_and_note_list_view on the table to have stronger CSS.
* *
* @override * @override
* @private * @private
*/ */
_renderView: function () { _renderView: function () {
var self = this; var self = this;
return this._super.apply(this, arguments).then(function () { return this._super.apply(this, arguments).then(function () {
self.$('.o_list_table').addClass('o_section_and_note_list_view'); self.$('.o_list_table').addClass('o_section_and_note_list_view');
}); });
} }
}); });
// We create a custom widget because this is the cleanest way to do it: // We create a custom widget because this is the cleanest way to do it:
// to be sure this custom code will only impact selected fields having the widget // to be sure this custom code will only impact selected fields having the widget
// and not applied to any other existing ListRenderer. // and not applied to any other existing ListRenderer.
var SectionAndNoteFieldOne2Many = FieldOne2Many.extend({ var SectionAndNoteFieldOne2Many = FieldOne2Many.extend({
/** /**
* We want to use our custom renderer for the list. * We want to use our custom renderer for the list.
* *
* @override * @override
*/ */
_getRenderer: function () { _getRenderer: function () {
if (this.view.arch.tag === 'tree') { if (this.view.arch.tag === 'tree') {
return SectionAndNoteListRenderer; return SectionAndNoteListRenderer;
} }
return this._super.apply(this, arguments); return this._super.apply(this, arguments);
}, },
}); });
// This is a merge between a FieldText and a FieldChar. // This is a merge between a FieldText and a FieldChar.
// We want a FieldChar for section, // We want a FieldChar for section,
// and a FieldText for the rest (product and note). // and a FieldText for the rest (product and note).
var SectionAndNoteFieldText = function (parent, name, record, options) { var SectionAndNoteFieldText = function (parent, name, record, options) {
var isSection = record.data.display_type === 'line_section'; var isSection = record.data.display_type === 'line_section';
var Constructor = isSection ? FieldChar : ListFieldText; var Constructor = isSection ? FieldChar : ListFieldText;
return new Constructor(parent, name, record, options); return new Constructor(parent, name, record, options);
}; };
fieldRegistry.add('section_and_note_one2many', SectionAndNoteFieldOne2Many); fieldRegistry.add('section_and_note_one2many', SectionAndNoteFieldOne2Many);
fieldRegistry.add('section_and_note_text', SectionAndNoteFieldText); fieldRegistry.add('section_and_note_text', SectionAndNoteFieldText);
return SectionAndNoteListRenderer; return SectionAndNoteListRenderer;
}); });
...@@ -20,19 +20,19 @@ odoo.define('account.tax_group', function (require) { ...@@ -20,19 +20,19 @@ odoo.define('account.tax_group', function (require) {
//-------------------------------------------------------------------------- //--------------------------------------------------------------------------
/** /**
* This method is called by "_setTaxGroups". It is * This method is called by "_setTaxGroups". It is
* responsible for calculating taxes based on * responsible for calculating taxes based on
* tax groups and triggering an event to * tax groups and triggering an event to
* notify the ORM of a change. * notify the ORM of a change.
* *
* @param {Id} taxGroupId * @param {Id} taxGroupId
* @param {Float} deltaAmount * @param {Float} deltaAmount
*/ */
_changeTaxValueByTaxGroup: function (taxGroupId, deltaAmount) { _changeTaxValueByTaxGroup: function (taxGroupId, deltaAmount) {
const self = this; const self = this;
// Search for the first tax line with the same tax group and modify its value // Search for the first tax line with the same tax group and modify its value
function applyChange(line_id){ function applyChange(line_id) {
let debitAmount = 0; let debitAmount = 0;
let creditAmount = 0; let creditAmount = 0;
let amount_currency = 0; let amount_currency = 0;
...@@ -63,14 +63,23 @@ odoo.define('account.tax_group', function (require) { ...@@ -63,14 +63,23 @@ odoo.define('account.tax_group', function (require) {
// Trigger ORM // Trigger ORM
self.trigger_up('field_changed', { self.trigger_up('field_changed', {
dataPointID: self.record.id, dataPointID: self.record.id,
changes: { line_ids: { operation: "UPDATE", id: line_id.id, data: { amount_currency: amount_currency, debit: debitAmount, credit: creditAmount } } }, // account.move change changes: {
initialEvent: { dataPointID: line_id.id, changes: { amount_currency: amount_currency, debit: debitAmount, credit: creditAmount }, }, // account.move.line change line_ids: {
operation: "UPDATE",
id: line_id.id,
data: {amount_currency: amount_currency, debit: debitAmount, credit: creditAmount}
}
}, // account.move change
initialEvent: {
dataPointID: line_id.id,
changes: {amount_currency: amount_currency, debit: debitAmount, credit: creditAmount},
}, // account.move.line change
}); });
} }
let line_id = self.record.data.line_ids.data.find(elem => elem.data.tax_group_id && elem.data.tax_group_id.data.id === taxGroupId); let line_id = self.record.data.line_ids.data.find(elem => elem.data.tax_group_id && elem.data.tax_group_id.data.id === taxGroupId);
if (line_id){ if (line_id) {
applyChange(line_id); applyChange(line_id);
} else { } else {
const {limit, id, count} = self.record.data.line_ids; const {limit, id, count} = self.record.data.line_ids;
...@@ -92,7 +101,7 @@ odoo.define('account.tax_group', function (require) { ...@@ -92,7 +101,7 @@ odoo.define('account.tax_group', function (require) {
* is located is of the "in_invoice" or "in_refund" type. * is located is of the "in_invoice" or "in_refund" type.
* This makes it possible to know if it is a purchase * This makes it possible to know if it is a purchase
* document. * document.
* *
* @returns boolean (true if the invoice is a purchase document) * @returns boolean (true if the invoice is a purchase document)
*/ */
_isPurchaseDocument: function () { _isPurchaseDocument: function () {
...@@ -100,11 +109,11 @@ odoo.define('account.tax_group', function (require) { ...@@ -100,11 +109,11 @@ odoo.define('account.tax_group', function (require) {
}, },
/** /**
* This method is part of the widget life cycle and allows you to render * This method is part of the widget life cycle and allows you to render
* the widget. * the widget.
* *
* @private * @private
* @override * @override
*/ */
_render: function () { _render: function () {
var self = this; var self = this;
...@@ -123,11 +132,11 @@ odoo.define('account.tax_group', function (require) { ...@@ -123,11 +132,11 @@ odoo.define('account.tax_group', function (require) {
//-------------------------------------------------------------------------- //--------------------------------------------------------------------------
/** /**
* This method is called when the user is in edit mode and * This method is called when the user is in edit mode and
* leaves the <input> field. Then, we execute the code that * leaves the <input> field. Then, we execute the code that
* modifies the information. * modifies the information.
* *
* @param {event} ev * @param {event} ev
*/ */
_onBlur: function (ev) { _onBlur: function (ev) {
ev.preventDefault(); ev.preventDefault();
...@@ -147,14 +156,14 @@ odoo.define('account.tax_group', function (require) { ...@@ -147,14 +156,14 @@ odoo.define('account.tax_group', function (require) {
return this._render(); return this._render();
} }
var taxGroupId = $input.parents('.oe_tax_group_editable').data('taxGroupId'); var taxGroupId = $input.parents('.oe_tax_group_editable').data('taxGroupId');
this._changeTaxValueByTaxGroup(taxGroupId, oldValue-newValue); this._changeTaxValueByTaxGroup(taxGroupId, oldValue - newValue);
}, },
/** /**
* This method is called when the user clicks on a specific <td>. * This method is called when the user clicks on a specific <td>.
* it will hide the edit button and display the field to be edited. * it will hide the edit button and display the field to be edited.
* *
* @param {event} ev * @param {event} ev
*/ */
_onClick: function (ev) { _onClick: function (ev) {
ev.preventDefault(); ev.preventDefault();
...@@ -170,11 +179,11 @@ odoo.define('account.tax_group', function (require) { ...@@ -170,11 +179,11 @@ odoo.define('account.tax_group', function (require) {
}, },
/** /**
* This method is called when the user is in edit mode and pressing * This method is called when the user is in edit mode and pressing
* a key on his keyboard. If this key corresponds to ENTER or TAB, * a key on his keyboard. If this key corresponds to ENTER or TAB,
* the code that modifies the information is executed. * the code that modifies the information is executed.
* *
* @param {event} ev * @param {event} ev
*/ */
_onKeydown: function (ev) { _onKeydown: function (ev) {
switch (ev.which) { switch (ev.which) {
......
odoo.define('account.tour', function(require) { odoo.define('account.tour', function (require) {
"use strict"; "use strict";
var core = require('web.core'); var core = require('web.core');
var tour = require('web_tour.tour'); var tour = require('web_tour.tour');
var _t = core._t; var _t = core._t;
tour.register('account_tour', { tour.register('account_tour', {
url: "/web", url: "/web",
sequence: 60, sequence: 60,
}, [ }, [
...tour.stepUtils.goToAppSteps('account.menu_finance', _t('Send invoices to your customers in no time with the <b>Invoicing app</b>.')), ...tour.stepUtils.goToAppSteps('account.menu_finance', _t('Send invoices to your customers in no time with the <b>Invoicing app</b>.')),
{ {
trigger: "a.o_onboarding_step_action[data-method=action_open_base_onboarding_company]", trigger: "a.o_onboarding_step_action[data-method=action_open_base_onboarding_company]",
content: _t("Start by checking your company's data."), content: _t("Start by checking your company's data."),
position: "bottom", position: "bottom",
}, { }, {
trigger: "button[name=action_save_onboarding_company_step]", trigger: "button[name=action_save_onboarding_company_step]",
extra_trigger: "a.o_onboarding_step_action[data-method=action_open_base_onboarding_company]", extra_trigger: "a.o_onboarding_step_action[data-method=action_open_base_onboarding_company]",
content: _t("Looks good. Let's continue."), content: _t("Looks good. Let's continue."),
position: "bottom", position: "bottom",
}, { }, {
trigger: "a.o_onboarding_step_action[data-method=action_open_base_document_layout]", trigger: "a.o_onboarding_step_action[data-method=action_open_base_document_layout]",
content: _t("Customize your layout."), content: _t("Customize your layout."),
position: "bottom", position: "bottom",
}, { }, {
trigger: "button[name=document_layout_save]", trigger: "button[name=document_layout_save]",
extra_trigger: "a.o_onboarding_step_action[data-method=action_open_base_document_layout]", extra_trigger: "a.o_onboarding_step_action[data-method=action_open_base_document_layout]",
content: _t("Once everything is as you want it, validate."), content: _t("Once everything is as you want it, validate."),
position: "top", position: "top",
}, { }, {
trigger: "a.o_onboarding_step_action[data-method=action_open_account_onboarding_create_invoice]", trigger: "a.o_onboarding_step_action[data-method=action_open_account_onboarding_create_invoice]",
content: _t("Now, we'll create your first invoice."), content: _t("Now, we'll create your first invoice."),
position: "bottom", position: "bottom",
}, { }, {
trigger: "div[name=partner_id] input", trigger: "div[name=partner_id] input",
extra_trigger: "[name=move_type][raw-value=out_invoice]", extra_trigger: "[name=move_type][raw-value=out_invoice]",
content: _t("Write a company name to <b>create one</b> or <b>see suggestions</b>."), content: _t("Write a company name to <b>create one</b> or <b>see suggestions</b>."),
position: "bottom", position: "bottom",
}, { }, {
trigger: ".o_m2o_dropdown_option a:contains('Create')", trigger: ".o_m2o_dropdown_option a:contains('Create')",
extra_trigger: "[name=move_type][raw-value=out_invoice]", extra_trigger: "[name=move_type][raw-value=out_invoice]",
content: _t("Select first partner"), content: _t("Select first partner"),
auto: true, auto: true,
}, { }, {
trigger: ".modal-content button.btn-primary", trigger: ".modal-content button.btn-primary",
extra_trigger: "[name=move_type][raw-value=out_invoice]", extra_trigger: "[name=move_type][raw-value=out_invoice]",
content: _t("Once everything is set, you are good to continue. You will be able to edit this later in the <b>Customers</b> menu."), content: _t("Once everything is set, you are good to continue. You will be able to edit this later in the <b>Customers</b> menu."),
auto: true, auto: true,
}, { }, {
trigger: "div[name=invoice_line_ids] .o_field_x2many_list_row_add a:not([data-context])", trigger: "div[name=invoice_line_ids] .o_field_x2many_list_row_add a:not([data-context])",
extra_trigger: "[name=move_type][raw-value=out_invoice]", extra_trigger: "[name=move_type][raw-value=out_invoice]",
content: _t("Add a line to your invoice"), content: _t("Add a line to your invoice"),
}, { }, {
trigger: "div[name=invoice_line_ids] textarea[name=name]", trigger: "div[name=invoice_line_ids] textarea[name=name]",
extra_trigger: "[name=move_type][raw-value=out_invoice]", extra_trigger: "[name=move_type][raw-value=out_invoice]",
content: _t("Fill in the details of the line."), content: _t("Fill in the details of the line."),
position: "bottom", position: "bottom",
}, { }, {
trigger: "div[name=invoice_line_ids] input[name=price_unit]", trigger: "div[name=invoice_line_ids] input[name=price_unit]",
extra_trigger: "[name=move_type][raw-value=out_invoice]", extra_trigger: "[name=move_type][raw-value=out_invoice]",
content: _t("Set a price"), content: _t("Set a price"),
position: "bottom", position: "bottom",
run: 'text 100', run: 'text 100',
}, { }, {
trigger: "button[name=action_post]", trigger: "button[name=action_post]",
extra_trigger: "[name=move_type][raw-value=out_invoice]", extra_trigger: "[name=move_type][raw-value=out_invoice]",
content: _t("Once your invoice is ready, press CONFIRM."), content: _t("Once your invoice is ready, press CONFIRM."),
}, { }, {
trigger: "button[name=action_invoice_sent]", trigger: "button[name=action_invoice_sent]",
extra_trigger: "[name=move_type][raw-value=out_invoice]", extra_trigger: "[name=move_type][raw-value=out_invoice]",
content: _t("Send the invoice and check what the customer will receive."), content: _t("Send the invoice and check what the customer will receive."),
}, { }, {
trigger: "input[name=email]", trigger: "input[name=email]",
extra_trigger: "[name=move_type][raw-value=out_invoice]", extra_trigger: "[name=move_type][raw-value=out_invoice]",
content: _t("Write here <b>your own email address</b> to test the flow."), content: _t("Write here <b>your own email address</b> to test the flow."),
run: 'text customer@example.com', run: 'text customer@example.com',
auto: true, auto: true,
}, { }, {
trigger: ".modal-content button.btn-primary", trigger: ".modal-content button.btn-primary",
extra_trigger: "[name=move_type][raw-value=out_invoice]", extra_trigger: "[name=move_type][raw-value=out_invoice]",
content: _t("Validate."), content: _t("Validate."),
auto: true, auto: true,
}, { }, {
trigger: "button[name=send_and_print_action]", trigger: "button[name=send_and_print_action]",
extra_trigger: "[name=move_type][raw-value=out_invoice]", extra_trigger: "[name=move_type][raw-value=out_invoice]",
content: _t("Let's send the invoice."), content: _t("Let's send the invoice."),
position: "top" position: "top"
} }
]); ]);
}); });
.o_journal_activity_kanban { .o_journal_activity_kanban {
display: block; display: block;
.align_activity_center {
width: 100%; .align_activity_center {
align-items: center; width: 100%;
margin-bottom: 5px; align-items: center;
} margin-bottom: 5px;
}
} }
\ No newline at end of file \ No newline at end of file
.o_kanban_view.o_kanban_dashboard.o_account_kanban { .o_kanban_view.o_kanban_dashboard.o_account_kanban {
&.o_kanban_ungrouped .o_account_dashboard_header { &.o_kanban_ungrouped .o_account_dashboard_header {
margin: (0 - $o-kanban-record-margin) ($o-kanban-record-margin - $o-horizontal-padding) $o-kanban-record-margin; margin: (0 - $o-kanban-record-margin) ($o-kanban-record-margin - $o-horizontal-padding) $o-kanban-record-margin;
}
.o_account_dashboard_header {
flex: 1 0 100%;
flex-flow: column nowrap;
align-self: flex-start;
width: 100%;
height: auto; // cancel o_form_view height 100%, which hides the help tip message at the bottom of the screen
min-height: 0%; // cancel o_form_view min-height 100%, which hides the help tip message at the bottom of the screen
background-color: $o-view-background-color;
.o_form_statusbar {
padding-right: $o-horizontal-padding;
} }
.o_account_dashboard_header { h4 {
flex: 1 0 100%; font-size: $font-size-base;
flex-flow: column nowrap; font-weight: 500;
align-self: flex-start; }
width: 100%;
height: auto; // cancel o_form_view height 100%, which hides the help tip message at the bottom of the screen
min-height: 0%; // cancel o_form_view min-height 100%, which hides the help tip message at the bottom of the screen
background-color: $o-view-background-color;
.o_form_statusbar {
padding-right: $o-horizontal-padding;
}
h4 { .fa-gift {
font-size: $font-size-base; color: #eeeeee;
font-weight: 500;
} &:hover {
color: #555555;
}
}
.fa-gift { .o_arrow_button.btn-secondary {
color: #eeeeee; color: $text-muted;
&:hover { text-transform: none;
color: #555555; font-weight: 500;
}
.o_account_dashboard_index {
color: gray('900');
}
&.o_action_done {
color: gray('900');
background-color: gray('200');
&:after {
border-left-color: gray('200');
} }
.o_arrow_button.btn-secondary { .fa-check {
color: $text-muted; color: theme-color('success');
text-transform: none;
font-weight: 500;
.o_account_dashboard_index {
color: gray('900');
}
&.o_action_done {
color: gray('900');
background-color: gray('200');
&:after {
border-left-color: gray('200');
}
.fa-check {
color: theme-color('success');
}
}
&:last-of-type {
margin-left: $o-horizontal-padding;
padding-left: $o-horizontal-padding*.5;
border-left: 1px solid gray('300');
}
} }
}
&:last-of-type {
margin-left: $o-horizontal-padding;
padding-left: $o-horizontal-padding*.5;
border-left: 1px solid gray('300');
}
} }
}
} }
.o_kanban_view.o_kanban_dashboard.o_account_kanban { .o_kanban_view.o_kanban_dashboard.o_account_kanban {
.o_kanban_record { .o_kanban_record {
@include media-breakpoint-up(sm) { @include media-breakpoint-up(sm) {
.oe_kanban_action_button { .oe_kanban_action_button {
display: block; display: block;
margin-bottom: 5px; margin-bottom: 5px;
} }
} }
.o_kanban_card_settings { .o_kanban_card_settings {
padding-top: $o-horizontal-padding/2; padding-top: $o-horizontal-padding/2;
padding-bottom: $o-horizontal-padding/2; padding-bottom: $o-horizontal-padding/2;
border-top: 1px solid; border-top: 1px solid;
border-color: $o-brand-lightsecondary; border-color: $o-brand-lightsecondary;
} }
.o_dashboard_star {
font-size: 12px;
&.fa-star-o { .o_dashboard_star {
color: $o-main-color-muted; font-size: 12px;
&:hover {
color: gold; &.fa-star-o {
} color: $o-main-color-muted;
}
&.fa-star {
color: gold;
}
}
.o_dashboard_graph { &:hover {
margin-bottom: -$o-horizontal-padding/2; color: gold;
} }
}
&.fa-star {
color: gold;
}
} }
&.o_kanban_ungrouped { .o_dashboard_graph {
.o_kanban_record { margin-bottom: -$o-horizontal-padding/2;
width: 450px;
}
} }
}
.o_kanban_group { &.o_kanban_ungrouped {
&:not(.o_column_folded) { .o_kanban_record {
width: 450px + 2*$o-kanban-group-padding; width: 450px;
}
}
@include media-breakpoint-down(sm) { .o_kanban_group {
width: 100%; &:not(.o_column_folded) {
} width: 450px + 2*$o-kanban-group-padding;
}
@include media-breakpoint-down(sm) {
width: 100%;
}
} }
}
} }
// Style for the widget "dashboard_graph" // Style for the widget "dashboard_graph"
.o_dashboard_graph { .o_dashboard_graph {
position: relative; position: relative;
margin: 16px -16px; margin: 16px -16px;
canvas { canvas {
height: 75px; height: 75px;
} }
} }
.o_sample_data .o_dashboard_graph.o_graph_linechart > svg g.nv-linesWrap g.nv-group.nv-series-0 { .o_sample_data .o_dashboard_graph.o_graph_linechart > svg g.nv-linesWrap g.nv-group.nv-series-0 {
fill: gray !important; fill: gray !important;
opacity: 0.1; opacity: 0.1;
} }
.o_search_panel.account_root { .o_search_panel.account_root {
flex: 0 0 50px; flex: 0 0 50px;
padding: 6px; padding: 6px;
scrollbar-width: thin; scrollbar-width: thin;
.o_search_panel_section_header {
display: none; .o_search_panel_section_header {
display: none;
}
.list-group-item span.o_search_panel_label_title {
display: contents;
}
.o_search_panel_category_value {
header {
margin-left: 0;
padding-left: 0;
} }
.list-group-item span.o_search_panel_label_title {
display: contents; .o_search_panel_category_value .o_toggle_fold {
} width: 0.3rem;
.o_search_panel_category_value {
header {
margin-left: 0;
padding-left: 0;
}
.o_search_panel_category_value .o_toggle_fold {
width: 0.3rem;
}
}
&::-webkit-scrollbar {
width: 4px;
}
&::-webkit-scrollbar-thumb {
background: lightgray;
} }
}
&::-webkit-scrollbar {
width: 4px;
}
&::-webkit-scrollbar-thumb {
background: lightgray;
}
} }
// The goal of this file is to contain CSS hacks related to allowing // The goal of this file is to contain CSS hacks related to allowing
// section and note on sale order and invoice. // section and note on sale order and invoice.
table.o_section_and_note_list_view tr.o_data_row.o_is_line_note, table.o_section_and_note_list_view tr.o_data_row.o_is_line_note,
table.o_section_and_note_list_view tr.o_data_row.o_is_line_note textarea[name="name"], table.o_section_and_note_list_view tr.o_data_row.o_is_line_note textarea[name="name"],
div.oe_kanban_card.o_is_line_note { div.oe_kanban_card.o_is_line_note {
font-style: italic; font-style: italic;
} }
table.o_section_and_note_list_view tr.o_data_row.o_is_line_section, table.o_section_and_note_list_view tr.o_data_row.o_is_line_section,
div.oe_kanban_card.o_is_line_section { div.oe_kanban_card.o_is_line_section {
font-weight: bold; font-weight: bold;
background-color: #DDDDDD; background-color: #DDDDDD;
} }
table.o_section_and_note_list_view tr.o_data_row.o_is_line_section { table.o_section_and_note_list_view tr.o_data_row.o_is_line_section {
border-top: 1px solid #BBB; border-top: 1px solid #BBB;
border-bottom: 1px solid #BBB; border-bottom: 1px solid #BBB;
} }
table.o_section_and_note_list_view tr.o_data_row { table.o_section_and_note_list_view tr.o_data_row {
&.o_is_line_note, &.o_is_line_note,
&.o_is_line_section { &.o_is_line_section {
td { td {
// There is an undeterministic CSS behaviour in Chrome related to // There is an undeterministic CSS behaviour in Chrome related to
// the combination of the row's and its children's borders. // the combination of the row's and its children's borders.
border: none !important; border: none !important;
}
} }
}
} }
...@@ -6,14 +6,14 @@ $o-account-info-color: #44c; ...@@ -6,14 +6,14 @@ $o-account-info-color: #44c;
@keyframes animate-red { @keyframes animate-red {
0% { 0% {
color: red; color: red;
} }
100% { 100% {
color: inherit; color: inherit;
} }
} }
.animate { .animate {
animation: animate-red 1s ease; animation: animate-red 1s ease;
} }
...@@ -2,16 +2,21 @@ ...@@ -2,16 +2,21 @@
<templates> <templates>
<t t-name="accountJournalDashboardActivity"> <t t-name="accountJournalDashboardActivity">
<t t-foreach="activities" t-as="activity"> <t t-foreach="activities" t-as="activity">
<div class="row"> <div class="row">
<div class="col-8 o_mail_activity"> <div class="col-8 o_mail_activity">
<a href="#" t-att-class="(activity.status == 'late' ? 'o_activity_color_overdue ' : ' ') + (activity.activity_category == 'tax_report' ? 'o_open_vat_report' : 'see_activity')" t-att-data-res-id="activity.res_id" t-att-data-id="activity.id" t-att-data-model="activity.res_model"> <a href="#"
t-att-class="(activity.status == 'late' ? 'o_activity_color_overdue ' : ' ') + (activity.activity_category == 'tax_report' ? 'o_open_vat_report' : 'see_activity')"
t-att-data-res-id="activity.res_id" t-att-data-id="activity.id"
t-att-data-model="activity.res_model">
<t t-esc="activity.name"/> <t t-esc="activity.name"/>
</a> </a>
</div> </div>
<div class="col-4 text-right"> <div class="col-4 text-right">
<span><t t-esc="activity.date"/></span> <span>
<t t-esc="activity.date"/>
</span>
</div> </div>
</div> </div>
</t> </t>
......
...@@ -14,22 +14,29 @@ ...@@ -14,22 +14,29 @@
<tr> <tr>
<t t-if="outstanding"> <t t-if="outstanding">
<td> <td>
<a title="assign to invoice" role="button" class="oe_form_field btn btn-link outstanding_credit_assign" t-att-data-id="line.id" style="margin-right: 10px;" href="#" data-toggle="tooltip">Add</a> <a title="assign to invoice" role="button"
class="oe_form_field btn btn-link outstanding_credit_assign" t-att-data-id="line.id"
style="margin-right: 10px;" href="#" data-toggle="tooltip">Add</a>
</td> </td>
<td style="max-width: 30em;"> <td style="max-width: 30em;">
<div class="oe_form_field" style="margin-right: 30px; text-overflow: ellipsis; overflow: hidden; white-space: nowrap;" t-att-title="line.date" data-toggle="tooltip"><t t-esc="line.journal_name"></t></div> <div class="oe_form_field"
style="margin-right: 30px; text-overflow: ellipsis; overflow: hidden; white-space: nowrap;"
t-att-title="line.date" data-toggle="tooltip"><t t-esc="line.journal_name"></t></div>
</td> </td>
</t> </t>
<t t-if="!outstanding"> <t t-if="!outstanding">
<td> <td>
<a role="button" tabindex="0" class="js_payment_info fa fa-info-circle" t-att-index="line.index" style="margin-right:5px;" aria-label="Info" title="Payment Info" data-toggle="tooltip"></a> <a role="button" tabindex="0" class="js_payment_info fa fa-info-circle"
t-att-index="line.index" style="margin-right:5px;" aria-label="Info" title="Payment Info"
data-toggle="tooltip"></a>
</td> </td>
<td> <td>
<i class="o_field_widget text-right o_payment_label">Paid on <t t-esc="line.date"></t></i> <i class="o_field_widget text-right o_payment_label">Paid on <t t-esc="line.date"></t></i>
</td> </td>
</t> </t>
<td style="text-align:right;"> <td style="text-align:right;">
<span class="oe_form_field oe_form_field_float oe_form_field_monetary" style="margin-left: -10px;"> <span class="oe_form_field oe_form_field_float oe_form_field_monetary"
style="margin-left: -10px;">
<t t-if="line.position === 'before'"> <t t-if="line.position === 'before'">
<t t-esc="line.currency"/> <t t-esc="line.currency"/>
</t> </t>
...@@ -60,7 +67,7 @@ ...@@ -60,7 +67,7 @@
</t> </t>
</td> </td>
</tr> </tr>
<tr> <tr>
<td><strong>Memo: </strong></td> <td><strong>Memo: </strong></td>
<td> <td>
<div style="width: 200px; word-wrap: break-word"> <div style="width: 200px; word-wrap: break-word">
...@@ -74,12 +81,16 @@ ...@@ -74,12 +81,16 @@
</tr> </tr>
<tr> <tr>
<td><strong>Payment Journal: </strong></td> <td><strong>Payment Journal: </strong></td>
<td><t t-esc="journal_name"/><span t-if="payment_method_name"> (<t t-esc="payment_method_name"/>)</span></td> <td><t t-esc="journal_name"/>
<span t-if="payment_method_name"> (<t t-esc="payment_method_name"/>)</span></td>
</tr> </tr>
</table> </table>
</div> </div>
<button class="btn btn-sm btn-primary js_unreconcile_payment float-left" t-att-partial-id="partial_id" t-att-payment-id="payment_id" t-att-move-id="move_id" style="margin-top:5px; margin-bottom:5px;" groups="account.group_account_invoice">Unreconcile</button> <button class="btn btn-sm btn-primary js_unreconcile_payment float-left" t-att-partial-id="partial_id"
<button class="btn btn-sm btn-secondary js_open_payment float-right" t-att-payment-id="account_payment_id" t-att-move-id="move_id" style="margin-top:5px; margin-bottom:5px;">View</button> t-att-payment-id="payment_id" t-att-move-id="move_id" style="margin-top:5px; margin-bottom:5px;"
groups="account.group_account_invoice">Unreconcile</button>
<button class="btn btn-sm btn-secondary js_open_payment float-right" t-att-payment-id="account_payment_id"
t-att-move-id="move_id" style="margin-top:5px; margin-bottom:5px;">View</button>
</t> </t>
</templates> </templates>
...@@ -3,11 +3,13 @@ ...@@ -3,11 +3,13 @@
<div t-name="account.ResequenceRenderer" owl="1" class="d-block"> <div t-name="account.ResequenceRenderer" owl="1" class="d-block">
<table t-if="data.changeLines.length" class="table table-sm"> <table t-if="data.changeLines.length" class="table table-sm">
<thead><tr> <thead>
<th>Date</th> <tr>
<th>Before</th> <th>Date</th>
<th>After</th> <th>Before</th>
</tr></thead> <th>After</th>
</tr>
</thead>
<tbody t-foreach="data.changeLines" t-as="changeLine" t-key="changeLine.id"> <tbody t-foreach="data.changeLines" t-as="changeLine" t-key="changeLine.id">
<ChangeLine changeLine="changeLine" ordering="data.ordering"/> <ChangeLine changeLine="changeLine" ordering="data.ordering"/>
</tbody> </tbody>
...@@ -18,8 +20,10 @@ ...@@ -18,8 +20,10 @@
<tr> <tr>
<td t-esc="props.changeLine.date"/> <td t-esc="props.changeLine.date"/>
<td t-esc="props.changeLine.current_name"/> <td t-esc="props.changeLine.current_name"/>
<td t-if="props.ordering == 'keep'" t-esc="props.changeLine.new_by_name" t-attf-class="{{ props.changeLine.new_by_name != props.changeLine.new_by_date ? 'animate' : ''}}"/> <td t-if="props.ordering == 'keep'" t-esc="props.changeLine.new_by_name"
<td t-else="" t-esc="props.changeLine.new_by_date" t-attf-class="{{ props.changeLine.new_by_name != props.changeLine.new_by_date ? 'animate' : ''}}"/> t-attf-class="{{ props.changeLine.new_by_name != props.changeLine.new_by_date ? 'animate' : ''}}"/>
<td t-else="" t-esc="props.changeLine.new_by_date"
t-attf-class="{{ props.changeLine.new_by_name != props.changeLine.new_by_date ? 'animate' : ''}}"/>
</tr> </tr>
</t> </t>
</templates> </templates>
<?xml version="1.0" encoding="utf-8"?> <?xml version="1.0" encoding="utf-8"?>
<templates> <templates>
<div t-name="account.GroupedListTemplate" owl="1" class="d-block"> <div t-name="account.GroupedListTemplate" owl="1" class="d-block">
<table t-if="data.groups_vals.length" class="table table-sm o_list_table table table-sm table-hover table-striped o_list_table_grouped"> <table t-if="data.groups_vals.length"
<thead><tr> class="table table-sm o_list_table table table-sm table-hover table-striped o_list_table_grouped">
<t t-foreach="data.options.columns" t-as="col"> <thead>
<th t-esc="col['label']" t-attf-class="{{col['class']}}"/> <tr>
</t> <t t-foreach="data.options.columns" t-as="col">
</tr></thead> <th t-esc="col['label']" t-attf-class="{{col['class']}}"/>
</t>
</tr>
</thead>
<t t-foreach="data.groups_vals" t-as="group_vals"> <t t-foreach="data.groups_vals" t-as="group_vals">
<ListGroup group_vals="group_vals" options="data.options"/> <ListGroup group_vals="group_vals" options="data.options"/>
</t> </t>
</table> </table>
<t t-if="data.options.discarded_number"> <t t-if="data.options.discarded_number">
<span><t t-esc="data.options.discarded_number"/> are not shown in the preview</span> <span>
<t t-esc="data.options.discarded_number"/>
are not shown in the preview
</span>
</t> </t>
</div> </div>
......
...@@ -18,7 +18,8 @@ ...@@ -18,7 +18,8 @@
</span> </span>
</span> </span>
<span class="tax_group_edit_input d-none"> <span class="tax_group_edit_input d-none">
<input type="text" class="o_field_float o_field_number o_input" t-att-data-original-value="line[1]"/> <input type="text" class="o_field_float o_field_number o_input"
t-att-data-original-value="line[1]"/>
</span> </span>
</t> </t>
<t t-if="!displayEditWidget or line[1] === 0"> <t t-if="!displayEditWidget or line[1] === 0">
......
odoo.define('account.reconciliation_field_tests', function (require) { odoo.define('account.reconciliation_field_tests', function (require) {
"use strict"; "use strict";
var FormView = require('web.FormView'); var FormView = require('web.FormView');
var testUtils = require('web.test_utils'); var testUtils = require('web.test_utils');
var createView = testUtils.createView; var createView = testUtils.createView;
QUnit.module('account', { QUnit.module('account', {
beforeEach: function () { beforeEach: function () {
this.data = { this.data = {
'account.move': { 'account.move': {
fields: { fields: {
payments_widget: {string: "payments_widget data", type: "char"}, payments_widget: {string: "payments_widget data", type: "char"},
outstanding_credits_debits_widget: {string: "outstanding_credits_debits_widget data", type: "char"}, outstanding_credits_debits_widget: {
string: "outstanding_credits_debits_widget data",
type: "char"
},
},
records: [{
id: 1,
payments_widget: '{"content": [{"digits": [69, 2], "currency": "$", "amount": 555.0, "name": "Customer Payment: INV/2017/0004", "date": "2017-04-25", "position": "before", "ref": "BNK1/2017/0003 (INV/2017/0004)", "payment_id": 22, "move_id": 10, "partial_id": 38, "journal_name": "Bank"}], "outstanding": false, "title": "Less Payment"}',
outstanding_credits_debits_widget: '{"content": [{"digits": [69, 2], "currency": "$", "amount": 100.0, "journal_name": "INV/2017/0004", "position": "before", "id": 20}], "move_id": 4, "outstanding": true, "title": "Outstanding credits"}',
}]
}, },
records: [{ };
id: 1, }
payments_widget: '{"content": [{"digits": [69, 2], "currency": "$", "amount": 555.0, "name": "Customer Payment: INV/2017/0004", "date": "2017-04-25", "position": "before", "ref": "BNK1/2017/0003 (INV/2017/0004)", "payment_id": 22, "move_id": 10, "partial_id": 38, "journal_name": "Bank"}], "outstanding": false, "title": "Less Payment"}', }, function () {
outstanding_credits_debits_widget: '{"content": [{"digits": [69, 2], "currency": "$", "amount": 100.0, "journal_name": "INV/2017/0004", "position": "before", "id": 20}], "move_id": 4, "outstanding": true, "title": "Outstanding credits"}', QUnit.module('Reconciliation');
}]
},
};
}
}, function () {
QUnit.module('Reconciliation');
QUnit.test('Reconciliation form field', async function (assert) { QUnit.test('Reconciliation form field', async function (assert) {
assert.expect(5); assert.expect(5);
var form = await createView({ var form = await createView({
View: FormView, View: FormView,
model: 'account.move', model: 'account.move',
data: this.data, data: this.data,
arch: '<form>'+ arch: '<form>' +
'<field name="outstanding_credits_debits_widget" widget="payment"/>'+ '<field name="outstanding_credits_debits_widget" widget="payment"/>' +
'<field name="payments_widget" widget="payment"/>'+ '<field name="payments_widget" widget="payment"/>' +
'</form>', '</form>',
res_id: 1, res_id: 1,
mockRPC: function (route, args) { mockRPC: function (route, args) {
if (args.method === 'js_remove_outstanding_partial') { if (args.method === 'js_remove_outstanding_partial') {
assert.deepEqual(args.args, [10, 38], "should call js_remove_outstanding_partial {warning: required focus}"); assert.deepEqual(args.args, [10, 38], "should call js_remove_outstanding_partial {warning: required focus}");
return Promise.resolve(); return Promise.resolve();
} }
if (args.method === 'js_assign_outstanding_line') { if (args.method === 'js_assign_outstanding_line') {
assert.deepEqual(args.args, [4, 20], "should call js_assign_outstanding_line {warning: required focus}"); assert.deepEqual(args.args, [4, 20], "should call js_assign_outstanding_line {warning: required focus}");
return Promise.resolve(); return Promise.resolve();
} }
return this._super.apply(this, arguments); return this._super.apply(this, arguments);
},
intercepts: {
do_action: function (event) {
assert.deepEqual(event.data.action, {
'type': 'ir.actions.act_window',
'res_model': 'account.move',
'res_id': 10,
'views': [[false, 'form']],
'target': 'current'
},
"should open the form view");
}, },
}, intercepts: {
}); do_action: function (event) {
assert.deepEqual(event.data.action, {
'type': 'ir.actions.act_window',
'res_model': 'account.move',
'res_id': 10,
'views': [[false, 'form']],
'target': 'current'
},
"should open the form view");
},
},
});
assert.strictEqual(form.$('.o_field_widget[name="payments_widget"]').text().replace(/[\s\n\r]+/g, ' '), assert.strictEqual(form.$('.o_field_widget[name="payments_widget"]').text().replace(/[\s\n\r]+/g, ' '),
" Paid on 04/25/2017 $ 555.00 ", " Paid on 04/25/2017 $ 555.00 ",
"should display payment information"); "should display payment information");
form.$('.o_field_widget[name="outstanding_credits_debits_widget"] .outstanding_credit_assign').trigger('click'); form.$('.o_field_widget[name="outstanding_credits_debits_widget"] .outstanding_credit_assign').trigger('click');
assert.strictEqual(form.$('.o_field_widget[name="outstanding_credits_debits_widget"]').text().replace(/[\s\n\r]+/g, ' '), assert.strictEqual(form.$('.o_field_widget[name="outstanding_credits_debits_widget"]').text().replace(/[\s\n\r]+/g, ' '),
" Outstanding credits Add INV/2017/0004 $ 100.00 ", " Outstanding credits Add INV/2017/0004 $ 100.00 ",
"should display outstanding information"); "should display outstanding information");
form.$('.o_field_widget[name="payments_widget"] .js_payment_info').trigger('focus'); form.$('.o_field_widget[name="payments_widget"] .js_payment_info').trigger('focus');
form.$('.popover .js_open_payment').trigger('click'); form.$('.popover .js_open_payment').trigger('click');
form.$('.o_field_widget[name="payments_widget"] .js_payment_info').trigger('focus'); form.$('.o_field_widget[name="payments_widget"] .js_payment_info').trigger('focus');
form.$('.popover .js_unreconcile_payment').trigger('click'); form.$('.popover .js_unreconcile_payment').trigger('click');
form.destroy(); form.destroy();
});
}); });
}); });
});
odoo.define('account.section_and_note_tests', function (require) { odoo.define('account.section_and_note_tests', function (require) {
"use strict"; "use strict";
var FormView = require('web.FormView'); var FormView = require('web.FormView');
var testUtils = require('web.test_utils'); var testUtils = require('web.test_utils');
var createView = testUtils.createView; var createView = testUtils.createView;
QUnit.module('section_and_note', { QUnit.module('section_and_note', {
beforeEach: function () { beforeEach: function () {
this.data = { this.data = {
invoice: { invoice: {
fields: { fields: {
invoice_line_ids: { invoice_line_ids: {
string: "Lines", string: "Lines",
type: 'one2many', type: 'one2many',
relation: 'invoice_line', relation: 'invoice_line',
relation_field: 'invoice_id' relation_field: 'invoice_id'
},
}, },
records: [
{id: 1, invoice_line_ids: [1, 2]},
],
}, },
records: [ invoice_line: {
{id: 1, invoice_line_ids: [1, 2]}, fields: {
], display_type: {
}, string: 'Type',
invoice_line: { type: 'selection',
fields: { selection: [['line_section', "Section"], ['line_note', "Note"]]
display_type: { },
string: 'Type', invoice_id: {
type: 'selection', string: "Invoice",
selection: [['line_section', "Section"], ['line_note', "Note"]] type: 'many2one',
}, relation: 'invoice'
invoice_id: { },
string: "Invoice", name: {
type: 'many2one', string: "Name",
relation: 'invoice' type: 'text'
}, },
name: {
string: "Name",
type: 'text'
}, },
records: [
{id: 1, display_type: false, invoice_id: 1, name: 'product\n2 lines'},
{id: 2, display_type: 'line_section', invoice_id: 1, name: 'section'},
]
}, },
records: [ };
{id: 1, display_type: false, invoice_id: 1, name: 'product\n2 lines'}, },
{id: 2, display_type: 'line_section', invoice_id: 1, name: 'section'}, }, function () {
] QUnit.test('correct display of section and note fields', async function (assert) {
}, assert.expect(5);
}; var form = await createView({
}, View: FormView,
}, function () { model: 'invoice',
QUnit.test('correct display of section and note fields', async function (assert) { data: this.data,
assert.expect(5); arch: '<form>' +
var form = await createView({
View: FormView,
model: 'invoice',
data: this.data,
arch: '<form>' +
'<field name="invoice_line_ids" widget="section_and_note_one2many"/>' + '<field name="invoice_line_ids" widget="section_and_note_one2many"/>' +
'</form>', '</form>',
archs: { archs: {
'invoice_line,false,list': '<tree editable="bottom">' + 'invoice_line,false,list': '<tree editable="bottom">' +
'<field name="display_type" invisible="1"/>' + '<field name="display_type" invisible="1"/>' +
'<field name="name" widget="section_and_note_text"/>' + '<field name="name" widget="section_and_note_text"/>' +
'</tree>', '</tree>',
}, },
res_id: 1, res_id: 1,
}); });
assert.hasClass(form.$('[name="invoice_line_ids"] table'), 'o_section_and_note_list_view'); assert.hasClass(form.$('[name="invoice_line_ids"] table'), 'o_section_and_note_list_view');
// section should be displayed correctly // section should be displayed correctly
var $tr0 = form.$('tr.o_data_row:eq(0)'); var $tr0 = form.$('tr.o_data_row:eq(0)');
assert.doesNotHaveClass($tr0, 'o_is_line_section', assert.doesNotHaveClass($tr0, 'o_is_line_section',
"should not have a section class"); "should not have a section class");
var $tr1 = form.$('tr.o_data_row:eq(1)'); var $tr1 = form.$('tr.o_data_row:eq(1)');
assert.hasClass($tr1, 'o_is_line_section', assert.hasClass($tr1, 'o_is_line_section',
"should have a section class"); "should have a section class");
// enter edit mode // enter edit mode
await testUtils.form.clickEdit(form); await testUtils.form.clickEdit(form);
// editing line should be textarea // editing line should be textarea
$tr0 = form.$('tr.o_data_row:eq(0)'); $tr0 = form.$('tr.o_data_row:eq(0)');
await testUtils.dom.click($tr0.find('td.o_data_cell')); await testUtils.dom.click($tr0.find('td.o_data_cell'));
assert.containsOnce($tr0, 'td.o_data_cell textarea[name="name"]', assert.containsOnce($tr0, 'td.o_data_cell textarea[name="name"]',
"editing line should be textarea"); "editing line should be textarea");
// editing section should be input // editing section should be input
$tr1 = form.$('tr.o_data_row:eq(1)'); $tr1 = form.$('tr.o_data_row:eq(1)');
await testUtils.dom.click($tr1.find('td.o_data_cell')); await testUtils.dom.click($tr1.find('td.o_data_cell'));
assert.containsOnce($tr1, 'td.o_data_cell input[name="name"]', assert.containsOnce($tr1, 'td.o_data_cell input[name="name"]',
"editing section should be input"); "editing section should be input");
form.destroy(); form.destroy();
});
}); });
}); });
});
...@@ -10,33 +10,33 @@ odoo.define('account.dashboard.setup.tour', function (require) { ...@@ -10,33 +10,33 @@ odoo.define('account.dashboard.setup.tour', function (require) {
test: true, test: true,
url: '/web', url: '/web',
}, [tour.stepUtils.showAppsMenuItem(), }, [tour.stepUtils.showAppsMenuItem(),
{ {
id: 'account_menu_click', id: 'account_menu_click',
trigger: '.o_app[data-menu-xmlid="account.menu_finance"]', trigger: '.o_app[data-menu-xmlid="account.menu_finance"]',
position: 'bottom', position: 'bottom',
}, { }, {
trigger: '.o_data_row:first', trigger: '.o_data_row:first',
extra_trigger: '.breadcrumb', extra_trigger: '.breadcrumb',
}, { }, {
trigger: '.o_control_panel button:contains("' + _t('Print') + '")', trigger: '.o_control_panel button:contains("' + _t('Print') + '")',
}, { }, {
trigger: '.o_control_panel .o_dropdown_menu a:contains("' + _t('Invoices without Payment') + '")', trigger: '.o_control_panel .o_dropdown_menu a:contains("' + _t('Invoices without Payment') + '")',
}, { }, {
trigger: 'iframe .o_report_layout_standard h2', trigger: 'iframe .o_report_layout_standard h2',
content: 'Primary color is correct', content: 'Primary color is correct',
run: function () { run: function () {
if (this.$anchor.css('color') !== "rgb(18, 52, 86)") { if (this.$anchor.css('color') !== "rgb(18, 52, 86)") {
console.error('The primary color should be the one set on the company.'); console.error('The primary color should be the one set on the company.');
} }
}, },
}, { }, {
trigger: 'iframe .o_report_layout_standard #informations div strong', trigger: 'iframe .o_report_layout_standard #informations div strong',
content: 'Secondary color is correct', content: 'Secondary color is correct',
run: function () { run: function () {
if (this.$anchor.css('color') !== "rgb(120, 145, 1)") { if (this.$anchor.css('color') !== "rgb(120, 145, 1)") {
console.error('The secondary color should be the one set on the company.'); console.error('The secondary color should be the one set on the company.');
} }
}, },
} }
]); ]);
}); });
...@@ -17,7 +17,9 @@ ...@@ -17,7 +17,9 @@
<field name="model">res.district</field> <field name="model">res.district</field>
<field name="arch" type="xml"> <field name="arch" type="xml">
<tree string="Kabupaten/Kota" sample="1"> <tree string="Kabupaten/Kota" sample="1">
<field name="display_code" string="Kode"/> <field name="display_code" string="Prov"/>
<field name="typ" string="Jenis"/>
<field name="code" string="Kode"/>
<field name="name" string="Nama"/> <field name="name" string="Nama"/>
</tree> </tree>
</field> </field>
...@@ -43,6 +45,7 @@ ...@@ -43,6 +45,7 @@
<form> <form>
<group> <group>
<field name="state_id" string="Provinsi"/> <field name="state_id" string="Provinsi"/>
<field name="typ" string="Jenis"/>
<field name="code" string="Kabupaten/Kota"/> <field name="code" string="Kabupaten/Kota"/>
<field name="display_code" string="Full Kode"/> <field name="display_code" string="Full Kode"/>
<field name="name" string="Nama"/> <field name="name" string="Nama"/>
......
...@@ -47,19 +47,19 @@ ...@@ -47,19 +47,19 @@
<field name="inherit_id" ref="account.product_template_form_view"/> <field name="inherit_id" ref="account.product_template_form_view"/>
<field name="arch" type="xml"> <field name="arch" type="xml">
<!-- <xpath expr="//page[@name='general_information']" position="replace"/>--> <!-- <xpath expr="//page[@name='general_information']" position="replace"/>-->
<xpath expr="//page[@name='inventory']" position="replace"/> <xpath expr="//page[@name='inventory']" position="replace"/>
<xpath expr="//page[@name='sales']" position="replace"/> <xpath expr="//page[@name='sales']" position="replace"/>
<xpath expr="//page[@name='purchase']" position="replace"/> <xpath expr="//page[@name='purchase']" position="replace"/>
<!-- <xpath expr="//div[@name='options']" position="after">--> <!-- <xpath expr="//div[@name='options']" position="after">-->
<!-- Menambah field setelah pricing --> <!-- Menambah field setelah pricing -->
<!-- <field name="active" invisible=""/>--> <!-- <field name="active" invisible=""/>-->
<!-- <field name="categ_id" invisible="0"/>--> <!-- <field name="categ_id" invisible="0"/>-->
<!-- </xpath>--> <!-- </xpath>-->
<!-- <xpath expr="//div[@name='default_code']" position="attribute">--> <!-- <xpath expr="//div[@name='default_code']" position="attribute">-->
<!-- <field name="string" string="NOPD"/>--> <!-- <field name="string" string="NOPD"/>-->
<!-- </xpath>--> <!-- </xpath>-->
</field> </field>
</record> </record>
......
...@@ -60,16 +60,16 @@ ...@@ -60,16 +60,16 @@
parent="config_pdl_kab_menu" parent="config_pdl_kab_menu"
action="action_district_config_pdl_kab" action="action_district_config_pdl_kab"
sequence="2"/> sequence="2"/>
<!-- <menuitem id="sub_district_config_pdl_kab_menu"--> <menuitem id="sub_district_config_pdl_kab_menu"
<!-- name="Kecamatan"--> name="Kecamatan"
<!-- parent="config_pdl_kab_menu"--> parent="config_pdl_kab_menu"
<!-- action="action_sub_district_config_pdl_kab"--> action="action_sub_district_config_pdl_kab"
<!-- sequence="2"/>--> sequence="2"/>
<!-- <menuitem id="village_config_pdl_kab_menu"--> <menuitem id="village_config_pdl_kab_menu"
<!-- name="Desa/Kelurahan"--> name="Desa/Kelurahan"
<!-- parent="config_pdl_kab_menu"--> parent="config_pdl_kab_menu"
<!-- action="action_village_config_pdl_kab"--> action="action_village_config_pdl_kab"
<!-- sequence="2"/>--> sequence="2"/>
<menuitem id="sudut_pandang_config_pdl_kab_menu" <menuitem id="sudut_pandang_config_pdl_kab_menu"
name="Sudut Pandang" name="Sudut Pandang"
parent="config_pdl_kab_menu" parent="config_pdl_kab_menu"
......
...@@ -7,6 +7,7 @@ ...@@ -7,6 +7,7 @@
<field name="arch" type="xml"> <field name="arch" type="xml">
<tree string="Kecamatan" sample="1"> <tree string="Kecamatan" sample="1">
<field name="display_code" string="Kode"/> <field name="display_code" string="Kode"/>
<field name="code" string="Kode"/>
<field name="name" string="Nama"/> <field name="name" string="Nama"/>
</tree> </tree>
</field> </field>
...@@ -25,8 +26,8 @@ ...@@ -25,8 +26,8 @@
</record> </record>
<record id="sub_district_template_form" model="ir.ui.view"> <record id="sub_district_template_form" model="ir.ui.view">
<field name="name">sub_district.template.form</field> <field name="name">sub.district.template.form</field>
<field name="model">res.sub_district</field> <field name="model">res.district.sub</field>
<field name="priority">4</field> <field name="priority">4</field>
<field name="arch" type="xml"> <field name="arch" type="xml">
<form> <form>
......
...@@ -3,37 +3,39 @@ ...@@ -3,37 +3,39 @@
<data> <data>
<record id="village_template_tree" model="ir.ui.view"> <record id="village_template_tree" model="ir.ui.view">
<field name="name">sub.district.template.tree</field> <field name="name">sub.district.template.tree</field>
<field name="model">res.district.sub</field> <field name="model">res.district.village</field>
<field name="arch" type="xml"> <field name="arch" type="xml">
<tree string="Kecamatan" sample="1"> <tree string="Desa/Kelurahan" sample="1">
<field name="display_code" string="Kode"/> <field name="display_code" string="Kode"/>
<field name="code" string="Kode"/>
<field name="name" string="Nama"/> <field name="name" string="Nama"/>
</tree> </tree>
</field> </field>
</record> </record>
<record id="action_village_config_pdl_kab" model="ir.actions.act_window"> <record id="action_village_config_pdl_kab" model="ir.actions.act_window">
<field name="name">Kecamatan</field> <field name="name">Desa/Kelurahan</field>
<field name="res_model">res.district.sub</field> <field name="res_model">res.district.village</field>
<field name="view_mode">kanban,tree,form</field> <field name="view_mode">kanban,tree,form</field>
<field name="view_id" ref="village_template_tree"/> <field name="view_id" ref="village_template_tree"/>
<field name="help" type="html"> <field name="help" type="html">
<p class="o_view_nocontent_smiling_face"> <p class="o_view_nocontent_smiling_face">
Kecamatan Desa/Kelurahan
</p> </p>
</field> </field>
</record> </record>
<record id="village_template_form" model="ir.ui.view"> <record id="village_template_form" model="ir.ui.view">
<field name="name">village.template.form</field> <field name="name">village.template.form</field>
<field name="model">res.village</field> <field name="model">res.district.village</field>
<field name="priority">4</field> <field name="priority">4</field>
<field name="arch" type="xml"> <field name="arch" type="xml">
<form> <form>
<group> <group>
<field name="display_code" string="Full Kode"/>
<field name="sub_district_id" string="Kecamatan"/> <field name="sub_district_id" string="Kecamatan"/>
<field name="typ" string="Jenis"/>
<field name="code" string="Desa/Kelurahan"/> <field name="code" string="Desa/Kelurahan"/>
<field name="display_code" string="Full Kode"/>
<field name="name" string="Nama"/> <field name="name" string="Nama"/>
</group> </group>
</form> </form>
......
Markdown is supported
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!