Commit 7f7e9479 by aa.gusti

BPHTb

1 parent b2869fc2
Showing 65 changed files with 3157 additions and 13 deletions
BPHTB
-----
\ No newline at end of file
# -*- coding: utf-8 -*-
# Part of Odoo. See LICENSE file for full copyright and licensing details.
from . import controllers
from . import models
from . import wizard
from . import report
# -*- coding: utf-8 -*-
# Part of Odoo. See LICENSE file for full copyright and licensing details.
{
'name': 'BPHTB Kab/Kota',
'version': '0.1',
'summary': 'Pajak & Retribusi Kab/Kota',
'sequence': 10,
'description': """
Pajak & Retribusi
=================
Memudahkan dalam mengelola tagihan kepada wajib pajak atau wajib retribusi
""",
'category': 'Indonesia Goverment',
'website': 'https://opensipkd.com',
'images': [],
'depends': ['id_gov'],
'data': [
'security/account_security.xml',
'security/ir.model.access.csv',
'security/ir_rule.xml',
'data/bphtb.jenis.csv',
'data/res_user.xml',
'views/jenis.xml',
'views/partner.xml',
'views/sales.xml',
'views/menus.xml',
'demo/bphtb.sales.csv',
],
'demo': [
'demo/bphtb.sales.csv'
],
'qweb': [],
'installable': True,
'application': True,
'auto_install': False,
'license': 'LGPL-3',
'module': 'pajak'
}
# -*- coding: utf-8 -*-
# Part of Odoo. See LICENSE file for full copyright and licensing details.
from . import onboarding
from . import portal
from odoo import http
from odoo.http import request
class OnboardingController(http.Controller):
@http.route('/pad/pad_invoice_onboarding', auth='user', type='json')
def pad_invoice_onboarding(self):
""" Returns the `banner` for the pad invoice onboarding panel.
It can be empty if the user has closed it or if he doesn't have
the permission to see it. """
company = request.env.company
if not request.env.is_admin() or \
company.pad_invoice_onboarding_state == 'closed':
return {}
return {
'html': request.env.ref('pad.pad_invoice_onboarding_panel')._render({
'company': company,
'state': company.get_and_update_pad_invoice_onboarding_state()
})
}
@http.route('/pad/pad_dashboard_onboarding', auth='user', type='json')
def pad_dashboard_onboarding(self):
""" Returns the `banner` for the pad dashboard onboarding panel.
It can be empty if the user has closed it or if he doesn't have
the permission to see it. """
company = request.env.company
if not request.env.is_admin() or \
company.pad_dashboard_onboarding_state == 'closed':
return {}
return {
'html': request.env.ref('pad.pad_dashboard_onboarding_panel')._render({
'company': company,
'state': company.get_and_update_pad_dashboard_onboarding_state()
})
}
# -*- coding: utf-8 -*-
# Part of Odoo. See LICENSE file for full copyright and licensing details.
from odoo import http, _
from odoo.addons.portal.controllers.portal import CustomerPortal, pager as portal_pager
from odoo.exceptions import AccessError, MissingError
from collections import OrderedDict
from odoo.http import request
class PortalAccount(CustomerPortal):
def _prepare_home_portal_values(self, counters):
values = super()._prepare_home_portal_values(counters)
if 'invoice_count' in counters:
invoice_count = request.env['account.move'].search_count(self._get_pad_domain()) \
if request.env['account.move'].check_access_rights('read', raise_exception=False) else 0
values['invoice_count'] = invoice_count
return values
# ------------------------------------------------------------
# My pad
# ------------------------------------------------------------
def _invoice_get_page_view_values(self, invoice, access_token, **kwargs):
values = {
'page_name': 'invoice',
'invoice': invoice,
}
return self._get_page_view_values(invoice, access_token, values, 'my_pad_history', False, **kwargs)
def _get_pad_domain(self):
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)
def portal_my_pad(self, page=1, date_begin=None, date_end=None, sortby=None, filterby=None, **kw):
values = self._prepare_portal_layout_values()
AccountInvoice = request.env['account.move']
domain = self._get_pad_domain()
searchbar_sortings = {
'date': {'label': _('Date'), 'order': 'invoice_date desc'},
'duedate': {'label': _('Due Date'), 'order': 'invoice_date_due desc'},
'name': {'label': _('Reference'), 'order': 'name desc'},
'state': {'label': _('Status'), 'order': 'state'},
}
# default sort by order
if not sortby:
sortby = 'date'
order = searchbar_sortings[sortby]['order']
searchbar_filters = {
'all': {'label': _('All'), 'domain': []},
'pad': {'label': _('pad'), 'domain': [('move_type', '=', ('out_invoice', 'out_refund'))]},
'bills': {'label': _('Bills'), 'domain': [('move_type', '=', ('in_invoice', 'in_refund'))]},
}
# default filter by value
if not filterby:
filterby = 'all'
domain += searchbar_filters[filterby]['domain']
if date_begin and date_end:
domain += [('create_date', '>', date_begin), ('create_date', '<=', date_end)]
# count for pager
invoice_count = AccountInvoice.search_count(domain)
# pager
pager = portal_pager(
url="/my/pad",
url_args={'date_begin': date_begin, 'date_end': date_end, 'sortby': sortby},
total=invoice_count,
page=page,
step=self._items_per_page
)
# content according to pager and archive selected
pad = AccountInvoice.search(domain, order=order, limit=self._items_per_page, offset=pager['offset'])
request.session['my_pad_history'] = pad.ids[:100]
values.update({
'date': date_begin,
'pad': pad,
'page_name': 'invoice',
'pager': pager,
'default_url': '/my/pad',
'searchbar_sortings': searchbar_sortings,
'sortby': sortby,
'searchbar_filters': OrderedDict(sorted(searchbar_filters.items())),
'filterby': filterby,
})
return request.render("account.portal_my_pad", values)
@http.route(['/my/pad/<int:invoice_id>'], type='http', auth="public", website=True)
def portal_my_invoice_detail(self, invoice_id, access_token=None, report_type=None, download=False, **kw):
try:
invoice_sudo = self._document_check_access('account.move', invoice_id, access_token)
except (AccessError, MissingError):
return request.redirect('/my')
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)
values = self._invoice_get_page_view_values(invoice_sudo, access_token, **kw)
acquirers = values.get('acquirers')
if acquirers:
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)
return request.render("account.portal_invoice_page", values)
# ------------------------------------------------------------
# My Home
# ------------------------------------------------------------
def details_form_validate(self, data):
error, error_message = super(PortalAccount, self).details_form_validate(data)
# prevent VAT/name change if pad exist
partner = request.env['res.users'].browse(request.uid).partner_id
if not partner.can_edit_vat():
if 'vat' in data and (data['vat'] or False) != (partner.vat or False):
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.'))
if 'name' in data and (data['name'] or False) != (partner.name or False):
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.'))
if 'company_name' in data and (data['company_name'] or False) != (partner.company_name or False):
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.'))
return error, error_message
id,code,name,min_omzet,rate,disc,company_id:id,under_value
bphtb_01,01,Jual Beli,60000000,5,0,base.main_company,False
bphtb_02,02,Tukar Menukar,60000000,5,0,base.main_company,False
bphtb_03,03,Hibah,60000000,5,0,base.main_company,False
bphtb_04,04,Hibah Wasiat,300000000,5,50,base.main_company,False
bphtb_05,05,Waris,300000000,5,50,base.main_company,False
bphtb_06,06,Pemasukan dalam Perseroan atau Badan Hukum Lainnya,60000000,5,0,base.main_company,False
bphtb_07,07,Pemisahan Hak yang mengakibatkan Peralihan,60000000,5,0,base.main_company,False
bphtb_08,08,Penunjukan Pembelli dalam Lelang,60000000,5,0,base.main_company,True
bphtb_09,09,Pelaksanaan Putusan Hakim yang mempunyai kekuatan hukum tetap,60000000,5,0,base.main_company,True
bphtb_10,10,Penggabungan Usaha,60000000,5,0,base.main_company,False
bphtb_11,11,Peleburan Usaha,60000000,5,0,base.main_company,False
bphtb_12,12,Pemekaran Usaha,60000000,5,0,base.main_company,False
bphtb_13,13,Hadiah,60000000,5,0,base.main_company,False
bphtb_14,14,Perolehan hak Rumah Sederhana Sehat dan RSS melalui KPR bersubsidi,60000000,5,0,base.main_company,False
bphtb_15,15,Pemberian Hak Baru Kelanjutan Pelepasan Hak,60000000,5,0,base.main_company,False
bphtb_16,16,Pemberian hak baru diluar pelepasan hak,60000000,5,0,base.main_company,False
bphtb_17,17,Tax Amnesti,60000000,5,0,base.main_company,False
bphtb_18,18,Peningkatan Status,0,0,0,base.main_company,False
<?xml version="1.0" encoding="utf-8"?>
<odoo>
<data noupdate="1">
<record id="bphtb_partner_admin_kab_id" model="res.partner">
<field name="name">Admin BPHTB Kab/Kota</field>
<field name="street">Jalan-jalan</field>
<field name="country_id" ref="base.id"/>
<field name="state_id" ref="base.state_id_jb"/>
<field name="district_id" ref="id_gov.ct_jb_71"/>
<field name="company_id" ref="base.main_company"/>
<field name="zip">90241</field>
<field name="phone">+62 812-345-678</field>
<field name="email">info@company.idexample.com</field>
<field name="website">www.idexample.com</field>
</record>
<record id="bphtb_user_admin_kab_id" model="res.users">
<field name="login">bphtb_admin</field>
<field name="password">admin</field>
<field name="partner_id" ref="bphtb_partner_admin_kab_id"/>
<field name="company_id" ref="base.main_company"/>
<field name="company_ids" eval="[(4,ref('base.main_company'))]"/>
<field name="groups_id"
eval="[(6,0,[ref('base.group_user'), ref('bphtb_kab.group_bphtb_admin')])]"/>
</record>
<record id="bphtb_partner_ppat_kab_id" model="res.partner">
<field name="name">PPAT Kab/Kota</field>
<field name="street">Jalan-jalan</field>
<field name="country_id" ref="base.id"/>
<field name="state_id" ref="base.state_id_jb"/>
<field name="district_id" ref="id_gov.ct_jb_71"/>
<field name="company_id" ref="base.main_company"/>
<field name="zip">90241</field>
<field name="type">ppat</field>
<field name="phone">+62 812-345-678</field>
<field name="email">info@company.idexample.com</field>
<field name="website">www.idexample.com</field>
</record>
<record id="bphtb_user_ppat_kab_id" model="res.users">
<field name="login">ppat</field>
<field name="password">ppat</field>
<field name="partner_id" ref="bphtb_partner_ppat_kab_id"/>
<field name="company_id" ref="base.main_company"/>
<field name="company_ids" eval="[(4,ref('base.main_company'))]"/>
<field name="groups_id"
eval="[(6,0,[ref('base.group_user'), ref('bphtb_kab.group_bphtb_ppat')])]"/>
</record>
<record id="bphtb_partner_wp_kab_id" model="res.partner">
<field name="name">Wajib Pajak BPHTB</field>
<field name="street">Jalan-jalan</field>
<field name="country_id" ref="base.id"/>
<field name="state_id" ref="base.state_id_jb"/>
<field name="district_id" ref="id_gov.ct_jb_71"/>
<field name="company_id" ref="base.main_company"/>
<field name="zip">90241</field>
<field name="type">wp</field>
<field name="phone">+62 812-345-678</field>
<field name="email">info@company.idexample.com</field>
<field name="website">www.idexample.com</field>
</record>
<record id="bphtb_partner_penjual_kab_id" model="res.partner">
<field name="name">Wajib Pajak Penjual BPHTB</field>
<field name="street">Jalan-jalan</field>
<field name="country_id" ref="base.id"/>
<field name="state_id" ref="base.state_id_jb"/>
<field name="district_id" ref="id_gov.ct_jb_71"/>
<field name="company_id" ref="base.main_company"/>
<field name="zip">90241</field>
<field name="type">wp</field>
<field name="phone">+62 812-345-678</field>
<field name="email">info@company.idexample.com</field>
<field name="website">www.idexample.com</field>
</record>
</data>
</odoo>
\ No newline at end of file
id,ppat_id:id,wp_id:id,seller_id:id,state,nop,tax_year,luas_bumi,luas_bng,njop_bumi,njop_bng,luas_bumi_bersama,luas_bng_bersama,njop_bumi_bersama,njop_bng_bersama,jenis_id:id,rate,min_omzet,disc_sk,npop,basic,fine,amount,disc_amount,owed,typ,company_id:id,certicate_no
sspd_01,bphtb_partner_ppat_kab_id,bphtb_partner_wp_kab_id,bphtb_partner_penjual_kab_id,draft,367601000100100010,2021,100,50,500000000,250000000,0,0,00,0,bphtb_01,5,60000000,,1000000000,0,0,0,0,0,sspd,base.main_company,-
<?xml version="1.0" encoding="utf-8"?>
<odoo>
<data noupdate="1">
<record id="pdl_partner_company_id" model="res.partner">
<field name="name">Kabupaten/Kota</field>
<field name="street">Jalan-jalan</field>
<field name="country_id" ref="base.id"/>
<field name="state_id" ref="base.state_id_jb"/>
<field name="district_id" ref="id_gov.ct_jb_71"/>
<field name="zip">90241</field>
<field name="phone">+62 812-345-678</field>
<field name="email">info@company.idexample.com</field>
<field name="website">www.idexample.com</field>
</record>
<record id="pdl_company_id" model="res.company">
<field name="name">Kabupaten/Kota Company</field>
<field name="partner_id" ref="pdl_partner_company_id"/>
</record>
</data>
<data>
<function model="res.users" name="write">
<value eval="[ref('base.user_root'), ref('base.user_admin'), ref('base.user_demo')]"/>
<value eval="{'company_ids': [(4, ref('pdl_kab.pdl_company_id'))]}"/>
</function>
</data>
</odoo>
\ No newline at end of file
from . import bphtb_ref
from . import partner
from . import sales
\ No newline at end of file
from sqlite3 import DatabaseError
from odoo import api, fields, models, _
from psycopg2 import sql, DatabaseError
import logging
_logger = logging.getLogger(__name__)
class BphtbJenis(models.Model):
_name = 'bphtb.jenis'
_description = 'Jenis BPHTB'
code = fields.Char(index=True, string='Code')
name = fields.Char(index=True, string='Name', size=64)
rate = fields.Float(required=True, default=5)
disc = fields.Float(required=True, default=0)
min_omzet = fields.Float(required=True, default=60000000)
under_value = fields.Boolean(required=True, default=False)
company_id = fields.Many2one('res.company',
default=lambda self: self.env.company.id
if not self.company_id else False)
_sql_constraints = [
('company_code_uniq', 'unique (company_id, code)', 'Kode harus unik')
]
from odoo import fields, models, api
class ResPartner(models.Model):
_inherit = 'res.partner'
supplier_rank = fields.Integer(default=0)
customer_rank = fields.Integer(default=0)
company_id = fields.Many2one(
'res.company', string="Company",
default=lambda self: self.env.company.id
if not self.company_id else False)
type = fields.Selection(
selection_add=[('ppat', 'PPAT'),
('wp', 'Wajib Pajak')],
ondelete={
'ppat': 'cascade',
'wp': 'cascade',
})
<?xml version="1.0" encoding="utf-8"?>
<odoo>
<data>
<record id="report_product_label" model="ir.actions.report">
<field name="name">Product Label (PDF)</field>
<field name="model">product.product</field>
<field name="report_type">qweb-pdf</field>
<field name="report_name">product.report_productlabel</field>
<field name="report_file">product.report_productlabel</field>
<field name="print_report_name">'Products Labels - %s' % (object.name)</field>
<!-- <field name="binding_model_id" ref="product.model_product_product"/>-->
<field name="binding_type">report</field>
<field name="binding_model_id" eval="False"/>
</record>
<record id="report_product_template_label" model="ir.actions.report">
<field name="name">Product Label (PDF)</field>
<field name="model">product.template</field>
<field name="report_type">qweb-pdf</field>
<field name="report_name">product.report_producttemplatelabel</field>
<field name="report_file">product.report_producttemplatelabel</field>
<field name="print_report_name">'Products Labels - %s' % (object.name)</field>
<field name="binding_model_id" eval="False"/>
<!-- <field name="binding_model_id" ref="product.model_product_template"/>-->
<field name="binding_type">report</field>
</record>
<record id="report_product_product_barcode" model="ir.actions.report">
<field name="name">Product Barcode (PDF)</field>
<field name="model">product.product</field>
<field name="report_type">qweb-pdf</field>
<field name="report_name">product.report_productbarcode</field>
<field name="report_file">product.report_productbarcode</field>
<field name="print_report_name">'Products barcode - %s' % (object.name)</field>
<field name="binding_model_id" eval="False"/>
<!-- <field name="binding_model_id" ref="product.model_product_product"/>-->
<field name="binding_type">report</field>
</record>
<record id="report_product_template_barcode" model="ir.actions.report">
<field name="name">Product Barcode (PDF)</field>
<field name="model">product.template</field>
<field name="report_type">qweb-pdf</field>
<field name="report_name">product.report_producttemplatebarcode</field>
<field name="report_file">product.report_producttemplatebarcode</field>
<field name="print_report_name">'Products barcode - %s' % (object.name)</field>
<field name="binding_model_id" eval="False"/>
<!-- <field name="binding_model_id" ref="product.model_product_template"/>-->
<field name="binding_type">report</field>
</record>
<record id="report_product_packaging" model="ir.actions.report">
<field name="name">Product Packaging (PDF)</field>
<field name="model">product.packaging</field>
<field name="report_type">qweb-pdf</field>
<field name="report_name">product.report_packagingbarcode</field>
<field name="report_file">product.report_packagingbarcode</field>
<field name="print_report_name">'Products packaging - %s' % (object.name)</field>
<field name="binding_model_id" eval="False"/>
<!-- <field name="binding_model_id" ref="product.model_product_packaging"/>-->
<field name="binding_type">report</field>
</record>
<record id="action_report_pricelist" model="ir.actions.report">
<field name="name">Pricelist</field>
<field name="model">product.product</field>
<field name="report_type">qweb-pdf</field>
<field name="report_name">product.report_pricelist</field>
<field name="report_file">product.report_pricelist</field>
<field name="binding_model_id" eval="False"/>
</record>
</data>
</odoo>
<?xml version="1.0" encoding="utf-8"?>
<odoo>
<data noupdate="0">
<record model="ir.module.category" id="bphtb">
<field name="name">BPHTB</field>
<field name="description">BPHTB</field>
<field name="sequence">1</field>
</record>
<record id="group_bphtb_admin" model="res.groups">
<field name="name">Administrator</field>
<field name="category_id" ref="bphtb"/>
</record>
<record id="group_bphtb_ppat" model="res.groups">
<field name="name">PPAT</field>
<field name="category_id" ref="bphtb"/>
</record>
</data>
</odoo>
"id","name","model_id:id","group_id:id","perm_read","perm_write","perm_create","perm_unlink"
"access_bphtb_jenis_admin","access.bphtb.jenis.admin","model_bphtb_jenis","base.group_system",1,1,1,1
"access_bphtb_jenis_bphtb_admin","access.bphtb.jenis.bphtb.admin","model_bphtb_jenis","group_bphtb_admin",1,1,1,1
"access_bphtb_jenis_bphtb_ppat","access.bphtb.jenis.bphtb.ppat","model_bphtb_jenis","group_bphtb_ppat",1,0,0,0
"access_district_bphtb_admin","access.district.bphtb.admin","id_gov.model_res_district","group_bphtb_admin",1,1,1,1
"access_district_sub_bphtb_admin","access.district.sub.bphtb.admin","id_gov.model_res_district_sub","group_bphtb_admin",1,1,1,1
"access_village_bphtb_admin","access.village.bphtb.admin","id_gov.model_res_district_village","group_bphtb_admin",1,1,1,1
"access_jenis_bphtb_admin","access.bphtb.jenis.bphtb.admin","model_bphtb_jenis","group_bphtb_admin",1,1,1,1
"access_bphtb_sales_admin","access.bphtb.sales.admin","model_bphtb_sales","base.group_system",1,1,1,1
"access_bphtb_sales_bphtb_admin","access.bphtb.sales.bphtb.admin","model_bphtb_sales","group_bphtb_admin",1,1,1,1
"access_bphtb_sales_bphtb_ppat","access.bphtb.sales.bphtb.ppt","model_bphtb_sales","group_bphtb_ppat",1,1,1,0
<?xml version="1.0"?>
<odoo>
<data noupdate="0">
</data>
</odoo>
\ No newline at end of file
odoo.define('account.payment', function (require) {
"use strict";
var AbstractField = require('web.AbstractField');
var core = require('web.core');
var field_registry = require('web.field_registry');
var field_utils = require('web.field_utils');
var QWeb = core.qweb;
var _t = core._t;
var ShowPaymentLineWidget = AbstractField.extend({
events: _.extend({
'click .outstanding_credit_assign': '_onOutstandingCreditAssign',
}, AbstractField.prototype.events),
supportedFieldTypes: ['char'],
//--------------------------------------------------------------------------
// Public
//--------------------------------------------------------------------------
/**
* @override
* @returns {boolean}
*/
isSet: function () {
return true;
},
//--------------------------------------------------------------------------
// Private
//--------------------------------------------------------------------------
/**
* @private
* @override
*/
_render: function () {
var self = this;
var info = JSON.parse(this.value);
if (!info) {
this.$el.html('');
return;
}
_.each(info.content, function (k, v) {
k.index = v;
k.amount = field_utils.format.float(k.amount, {digits: k.digits});
if (k.date) {
k.date = field_utils.format.date(field_utils.parse.date(k.date, {}, {isUTC: true}));
}
});
this.$el.html(QWeb.render('ShowPaymentInfo', {
lines: info.content,
outstanding: info.outstanding,
title: info.title
}));
_.each(this.$('.js_payment_info'), function (k, v) {
var isRTL = _t.database.parameters.direction === "rtl";
var content = info.content[v];
var options = {
content: function () {
var $content = $(QWeb.render('PaymentPopOver', content));
var unreconcile_button = $content.filter('.js_unreconcile_payment').on('click', self._onRemoveMoveReconcile.bind(self));
$content.filter('.js_open_payment').on('click', self._onOpenPayment.bind(self));
return $content;
},
html: true,
placement: isRTL ? 'bottom' : 'left',
title: 'Payment Information',
trigger: 'focus',
delay: {"show": 0, "hide": 100},
container: $(k).parent(), // FIXME Ugly, should use the default body container but system & tests to adapt to properly destroy the popover
};
$(k).popover(options);
});
},
//--------------------------------------------------------------------------
// Handlers
//--------------------------------------------------------------------------
/**
* @private
* @override
* @param {MouseEvent} event
*/
_onOpenPayment: function (event) {
var paymentId = parseInt($(event.target).attr('payment-id'));
var moveId = parseInt($(event.target).attr('move-id'));
var res_model;
var id;
if (paymentId !== undefined && !isNaN(paymentId)) {
res_model = "account.payment";
id = paymentId;
} else if (moveId !== undefined && !isNaN(moveId)) {
res_model = "account.move";
id = moveId;
}
//Open form view of account.move with id = move_id
if (res_model && id) {
this.do_action({
type: 'ir.actions.act_window',
res_model: res_model,
res_id: id,
views: [[false, 'form']],
target: 'current'
});
}
},
/**
* @private
* @override
* @param {MouseEvent} event
*/
_onOutstandingCreditAssign: function (event) {
event.stopPropagation();
event.preventDefault();
var self = this;
var id = $(event.target).data('id') || false;
this._rpc({
model: 'account.move',
method: 'js_assign_outstanding_line',
args: [JSON.parse(this.value).move_id, id],
}).then(function () {
self.trigger_up('reload');
});
},
/**
* @private
* @override
* @param {MouseEvent} event
*/
_onRemoveMoveReconcile: function (event) {
var self = this;
var moveId = parseInt($(event.target).attr('move-id'));
var partialId = parseInt($(event.target).attr('partial-id'));
if (partialId !== undefined && !isNaN(partialId)) {
this._rpc({
model: 'account.move',
method: 'js_remove_outstanding_partial',
args: [moveId, partialId],
}).then(function () {
self.trigger_up('reload');
});
}
},
});
field_registry.add('payment', ShowPaymentLineWidget);
return {
ShowPaymentLineWidget: ShowPaymentLineWidget
};
});
odoo.define('account.AccountPortalSidebar', function (require) {
'use strict';
const dom = require('web.dom');
var publicWidget = require('web.public.widget');
var PortalSidebar = require('portal.PortalSidebar');
var utils = require('web.utils');
publicWidget.registry.AccountPortalSidebar = PortalSidebar.extend({
selector: '.o_portal_invoice_sidebar',
events: {
'click .o_portal_invoice_print': '_onPrintInvoice',
},
/**
* @override
*/
start: function () {
var def = this._super.apply(this, arguments);
var $invoiceHtml = this.$el.find('iframe#invoice_html');
var updateIframeSize = this._updateIframeSize.bind(this, $invoiceHtml);
$(window).on('resize', updateIframeSize);
var iframeDoc = $invoiceHtml[0].contentDocument || $invoiceHtml[0].contentWindow.document;
if (iframeDoc.readyState === 'complete') {
updateIframeSize();
} else {
$invoiceHtml.on('load', updateIframeSize);
}
return def;
},
//--------------------------------------------------------------------------
// Handlers
//--------------------------------------------------------------------------
/**
* 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.
*
* @private
* @param {object} $el: the iframe
*/
_updateIframeSize: function ($el) {
var $wrapwrap = $el.contents().find('div#wrapwrap');
// Set it to 0 first to handle the case where scrollHeight is too big for its content.
$el.height(0);
$el.height($wrapwrap[0].scrollHeight);
// scroll to the right place after iframe resize
if (!utils.isValidAnchor(window.location.hash)) {
return;
}
var $target = $(window.location.hash);
if (!$target.length) {
return;
}
dom.scrollTo($target[0], {duration: 0});
},
/**
* @private
* @param {MouseEvent} ev
*/
_onPrintInvoice: function (ev) {
ev.preventDefault();
var href = $(ev.currentTarget).attr('href');
this._printIframeContent(href);
},
});
});
odoo.define('account.ShowResequenceRenderer', function (require) {
"use strict";
const {Component} = owl;
const {useState} = owl.hooks;
const AbstractFieldOwl = require('web.AbstractFieldOwl');
const field_registry = require('web.field_registry_owl');
class ChangeLine extends Component {
}
ChangeLine.template = 'account.ResequenceChangeLine';
ChangeLine.props = ["changeLine", 'ordering'];
class ShowResequenceRenderer extends AbstractFieldOwl {
constructor(...args) {
super(...args);
this.data = this.value ? JSON.parse(this.value) : {
changeLines: [],
ordering: 'date',
};
}
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);
return ShowResequenceRenderer;
});
odoo.define('account.hierarchy.selection', function (require) {
"use strict";
var core = require('web.core');
var relational_fields = require('web.relational_fields');
var _t = core._t;
var registry = require('web.field_registry');
var FieldSelection = relational_fields.FieldSelection;
var qweb = core.qweb;
var HierarchySelection = FieldSelection.extend({
_renderEdit: function () {
var self = this;
var prom = Promise.resolve()
if (!self.hierarchy_groups) {
prom = this._rpc({
model: 'account.account.type',
method: 'search_read',
kwargs: {
domain: [],
fields: ['id', 'internal_group', 'display_name'],
},
}).then(function (arg) {
self.values = _.map(arg, v => [v['id'], v['display_name']])
self.hierarchy_groups = [
{
'name': _t('Balance Sheet'),
'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('Equity'),
'ids': _.map(_.filter(arg, v => v['internal_group'] == 'equity'), v => v['id'])
},
],
},
{
'name': _t('Profit & Loss'),
'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('Other'),
'ids': _.map(_.filter(arg, v => !['asset', 'liability', 'equity', 'income', 'expense'].includes(v['internal_group'])), v => v['id'])
},
]
});
}
Promise.resolve(prom).then(function () {
self.$el.empty();
self._addHierarchy(self.$el, self.hierarchy_groups, 0);
var value = self.value;
if (self.field.type === 'many2one' && value) {
value = value.data.id;
}
self.$el.val(JSON.stringify(value));
});
},
_addHierarchy: function (el, group, level) {
var self = this;
_.each(group, function (item) {
var optgroup = $('<optgroup/>').attr(({
'label': $('<div/>').html('&nbsp;'.repeat(6 * level) + item['name']).text(),
}))
_.each(item['ids'], function (id) {
var value = _.find(self.values, v => v[0] == id)
optgroup.append($('<option/>', {
value: JSON.stringify(value[0]),
text: value[1],
}));
})
el.append(optgroup)
if (item['children']) {
self._addHierarchy(el, item['children'], level + 1);
}
})
}
});
registry.add("account_hierarchy_selection", HierarchySelection);
});
odoo.define('account.bank_statement', function (require) {
"use strict";
var KanbanController = require("web.KanbanController");
var ListController = require("web.ListController");
var includeDict = {
renderButtons: function () {
this._super.apply(this, arguments);
if (this.modelName === "account.bank.statement") {
var data = this.model.get(this.handle);
if (data.context.journal_type !== 'cash') {
this.$buttons.find('button.o_button_import').hide();
}
}
}
};
KanbanController.include(includeDict);
ListController.include(includeDict);
});
\ No newline at end of file
odoo.define('account.upload.bill.mixin', function (require) {
"use strict";
var core = require('web.core');
var _t = core._t;
var qweb = core.qweb;
var UploadBillMixin = {
start: function () {
// define a unique uploadId and a callback method
this.fileUploadID = _.uniqueId('account_bill_file_upload');
$(window).on(this.fileUploadID, this._onFileUploaded.bind(this));
return this._super.apply(this, arguments);
},
_onAddAttachment: function (ev) {
// Auto submit form once we've selected an attachment
var $input = $(ev.currentTarget).find('input.o_input_file');
if ($input.val() !== '') {
var $binaryForm = this.$('.o_vendor_bill_upload form.o_form_binary_form');
$binaryForm.submit();
}
},
_onFileUploaded: function () {
// Callback once attachment have been created, create a bill with attachment ids
var self = this;
var attachments = Array.prototype.slice.call(arguments, 1);
// Get id from result
var attachent_ids = attachments.reduce(function (filtered, record) {
if (record.id) {
filtered.push(record.id);
}
return filtered;
}, []);
return this._rpc({
model: 'account.journal',
method: 'create_invoice_from_attachment',
args: ["", attachent_ids],
context: this.initialState.context,
}).then(function (result) {
self.do_action(result);
});
},
_onUpload: function (event) {
var self = this;
// If hidden upload form don't exists, create it
var $formContainer = this.$('.o_content').find('.o_vendor_bill_upload');
if (!$formContainer.length) {
$formContainer = $(qweb.render('account.BillsHiddenUploadForm', {widget: this}));
$formContainer.appendTo(this.$('.o_content'));
}
// Trigger the input to select a file
this.$('.o_vendor_bill_upload .o_input_file').click();
},
}
return UploadBillMixin;
});
odoo.define('account.bills.tree', function (require) {
"use strict";
var core = require('web.core');
var ListController = require('web.ListController');
var ListView = require('web.ListView');
var UploadBillMixin = require('account.upload.bill.mixin');
var viewRegistry = require('web.view_registry');
var BillsListController = ListController.extend(UploadBillMixin, {
buttons_template: 'BillsListView.buttons',
events: _.extend({}, ListController.prototype.events, {
'click .o_button_upload_bill': '_onUpload',
'change .o_vendor_bill_upload .o_form_binary_form': '_onAddAttachment',
}),
});
var BillsListView = ListView.extend({
config: _.extend({}, ListView.prototype.config, {
Controller: BillsListController,
}),
});
viewRegistry.add('account_tree', BillsListView);
});
odoo.define('account.dashboard.kanban', function (require) {
"use strict";
var core = require('web.core');
var KanbanController = require('web.KanbanController');
var KanbanView = require('web.KanbanView');
var UploadBillMixin = require('account.upload.bill.mixin');
var viewRegistry = require('web.view_registry');
var DashboardKanbanController = KanbanController.extend(UploadBillMixin, {
events: _.extend({}, KanbanController.prototype.events, {
'click .o_button_upload_bill': '_onUpload',
'change .o_vendor_bill_upload .o_form_binary_form': '_onAddAttachment',
}),
/**
* We override _onUpload (from the upload bill mixin) to pass default_journal_id
* and default_move_type in context.
*
* @override
*/
_onUpload: function (event) {
var kanbanRecord = $(event.currentTarget).closest('.o_kanban_record').data('record');
this.initialState.context['default_journal_id'] = kanbanRecord.id;
if ($(event.currentTarget).attr('journal_type') == 'sale') {
this.initialState.context['default_move_type'] = 'out_invoice'
} else if ($(event.currentTarget).attr('journal_type') == 'purchase') {
this.initialState.context['default_move_type'] = 'in_invoice'
}
UploadBillMixin._onUpload.apply(this, arguments);
}
});
var DashboardKanbanView = KanbanView.extend({
config: _.extend({}, KanbanView.prototype.config, {
Controller: DashboardKanbanController,
}),
});
viewRegistry.add('account_dashboard_kanban', DashboardKanbanView);
});
odoo.define('account.ShowGroupedList', function (require) {
"use strict";
const {Component} = owl;
const {useState} = owl.hooks;
const AbstractFieldOwl = require('web.AbstractFieldOwl');
const field_registry = require('web.field_registry_owl');
class ListItem extends Component {
}
ListItem.template = 'account.GroupedItemTemplate';
ListItem.props = ["item_vals", "options"];
class ListGroup extends Component {
}
ListGroup.template = 'account.GroupedItemsTemplate';
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);
return ShowGroupedList;
});
odoo.define('account.activity', function (require) {
"use strict";
var AbstractField = require('web.AbstractField');
var core = require('web.core');
var field_registry = require('web.field_registry');
var QWeb = core.qweb;
var _t = core._t;
var VatActivity = AbstractField.extend({
className: 'o_journal_activity_kanban',
events: {
'click .see_all_activities': '_onOpenAll',
'click .see_activity': '_onOpenActivity',
},
init: function () {
this.MAX_ACTIVITY_DISPLAY = 5;
this._super.apply(this, arguments);
},
//------------------------------------------------------------
// Private
//------------------------------------------------------------
_render: function () {
var self = this;
var info = JSON.parse(this.value);
if (!info) {
this.$el.html('');
return;
}
info.more_activities = false;
if (info.activities.length > this.MAX_ACTIVITY_DISPLAY) {
info.more_activities = true;
info.activities = info.activities.slice(0, this.MAX_ACTIVITY_DISPLAY);
}
this.$el.html(QWeb.render('accountJournalDashboardActivity', info));
},
_onOpenActivity: function (e) {
e.preventDefault();
var self = this;
self.do_action({
type: 'ir.actions.act_window',
name: _t('Journal Entry'),
target: 'current',
res_id: $(e.target).data('resId'),
res_model: 'account.move',
views: [[false, 'form']],
});
},
_onOpenAll: function (e) {
e.preventDefault();
var self = this;
self.do_action({
type: 'ir.actions.act_window',
name: _t('Journal Entries'),
res_model: 'account.move',
views: [[false, 'kanban'], [false, 'form']],
search_view_id: [false],
domain: [['journal_id', '=', self.res_id], ['activity_ids', '!=', false]],
});
}
})
field_registry.add('kanban_vat_activity', VatActivity);
return VatActivity;
});
odoo.define('account.section_and_note_backend', function (require) {
// The goal of this file is to contain JS hacks related to allowing
// section and note on sale order and invoice.
// [UPDATED] now also allows configuring products on sale order.
"use strict";
var FieldChar = require('web.basic_fields').FieldChar;
var FieldOne2Many = require('web.relational_fields').FieldOne2Many;
var fieldRegistry = require('web.field_registry');
var ListFieldText = require('web.basic_fields').ListFieldText;
var ListRenderer = require('web.ListRenderer');
var SectionAndNoteListRenderer = ListRenderer.extend({
/**
* We want section and note to take the whole line (except handle and trash)
* to look better and to hide the unnecessary fields.
*
* @override
*/
_renderBodyCell: function (record, node, index, options) {
var $cell = this._super.apply(this, arguments);
var isSection = record.data.display_type === 'line_section';
var isNote = record.data.display_type === 'line_note';
if (isSection || isNote) {
if (node.attrs.widget === "handle") {
return $cell;
} else if (node.attrs.name === "name") {
var nbrColumns = this._getNumberOfCols();
if (this.handleField) {
nbrColumns--;
}
if (this.addTrashIcon) {
nbrColumns--;
}
$cell.attr('colspan', nbrColumns);
} else {
$cell.removeClass('o_invisible_modifier');
return $cell.addClass('o_hidden');
}
}
return $cell;
},
/**
* We add the o_is_{display_type} class to allow custom behaviour both in JS and CSS.
*
* @override
*/
_renderRow: function (record, index) {
var $row = this._super.apply(this, arguments);
if (record.data.display_type) {
$row.addClass('o_is_' + record.data.display_type);
}
return $row;
},
/**
* We want to add .o_section_and_note_list_view on the table to have stronger CSS.
*
* @override
* @private
*/
_renderView: function () {
var self = this;
return this._super.apply(this, arguments).then(function () {
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:
// to be sure this custom code will only impact selected fields having the widget
// and not applied to any other existing ListRenderer.
var SectionAndNoteFieldOne2Many = FieldOne2Many.extend({
/**
* We want to use our custom renderer for the list.
*
* @override
*/
_getRenderer: function () {
if (this.view.arch.tag === 'tree') {
return SectionAndNoteListRenderer;
}
return this._super.apply(this, arguments);
},
});
// This is a merge between a FieldText and a FieldChar.
// We want a FieldChar for section,
// and a FieldText for the rest (product and note).
var SectionAndNoteFieldText = function (parent, name, record, options) {
var isSection = record.data.display_type === 'line_section';
var Constructor = isSection ? FieldChar : ListFieldText;
return new Constructor(parent, name, record, options);
};
fieldRegistry.add('section_and_note_one2many', SectionAndNoteFieldOne2Many);
fieldRegistry.add('section_and_note_text', SectionAndNoteFieldText);
return SectionAndNoteListRenderer;
});
odoo.define('account.tax_group', function (require) {
"use strict";
var core = require('web.core');
var session = require('web.session');
var fieldRegistry = require('web.field_registry');
var AbstractField = require('web.AbstractField');
var fieldUtils = require('web.field_utils');
var QWeb = core.qweb;
var TaxGroupCustomField = AbstractField.extend({
events: {
'click .tax_group_edit': '_onClick',
'keydown .oe_tax_group_editable .tax_group_edit_input input': '_onKeydown',
'blur .oe_tax_group_editable .tax_group_edit_input input': '_onBlur',
},
//--------------------------------------------------------------------------
// Private
//--------------------------------------------------------------------------
/**
* This method is called by "_setTaxGroups". It is
* responsible for calculating taxes based on
* tax groups and triggering an event to
* notify the ORM of a change.
*
* @param {Id} taxGroupId
* @param {Float} deltaAmount
*/
_changeTaxValueByTaxGroup: function (taxGroupId, deltaAmount) {
const self = this;
// Search for the first tax line with the same tax group and modify its value
function applyChange(line_id) {
let debitAmount = 0;
let creditAmount = 0;
let amount_currency = 0;
if (line_id.data.currency_id) { // If multi currency enable
if (self.record.data.move_type === "in_invoice") {
amount_currency = line_id.data.amount_currency - deltaAmount;
} else {
amount_currency = line_id.data.amount_currency + deltaAmount;
}
} else {
let balance = line_id.data.price_subtotal;
balance -= deltaAmount;
if (self.record.data.move_type === "in_invoice") { // For vendor bill
if (balance > 0) {
debitAmount = balance;
} else if (balance < 0) {
creditAmount = -balance;
}
} else { // For refund
if (balance > 0) {
creditAmount = balance;
} else if (balance < 0) {
debitAmount = -balance;
}
}
}
// Trigger ORM
self.trigger_up('field_changed', {
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
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);
if (line_id) {
applyChange(line_id);
} else {
const {limit, id, count} = self.record.data.line_ids;
let offset = count - limit;
self.trigger_up('load', {
id,
limit,
offset,
on_success: value => {
line_id = value.data.find(elem => elem.data.tax_group_id && elem.data.tax_group_id.data.id === taxGroupId);
applyChange(line_id);
},
});
}
},
/**
* This method checks that the document where the widget
* is located is of the "in_invoice" or "in_refund" type.
* This makes it possible to know if it is a purchase
* document.
*
* @returns boolean (true if the invoice is a purchase document)
*/
_isPurchaseDocument: function () {
return this.record.data.move_type === "in_invoice" || this.record.data.move_type === 'in_refund';
},
/**
* This method is part of the widget life cycle and allows you to render
* the widget.
*
* @private
* @override
*/
_render: function () {
var self = this;
// Display the pencil and allow the event to click and edit only on purchase that are not posted and in edit mode.
// since the field is readonly its mode will always be readonly. Therefore we have to use a trick by checking the
// formRenderer (the parent) and check if it is in edit in order to know the correct mode.
var displayEditWidget = self._isPurchaseDocument() && this.record.data.state === 'draft' && this.getParent().mode === 'edit';
this.$el.html($(QWeb.render('AccountTaxGroupTemplate', {
lines: self.value,
displayEditWidget: displayEditWidget,
})));
},
//--------------------------------------------------------------------------
// Handler
//--------------------------------------------------------------------------
/**
* This method is called when the user is in edit mode and
* leaves the <input> field. Then, we execute the code that
* modifies the information.
*
* @param {event} ev
*/
_onBlur: function (ev) {
ev.preventDefault();
var $input = $(ev.target);
var newValue = $input.val();
var currency = session.get_currency(this.record.data.currency_id.data.id);
try {
newValue = fieldUtils.parse.float(newValue); // Need a float for format the value.
newValue = fieldUtils.format.float(newValue, null, {digits: currency.digits}); // return a string rounded to currency precision
newValue = fieldUtils.parse.float(newValue); // convert back to Float to compare with oldValue to know if value has changed
} catch (err) {
$input.addClass('o_field_invalid');
return;
}
var oldValue = $input.data('originalValue');
if (newValue === oldValue || newValue === 0) {
return this._render();
}
var taxGroupId = $input.parents('.oe_tax_group_editable').data('taxGroupId');
this._changeTaxValueByTaxGroup(taxGroupId, oldValue - newValue);
},
/**
* 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.
*
* @param {event} ev
*/
_onClick: function (ev) {
ev.preventDefault();
var $taxGroupElement = $(ev.target).parents('.oe_tax_group_editable');
// Show input and hide previous element
$taxGroupElement.find('.tax_group_edit').addClass('d-none');
$taxGroupElement.find('.tax_group_edit_input').removeClass('d-none');
var $input = $taxGroupElement.find('.tax_group_edit_input input');
// Get original value and display it in user locale in the input
var formatedOriginalValue = fieldUtils.format.float($input.data('originalValue'), {}, {});
$input.focus(); // Focus the input
$input.val(formatedOriginalValue); //add value in user locale to the input
},
/**
* 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,
* the code that modifies the information is executed.
*
* @param {event} ev
*/
_onKeydown: function (ev) {
switch (ev.which) {
// Trigger only if the user clicks on ENTER or on TAB.
case $.ui.keyCode.ENTER:
case $.ui.keyCode.TAB:
// trigger blur to prevent the code being executed twice
$(ev.target).blur();
}
},
});
fieldRegistry.add('tax-group-custom-field', TaxGroupCustomField)
});
odoo.define('account.tour', function (require) {
"use strict";
var core = require('web.core');
var tour = require('web_tour.tour');
var _t = core._t;
tour.register('account_tour', {
url: "/web",
sequence: 60,
}, [
...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]",
content: _t("Start by checking your company's data."),
position: "bottom",
}, {
trigger: "button[name=action_save_onboarding_company_step]",
extra_trigger: "a.o_onboarding_step_action[data-method=action_open_base_onboarding_company]",
content: _t("Looks good. Let's continue."),
position: "bottom",
}, {
trigger: "a.o_onboarding_step_action[data-method=action_open_base_document_layout]",
content: _t("Customize your layout."),
position: "bottom",
}, {
trigger: "button[name=document_layout_save]",
extra_trigger: "a.o_onboarding_step_action[data-method=action_open_base_document_layout]",
content: _t("Once everything is as you want it, validate."),
position: "top",
}, {
trigger: "a.o_onboarding_step_action[data-method=action_open_account_onboarding_create_invoice]",
content: _t("Now, we'll create your first invoice."),
position: "bottom",
}, {
trigger: "div[name=partner_id] input",
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>."),
position: "bottom",
}, {
trigger: ".o_m2o_dropdown_option a:contains('Create')",
extra_trigger: "[name=move_type][raw-value=out_invoice]",
content: _t("Select first partner"),
auto: true,
}, {
trigger: ".modal-content button.btn-primary",
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."),
auto: true,
}, {
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]",
content: _t("Add a line to your invoice"),
}, {
trigger: "div[name=invoice_line_ids] textarea[name=name]",
extra_trigger: "[name=move_type][raw-value=out_invoice]",
content: _t("Fill in the details of the line."),
position: "bottom",
}, {
trigger: "div[name=invoice_line_ids] input[name=price_unit]",
extra_trigger: "[name=move_type][raw-value=out_invoice]",
content: _t("Set a price"),
position: "bottom",
run: 'text 100',
}, {
trigger: "button[name=action_post]",
extra_trigger: "[name=move_type][raw-value=out_invoice]",
content: _t("Once your invoice is ready, press CONFIRM."),
}, {
trigger: "button[name=action_invoice_sent]",
extra_trigger: "[name=move_type][raw-value=out_invoice]",
content: _t("Send the invoice and check what the customer will receive."),
}, {
trigger: "input[name=email]",
extra_trigger: "[name=move_type][raw-value=out_invoice]",
content: _t("Write here <b>your own email address</b> to test the flow."),
run: 'text customer@example.com',
auto: true,
}, {
trigger: ".modal-content button.btn-primary",
extra_trigger: "[name=move_type][raw-value=out_invoice]",
content: _t("Validate."),
auto: true,
}, {
trigger: "button[name=send_and_print_action]",
extra_trigger: "[name=move_type][raw-value=out_invoice]",
content: _t("Let's send the invoice."),
position: "top"
}
]);
});
.o_journal_activity_kanban {
display: block;
.align_activity_center {
width: 100%;
align-items: center;
margin-bottom: 5px;
}
}
\ No newline at end of file
.o_kanban_view.o_kanban_dashboard.o_account_kanban {
&.o_kanban_ungrouped .o_account_dashboard_header {
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;
}
h4 {
font-size: $font-size-base;
font-weight: 500;
}
.fa-gift {
color: #eeeeee;
&:hover {
color: #555555;
}
}
.o_arrow_button.btn-secondary {
color: $text-muted;
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');
}
}
}
}
.o_kanban_view.o_kanban_dashboard.o_account_kanban {
.o_kanban_record {
@include media-breakpoint-up(sm) {
.oe_kanban_action_button {
display: block;
margin-bottom: 5px;
}
}
.o_kanban_card_settings {
padding-top: $o-horizontal-padding/2;
padding-bottom: $o-horizontal-padding/2;
border-top: 1px solid;
border-color: $o-brand-lightsecondary;
}
.o_dashboard_star {
font-size: 12px;
&.fa-star-o {
color: $o-main-color-muted;
&:hover {
color: gold;
}
}
&.fa-star {
color: gold;
}
}
.o_dashboard_graph {
margin-bottom: -$o-horizontal-padding/2;
}
}
&.o_kanban_ungrouped {
.o_kanban_record {
width: 450px;
}
}
.o_kanban_group {
&:not(.o_column_folded) {
width: 450px + 2*$o-kanban-group-padding;
@include media-breakpoint-down(sm) {
width: 100%;
}
}
}
}
// Style for the widget "dashboard_graph"
.o_dashboard_graph {
position: relative;
margin: 16px -16px;
canvas {
height: 75px;
}
}
.o_sample_data .o_dashboard_graph.o_graph_linechart > svg g.nv-linesWrap g.nv-group.nv-series-0 {
fill: gray !important;
opacity: 0.1;
}
.progress-reconciliation {
.progress-bar {
font-size: 1.08333333rem;
height: 14px;
background-color: $o-enterprise-color;
span {
display: contents;
}
}
}
.o_reconciliation {
.o_filter_input_wrapper {
position: relative;
width: 150px;
margin: 0.5rem !important;
.searchIcon {
position: absolute;
right: 10px;
}
.o_filter_input {
border: none;
border-bottom: 1px black solid;
}
}
.import_to_suspense {
margin: 0.5rem !important;
}
.notification_area {
clear: both;
}
.o_view_noreconciliation {
max-width: none;
padding: 0 10%;
color: $o-main-color-muted;
font-size: 125%;
}
.accounting_view {
width: 100%;
.cell_left {
border-right: 1px solid #333;
padding-right: 5px;
}
.edit_amount {
margin-left: 20px;
color: #bbb;
}
.cell:hover .edit_amount {
color: #00A09D;
}
.strike_amount {
text-decoration: line-through;
}
tbody tr:hover .cell_account_code::before {
content: "\f068";
font-family: FontAwesome;
position: relative;
margin-left: -17px;
left: -4px;
line-height: 0;
padding: 3px 2px 5px 5px;
}
}
.o_multi_currency {
margin-right: 5px;
&.o_multi_currency_color_0 {
color: #dd6666;
}
&.o_multi_currency_color_1 {
color: #aaaaaa;
}
&.o_multi_currency_color_2 {
color: #66dd66;
}
&.o_multi_currency_color_3 {
color: #6666dd;
}
&.o_multi_currency_color_4 {
color: #dddd66;
}
&.o_multi_currency_color_5 {
color: #dd66dd;
}
&.o_multi_currency_color_6 {
color: #66dddd;
}
&.o_multi_currency_color_7 {
color: #aaa333;
}
}
.o_reconciliation_line {
margin-bottom: 30px;
table {
width: 100%;
vertical-align: top;
}
tbody tr {
cursor: pointer;
}
tr.already_reconciled {
color: $o-account-info-color;
}
tr.invalid {
text-decoration: line-through;
}
td {
padding: 1px 2px;
}
thead td {
border-top: $o-account-light-border;
padding-top: 4px;
padding-bottom: 5px;
background-color: $o-account-initial-line-background;
}
tfoot td {
color: #bbb;
}
/* columns */
.cell_action {
width: 15px;
color: gray('700');
background: #fff;
border: 0;
text-align: center;
.fa-add-remove:before {
content: "";
}
}
tr:hover .cell_action .fa-add-remove:before {
content: "\f068";
}
.is_tax .cell_action .fa-add-remove:before {
position: relative;
top: -18px;
}
.cell_account_code {
width: 80px;
padding-left: 5px;
}
.cell_due_date {
width: 100px;
}
.cell_label {
width: auto;
}
.cell_left {
padding-right: 5px;
}
.cell_right, .cell_left {
text-align: right;
width: 120px;
}
.cell_info_popover {
text-align: right;
width: 15px;
color: #ccc;
&:empty {
padding: 0;
width: 0;
}
}
table.accounting_view {
.cell_right, .cell_left, .cell_label, .cell_due_date, .cell_account_code, .cell_info_popover {
box-shadow: 0 1px 0 #EAEAEA;
}
}
/* info popover */
.popover {
max-width: none;
}
table.details {
vertical-align: top;
td:first-child {
vertical-align: top;
padding-right: 10px;
font-weight: bold;
}
}
tr.one_line_info {
td {
padding-top: 10px;
text-align: center;
color: $o-account-info-color;
}
}
/* Icons */
.toggle_match, .toggle_create {
transform: rotate(0deg);
transition: transform 300ms ease 0s;
}
.visible_toggle, &[data-mode="match"] .toggle_match, &[data-mode="create"] .toggle_create {
visibility: visible !important;
transform: rotate(90deg);
}
.toggle_create {
font-size: 10px;
}
/* Match view & Create view */
> .o_notebook {
display: none;
> .o_notebook_headers {
margin-right: 0;
margin-left: 0;
}
}
> .o_notebook > .tab-content > div {
border: 1px solid #ddd;
border-top: 0;
}
> .o_notebook .match table tr:hover {
background-color: #eee;
}
&:not([data-mode="inactive"]) > .o_notebook {
display: block;
}
&:not(:focus-within) .o_web_accesskey_overlay {
display: none;
}
&:focus caption .o_buttons button {
outline: none;
box-shadow: 4px 4px 4px 0px $o-enterprise-color;
}
&:focus {
outline: none;
box-shadow: 0 0 0 0;
}
}
.o_reconcile_models .btn-primary {
margin: 0 2px 3px 0;
}
/* Match view */
.match {
.cell_action .fa-add-remove:before {
content: "";
}
tr:hover .cell_action .fa-add-remove:before {
content: "\f067";
}
.match_controls {
padding: 5px 0 5px ($o-account-action-col-width+$o-account-main-table-borders-padding);
.filter {
width: 240px;
display: inline-block;
}
.fa-chevron-left, .fa-chevron-right {
display: inline-block;
cursor: pointer;
}
.fa-chevron-left {
margin-right: 10px;
}
.fa-chevron-left.disabled, .fa-chevron-right.disabled {
color: #ddd;
cursor: default;
}
}
.show_more {
display: inline-block;
margin-left: ($o-account-action-col-width+$o-account-main-table-borders-padding);
margin-top: 5px;
}
}
/* Create view */
.create {
> div > div.quick_add > .o_reconcile_models {
max-width: 100%;
max-height: 70px;
flex-wrap: wrap;
overflow: auto;
& > * {
flex-grow: 0;
}
}
.quick_add {
margin-bottom: 7px;
padding: 0 8px;
}
.o_group table.o_group_col_6 {
width: 49%;
margin: 0;
vertical-align: top;
}
.o_group table.o_group_col_6:first-child {
margin-left: 8px;
}
.btn {
padding-top: 0;
padding-bottom: 0;
}
.add_line_container {
text-align: center;
clear: both;
color: $o-enterprise-primary-color;
cursor: pointer;
}
}
.o_notebook .tab-content > .tab-pane {
padding: 5px 0;
}
}
/*Manual Reconciliation*/
.o_manual_statement {
.accounting_view {
td[colspan="3"] span:first-child {
width: 100%;
display: inline-block;
}
td[colspan="2"] {
border-bottom: 1px solid #333;
text-align: center;
width: 240px;
}
.do_partial_reconcile_true {
display: none;
}
}
}
// This is rtl language specific fix
// It will flip the fa-fa play icon in left direction
.o_rtl {
.o_reconciliation {
.o_reconciliation_line {
.toggle_match, .toggle_create {
transform: rotate(180deg);
transition: transform 300ms;
}
.visible_toggle, &[data-mode="match"] .toggle_match, &[data-mode="create"] .toggle_create {
transform: rotate(270deg);
}
}
}
}
.o_search_panel.account_root {
flex: 0 0 50px;
padding: 6px;
scrollbar-width: thin;
.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;
}
.o_search_panel_category_value .o_toggle_fold {
width: 0.3rem;
}
}
&::-webkit-scrollbar {
width: 4px;
}
&::-webkit-scrollbar-thumb {
background: lightgray;
}
}
// The goal of this file is to contain CSS hacks related to allowing
// 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 textarea[name="name"],
div.oe_kanban_card.o_is_line_note {
font-style: italic;
}
table.o_section_and_note_list_view tr.o_data_row.o_is_line_section,
div.oe_kanban_card.o_is_line_section {
font-weight: bold;
background-color: #DDDDDD;
}
table.o_section_and_note_list_view tr.o_data_row.o_is_line_section {
border-top: 1px solid #BBB;
border-bottom: 1px solid #BBB;
}
table.o_section_and_note_list_view tr.o_data_row {
&.o_is_line_note,
&.o_is_line_section {
td {
// There is an undeterministic CSS behaviour in Chrome related to
// the combination of the row's and its children's borders.
border: none !important;
}
}
}
$o-account-action-col-width: 15px;
$o-account-main-table-borders-padding: 3px;
$o-account-light-border: 1px solid #bbb;
$o-account-initial-line-background: #f0f0f0;
$o-account-info-color: #44c;
@keyframes animate-red {
0% {
color: red;
}
100% {
color: inherit;
}
}
.animate {
animation: animate-red 1s ease;
}
<?xml version="1.0" encoding="UTF-8"?>
<templates>
<t t-name="accountJournalDashboardActivity">
<t t-foreach="activities" t-as="activity">
<div class="row">
<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">
<t t-esc="activity.name"/>
</a>
</div>
<div class="col-4 text-right">
<span>
<t t-esc="activity.date"/>
</span>
</div>
</div>
</t>
<a t-if="more_activities" class="pull-right see_all_activities" href="#">See all activities</a>
</t>
</templates>
<?xml version="1.0" encoding="UTF-8"?>
<templates xml:space="preserve">
<t t-name="ShowPaymentInfo">
<div>
<t t-if="outstanding">
<div>
<strong class="float-left" id="outstanding"><t t-esc="title"></t></strong>
</div>
</t>
<table style="width:100%;">
<t t-foreach="lines" t-as="line">
<tr>
<t t-if="outstanding">
<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>
</td>
<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>
</td>
</t>
<t t-if="!outstanding">
<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>
</td>
<td>
<i class="o_field_widget text-right o_payment_label">Paid on <t t-esc="line.date"></t></i>
</td>
</t>
<td style="text-align:right;">
<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-esc="line.currency"/>
</t>
<t t-esc="line.amount"></t>
<t t-if="line.position === 'after'">
<t t-esc="line.currency"/>
</t>
</span>
</td>
</tr>
</t>
</table>
</div>
</t>
<t t-name="PaymentPopOver">
<div>
<table>
<tr>
<td><strong>Amount: </strong></td>
<td>
<t t-if="position === 'before'">
<t t-esc="currency"/>
</t>
<t t-esc="amount"></t>
<t t-if="position === 'after'">
<t t-esc="currency"/>
</t>
</td>
</tr>
<tr>
<td><strong>Memo: </strong></td>
<td>
<div style="width: 200px; word-wrap: break-word">
<t t-esc="ref"/>
</div>
</td>
</tr>
<tr>
<td><strong>Date: </strong></td>
<td><t t-esc="date"/></td>
</tr>
<tr>
<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>
</tr>
</table>
</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-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>
</templates>
<?xml version="1.0" encoding="utf-8"?>
<templates>
<div t-name="account.ResequenceRenderer" owl="1" class="d-block">
<table t-if="data.changeLines.length" class="table table-sm">
<thead>
<tr>
<th>Date</th>
<th>Before</th>
<th>After</th>
</tr>
</thead>
<tbody t-foreach="data.changeLines" t-as="changeLine" t-key="changeLine.id">
<ChangeLine changeLine="changeLine" ordering="data.ordering"/>
</tbody>
</table>
</div>
<t t-name="account.ResequenceChangeLine" owl="1">
<tr>
<td t-esc="props.changeLine.date"/>
<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-else="" t-esc="props.changeLine.new_by_date"
t-attf-class="{{ props.changeLine.new_by_name != props.changeLine.new_by_date ? 'animate' : ''}}"/>
</tr>
</t>
</templates>
<?xml version="1.0" encoding="UTF-8"?>
<templates>
<t t-name="account.BillsHiddenUploadForm">
<div class="d-none o_vendor_bill_upload">
<t t-call="HiddenInputFile">
<t t-set="multi_upload" t-value="true"/>
<t t-set="fileupload_id" t-value="widget.fileUploadID"/>
<t t-set="fileupload_action" t-translation="off">/web/binary/upload_attachment</t>
<input type="hidden" name="model" value=""/>
<input type="hidden" name="id" value="0"/>
</t>
</div>
</t>
<t t-extend="ListView.buttons" t-name="BillsListView.buttons">
<t t-jquery="button.o_list_button_add" t-operation="after">
<button type="button" class="btn btn-secondary o_button_upload_bill">
Upload
</button>
</t>
</t>
</templates>
<?xml version="1.0" encoding="utf-8"?>
<templates>
<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">
<thead>
<tr>
<t t-foreach="data.options.columns" t-as="col">
<th t-esc="col['label']" t-attf-class="{{col['class']}}"/>
</t>
</tr>
</thead>
<t t-foreach="data.groups_vals" t-as="group_vals">
<ListGroup group_vals="group_vals" options="data.options"/>
</t>
</table>
<t t-if="data.options.discarded_number">
<span>
<t t-esc="data.options.discarded_number"/>
are not shown in the preview
</span>
</t>
</div>
<tbody t-name="account.GroupedItemsTemplate" owl="1">
<tr style="background-color: #dee2e6;">
<td t-attf-colspan="{{props.options.columns.length}}">
<t t-esc="props.group_vals.group_name"/>
</td>
</tr>
<t t-foreach="props.group_vals.items_vals" t-as="item_vals">
<ListItem item_vals="item_vals[2]" options="props.options"/>
</t>
</tbody>
<tr t-name="account.GroupedItemTemplate" owl="1">
<t t-foreach="props.options.columns" t-as="col">
<td t-esc="props.item_vals[col['field']]" t-attf-class="{{col['class']}}"/>
</t>
</tr>
</templates>
<?xml version='1.0' encoding='utf-8'?>
<templates>
<t t-name="AccountTaxGroupTemplate">
<table class="o_group o_inner_group oe_subtotal_footer border-0 my-0" style="min-width: 100%;">
<tbody>
<t t-foreach="lines" t-as="line">
<tr>
<td class="o_td_label oe_tax_group_name">
<label class="o_form_label" t-esc="line[0]"/>
</td>
<td class="oe_tax_group_editable" t-att-data-tax-group-id="line[6]">
<t t-if="displayEditWidget and line[1] !== 0">
<span class="tax_group_edit">
<i class="fa fa-pencil"></i>
<span class="oe_tax_group_amount_value">
<t t-esc="line[3]"/>
</span>
</span>
<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]"/>
</span>
</t>
<t t-if="!displayEditWidget or line[1] === 0">
<span class="oe_tax_group_amount_value">
<t t-esc="line[3]"/>
</span>
</t>
</td>
</tr>
</t>
</tbody>
</table>
</t>
</templates>
odoo.define('account.reconciliation_field_tests', function (require) {
"use strict";
var FormView = require('web.FormView');
var testUtils = require('web.test_utils');
var createView = testUtils.createView;
QUnit.module('account', {
beforeEach: function () {
this.data = {
'account.move': {
fields: {
payments_widget: {string: "payments_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"}',
}]
},
};
}
}, function () {
QUnit.module('Reconciliation');
QUnit.test('Reconciliation form field', async function (assert) {
assert.expect(5);
var form = await createView({
View: FormView,
model: 'account.move',
data: this.data,
arch: '<form>' +
'<field name="outstanding_credits_debits_widget" widget="payment"/>' +
'<field name="payments_widget" widget="payment"/>' +
'</form>',
res_id: 1,
mockRPC: function (route, args) {
if (args.method === 'js_remove_outstanding_partial') {
assert.deepEqual(args.args, [10, 38], "should call js_remove_outstanding_partial {warning: required focus}");
return Promise.resolve();
}
if (args.method === 'js_assign_outstanding_line') {
assert.deepEqual(args.args, [4, 20], "should call js_assign_outstanding_line {warning: required focus}");
return Promise.resolve();
}
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");
},
},
});
assert.strictEqual(form.$('.o_field_widget[name="payments_widget"]').text().replace(/[\s\n\r]+/g, ' '),
" Paid on 04/25/2017 $ 555.00 ",
"should display payment information");
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, ' '),
" Outstanding credits Add INV/2017/0004 $ 100.00 ",
"should display outstanding information");
form.$('.o_field_widget[name="payments_widget"] .js_payment_info').trigger('focus');
form.$('.popover .js_open_payment').trigger('click');
form.$('.o_field_widget[name="payments_widget"] .js_payment_info').trigger('focus');
form.$('.popover .js_unreconcile_payment').trigger('click');
form.destroy();
});
});
});
odoo.define('account.section_and_note_tests', function (require) {
"use strict";
var FormView = require('web.FormView');
var testUtils = require('web.test_utils');
var createView = testUtils.createView;
QUnit.module('section_and_note', {
beforeEach: function () {
this.data = {
invoice: {
fields: {
invoice_line_ids: {
string: "Lines",
type: 'one2many',
relation: 'invoice_line',
relation_field: 'invoice_id'
},
},
records: [
{id: 1, invoice_line_ids: [1, 2]},
],
},
invoice_line: {
fields: {
display_type: {
string: 'Type',
type: 'selection',
selection: [['line_section', "Section"], ['line_note', "Note"]]
},
invoice_id: {
string: "Invoice",
type: 'many2one',
relation: 'invoice'
},
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'},
]
},
};
},
}, function () {
QUnit.test('correct display of section and note fields', async function (assert) {
assert.expect(5);
var form = await createView({
View: FormView,
model: 'invoice',
data: this.data,
arch: '<form>' +
'<field name="invoice_line_ids" widget="section_and_note_one2many"/>' +
'</form>',
archs: {
'invoice_line,false,list': '<tree editable="bottom">' +
'<field name="display_type" invisible="1"/>' +
'<field name="name" widget="section_and_note_text"/>' +
'</tree>',
},
res_id: 1,
});
assert.hasClass(form.$('[name="invoice_line_ids"] table'), 'o_section_and_note_list_view');
// section should be displayed correctly
var $tr0 = form.$('tr.o_data_row:eq(0)');
assert.doesNotHaveClass($tr0, 'o_is_line_section',
"should not have a section class");
var $tr1 = form.$('tr.o_data_row:eq(1)');
assert.hasClass($tr1, 'o_is_line_section',
"should have a section class");
// enter edit mode
await testUtils.form.clickEdit(form);
// editing line should be textarea
$tr0 = form.$('tr.o_data_row:eq(0)');
await testUtils.dom.click($tr0.find('td.o_data_cell'));
assert.containsOnce($tr0, 'td.o_data_cell textarea[name="name"]',
"editing line should be textarea");
// editing section should be input
$tr1 = form.$('tr.o_data_row:eq(1)');
await testUtils.dom.click($tr1.find('td.o_data_cell'));
assert.containsOnce($tr1, 'td.o_data_cell input[name="name"]',
"editing section should be input");
form.destroy();
});
});
});
odoo.define('account.dashboard.setup.tour', function (require) {
"use strict";
var core = require('web.core');
var tour = require('web_tour.tour');
var _t = core._t;
tour.register('account_render_report', {
test: true,
url: '/web',
}, [tour.stepUtils.showAppsMenuItem(),
{
id: 'account_menu_click',
trigger: '.o_app[data-menu-xmlid="account.menu_finance"]',
position: 'bottom',
}, {
trigger: '.o_data_row:first',
extra_trigger: '.breadcrumb',
}, {
trigger: '.o_control_panel button:contains("' + _t('Print') + '")',
}, {
trigger: '.o_control_panel .o_dropdown_menu a:contains("' + _t('Invoices without Payment') + '")',
}, {
trigger: 'iframe .o_report_layout_standard h2',
content: 'Primary color is correct',
run: function () {
if (this.$anchor.css('color') !== "rgb(18, 52, 86)") {
console.error('The primary color should be the one set on the company.');
}
},
}, {
trigger: 'iframe .o_report_layout_standard #informations div strong',
content: 'Secondary color is correct',
run: function () {
if (this.$anchor.css('color') !== "rgb(120, 145, 1)") {
console.error('The secondary color should be the one set on the company.');
}
},
}
]);
});
odoo.define('account.tax.group.tour.tests', function (require) {
"use strict";
var core = require('web.core');
var tour = require('web_tour.tour');
var _t = core._t;
tour.register('account_tax_group', {
test: true,
url: "/web",
}, [tour.stepUtils.showAppsMenuItem(),
{
content: "Go to Invoicing",
trigger: '.o_app[data-menu-xmlid="account.menu_finance"]',
edition: 'community',
},
{
content: "Go to Accounting",
trigger: '.o_app[data-menu-xmlid="account_accountant.menu_accounting"]',
edition: 'enterprise',
},
{
content: "Go to Vendors",
trigger: 'a:contains("Vendors")',
},
{
content: "Go to Bills",
trigger: 'span:contains("Bills")',
},
{
extra_trigger: '.breadcrumb:contains("Bills")',
content: "Create new bill",
trigger: '.o_list_button_add',
},
// Set a vendor
{
content: "Add vendor",
trigger: 'div.o_field_widget.o_field_many2one[name="partner_id"] div input',
run: 'text Azure Interior',
},
{
content: "Valid vendor",
trigger: '.ui-menu-item a:contains("Azure Interior")',
},
// Add First product
{
content: "Add items",
trigger: 'div[name="invoice_line_ids"] .o_field_x2many_list_row_add a:contains("Add a line")',
},
{
content: "Select input",
trigger: 'div[name="invoice_line_ids"] .o_list_view .o_selected_row .o_list_many2one:first input',
},
{
content: "Type item",
trigger: 'div[name="invoice_line_ids"] .o_list_view .o_selected_row .o_list_many2one:first input',
run: "text Large Desk",
},
{
content: "Valid item",
trigger: '.ui-menu-item-wrapper:contains("Large Desk")',
},
// Save account.move
{
content: "Save the account move",
trigger: '.o_form_button_save',
},
// Edit account.move
{
content: "Edit the account move",
trigger: '.o_form_button_edit',
},
// Edit tax group amount
{
content: "Edit tax group amount",
trigger: '.oe_tax_group_amount_value',
},
{
content: "Modify the input value",
trigger: '.tax_group_edit_input input',
run: function (actions) {
$('.tax_group_edit_input input').val(200);
$('.tax_group_edit_input input').select();
var keydownEvent = jQuery.Event('keydown');
keydownEvent.which = 13;
this.$anchor.trigger(keydownEvent);
},
},
// Check new value for total (with modified tax_group_amount).
{
content: "Valid total amount",
trigger: 'span[name="amount_total"]:contains("1,499.00")',
},
// Modify the quantity of the object
{
content: "Select item quantity",
trigger: 'div[name="invoice_line_ids"] .o_list_view tbody tr.o_data_row .o_list_number[title="1.000"]',
},
{
content: "Change item quantity",
trigger: 'div[name="invoice_line_ids"] .o_list_view tbody tr.o_data_row .o_list_number[title="1.000"] input',
run: 'text 2',
},
{
content: "Valid the new value",
trigger: 'div[name="invoice_line_ids"] .o_list_view tbody tr.o_data_row .o_list_number[title="1.000"] input',
run: function (actions) {
var keydownEvent = jQuery.Event('keydown');
keydownEvent.which = 13;
this.$anchor.trigger(keydownEvent);
},
},
// Save form
{
content: "Save the account move",
trigger: '.o_form_button_save',
},
// Check new tax group value
{
content: "Check new value of tax group",
trigger: '.oe_tax_group_amount_value:contains("389.70")',
},
]);
});
<?xml version="1.0" encoding="utf-8"?>
<odoo>
<data>
<record id="bphtb_jenis_tree" model="ir.ui.view">
<field name="name">bphtb.jenis.tree</field>
<field name="model">bphtb.jenis</field>
<field name="arch" type="xml">
<tree string="Jenis Perolehan" sample="1" create="1" delete="1" multi_edit="0">
<field name="code" string="Kode"/>
<field name="name" string="Nama"/>
<field name="rate" string="Tarif"/>
<field name="min_omzet" string="NJOPTKP"/>
<field name="disc" string="Pengurangan"/>
</tree>
</field>
</record>
<record id="bphtb_jenis_form" model="ir.ui.view">
<field name="name">bphtb.jenis.form</field>
<field name="model">bphtb.jenis</field>
<field name="arch" type="xml">
<form string="Jenis Perolehan" sample="1">
<sheet>
<h1>
<field name="name" string="Nama"/>
</h1>
<group>
<field name="code" string="Kode"/>
<field name="rate" string="Tarif"/>
<field name="min_omzet" string="NJOPTKP"/>
<field name="disc" string="Pengurangan"/>
<field name="company_id" groups="base.group_multi_company"
options="{'no_create': True}"
force_save="1"/>
</group>
</sheet>
</form>
</field>
</record>
<record id="action_jenis_bphtb_kab" model="ir.actions.act_window">
<field name="name">Jenis Perolehan</field>
<field name="res_model">bphtb.jenis</field>
<field name="view_mode">kanban,tree,form</field>
<field name="view_id" ref="bphtb_jenis_tree"/>
<field name="help" type="html">
<p class="o_view_nocontent_smiling_face">
Jenis Perolehan
</p>
</field>
</record>
</data>
</odoo>
\ No newline at end of file
<?xml version="1.0" encoding="utf-8"?>
<odoo>
<!-- Top menu item -->
<menuitem id="bphtb_menu_root"
name="BPHTB"
web_icon="bphtb_icon,static/description/icon.png"
active="True"
sequence="400"/>
<!-- Konfigurasi-->
<menuitem id="config_bphtb_menu"
name="Konfigurasi"
parent="bphtb_menu_root"
sequence="7"/>
<menuitem id="wilayah_config_bphtb_menu"
name="Wilayah"
parent="config_bphtb_menu"
sequence="2"/>
<menuitem id="state_config_bphtb_menu"
name="Provinsi"
parent="wilayah_config_bphtb_menu"
action="id_gov.action_state_config_id"
sequence="2"/>
<menuitem id="district_config_bphtb_menu"
name="Kabupaten/Kota"
parent="wilayah_config_bphtb_menu"
action="id_gov.action_district_config_id"
sequence="2"/>
<menuitem id="sub_district_config_bphtb_menu"
name="Kecamatan"
parent="wilayah_config_bphtb_menu"
action="id_gov.action_sub_district_config_id"
sequence="2"/>
<menuitem id="village_config_bphtb_menu"
name="Desa/Kelurahan"
parent="wilayah_config_bphtb_menu"
action="id_gov.action_village_config_id"
sequence="2"/>
<menuitem id="company_config_bphtb_menu"
name="Pemerintah Daerah/Organisasi"
parent="config_bphtb_menu"
action="id_gov.action_company_config_id"
sequence="4"/>
<menuitem id="jenis_config_bphtb_menu"
name="Jenis Perolehan"
parent="config_bphtb_menu"
action="action_jenis_bphtb_kab"
sequence="4"/>
<menuitem id="ppat_config_bphtb_menu"
name="PPAT"
parent="config_bphtb_menu"
action="action_ppat_config_bphtb_kab"
sequence="4"/>
<menuitem id="wp_config_bphtb_menu"
name="Wajib Pajak"
parent="config_bphtb_menu"
action="action_wp_config_bphtb_kab"
sequence="4"/>
<!-- Transaksi-->
<menuitem id="transaksi_bphtb_menu"
name="Transaksi"
parent="bphtb_menu_root"
sequence="6"/>
<menuitem id="sspd_bphtb_menu"
name="SSPD"
parent="transaksi_bphtb_menu"
action="action_sspd_bphtb_kab"
sequence="4"/>
</odoo>
<odoo>
<data>
<!-- <record id="res_partner_ppat_search" model="ir.ui.view">-->
<!-- <field name="name">res.partner.ppat.search.inherit</field>-->
<!-- <field name="model">res.partner</field>-->
<!-- <field name="inherit_id" ref="base.view_res_partner_filter"/>-->
<!-- <field name="arch" type="xml">-->
<!-- <xpath expr="//filter[@name='inactive']" position="before">-->
<!-- <filter string="PPAT" name="ppat" domain="[('type','=', 'ppat')]"/>-->
<!-- <filter string="Wajib Pajak" name="wp" domain="[('type','=', 'wp')]"/>-->
<!-- <separator/>-->
<!-- </xpath>-->
<!-- </field>-->
<!-- </record>-->
<record id="action_ppat_config_bphtb_kab" model="ir.actions.act_window">
<field name="name">PPAT</field>
<field name="res_model">res.partner</field>
<field name="view_mode">kanban,tree,form</field>
<field name="domain">[('type','=','ppat')]</field>
<field name="context">{
'default_is_company': True,
'default_type': 'ppat'}
</field>
<field name="help" type="html">
<p class="o_view_nocontent_smiling_face">
Partner
</p>
</field>
</record>
<record id="action_wp_config_bphtb_kab" model="ir.actions.act_window">
<field name="name">Wajib Pajak</field>
<field name="res_model">res.partner</field>
<field name="view_mode">kanban,tree,form</field>
<field name="domain">[('type','=','wp')]</field>
<field name="context">{
'default_is_company': True,
'default_type': 'ppat'}
</field>
<field name="help" type="html">
<p class="o_view_nocontent_smiling_face">
Partner
</p>
</field>
</record>
</data>
</odoo>
\ No newline at end of file
<odoo>
<data>
<record id="bphtb_sspd_tree" model="ir.ui.view">
<field name="name">bphtb.sspd.tree</field>
<field name="model">bphtb.sales</field>
<field name="arch" type="xml">
<tree string="SSPD" sample="1" create="1" delete="1" multi_edit="0">
<field name="nop" string="NOP"/>
<field name="tax_year" string="Tahun"/>
<field name="jenis_id" string="Jenis"/>
<field name="wp_id" string="Wajib Pajak"/>
<field name="owed" string="Terutang"/>
<field name="state" string="Status"/>
</tree>
</field>
</record>
<record id="bphtb_sspd_form" model="ir.ui.view">
<field name="name">bphtb.sspd.form</field>
<field name="model">bphtb.sales</field>
<field name="arch" type="xml">
<form string="Jenis Perolehan" sample="1">
<sheet>
<label for="nop" string="NOP"/>
<h1>
<field name="nop"/>
</h1>
<group>
<field name="tax_year"/>
<field name="request_date"/>
<field name="typ" readonly="1"/>
<field name="ppat_id"/>
<field name="state"/>
<field name="company_id"/>
</group>
<notebook col_span="4">
<page name="op" string="Objek Pajak">
<group>
<group>
<field name="luas_bumi"/>
<field name="luas_bng"/>
<field name="njop_bumi"/>
<field name="njop_bng"/>
</group>
<group>
<field name="luas_bumi_bersama"/>
<field name="luas_bng_bersama"/>
<field name="njop_bumi_bersama"/>
<field name="njop_bng_bersama"/>
<field name="certicate_no"/>
</group>
</group>
</page>
<page name="wp" string="Wajib Pajak">
<group>
<group>
<field name="wp_id"/>
<field name="wp_identity_number"/>
<label for="wp_street" string="Address"/>
<div class="o_address_format">
<field name="wp_street" placeholder="Street..." class="o_address_street"/>
<field name="wp_street2" placeholder="Street 2..."
class="o_address_street"/>
<field name="wp_village_id" class="o_address_city"
placeholder="Desa/Keurahan"
options="{'no_open': True, 'no_quick_create': True}"/>
<field name="wp_sub_district_id" class="o_address_city"
placeholder="Kecamatan"
options="{'no_open': True, 'no_quick_create': True}"/>
<field name="wp_district_id" class="o_address_city" placeholder="Kab/Kota"
options="{'no_open': True, 'no_quick_create': True}"/>
<field name="wp_state_id" class="o_address_city" placeholder="State"
options="{'no_open': True, 'no_quick_create': True}"/>
<field name="wp_zip" placeholder="ZIP" class="o_address_zip"/>
</div>
</group>
<group>
<field name="wp_email" placeholder="ZIP" class="o_address_zip"/>
<field name="wp_phone" placeholder="ZIP" class="o_address_zip"/>
</group>
</group>
</page>
<page name="seller" string="Penjual">
<group>
<group>
<field name="seller_id"/>
<field name="seller_identity_number"/>
<label for="seller_street" string="Address"/>
<div class="o_address_format">
<field name="seller_street" placeholder="Street..."
class="o_address_street"/>
<field name="seller_street2" placeholder="Street 2..."
class="o_address_street"/>
<field name="seller_village_id" class="o_address_city"
placeholder="Desa/Keurahan"
options="{'no_open': True, 'no_quick_create': True}"/>
<field name="seller_sub_district_id" class="o_address_city"
placeholder="Kecamatan"
options="{'no_open': True, 'no_quick_create': True}"/>
<field name="seller_district_id" class="o_address_city"
placeholder="Kab/Kota"
options="{'no_open': True, 'no_quick_create': True}"/>
<field name="seller_state_id" class="o_address_city" placeholder="State"
options="{'no_open': True, 'no_quick_create': True}"/>
<field name="seller_zip" placeholder="ZIP" class="o_address_zip"/>
</div>
</group>
<group>
<field name="seller_email" placeholder="ZIP" class="o_address_zip"/>
<field name="seller_phone" placeholder="ZIP" class="o_address_zip"/>
</group>
</group>
</page>
<page name="calc" string="Perhitungan">
<group>
<group>
<field name="jenis_id"/>
<field name="njop" readonly="1"/>
<field name="npop"/>
<field name="basic_calc" readonly="1"/>
<field name="min_omzet"/>
<field name="rate"/>
<field name="basic" readonly="1"/>
<field name="fine" readonly="1"/>
</group>
<group>
<!-- <label for="disc_sk" string="Pengurangan Dihitung Sendiri"/>-->
<!-- <group>-->
<field name="disc_sk"/>
<field name="disc"/>
<field name="amount" readonly="1"/>
<field name="payment"/>
<field name="disc_amount" readonly="1"/>
<field name="owed" readonly="1"/>
<!-- </group>-->
</group>
</group>
</page>
</notebook>
</sheet>
</form>
</field>
</record>
<record id="action_sspd_bphtb_kab" model="ir.actions.act_window">
<field name="name">SSPD</field>
<field name="res_model">bphtb.sales</field>
<field name="view_mode">tree,form</field>
<field name="domain">[('typ','=','sspd')]</field>
<field name="view_id" ref="bphtb_sspd_tree"/>
<field name="context">{
'default_typ': 'sspd'}
</field>
<field name="help" type="html">
<p class="o_view_nocontent_smiling_face">
Surat Setoran Pajak Daerah BPHTB
</p>
</field>
</record>
</data>
</odoo>
\ No newline at end of file
......@@ -22,6 +22,7 @@ Module yang sering digunakan dalam aplikasi pemerintah
'views/district.xml',
'views/partner.xml',
'views/company.xml',
'views/menus.xml',
],
'demo': [],
'qweb': [],
......
<?xml version="1.0" encoding="utf-8"?>
<odoo>
<!-- Top menu item -->
<menuitem id="id_gov_menu_root"
name="Referensi"
web_icon="id_gov_icon,static/description/icon.png"
active="True"
sequence="400"/>
<menuitem id="wilayah_id_gov_menu"
name="Wilayah"
parent="id_gov_menu_root"
sequence="2"/>
<menuitem id="propinsi_menu"
name="Propinsi"
parent="wilayah_id_gov_menu"
action="action_state_config_id"
sequence="3"/>
<menuitem id="kota_menu"
name="Kabupaten/Kota"
parent="wilayah_id_gov_menu"
action="action_district_config_id"
sequence="4"/>
<menuitem id="kecamatan_menu"
name="Kecamatan"
parent="wilayah_id_gov_menu"
action="action_sub_district_config_id"
sequence="5"/>
<menuitem id="kelurahan_menu"
name="Kelurahan"
parent="wilayah_id_gov_menu"
action="action_village_config_id"
sequence="6"/>
</odoo>
......@@ -29,8 +29,9 @@ Menydiakan module untuk followup Wajib Pajak/Retribusi.
'views/menus.xml',
# 'security/account_security.xml',
# 'demo/demo_data.xml',
'demo/product.category.csv',
'security/ir_rule.xml',
'report/hide_default_report.xml',
'demo/product.category.csv',
],
'demo': [],
......
......@@ -8,9 +8,9 @@
<field name="report_name">product.report_productlabel</field>
<field name="report_file">product.report_productlabel</field>
<field name="print_report_name">'Products Labels - %s' % (object.name)</field>
<field name="binding_model_id" ref="product.model_product_product"/>
<!-- <field name="binding_model_id" ref="product.model_product_product"/>-->
<field name="binding_type">report</field>
<field name="menu">False</field>
<field name="binding_model_id" eval="False"/>
</record>
<record id="report_product_template_label" model="ir.actions.report">
......@@ -20,9 +20,9 @@
<field name="report_name">product.report_producttemplatelabel</field>
<field name="report_file">product.report_producttemplatelabel</field>
<field name="print_report_name">'Products Labels - %s' % (object.name)</field>
<field name="binding_model_id" ref="product.model_product_template"/>
<field name="binding_model_id" eval="False"/>
<!-- <field name="binding_model_id" ref="product.model_product_template"/>-->
<field name="binding_type">report</field>
<field name="menu">False</field>
</record>
<record id="report_product_product_barcode" model="ir.actions.report">
......@@ -32,9 +32,9 @@
<field name="report_name">product.report_productbarcode</field>
<field name="report_file">product.report_productbarcode</field>
<field name="print_report_name">'Products barcode - %s' % (object.name)</field>
<field name="binding_model_id" ref="product.model_product_product"/>
<field name="binding_model_id" eval="False"/>
<!-- <field name="binding_model_id" ref="product.model_product_product"/>-->
<field name="binding_type">report</field>
<field name="menu">False</field>
</record>
<record id="report_product_template_barcode" model="ir.actions.report">
......@@ -44,9 +44,10 @@
<field name="report_name">product.report_producttemplatebarcode</field>
<field name="report_file">product.report_producttemplatebarcode</field>
<field name="print_report_name">'Products barcode - %s' % (object.name)</field>
<field name="binding_model_id" ref="product.model_product_template"/>
<field name="binding_model_id" eval="False"/>
<!-- <field name="binding_model_id" ref="product.model_product_template"/>-->
<field name="binding_type">report</field>
<field name="menu">False</field>
</record>
<record id="report_product_packaging" model="ir.actions.report">
......@@ -56,9 +57,9 @@
<field name="report_name">product.report_packagingbarcode</field>
<field name="report_file">product.report_packagingbarcode</field>
<field name="print_report_name">'Products packaging - %s' % (object.name)</field>
<field name="binding_model_id" ref="product.model_product_packaging"/>
<field name="binding_model_id" eval="False"/>
<!-- <field name="binding_model_id" ref="product.model_product_packaging"/>-->
<field name="binding_type">report</field>
<field name="menu">False</field>
</record>
<record id="action_report_pricelist" model="ir.actions.report">
......@@ -67,8 +68,7 @@
<field name="report_type">qweb-pdf</field>
<field name="report_name">product.report_pricelist</field>
<field name="report_file">product.report_pricelist</field>
<field name="menu">False</field>
<field name="binding_model_id" eval="False"/>
</record>
</data>
</odoo>
Markdown is supported
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!