Commit 3828f9e8 by aa.gusti

penambahan autonumber

1 parent a2786de9
......@@ -13,7 +13,7 @@ Memudahkan dalam mengelola tagihan kepada wajib pajak atau wajib retribusi
'category': 'Indonesia Goverment',
'website': 'https://opensipkd.com',
'images': [],
'depends': ['id_gov'],
'depends': ['id_gov','portal'],
'data': [
'security/account_security.xml',
'security/ir.model.access.csv',
......
from . import bphtb_ref
from . import partner
from . import sales
\ No newline at end of file
from . import sequence_mixin
from . import sales
import logging
from psycopg2 import DatabaseError, sql
from odoo import fields, models, api, _
from odoo.exceptions import UserError
# from odoo.tools import sql
_logger = logging.getLogger(__name__)
......@@ -23,15 +26,28 @@ class ResPartner(models.Model):
def write(self, vals):
_logger.info(vals)
_logger.info(self.create_uid)
_logger.info(self.env)
_logger.info(self.env.user.id)
if self.create_uid.id != self.env.user.id:
return {
'warning': {
'title': _('Warning'),
'message': _('Modified Record Forbidden')
}
}
res = super().write(vals)
if self.env.user.has_group('bphtb_kab.group_bphtb_ppat'):
_logger.info(f'has groups {self.create_uid.id} {self.env.user.id}')
if self.create_uid.id != self.env.user.id:
_logger.info('different')
raise UserError(_("You are not a record owner"))
res = super(ResPartner, self).write(vals)
return res
def _increase_rank(self, field, n=1):
if self.ids and field in ['customer_rank', 'supplier_rank']:
try:
with self.env.cr.savepoint(flush=False):
query = sql.SQL("""
SELECT {field} FROM res_partner WHERE ID IN %(partner_ids)s FOR UPDATE NOWAIT;
UPDATE res_partner SET {field} = {field} + %(n)s
WHERE id IN %(partner_ids)s
""").format(field=sql.Identifier(field))
self.env.cr.execute(query, {'partner_ids': tuple(self.ids), 'n': n})
for partner in self:
self.env.cache.remove(partner, partner._fields[field])
except DatabaseError as e:
if e.pgcode == '55P03':
_logger.debug('Another transaction already locked partner rows. Cannot update partner ranks.')
else:
raise e
import re
from collections import defaultdict
from sqlite3 import DatabaseError
from odoo import api, fields, models, _
from odoo.exceptions import AccessError, UserError, ValidationError
from odoo.tools import format_date
from psycopg2 import sql, DatabaseError
import logging
......@@ -11,14 +15,17 @@ class BphtbSales(models.Model):
_name = 'bphtb.sales'
_description = 'Transaksi BPHTB'
# code = fields.Char(index=True, string='Code')
# name = fields.Char(index=True, string='Name', size=64)
_inherit = ['portal.mixin', 'mail.thread', 'mail.activity.mixin', 'sequence.mixin']
name = fields.Char(string='Number', copy=False, compute='_compute_name', readonly=False, store=True, index=True,
tracking=True)
ref = fields.Char(string='Reference', copy=False, tracking=True)
#
# rate = fields.Float(required=True)
# disc = fields.Float(required=True)
# min_omzet = fields.Integer(required=True)
request_date = fields.Date(readonly=True,
states={'draft': [('readonly', False)]}, )
code = fields.Char()
date = fields.Date(readonly=True,
states={'draft': [('readonly', False)]}, )
ppat_id = fields.Many2one('res.partner',
readonly=True,
states={'draft': [('readonly', False)]},
......@@ -104,23 +111,23 @@ class BphtbSales(models.Model):
states={'draft': [('readonly', False)]}, )
njop = fields.Float(compute="_compute_njop", store=True)
jenis_id = fields.Many2one('bphtb.jenis', readonly=True,
states={'draft': [('readonly', False)]},)
states={'draft': [('readonly', False)]}, )
rate = fields.Float(related='jenis_id.rate', store=True, readonly=True)
min_omzet = fields.Float(related='jenis_id.min_omzet', store=True, readonly=True)
disc_sk = fields.Char(required=False, readonly=True,
states={'draft': [('readonly', False)]},)
states={'draft': [('readonly', False)]}, )
disc = fields.Float(related='jenis_id.disc', store=True, readonly=True)
npop = fields.Float( readonly=True,
states={'draft': [('readonly', False)]},)
npop = fields.Float(readonly=True,
states={'draft': [('readonly', False)]}, )
basic_calc = fields.Float(compute="_compute_basic_calc")
npopkp = fields.Float( readonly=True,
states={'draft': [('readonly', False)]},)
npopkp = fields.Float(readonly=True,
states={'draft': [('readonly', False)]}, )
basic = fields.Float()
fine = fields.Float()
amount = fields.Float()
disc_amount = fields.Float()
payment = fields.Float( readonly=True,
states={'draft': [('readonly', False)]},)
payment = fields.Float(readonly=True,
states={'draft': [('readonly', False)]}, )
owed = fields.Float(compute="_compute_owed")
typ = fields.Selection([
('sspd', 'SSPD'),
......@@ -147,6 +154,7 @@ class BphtbSales(models.Model):
company_id = fields.Many2one('res.company',
default=lambda self: self.env.company.id
if not self.company_id else False)
district_id = fields.Many2one('res.district', related='company_id.district_id', store=True)
def _my_check_method(self, cr, uid, ids, context=None):
# Your code goes here
......@@ -254,16 +262,154 @@ class BphtbSales(models.Model):
rec.njop = rec.njop_bng + rec.njop_bumi + rec.njop_bumi_bersama + \
rec.njop_bng_bersama
@api.depends('name', 'state')
def name_get(self):
_logger.info("Name Get")
result = []
for move in self:
if self._context.get('name_groupby'):
name = '**%s**, %s' % (format_date(self.env, move.date), move._get_sales_display_name())
if move.ref:
name += ' (%s)' % move.ref
if move.ppat_id.name:
name += ' - %s' % move.ppat_id.name
else:
name = move._get_sales_display_name(show_ref=True)
result.append((move.id, name))
return result
@api.depends('state', 'date')
def _compute_name(self):
_logger.info('Compute Name')
def journal_key(move):
return (move.id, move.typ)
def date_key(move):
return (move.date.year, move.date.month)
grouped = defaultdict( # key: journal_id, move_type
lambda: defaultdict( # key: first adjacent (date.year, date.month)
lambda: {
'records': self.env['bphtb.sales'],
'format': False,
'format_values': False,
'reset': False
}
)
)
self = self.sorted(lambda m: (m.typ, m.company_id, m.date, m.ref or '', m.id,))
highest_name = self[0]._get_last_sequence() if self else False
# Group the moves by journal and month
for move in self:
if not highest_name and move == self[0] and move.date: # not move.posted_before and
# In the form view, we need to compute a default sequence so that the user can edit
# it. We only check the first move as an approximation (enough for new in form view)
pass
elif (move.name and move.name != '/') or move.state != 'confirmed':
try:
# if not move.posted_before:
move._constrains_date_sequence()
# Has already a name or is not posted, we don't add to a batch
continue
except ValidationError:
# Has never been posted and the name doesn't match the date: recompute it
pass
group = grouped[journal_key(move)][date_key(move)] #
_logger.info(group)
_logger.info(group['records'])
if not group['records']:
# Compute all the values needed to sequence this whole group
move._set_next_sequence()
group['format'], group['format_values'] = move._get_sequence_format_param(move.name)
group['reset'] = move._deduce_sequence_number_reset(move.name)
group['records'] += move
_logger.info(group)
# Fusion the groups depending on the sequence reset and the format used because `seq` is
# the same counter for multiple groups that might be spread in multiple months.
final_batches = []
for journal_group in grouped.values():
journal_group_changed = True
_logger.info(f"Jurnal Group {journal_group}")
for date_group in journal_group.values():
_logger.info(f"Date Group {date_group}")
if (journal_group_changed or final_batches[-1]['format'] != date_group['format']
or dict(final_batches[-1]['format_values'], seq=0) != dict(date_group['format_values'], seq=0)
):
final_batches += [date_group]
journal_group_changed = False
elif date_group['reset'] == 'never':
final_batches[-1]['records'] += date_group['records']
elif (
date_group['reset'] == 'year'
and final_batches[-1]['records'][0].date.year == date_group['records'][0].date.year
):
final_batches[-1]['records'] += date_group['records']
else:
final_batches += [date_group]
# Give the name based on previously computed values
for batch in final_batches:
for move in batch['records']:
move.name = batch['format'].format(**batch['format_values'])
batch['format_values']['seq'] += 1
batch['records']._compute_split_sequence()
_logger.info(batch['records'])
self.filtered(lambda m: not m.name).name = '/'
# @api.depends('njop')
# def njop_change(self):
def action_confirm(self):
self.state = 'confirmed'
_logger.info("Confirm")
to_post = self
if not self.env.su and not self.env.user.has_group('bphtb_kab.group_bphtb_ppat'):
raise AccessError(_("You don't have the access rights to confirm an sspd."))
for sales in to_post:
if sales.state == 'confirmed':
raise UserError(_('The entry %s (id %s) is already confirmed.') % (sales.name, sales.id))
if not sales.wp_id or not sales.seller_id:
raise UserError(
_("The field 'Wajib Pajak' and 'Penjual' is required."))
if not sales.date or sales.date > fields.Date.context_today(self):
raise UserError(_("The request date is required to validate this document."))
if not sales.date or sales.date < fields.Date.context_today(self):
raise UserError(_("Check request date."))
# to_post.mapped('line_ids').create_analytic_lines()
to_post.write({
'state': 'confirmed',
# 'posted_before': True,
})
for sales in to_post:
sales.message_subscribe([p.id for p in [sales.ppat_id] if p not in sales.sudo().message_partner_ids])
customer_count, supplier_count = defaultdict(int), defaultdict(int)
for sales in to_post:
customer_count[sales.ppat_id] += 1
for partner, count in customer_count.items():
(partner | partner.commercial_partner_id)._increase_rank('customer_rank', count)
# Trigger action for paid invoices in amount is zero
# to_post.filtered(
# lambda m: m.is_invoice(include_receipts=True) and m.currency_id.is_zero(m.amount_total)
# ).action_invoice_paid()
# Force balance check since nothing prevents another module to create an incorrect entry.
# This is performed at the very end to avoid flushing fields before the whole processing.
return to_post
def action_cancel(self):
self.state = 'canceled'
def action_draft(self):
self.state = 'draft'
self.name = '/'
def action_print(self):
if not self.printed:
......@@ -275,12 +421,6 @@ class BphtbSales(models.Model):
# docids = self.env['sale.order'].search([]).ids
return self.env.ref('bphtb_kab.action_report_bphtb_sspd').report_action(self) # None, data=data
def name_get(self):
result = []
for record in self:
result.append((record.id, "{} ({})".format(record.wp_name, record.nop)))
return result
def _validate_wp_seller(self):
if self.wp_id == self.seller_id:
raise ValueError("Pembeli dan penjual harus berbeda")
......@@ -297,3 +437,81 @@ class BphtbSales(models.Model):
# # Your code goes here
# self._validate_wp_seller()
# return res
#
def _get_sales_display_name(self, show_ref=False):
''' Helper to get the display name of an invoice depending of its type.
:param show_ref: A flag indicating of the display name must include or not the journal entry reference.
:return: A string representing the invoice.
'''
self.ensure_one()
draft_name = ''
if self.state == 'draft':
# draft_name += {
# 'out_invoice': _('Draft SSPD'),
# 'out_refund': _('Draft Credit Note'),
# 'in_invoice': _('Draft Bill'),
# 'in_refund': _('Draft Vendor Credit Note'),
# 'out_receipt': _('Draft Sales Receipt'),
# 'in_receipt': _('Draft Purchase Receipt'),
# 'entry': _('Draft Entry'),
# }[self.move_type]
if not self.name or self.name == '/':
draft_name += ' (* %s)' % str(self.id)
else:
draft_name += ' ' + self.name
return draft_name or self.name
# return draft_name or self.name + (show_ref and self.ref
# and ' (%s%s)' % (self.ref[:50],
# '...' if len(self.ref) > 50 else '') or '')
def _get_report_base_filename(self):
return self._get_sales_display_name()
def _get_last_sequence_domain(self, relaxed=False):
self.ensure_one()
if not self.date: # or not self.journal_id
return "WHERE FALSE", {}
where_string = "WHERE company_id= %(company_id)s AND typ = %(typ)s AND name != '/'"
param = {
'typ': self.typ,
'company_id': self.company_id.id
}
if not relaxed:
domain = [('typ', '=', self.typ), ('id', '!=', self.id or self._origin.id),
('name', 'not in', ('/', '', False))] #
reference_move_name = self.search(domain + [('date', '<=', self.date)], order='date desc', limit=1).name
if not reference_move_name:
reference_move_name = self.search(domain, order='date asc', limit=1).name
sequence_number_reset = self._deduce_sequence_number_reset(reference_move_name)
if sequence_number_reset == 'year':
where_string += " AND date_trunc('year', date::timestamp without time zone) = date_trunc('year', " \
"%(date)s) "
param['date'] = self.date
param['anti_regex'] = re.sub(r"\?P<\w+>", "?:", self._sequence_monthly_regex.split('(?P<seq>')[0]) + '$'
elif sequence_number_reset == 'month':
where_string += " AND date_trunc('month', date::timestamp without time zone) = date_trunc('month', " \
"%(date)s) "
param['date'] = self.date
else:
param['anti_regex'] = re.sub(r"\?P<\w+>", "?:", self._sequence_yearly_regex.split('(?P<seq>')[0]) + '$'
if param.get('anti_regex'): # and not self.journal_id.sequence_override_regex:
where_string += " AND sequence_prefix !~ %(anti_regex)s "
# if self.journal_id.refund_sequence:
# if self.move_type in ('out_refund', 'in_refund'):
# where_string += " AND move_type IN ('out_refund', 'in_refund') "
# else:
# where_string += " AND move_type NOT IN ('out_refund', 'in_refund') "
return where_string, param
def _get_starting_sequence(self):
self.ensure_one()
# self.typ.upper()
prefix1 = F"{self.typ.upper()}/{self.company_id.district_id.state_id.code}{self.company_id.district_id.code}"
starting_sequence = "%s/%04d/%02d/00000" % (prefix1, self.date.year, self.date.month)
# if self.journal_id.refund_sequence and self.move_type in ('out_refund', 'in_refund'):
# starting_sequence = "R" + starting_sequence
return starting_sequence
# -*- coding: utf-8 -*-
import logging
from odoo import api, fields, models, _
from odoo.exceptions import ValidationError
from odoo.tools.misc import format_date
import re
from psycopg2 import sql
_logger = logging.getLogger(__name__)
class SequenceMixin(models.AbstractModel):
"""Mechanism used to have an editable sequence number.
Be careful of how you use this regarding the prefixes. More info in the
docstring of _get_last_sequence.
"""
_name = 'sequence.mixin'
_description = "Automatic sequence"
_sequence_field = "name"
_sequence_date_field = "date"
_sequence_index = False
_sequence_monthly_regex = r'^(?P<prefix1>.*?)(?P<year>((?<=\D)|(?<=^))((19|20|21)\d{2}|(\d{2}(?=\D))))(' \
r'?P<prefix2>\D*?)(?P<month>(0[1-9]|1[0-2]))(?P<prefix3>\D+?)(?P<seq>\d*)(' \
r'?P<suffix>\D*?)$'
_sequence_yearly_regex = r'^(?P<prefix1>.*?)(?P<year>((?<=\D)|(?<=^))((19|20|21)?\d{2}))(?P<prefix2>\D+?)(' \
r'?P<seq>\d*)(?P<suffix>\D*?)$'
_sequence_fixed_regex = r'^(?P<prefix1>.*?)(?P<seq>\d{0,9})(?P<suffix>\D*?)$'
sequence_prefix = fields.Char(compute='_compute_split_sequence', store=True)
sequence_number = fields.Integer(compute='_compute_split_sequence', store=True)
def init(self):
# Add an index to optimise the query searching for the highest sequence number
if not self._abstract and self._sequence_index:
index_name = self._table + '_sequence_index'
self.env.cr.execute('SELECT indexname FROM pg_indexes WHERE indexname = %s', (index_name,))
if not self.env.cr.fetchone():
self.env.cr.execute(sql.SQL("""
CREATE INDEX {index_name} ON {table} ({sequence_index}, sequence_prefix desc, sequence_number
desc, {field});
CREATE INDEX {index2_name} ON {table} ({sequence_index}, id desc, sequence_prefix);
""").format(
sequence_index=sql.Identifier(self._sequence_index),
index_name=sql.Identifier(index_name),
index2_name=sql.Identifier(index_name + "2"),
table=sql.Identifier(self._table),
field=sql.Identifier(self._sequence_field),
))
def __init__(self, pool, cr):
api.constrains(self._sequence_field,
self._sequence_date_field)(type(self)._constrains_date_sequence)
return super().__init__(pool, cr)
def _constrains_date_sequence(self):
# Make it possible to bypass the constraint to allow edition of already messed up documents.
# /!\ Do not use this to completely disable the constraint as it will make this mixin unreliable.
constraint_date = fields.Date.to_date(self.env['ir.config_parameter'].sudo().get_param(
'sequence.mixin.constraint_start_date',
'1970-01-01'
))
for record in self:
date = fields.Date.to_date(record[record._sequence_date_field])
sequence = record[record._sequence_field]
if sequence and date and date > constraint_date:
format_values = record._get_sequence_format_param(sequence)[1]
if (
format_values['year'] and format_values['year'] != date.year % 10 ** len(
str(format_values['year']))
or format_values['month'] and format_values['month'] != date.month
):
raise ValidationError(_(
"The %(date_field)s (%(date)s) doesn't match the %(sequence_field)s (%(sequence)s).\n"
"You might want to clear the field %(sequence_field)s before proceeding with the change of "
"the date.",
date=format_date(self.env, date),
sequence=sequence,
date_field=record._fields[record._sequence_date_field]._description_string(self.env),
sequence_field=record._fields[record._sequence_field]._description_string(self.env),
))
@api.depends(lambda self: [self._sequence_field])
def _compute_split_sequence(self):
for record in self:
sequence = record[record._sequence_field] or ''
regex = re.sub(r"\?P<\w+>", "?:",
record._sequence_fixed_regex.replace(r"?P<seq>", "")) # make the seq the only matching group
matching = re.match(regex, sequence)
record.sequence_prefix = sequence[:matching.start(1)]
record.sequence_number = int(matching.group(1) or 0)
@api.model
def _deduce_sequence_number_reset(self, name):
"""Detect if the used sequence resets yearly, montly or never.
:param name: the sequence that is used as a reference to detect the resetting
periodicity. Typically, it is the last before the one you want to give a
sequence.
"""
for regex, ret_val, requirements in [
(self._sequence_monthly_regex, 'month', ['seq', 'month', 'year']),
(self._sequence_yearly_regex, 'year', ['seq', 'year']),
(self._sequence_fixed_regex, 'never', ['seq']),
]:
_logger.info(f"{regex}, {ret_val}, {requirements} ")
match = re.match(regex, name or '')
if match:
groupdict = match.groupdict()
if all(req in groupdict for req in requirements):
return ret_val
raise ValidationError(_(
'The sequence regex should at least contain the seq grouping keys. For instance:\n'
'^(?P<prefix1>.*?)(?P<seq>\d*)(?P<suffix>\D*?)$'
))
def _get_last_sequence_domain(self, relaxed=False):
"""Get the sql domain to retreive the previous sequence number.
This function should be overriden by models heriting from this mixin.
:param relaxed: see _get_last_sequence.
:returns: tuple(where_string, where_params): with
where_string: the entire SQL WHERE clause as a string.
where_params: a dictionary containing the parameters to substitute
at the execution of the query.
"""
self.ensure_one()
return "", {}
def _get_starting_sequence(self):
"""Get a default sequence number.
This function should be overriden by models heriting from this mixin
This number will be incremented so you probably want to start the sequence at 0.
:return: string to use as the default sequence to increment
"""
self.ensure_one()
return "00000000"
def _get_last_sequence(self, relaxed=False):
"""Retrieve the previous sequence.
This is done by taking the number with the greatest alphabetical value within
the domain of _get_last_sequence_domain. This means that the prefix has a
huge importance.
For instance, if you have INV/2019/0001 and INV/2019/0002, when you rename the
last one to FACT/2019/0001, one might expect the next number to be
FACT/2019/0002 but it will be INV/2019/0002 (again) because INV > FACT.
Therefore, changing the prefix might not be convenient during a period, and
would only work when the numbering makes a new start (domain returns by
_get_last_sequence_domain is [], i.e: a new year).
:param field_name: the field that contains the sequence.
:param relaxed: this should be set to True when a previous request didn't find
something without. This allows to find a pattern from a previous period, and
try to adapt it for the new period.
:return: the string of the previous sequence or None if there wasn't any.
"""
self.ensure_one()
if self._sequence_field not in self._fields or not self._fields[self._sequence_field].store:
raise ValidationError(_('%s is not a stored field', self._sequence_field))
where_string, param = self._get_last_sequence_domain(relaxed)
if self.id or self.id.origin:
where_string += " AND id != %(id)s "
param['id'] = self.id or self.id.origin
query = """
UPDATE {table} SET write_date = write_date WHERE id = (
SELECT id FROM {table}
{where_string}
AND sequence_prefix = (SELECT sequence_prefix FROM {table} {where_string} ORDER BY id DESC LIMIT 1)
ORDER BY sequence_number DESC
LIMIT 1
)
RETURNING {field};
""".format(
table=self._table,
where_string=where_string,
field=self._sequence_field,
)
self.flush([self._sequence_field, 'sequence_number', 'sequence_prefix'])
self.env.cr.execute(query, param)
return (self.env.cr.fetchone() or [None])[0]
def _get_sequence_format_param(self, previous):
"""Get the python format and format values for the sequence.
:param previous: the sequence we want to extract the format from
:return tuple(format, format_values):
format is the format string on which we should call .format()
format_values is the dict of values to format the `format` string
``format.format(**format_values)`` should be equal to ``previous``
"""
sequence_number_reset = self._deduce_sequence_number_reset(previous)
regex = self._sequence_fixed_regex
if sequence_number_reset == 'year':
regex = self._sequence_yearly_regex
elif sequence_number_reset == 'month':
regex = self._sequence_monthly_regex
_logger.info(f'{regex} {sequence_number_reset}')
format_values = re.match(regex, previous).groupdict()
format_values['seq_length'] = len(format_values['seq'])
format_values['year_length'] = len(format_values.get('year', ''))
if not format_values.get('seq') and 'prefix1' in format_values and 'suffix' in format_values:
# if we don't have a seq, consider we only have a prefix and not a suffix
format_values['prefix1'] = format_values['suffix']
format_values['suffix'] = ''
for field in ('seq', 'year', 'month'):
format_values[field] = int(format_values.get(field) or 0)
placeholders = re.findall(r'(prefix\d|seq|suffix\d?|year|month)', regex)
format = ''.join(
"{seq:0{seq_length}d}" if s == 'seq' else
"{month:02d}" if s == 'month' else
"{year:0{year_length}d}" if s == 'year' else
"{%s}" % s
for s in placeholders
)
return format, format_values
def _set_next_sequence(self):
"""Set the next sequence.
This method ensures that the field is set both in the ORM and in the database.
This is necessary because we use a database query to get the previous sequence,
and we need that query to always be executed on the latest data.
:param field_name: the field that contains the sequence.
"""
self.ensure_one()
last_sequence = self._get_last_sequence()
new = not last_sequence
if new:
last_sequence = self._get_last_sequence(relaxed=True) or self._get_starting_sequence()
format, format_values = self._get_sequence_format_param(last_sequence)
if new:
format_values['seq'] = 0
format_values['year'] = self[self._sequence_date_field].year % (10 ** format_values['year_length'])
format_values['month'] = self[self._sequence_date_field].month
format_values['seq'] = format_values['seq'] + 1
self[self._sequence_field] = format.format(**format_values)
self._compute_split_sequence()
<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="partner_form_id_bphtb" model="ir.ui.view">
<field name="name">partner.form.id.bphtb.inherit</field>
<field name="model">res.partner</field>
<field name="inherit_id" ref="id_gov.partner_form_id"/>
<field name="groups_id" eval="[(6, 0, [ref('bphtb_kab.group_bphtb_ppat') ])]"/>
<field name="arch" type="xml">
<xpath expr="//field[@name='type']" position="attributes">
<attribute name="invisible">True</attribute>
</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>
......@@ -35,7 +34,9 @@
<field name="domain">[('type','=','wp')]</field>
<field name="context">{
'default_is_company': True,
'default_type': 'ppat'}
'default_type': 'wp',
'show_type': 0}
</field>
<field name="help" type="html">
<p class="o_view_nocontent_smiling_face">
......
<odoo>
<data>
<record id="action_report_bphtb_sspd" model="ir.actions.report">
<field name="name">Format SSPD PDF</field>
<field name="model">bphtb.sales</field>
......@@ -10,29 +11,30 @@
<field name="binding_type">report</field>
</record>
<record id="view_bphtb_sspd_filter" model="ir.ui.view">
<record id="view_bphtb_sspd_filter" model="ir.ui.view">
<field name="name">view.bphtb.sspd.filter</field>
<field name="model">bphtb.sales</field>
<field name="arch" type="xml">
<search string="Search SSPD">
<!-- <field name="name" string="SSPD" filter_domain="[('nop', 'ilike', self)]"/>-->
<!-- <field name="name" string="SSPD" filter_domain="[('nop', 'ilike', self)]"/>-->
<field name="ppat_id"/>
<field name="wp_id"/>
<field name="request_date"/>
<field name="date"/>
<separator/>
<filter string="Draft" name="draft" domain="[('state', '=', 'draft')]" help="Draft"/>
<filter string="Confirmed" name="confirmed" domain="[('state', '=', 'confirmed')]" help="Confirmed"/>
<filter string="Confirmed" name="confirmed" domain="[('state', '=', 'confirmed')]"
help="Confirmed"/>
<filter string="Canceled" name="canceled" domain="[('state', '=', 'canceled')]"/>
<separator/>
<!-- <filter string="Jenis" name="jenis" date="jenis_id"/>-->
<filter string="Date" name="date" date="request_date"/>
<!-- <separator/>-->
<!-- <filter string="Jenis" name="jenis" date="jenis_id"/>-->
<filter string="Date" name="date" date="date"/>
<!-- <separator/>-->
<group expand="0" string="Group By">
<filter string="PPAT" name="ppat" domain="[]" context="{'group_by': 'ppat_id'}"/>
<filter string="Jenis" name="jenis" domain="[]" context="{'group_by': 'jenis_id'}"/>
<filter string="WP" name="wp" domain="[]" context="{'group_by': 'wp_id'}"/>
<filter string="Status" name="status" domain="[]" context="{'group_by': 'state'}"/>
<filter string="Date" name="by_date" domain="[]" context="{'group_by': 'request_date'}"/>
<filter string="Date" name="by_date" domain="[]" context="{'group_by': 'date'}"/>
</group>
</search>
</field>
......@@ -54,6 +56,8 @@
<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="date"/>
<field name="name" string="Invoice No"/>
<field name="nop" string="NOP"/>
<field name="tax_year" string="Tahun"/>
<field name="jenis_id" string="Jenis"/>
......@@ -94,13 +98,16 @@
attrs="{'invisible': [('state', '!=', 'draft')]}"/>
<widget name="web_ribbon" title="Confirmed"
attrs="{'invisible': [('state', '!=', 'confirmed')]}"/>
<h1>
<h2>
<field name="company_id" readonly="1"/>
</h1>
</h2>
<h3>
<field name="district_id" readonly="1"/>
</h3>
<group>
<field name="typ" readonly="1"/>
<field name="ppat_id" domain="[('type','=','ppat')]"/>
<field name="request_date"/>
<field name="date"/>
</group>
<notebook col_span="4">
<page name="op" string="Objek Pajak">
......
Markdown is supported
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!