brotherton-erpnext/erpnext/patches/v14_0/migrate_gl_to_payment_ledger.py

141 lines
3.7 KiB
Python

import frappe
from frappe import qb
from frappe.query_builder import Case, CustomFunction
from frappe.query_builder.custom import ConstantColumn
from frappe.query_builder.functions import IfNull
from erpnext.accounts.doctype.accounting_dimension.accounting_dimension import (
get_dimensions,
make_dimension_in_accounting_doctypes,
)
def create_accounting_dimension_fields():
dimensions_and_defaults = get_dimensions()
if dimensions_and_defaults:
for dimension in dimensions_and_defaults[0]:
make_dimension_in_accounting_doctypes(dimension, ["Payment Ledger Entry"])
def generate_name_for_payment_ledger_entries(gl_entries):
for index, entry in enumerate(gl_entries, 1):
entry.name = index
def get_columns():
columns = [
"name",
"creation",
"modified",
"modified_by",
"owner",
"docstatus",
"posting_date",
"account_type",
"account",
"party_type",
"party",
"voucher_type",
"voucher_no",
"against_voucher_type",
"against_voucher_no",
"amount",
"amount_in_account_currency",
"account_currency",
"company",
"cost_center",
"due_date",
"finance_book",
]
dimensions_and_defaults = get_dimensions()
if dimensions_and_defaults:
for dimension in dimensions_and_defaults[0]:
columns.append(dimension.fieldname)
return columns
def build_insert_query():
ple = qb.DocType("Payment Ledger Entry")
columns = get_columns()
insert_query = qb.into(ple)
# build 'insert' columns in query
insert_query = insert_query.columns(tuple(columns))
return insert_query
def insert_chunk_into_payment_ledger(insert_query, gl_entries):
if gl_entries:
columns = get_columns()
# build tuple of data with same column order
for entry in gl_entries:
data = ()
for column in columns:
data += (entry[column],)
insert_query = insert_query.insert(data)
insert_query.run()
def execute():
if frappe.reload_doc("accounts", "doctype", "payment_ledger_entry"):
# create accounting dimension fields in Payment Ledger
create_accounting_dimension_fields()
gl = qb.DocType("GL Entry")
account = qb.DocType("Account")
ifelse = CustomFunction("IF", ["condition", "then", "else"])
gl_entries = (
qb.from_(gl)
.inner_join(account)
.on((gl.account == account.name) & (account.account_type.isin(["Receivable", "Payable"])))
.select(
gl.star,
ConstantColumn(1).as_("docstatus"),
account.account_type.as_("account_type"),
IfNull(
ifelse(gl.against_voucher_type == "", None, gl.against_voucher_type), gl.voucher_type
).as_("against_voucher_type"),
IfNull(ifelse(gl.against_voucher == "", None, gl.against_voucher), gl.voucher_no).as_(
"against_voucher_no"
),
# convert debit/credit to amount
Case()
.when(account.account_type == "Receivable", gl.debit - gl.credit)
.else_(gl.credit - gl.debit)
.as_("amount"),
# convert debit/credit in account currency to amount in account currency
Case()
.when(
account.account_type == "Receivable",
gl.debit_in_account_currency - gl.credit_in_account_currency,
)
.else_(gl.credit_in_account_currency - gl.debit_in_account_currency)
.as_("amount_in_account_currency"),
)
.where(gl.is_cancelled == 0)
.orderby(gl.creation)
.run(as_dict=True)
)
# primary key(name) for payment ledger records
generate_name_for_payment_ledger_entries(gl_entries)
# split data into chunks
chunk_size = 1000
try:
for i in range(0, len(gl_entries), chunk_size):
insert_query = build_insert_query()
insert_chunk_into_payment_ledger(insert_query, gl_entries[i : i + chunk_size])
frappe.db.commit()
except Exception as err:
frappe.db.rollback()
ple = qb.DocType("Payment Ledger Entry")
qb.from_(ple).delete().where(ple.docstatus >= 0).run()
frappe.db.commit()
raise err