Advertisement
SherinKR

budget_tool.py

Mar 2nd, 2024
809
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
Python 17.99 KB | Source Code | 0 0
  1. # Copyright (c) 2023, New Indictranstech and contributors
  2. # For license information, please see license.txt
  3.  
  4. import frappe
  5. import json
  6. from frappe.model.document import Document
  7. from pradan.pradan.utils import *
  8. from frappe.utils import get_absolute_url
  9.  
  10. class BudgetTool(Document):
  11.     #Not saving this document to avoid modified error when more than one persons/sessions are doing same action in this page
  12.     def validate(self):
  13.         frappe.throw('You were not able to do this action. Please try again.')
  14.  
  15. @frappe.whitelist()
  16. def create_budget_if_not_exists(project):
  17.     '''
  18.         Method to create Budget if it is not created
  19.     '''
  20.     budget_id = get_budget_from_project(project)
  21.     if not budget_id:
  22.         budget_doc = frappe.new_doc('Pradan Budget')
  23.         budget_doc.project = project
  24.         budget_doc.save()
  25.         budget_id = budget_doc.name
  26.  
  27.     #Preparing HTML Tables
  28.     budget_doc = frappe.get_doc('Pradan Budget', budget_id)
  29.     data = []
  30.     allocation_status = 'Draft'
  31.     workflow_state = budget_doc.workflow_state
  32.     if budget_doc.docstatus:
  33.         allocation_status='Submitted'
  34.     budget_items = budget_doc.ledger_mapping
  35.  
  36.     #Defining Columns
  37.     columns = ['BL Code', 'BLI', 'SBL Code', 'SBLI']
  38.     year = 1
  39.     columns.append('Project Years Maximum')
  40.     for year_wise in budget_doc.year_wise_breakup:
  41.         columns.append('Project Year {0}'.format(year))
  42.         year += 1
  43.     columns.append('Project Total')
  44.     columns.append('Check')
  45.     columns.append('FY Maximum')
  46.     for fy_year in budget_doc.fy_wise_breakup:
  47.         columns.append(fy_year.fiscal_year)
  48.     columns.append('FY Total')
  49.     columns.append('FY Check')
  50.     columns.append('Budget Note')
  51.  
  52.     #Defining Data rows
  53.     previous_budget_line_item = budget_items[0].budget_line_item
  54.     for budget_item in budget_items:
  55.         if previous_budget_line_item != budget_item.budget_line_item:
  56.             sub_total_row = get_sub_total_row(budget_id, previous_budget_line_item)
  57.             data.append(sub_total_row)
  58.             previous_budget_line_item = budget_item.budget_line_item
  59.         row = get_budget_item_details(project, budget_item)
  60.         data.append(row)
  61.     if budget_items:
  62.         sub_total_row = get_sub_total_row(budget_id, budget_items[-1].budget_line_item)
  63.         data.append(sub_total_row)
  64.  
  65.     total_row = get_total_row(project)
  66.  
  67.     html_data = frappe.render_template('pradan/doctype/budget_tool/budget_tool.html', {
  68.         'columns': columns,
  69.         'data': data,
  70.         'status': allocation_status,
  71.         'total_row': total_row,
  72.         'workflow_state': workflow_state
  73.     });
  74.     return {
  75.         'html':html_data,
  76.         'budget_id': budget_doc.name,
  77.         'status': allocation_status,
  78.         'workflow_state': budget_doc.workflow_state,
  79.         'budget_owner': budget_doc.owner
  80.     }
  81.  
  82. def get_budget_item_details(project, budget_item):
  83.     budget_id = get_budget_from_project(project)
  84.     budget_line_item = budget_item.budget_line_item
  85.     sub_budget_item = budget_item.sub_budget_line_item
  86.     fy_max = budget_item.fy_maximum
  87.     year_max = budget_item.year_maximum
  88.     budget_notes = budget_item.budget_notes or ''
  89.     data = []
  90.  
  91.     #Set comon data
  92.     data.append({ 'type':'text', 'value': budget_line_item, 'read_only':1, 'primary': 1, 'ref_link': get_absolute_url('Budget Item', budget_line_item) })
  93.     data.append({ 'type':'text', 'value': frappe.db.get_value('Budget Item', budget_line_item, 'budget_item_name'), 'read_only':1, 'primary': 1 })
  94.     data.append({ 'type':'text', 'value': sub_budget_item, 'read_only':1, 'primary': 1, 'ref_link': get_absolute_url('Budget Item', sub_budget_item) })
  95.     data.append({ 'type':'text', 'value': frappe.db.get_value('Budget Item', sub_budget_item, 'budget_item_name'), 'read_only':1, 'primary': 1 })
  96.  
  97.     #Get FY wise data
  98.     query = '''
  99.         SELECT
  100.             fiscal_year,
  101.             available_amount
  102.         FROM
  103.             `tabPradan Budget Items`
  104.         WHERE
  105.             parent = '{0}' AND
  106.             sub_budget_item = '{1}'
  107.         ORDER BY
  108.             idx ASC
  109.     '''
  110.     budget_item_details = frappe.db.sql(query.format(budget_id, sub_budget_item), as_dict=1)
  111.     if budget_item_details:
  112.         #Get year wise data
  113.         query = '''
  114.             SELECT
  115.                 sub_budget_item,
  116.                 available_amount
  117.             FROM
  118.                 `tabYear wise Allocation`
  119.             WHERE
  120.                 parent = '{0}' AND
  121.                 sub_budget_item = '{1}'
  122.             ORDER BY
  123.                 idx ASC
  124.         '''
  125.         year_wise_budget = frappe.db.sql(query.format(budget_id, sub_budget_item), as_dict=1)
  126.         year_total = 0
  127.         bg_color = '#ffffff'
  128.         data.append({ 'type':'number', 'value': int(year_max), 'read_only':0, 'class_name':'text-right year_max', 'bg_color':bg_color })
  129.         for row in year_wise_budget:
  130.             year_total += row.available_amount
  131.             data.append({ 'type':'number', 'value': int(row.available_amount), 'read_only':0, 'class_name':'text-right year_input', 'bg_color':bg_color })
  132.         year_balance = year_max - year_total
  133.         data.append({ 'type':'number', 'value': int(year_total), 'read_only':1, 'class_name':'text-right year_total', 'bg_color':bg_color })
  134.         data.append({ 'type':'number', 'value': int(year_balance), 'read_only':1, 'class_name':'text-right year_balance', 'bg_color':bg_color })
  135.         fy_total = 0
  136.         data.append({ 'type':'number', 'value': int(fy_max), 'read_only':1, 'class_name':'text-right fy_max', 'bg_color':bg_color })
  137.         for row in budget_item_details:
  138.             if is_allocated(budget_id, row.fiscal_year, sub_budget_item):
  139.                 bg_color = '#23cd4a'
  140.             else:
  141.                 bg_color = '#ffffff'
  142.             fy_total += row.available_amount
  143.             fy_read_only = is_period_closing_voucher_created(row.fiscal_year)
  144.             data.append({ 'type':'number', 'value': int(row.available_amount), 'read_only':fy_read_only, 'class_name':'text-right fy_input', 'bg_color':bg_color })
  145.         fy_balance = fy_max - fy_total
  146.         data.append({ 'type':'number', 'value': int(fy_total), 'read_only':1, 'class_name':'text-right fy_total', 'bg_color':bg_color })
  147.         data.append({ 'type':'number', 'value': int(fy_balance), 'read_only':1, 'class_name':'text-right fy_balance', 'bg_color':bg_color })
  148.         data.append({ 'type':'text', 'value': budget_notes, 'read_only':0, 'class_name':'budget_notes', 'bg_color':bg_color })
  149.         allocation_details = get_allocation_status(project, sub_budget_item)
  150.         allocated = allocation_details.get('allocated')
  151.         allocation_message = allocation_details.get('message') or ''
  152.         if 'Budget Admin (HO)' not in frappe.get_roles(frappe.session.user):
  153.             allocated = 1
  154.             allocation_message = "You don't have permission to Allocate"
  155.         data.append({ 'type':'button', 'allocated':allocated, 'message':allocation_message })
  156.     return data
  157.  
  158. @frappe.whitelist()
  159. def save_budget_data(project, data):
  160.     budget_id = get_budget_from_project(project)
  161.     ledger_mapping = frappe.db.get_all('Ledger Mapping', filters={ 'parent': budget_id}, fields=['name', 'parent', 'sub_budget_line_item'], order_by='idx asc')
  162.     budget_items_count = len(ledger_mapping)
  163.     year_wise_breakup = frappe.db.get_all('Year wise Breakup', filters={ 'parent': budget_id}, fields=['name', 'parent', 'from_date', 'to_date'], order_by='idx asc')
  164.     years = len(year_wise_breakup)
  165.     fy_wise_breakup = frappe.db.get_all('FY wise Breakup', filters={ 'parent': budget_id}, fields=['name', 'parent', 'fiscal_year'], order_by='idx asc')
  166.     fys = len(fy_wise_breakup)
  167.     data = json.loads(data)
  168.     idx = 0
  169.     for row in data:
  170.         sub_budget_item = ledger_mapping[idx].sub_budget_line_item
  171.         year_wise_budget = []
  172.         fy_wise_budget = []
  173.         year_max = 0
  174.         fy_max = 0
  175.         budget_notes = ''
  176.         if years:
  177.             #Set year wise available_amount
  178.             year_max = row[5]
  179.             j = 6
  180.             for year_wise in year_wise_breakup:
  181.                 amount = row[j] or 0
  182.                 update_year_wise_allocation_id(budget_id, sub_budget_item, year_wise.from_date, year_wise.to_date, float(amount))
  183.                 j += 1
  184.  
  185.             #Set FY wise available_amount
  186.             if fys:
  187.                 fy_max = row[8+years]
  188.                 k = 9+years
  189.                 for fy_wise in fy_wise_breakup:
  190.                     amount = row[k] or 0
  191.                     update_fy_wise_allocation_id(budget_id, sub_budget_item, fy_wise.fiscal_year, float(amount))
  192.                     k += 1
  193.                 budget_notes = row[k+2]
  194.  
  195.         #set maximum for fy and year wise, budget notes
  196.         row_id = ledger_mapping[idx].name
  197.         frappe.db.set_value('Ledger Mapping', row_id, 'year_maximum', year_max)
  198.         frappe.db.set_value('Ledger Mapping', row_id, 'fy_maximum', fy_max)
  199.         frappe.db.set_value('Ledger Mapping', row_id, 'budget_notes', budget_notes)
  200.         idx += 1
  201.  
  202.     #To trigger toal calculation at databasebudget_item_details
  203.     budget_doc = frappe.get_doc('Pradan Budget', budget_id)
  204.     budget_doc.save()
  205.     return 1
  206.  
  207. @frappe.whitelist()
  208. def submit_form(project):
  209.     budget_id = get_budget_from_project(project)
  210.     if budget_id:
  211.         budget_doc = frappe.get_doc('Pradan Budget', budget_id)
  212.         budget_doc.submit()
  213.         return 1
  214.     else:
  215.         return 0
  216.  
  217. @frappe.whitelist()
  218. def get_allocation_data(project, sub_budget_item):
  219.     '''
  220.         Method to get remaining Fiscal Years and teams that need to be allocated
  221.     '''
  222.     fiscal_years = []
  223.     available_amount = []
  224.     labels = []
  225.     budget_id = get_budget_from_project(project)
  226.     if frappe.db.exists('Pradan Budget Items', { 'parent':budget_id, 'sub_budget_item':sub_budget_item }):
  227.         fiscal_years = frappe.db.get_all('Pradan Budget Items', { 'parent':budget_id, 'sub_budget_item':sub_budget_item }, pluck='fiscal_year', order_by='fiscal_year asc')
  228.         available_amount = frappe.db.get_all('Pradan Budget Items', { 'parent':budget_id, 'sub_budget_item':sub_budget_item }, pluck='available_amount', order_by='fiscal_year asc')
  229.         allocation_status = frappe.db.get_all('Pradan Budget Items', { 'parent':budget_id, 'sub_budget_item':sub_budget_item }, pluck='allocated', order_by='fiscal_year asc')
  230.         for status in allocation_status:
  231.             label = 'Reallocate' if status else 'Allocate'
  232.             labels.append(label)
  233.     return { 'fiscal_years':fiscal_years, 'available_amount':available_amount, 'labels':labels }
  234.  
  235. @frappe.whitelist()
  236. def get_allocation_status(project, sub_budget_item):
  237.     '''
  238.         Method will return 1 if budget allocation is done for all fiscal years else return 0. Also Budget should be Submitted
  239.     '''
  240.     budget_id = get_budget_from_project(project)
  241.     workflow_state = frappe.db.get_value('Pradan Budget', budget_id, 'workflow_state')
  242.     if workflow_state=='Approved':
  243.         message = "Already allocated"
  244.     else:
  245.         message = "Budget not Approved"
  246.     if workflow_state=='Approved':
  247.         return { 'allocated':0, 'message':message }
  248.     else:
  249.         return { 'allocated':1, 'message':message }
  250.  
  251. @frappe.whitelist()
  252. def get_total_row(project):
  253.     budget_id = get_budget_from_project(project)
  254.     budget_doc = frappe.get_doc('Pradan Budget', budget_id)
  255.     total_row = []
  256.     # Setting primary values
  257.     total_row.append({ 'value':'' })
  258.     total_row.append({ 'value':'' })
  259.     total_row.append({ 'value':'' })
  260.     total_row.append({ 'value':'Total' })
  261.     total_row.append({ 'value':'' })
  262.  
  263.     # Setting totals for years
  264.     total_row.append({ 'value': int(budget_doc.total_year_maximum) })
  265.     for row in budget_doc.year_wise_breakup:
  266.         total_row.append({ 'value': int(row.amount) })
  267.     total_row.append({ 'value': int(budget_doc.year_wise_total) })
  268.     total_row.append({ 'value': int(budget_doc.total_year_maximum - budget_doc.year_wise_total) })
  269.  
  270.     # Setting totals for fys
  271.     total_row.append({ 'value': int(budget_doc.total_fy_maximum) })
  272.     for row in budget_doc.fy_wise_breakup:
  273.         total_row.append({ 'value': int(row.amount) })
  274.     total_row.append({ 'value': int(budget_doc.fy_wise_total) })
  275.     total_row.append({ 'value': int(budget_doc.total_fy_maximum - budget_doc.fy_wise_total) })
  276.     return total_row
  277.  
  278. def update_fy_wise_allocation_id(budget_id, sub_budget_item, fy, amount):
  279.     '''
  280.         Method to get id of `allocations` child table row
  281.     '''
  282.     if frappe.db.exists('Pradan Budget Items', { 'parent':budget_id, 'sub_budget_item':sub_budget_item , 'fiscal_year':fy }):
  283.         row_id = frappe.db.get_value('Pradan Budget Items', { 'parent':budget_id, 'sub_budget_item':sub_budget_item , 'fiscal_year':fy })
  284.         frappe.db.set_value('Pradan Budget Items', row_id, 'available_amount', amount)
  285.  
  286. def update_year_wise_allocation_id(budget_id, sub_budget_item, from_date, to_date, amount):
  287.     '''
  288.         Method to get id of `year_wise_allocation` child table row
  289.     '''
  290.     if frappe.db.exists('Year wise Allocation', { 'parent':budget_id, 'sub_budget_item':sub_budget_item , 'from_date':from_date, 'to_date':to_date }):
  291.         row_id = frappe.db.get_value('Year wise Allocation', { 'parent':budget_id, 'sub_budget_item':sub_budget_item , 'from_date':from_date, 'to_date':to_date })
  292.         frappe.db.set_value('Year wise Allocation', row_id, 'available_amount', amount)
  293.  
  294. def get_sub_total_row(budget_id, budget_line_item):
  295.     '''
  296.         Method to get Sub Total row for each budget line item
  297.     '''
  298.     budget_doc = frappe.get_doc('Pradan Budget', budget_id)
  299.     sub_total_row = []
  300.     sub_total_row.append({ 'type':'text', 'value': '', 'read_only':1, 'class_name':'sub_total', 'style':'border-right: 0 !important;' })
  301.     sub_total_row.append({ 'type':'text', 'value': '', 'read_only':1, 'class_name':'sub_total', 'style':'border-left: 0 !important; border-right: 0 !important;' })
  302.     sub_total_row.append({ 'type':'text', 'value': '', 'read_only':1, 'class_name':'sub_total', 'style':'border-left: 0 !important; border-right: 0 !important;' })
  303.     sub_total_row.append({ 'type':'text', 'value': 'Sub Total', 'read_only':1, 'class_name':'sub_total', 'style':'border-left: 0 !important;' })
  304.  
  305.     #Year wise total
  306.     year_totals = get_budget_item_wise_year_total(budget_id, budget_line_item)
  307.     year_maximum = year_totals.get('year_maximum') or 0
  308.     year_total = year_totals.get('year_total') or 0
  309.     year_balance = year_totals.get('year_balance') or 0
  310.     sub_total_row.append({ 'value': int(year_maximum), 'read_only':1, 'class_name':'text-right sub_total' })
  311.     for row in budget_doc.year_wise_breakup:
  312.         amount = get_budget_item_wise_total_for_an_year(budget_id, budget_line_item, row.from_date, row.to_date)
  313.         sub_total_row.append({ 'value': int(amount), 'read_only':1, 'class_name':'text-right sub_total' })
  314.     sub_total_row.append({ 'value': int(year_total), 'read_only':1, 'class_name':'text-right sub_total' })
  315.     sub_total_row.append({ 'value': int(year_balance), 'read_only':1, 'class_name':'text-right sub_total' })
  316.  
  317.     #FY wise total
  318.     fy_totals = get_budget_item_wise_fy_total(budget_id, budget_line_item)
  319.     fy_maximum = fy_totals.get('fy_maximum') or 0
  320.     fy_total = fy_totals.get('fy_total') or 0
  321.     fy_balance = fy_totals.get('fy_balance') or 0
  322.     sub_total_row.append({ 'value': int(fy_maximum), 'read_only':1, 'class_name':'text-right sub_total' })
  323.     for row in budget_doc.fy_wise_breakup:
  324.         amount = get_budget_item_wise_total_for_an_fy(budget_id, budget_line_item, row.fiscal_year)
  325.         sub_total_row.append({ 'value': int(amount), 'read_only':1, 'class_name':'text-right sub_total' })
  326.     sub_total_row.append({ 'value': int(fy_total), 'read_only':1, 'class_name':'text-right sub_total' })
  327.     sub_total_row.append({ 'value': int(fy_balance), 'read_only':1, 'class_name':'text-right sub_total' })
  328.     return sub_total_row
  329.  
  330. def get_budget_item_wise_year_total(budget_id, budget_line_item):
  331.     '''
  332.         Method to get maximum, total and balance year value for a budget item
  333.     '''
  334.     year_maximum, year_total, year_balance = 0, 0, 0
  335.     query = '''
  336.         SELECT
  337.             SUM(year_maximum) as year_maximum,
  338.             SUM(year_total) as year_total
  339.         FROM
  340.             `tabLedger Mapping`
  341.         WHERE
  342.             parent = '{0}' AND
  343.             budget_line_item = '{1}'
  344.     '''
  345.     result = frappe.db.sql(query.format(budget_id, budget_line_item), as_dict=1)
  346.     if result:
  347.         year_maximum = result[0].get('year_maximum') or 0
  348.         year_total = result[0].get('year_total') or 0
  349.         year_balance = year_maximum - year_total
  350.     return { 'year_maximum': year_maximum, 'year_total': year_total, 'year_balance': year_balance }
  351.  
  352. def get_budget_item_wise_fy_total(budget_id, budget_line_item):
  353.     '''
  354.         Method to get maximum, total and balance fy value for a budget item
  355.     '''
  356.     fy_maximum, fy_total, fy_balance = 0, 0, 0
  357.     query = '''
  358.         SELECT
  359.             SUM(fy_maximum) as fy_maximum,
  360.             SUM(fy_total) as fy_total
  361.         FROM
  362.             `tabLedger Mapping`
  363.         WHERE
  364.             parent = '{0}' AND
  365.             budget_line_item = '{1}'
  366.     '''
  367.     result = frappe.db.sql(query.format(budget_id, budget_line_item), as_dict=1)
  368.     if result:
  369.         fy_maximum = result[0].get('fy_maximum') or 0
  370.         fy_total = result[0].get('fy_total') or 0
  371.         fy_balance = fy_maximum - fy_total
  372.     return { 'fy_maximum': fy_maximum, 'fy_total': fy_total, 'fy_balance': fy_balance }
  373.  
  374. def get_budget_item_wise_total_for_an_fy(budget_id, budget_line_item, fiscal_year):
  375.     '''
  376.         Method to get total availability for an fiscal year
  377.     '''
  378.     total_amount = 0
  379.     query = '''
  380.         SELECT
  381.             SUM(available_amount) as total_amount
  382.         FROM
  383.             `tabPradan Budget Items`
  384.         WHERE
  385.             parent = '{0}' AND
  386.             budget_item = '{1}' AND
  387.             fiscal_year = '{2}'
  388.     '''
  389.     result = frappe.db.sql(query.format(budget_id, budget_line_item, fiscal_year), as_dict=1)
  390.     if result:
  391.         total_amount = result[0].get('total_amount') or 0
  392.     return total_amount
  393.  
  394. def get_budget_item_wise_total_for_an_year(budget_id, budget_line_item, from_date, to_date):
  395.     '''
  396.         Method to get total availability for an year
  397.     '''
  398.     total_amount = 0
  399.     query = '''
  400.         SELECT
  401.             SUM(available_amount) as total_amount
  402.         FROM
  403.             `tabYear wise Allocation`
  404.         WHERE
  405.             parent = '{0}' AND
  406.             budget_item = '{1}' AND
  407.             from_date = '{2}' AND
  408.             to_date = '{3}'
  409.     '''
  410.     result = frappe.db.sql(query.format(budget_id, budget_line_item, from_date, to_date), as_dict=1)
  411.     if result:
  412.         total_amount = result[0].get('total_amount') or 0
  413.     return total_amount
  414.  
  415. def is_allocated(budget_id, fiscal_year, sub_budget_item):
  416.     '''
  417.         Method to check wether allocation is done or not
  418.     '''
  419.     allocated_amount = 0
  420.     query = '''
  421.         SELECT
  422.             bali.allocated_amount as allocated_amount
  423.         FROM
  424.             `tabBudget Allocation Line Item` as bali,
  425.             `tabBudget Allocation` as ba
  426.         WHERE
  427.             bali.parent = ba.name AND
  428.             ba.pradan_budget = %(budget_id)s AND
  429.             ba.fiscal_year = %(fiscal_year)s AND
  430.             bali.sub_budget_line_item = %(sub_budget_item)s
  431.     '''
  432.     result = frappe.db.sql(query, { 'budget_id':budget_id, 'fiscal_year':fiscal_year, 'sub_budget_item':sub_budget_item }, as_dict=1)
  433.     if result:
  434.         if result[0]:
  435.             if result[0].get('allocated_amount'):
  436.                 return 1
  437.     return 0
  438.  
  439. def is_period_closing_voucher_created(fiscal_year):
  440.     '''
  441.         Method to check whether Period Closing Voucher is created or not
  442.     '''
  443.     period_closing_voucher_created = 0
  444.     if frappe.db.exists('Period Closing Voucher', { 'fiscal_year':fiscal_year, 'docstatus':1 }):
  445.         period_closing_voucher_created = 1
  446.     return period_closing_voucher_created
  447.  
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement