211 lines
9.4 KiB
Python
211 lines
9.4 KiB
Python
# -*- coding: utf-8 -*-
|
|
import logging
|
|
import pytz
|
|
from datetime import datetime, timedelta
|
|
|
|
from odoo import models, fields, api, _
|
|
from odoo.exceptions import UserError
|
|
|
|
_logger = logging.getLogger(__name__)
|
|
|
|
class AccountJournal(models.Model):
|
|
_inherit = 'account.journal'
|
|
|
|
@api.model
|
|
def _cron_create_cash_intercompany_entries(self, target_date=None, journal_ids=None):
|
|
"""Cron action to automatically generate cash centralization entries.
|
|
Runs daily at 10:00 AM WIB (03:00 AM UTC) to process the previous day's net cash movements.
|
|
"""
|
|
# Determine the target date (previous day in WIB by default)
|
|
if not target_date:
|
|
jakarta_tz = pytz.timezone('Asia/Jakarta')
|
|
now_jakarta = datetime.now(jakarta_tz)
|
|
prev_day = (now_jakarta - timedelta(days=1)).date()
|
|
elif isinstance(target_date, str):
|
|
prev_day = fields.Date.from_string(target_date)
|
|
else:
|
|
prev_day = target_date
|
|
|
|
_logger.info("Starting cash intercompany centralization for date %s", prev_day)
|
|
|
|
# Search for all cash journals that have centralization configured
|
|
domain = [
|
|
('type', '=', 'cash'),
|
|
('is_centralized', '=', True)
|
|
]
|
|
if journal_ids:
|
|
domain.append(('id', 'in', journal_ids))
|
|
journals = self.sudo().search(domain)
|
|
|
|
results = []
|
|
|
|
for journal in journals:
|
|
# Check basic configuration requirements
|
|
parent_company = journal.parent_company_id
|
|
parent_journal = journal.parent_journal_id
|
|
branch_rk_account = journal.branch_intercompany_account_id
|
|
parent_rk_account = journal.parent_intercompany_account_id
|
|
|
|
if not parent_company or not parent_journal or not branch_rk_account or not parent_rk_account:
|
|
msg = _("Centralization settings are incomplete (Parent Company, Parent Journal, or RK accounts not configured).")
|
|
_logger.warning("Skipping cash journal %s (%s): %s", journal.name, journal.company_id.name, msg)
|
|
results.append((journal.id, 'incomplete', msg))
|
|
continue
|
|
|
|
# Ensure default cash account is present
|
|
branch_cash_account = journal.default_account_id
|
|
if not branch_cash_account:
|
|
msg = _("Journal has no default account configured.")
|
|
_logger.warning("Skipping cash journal %s (%s): %s", journal.name, journal.company_id.name, msg)
|
|
results.append((journal.id, 'no_account', msg))
|
|
continue
|
|
|
|
# Check for existing intercompany transfer entries for this day to prevent duplication
|
|
existing_move = self.env['account.move'].sudo().search([
|
|
('journal_id', '=', journal.id),
|
|
('date', '=', prev_day),
|
|
('is_cash_intercompany_transfer', '=', True),
|
|
('state', '!=', 'cancel')
|
|
], limit=1)
|
|
|
|
if existing_move:
|
|
msg = _("Centralization already run for this date. (Move: %s)") % existing_move.name
|
|
_logger.info("Skipping cash journal %s (%s): %s", journal.name, journal.company_id.name, msg)
|
|
results.append((journal.id, 'already_run', msg))
|
|
continue
|
|
|
|
# Find all posted journal items on the branch cash account for the target date,
|
|
# excluding centralization transfers we generate.
|
|
lines = self.env['account.move.line'].sudo().search([
|
|
('company_id', '=', journal.company_id.id),
|
|
('account_id', '=', branch_cash_account.id),
|
|
('date', '=', prev_day),
|
|
('parent_state', '=', 'posted'),
|
|
('move_id.is_cash_intercompany_transfer', '=', False)
|
|
])
|
|
|
|
# Calculate net movement (debit - credit)
|
|
debit_sum = sum(lines.mapped('debit'))
|
|
credit_sum = sum(lines.mapped('credit'))
|
|
net_balance = debit_sum - credit_sum
|
|
|
|
_logger.info(
|
|
"Journal %s (%s) net movement on %s: Debits=%s, Credits=%s, Net=%s",
|
|
journal.name, journal.company_id.name, prev_day, debit_sum, credit_sum, net_balance
|
|
)
|
|
|
|
if net_balance <= 0.0:
|
|
msg = _("No positive net cash movement (Net Balance: %s).") % net_balance
|
|
_logger.info("Skipping journal %s on %s: %s", journal.name, prev_day, msg)
|
|
results.append((journal.id, 'no_balance', msg))
|
|
continue
|
|
|
|
# Identify the parent cash account ( Kas Besar Hasil Penjualan - 111103)
|
|
# Search by code 111103 with the parent company's context
|
|
parent_cash_account = self.env['account.account'].sudo().with_company(parent_company).search([
|
|
('code', '=', '111103'),
|
|
('company_ids', 'in', [parent_company.id])
|
|
], limit=1)
|
|
|
|
if not parent_cash_account:
|
|
parent_cash_account = parent_journal.default_account_id
|
|
|
|
if not parent_cash_account:
|
|
msg = _("Parent cash account (111103 or default account of parent journal) not found in company %s.") % parent_company.name
|
|
_logger.error("Skipping journal %s: %s", journal.name, msg)
|
|
results.append((journal.id, 'no_parent_account', msg))
|
|
continue
|
|
|
|
# Handle currency conversion if parent and branch currencies differ
|
|
parent_currency = parent_company.currency_id
|
|
branch_currency = journal.company_id.currency_id
|
|
|
|
if branch_currency != parent_currency:
|
|
amount_parent_curr = branch_currency._convert(
|
|
net_balance, parent_currency, parent_company, prev_day
|
|
)
|
|
else:
|
|
amount_parent_curr = net_balance
|
|
|
|
# 1. Create the Branch company journal entry
|
|
branch_line_ids = [
|
|
(0, 0, {
|
|
'name': _("Cash Centralization - %s") % prev_day,
|
|
'account_id': branch_rk_account.id,
|
|
'debit': net_balance,
|
|
'credit': 0.0,
|
|
'company_id': journal.company_id.id,
|
|
'display_type': 'product',
|
|
}),
|
|
(0, 0, {
|
|
'name': _("Cash Centralization - %s") % prev_day,
|
|
'account_id': branch_cash_account.id,
|
|
'debit': 0.0,
|
|
'credit': net_balance,
|
|
'company_id': journal.company_id.id,
|
|
'display_type': 'product',
|
|
})
|
|
]
|
|
|
|
# 2. Create the Parent company journal entry
|
|
parent_line_ids = [
|
|
(0, 0, {
|
|
'name': _("Cash Centralization: %s - %s") % (journal.company_id.name, prev_day),
|
|
'account_id': parent_cash_account.id,
|
|
'debit': amount_parent_curr,
|
|
'credit': 0.0,
|
|
'partner_id': journal.company_id.partner_id.id,
|
|
'company_id': parent_company.id,
|
|
'display_type': 'product',
|
|
}),
|
|
(0, 0, {
|
|
'name': _("Cash Centralization: %s - %s") % (journal.company_id.name, prev_day),
|
|
'account_id': parent_rk_account.id,
|
|
'debit': 0.0,
|
|
'credit': amount_parent_curr,
|
|
'partner_id': journal.company_id.partner_id.id,
|
|
'company_id': parent_company.id,
|
|
'display_type': 'product',
|
|
})
|
|
]
|
|
|
|
try:
|
|
# Create and post branch entry
|
|
branch_move = self.env['account.move'].sudo().with_company(journal.company_id).create({
|
|
'move_type': 'entry',
|
|
'date': prev_day,
|
|
'company_id': journal.company_id.id,
|
|
'journal_id': journal.id,
|
|
'ref': f"Cash Centralization - {prev_day}",
|
|
'is_cash_intercompany_transfer': True,
|
|
'line_ids': branch_line_ids,
|
|
})
|
|
branch_move.action_post()
|
|
|
|
# Create and post parent entry
|
|
parent_move = self.env['account.move'].sudo().with_company(parent_company).create({
|
|
'move_type': 'entry',
|
|
'date': prev_day,
|
|
'company_id': parent_company.id,
|
|
'journal_id': parent_journal.id,
|
|
'partner_id': journal.company_id.partner_id.id,
|
|
'ref': f"Cash Centralization - {prev_day} ({journal.company_id.name})",
|
|
'is_cash_intercompany_transfer': True,
|
|
'line_ids': parent_line_ids,
|
|
})
|
|
parent_move.action_post()
|
|
|
|
msg = _("Successfully created branch entry %s and parent entry %s.") % (branch_move.name, parent_move.name)
|
|
_logger.info("Successfully centralized cash for %s: %s", journal.name, msg)
|
|
results.append((journal.id, 'success', msg))
|
|
except Exception as e:
|
|
msg = str(e)
|
|
_logger.error(
|
|
"Failed to create cash centralization entries for journal %s on %s: %s",
|
|
journal.name, prev_day, msg
|
|
)
|
|
results.append((journal.id, 'error', msg))
|
|
|
|
return results
|
|
|