2015-03-03 09:25:30 +00:00
# Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors
2013-08-05 09:29:54 +00:00
# License: GNU General Public License v3. See license.txt
2013-01-29 06:04:39 +00:00
from __future__ import unicode_literals
2017-03-31 07:14:29 +00:00
import frappe , erpnext
2014-02-14 10:17:51 +00:00
from frappe import _ , throw
2018-05-16 06:01:41 +00:00
from frappe . utils import today , flt , cint , fmt_money , formatdate , getdate , add_days , add_months , get_last_day , nowdate
2017-03-31 07:14:29 +00:00
from erpnext . setup . utils import get_exchange_rate
2016-03-29 07:44:17 +00:00
from erpnext . accounts . utils import get_fiscal_years , validate_fiscal_year , get_account_currency
2014-02-10 09:17:54 +00:00
from erpnext . utilities . transaction_base import TransactionBase
2015-07-24 07:56:36 +00:00
from erpnext . controllers . sales_and_purchase_return import validate_return
2016-01-25 12:00:49 +00:00
from erpnext . accounts . party import get_party_account_currency , validate_party_frozen_disabled
2016-01-25 15:55:11 +00:00
from erpnext . exceptions import InvalidCurrency
2018-03-08 07:25:41 +00:00
from six import text_type
2015-07-17 09:49:02 +00:00
2018-05-16 05:19:12 +00:00
force_item_fields = ( " item_group " , " brand " , " stock_uom " , " is_fixed_asset " )
2015-08-03 10:43:33 +00:00
2013-01-30 07:19:08 +00:00
class AccountsController ( TransactionBase ) :
2017-10-02 10:29:27 +00:00
def __init__ ( self , * args , * * kwargs ) :
super ( AccountsController , self ) . __init__ ( * args , * * kwargs )
2015-09-11 10:52:37 +00:00
2015-08-27 06:58:36 +00:00
@property
def company_currency ( self ) :
if not hasattr ( self , " __company_currency " ) :
2017-03-31 07:14:29 +00:00
self . __company_currency = erpnext . get_company_currency ( self . company )
2015-09-11 10:52:37 +00:00
2015-08-27 06:58:36 +00:00
return self . __company_currency
2015-09-11 10:52:37 +00:00
2016-10-06 09:05:04 +00:00
def onload ( self ) :
2017-11-21 14:28:16 +00:00
self . get ( " __onload " ) . make_payment_via_journal_entry \
= frappe . db . get_single_value ( ' Accounts Settings ' , ' make_payment_via_journal_entry ' )
2017-09-25 08:02:23 +00:00
if self . is_new ( ) :
2017-11-21 14:28:16 +00:00
relevant_docs = ( " Quotation " , " Purchase Order " , " Sales Order " ,
" Purchase Invoice " , " Sales Invoice " )
2017-09-25 08:02:23 +00:00
if self . doctype in relevant_docs :
self . set_payment_schedule ( )
2016-10-06 09:05:04 +00:00
2018-05-16 06:01:41 +00:00
def ensure_supplier_is_not_blocked ( self ) :
is_supplier_payment = self . doctype == ' Payment Entry ' and self . party_type == ' Supplier '
is_buying_invoice = self . doctype in [ ' Purchase Invoice ' , ' Purchase Order ' ]
supplier = None
supplier_name = None
if is_buying_invoice or is_supplier_payment :
supplier_name = self . supplier if is_buying_invoice else self . party
supplier = frappe . get_doc ( ' Supplier ' , supplier_name )
if supplier and supplier_name and supplier . on_hold :
if ( is_buying_invoice and supplier . hold_type in [ ' All ' , ' Invoices ' ] ) or \
( is_supplier_payment and supplier . hold_type in [ ' All ' , ' Payments ' ] ) :
if not supplier . release_date or getdate ( nowdate ( ) ) < = supplier . release_date :
frappe . msgprint (
_ ( ' {0} is blocked so this transaction cannot proceed ' . format ( supplier_name ) ) , raise_exception = 1 )
2013-03-20 07:25:28 +00:00
def validate ( self ) :
2014-07-03 06:55:06 +00:00
if self . get ( " _action " ) and self . _action != " update_after_submit " :
2014-06-05 11:25:31 +00:00
self . set_missing_values ( for_validate = True )
2017-09-22 14:16:38 +00:00
2018-05-16 06:01:41 +00:00
self . ensure_supplier_is_not_blocked ( )
2013-07-11 12:19:18 +00:00
self . validate_date_with_fiscal_year ( )
2015-08-03 10:43:33 +00:00
2013-05-24 13:55:01 +00:00
if self . meta . get_field ( " currency " ) :
2013-05-28 11:53:36 +00:00
self . calculate_taxes_and_totals ( )
2016-04-09 09:01:09 +00:00
2015-07-17 09:49:02 +00:00
if not self . meta . get_field ( " is_return " ) or not self . is_return :
self . validate_value ( " base_grand_total " , " >= " , 0 )
2015-08-03 10:43:33 +00:00
2015-07-24 07:56:36 +00:00
validate_return ( self )
2013-05-24 13:55:01 +00:00
self . set_total_in_words ( )
2014-04-08 14:40:03 +00:00
2017-09-22 14:16:38 +00:00
self . validate_all_documents_schedule ( )
2014-04-08 14:40:03 +00:00
2014-10-06 07:50:53 +00:00
if self . meta . get_field ( " taxes_and_charges " ) :
self . validate_enabled_taxes_and_charges ( )
2018-01-15 12:15:46 +00:00
self . validate_tax_account_company ( )
2015-08-03 10:43:33 +00:00
2015-07-17 06:40:12 +00:00
self . validate_party ( )
2015-08-20 09:25:39 +00:00
self . validate_currency ( )
2016-06-15 11:15:03 +00:00
2016-04-09 09:01:09 +00:00
if self . doctype == ' Purchase Invoice ' :
self . validate_paid_amount ( )
2018-06-11 06:32:14 +00:00
if self . doctype in [ ' Purchase Invoice ' , ' Sales Invoice ' ] and self . is_return :
self . validate_qty ( )
2017-09-22 14:16:38 +00:00
def validate_invoice_documents_schedule ( self ) :
2017-11-21 14:28:16 +00:00
self . validate_payment_schedule_dates ( )
self . set_due_date ( )
self . set_payment_schedule ( )
self . validate_payment_schedule_amount ( )
2017-09-22 14:16:38 +00:00
self . validate_due_date ( )
self . validate_advance_entries ( )
def validate_non_invoice_documents_schedule ( self ) :
2017-11-21 14:28:16 +00:00
self . set_payment_schedule ( )
2017-12-20 06:54:59 +00:00
self . validate_payment_schedule_dates ( )
2017-11-21 14:28:16 +00:00
self . validate_payment_schedule_amount ( )
2017-09-22 14:16:38 +00:00
def validate_all_documents_schedule ( self ) :
if self . doctype in ( " Sales Invoice " , " Purchase Invoice " ) and not self . is_return :
self . validate_invoice_documents_schedule ( )
elif self . doctype in ( " Quotation " , " Purchase Order " , " Sales Order " ) :
self . validate_non_invoice_documents_schedule ( )
2016-11-18 08:27:53 +00:00
def before_print ( self ) :
2018-05-28 12:11:09 +00:00
if self . doctype in [ ' Purchase Order ' , ' Sales Order ' , ' Sales Invoice ' , ' Purchase Invoice ' ,
' Supplier Quotation ' , ' Purchase Receipt ' , ' Delivery Note ' , ' Quotation ' ] :
2016-11-18 08:27:53 +00:00
if self . get ( " group_same_items " ) :
self . group_similar_items ( )
2016-03-21 13:02:48 +00:00
def validate_paid_amount ( self ) :
if hasattr ( self , " is_pos " ) or hasattr ( self , " is_paid " ) :
is_paid = self . get ( " is_pos " ) or self . get ( " is_paid " )
if cint ( is_paid ) == 1 :
2016-04-09 09:01:09 +00:00
if flt ( self . paid_amount ) == 0 and flt ( self . outstanding_amount ) > 0 :
2016-03-21 13:02:48 +00:00
if self . cash_bank_account :
2017-11-17 06:57:43 +00:00
self . paid_amount = flt ( flt ( self . outstanding_amount ) , self . precision ( " paid_amount " ) )
self . base_paid_amount = flt ( self . paid_amount * self . conversion_rate ,
self . precision ( " base_paid_amount " ) )
2016-03-21 13:02:48 +00:00
else :
# show message that the amount is not paid
2016-04-15 09:12:08 +00:00
self . paid_amount = 0
2016-04-04 06:25:52 +00:00
frappe . throw ( _ ( " Note: Payment Entry will not be created since ' Cash or Bank Account ' was not specified " ) )
2016-03-21 13:02:48 +00:00
else :
2017-09-22 14:16:38 +00:00
frappe . db . set ( self , ' paid_amount ' , 0 )
2016-03-21 13:02:48 +00:00
2013-06-14 12:14:03 +00:00
def set_missing_values ( self , for_validate = False ) :
2016-05-12 07:28:29 +00:00
if frappe . flags . in_test :
2017-09-22 14:16:38 +00:00
for fieldname in [ " posting_date " , " transaction_date " ] :
2016-05-12 07:28:29 +00:00
if self . meta . get_field ( fieldname ) and not self . get ( fieldname ) :
self . set ( fieldname , today ( ) )
break
2014-04-08 14:40:03 +00:00
2015-02-17 05:41:11 +00:00
def calculate_taxes_and_totals ( self ) :
2015-02-18 06:53:18 +00:00
from erpnext . controllers . taxes_and_totals import calculate_taxes_and_totals
calculate_taxes_and_totals ( self )
2015-02-17 05:41:11 +00:00
if self . doctype in [ " Quotation " , " Sales Order " , " Delivery Note " , " Sales Invoice " ] :
self . calculate_commission ( )
self . calculate_contribution ( )
2013-07-11 12:19:18 +00:00
def validate_date_with_fiscal_year ( self ) :
if self . meta . get_field ( " fiscal_year " ) :
date_field = " "
if self . meta . get_field ( " posting_date " ) :
date_field = " posting_date "
elif self . meta . get_field ( " transaction_date " ) :
date_field = " transaction_date "
2014-04-08 14:40:03 +00:00
2014-03-31 18:07:40 +00:00
if date_field and self . get ( date_field ) :
2017-01-16 11:27:53 +00:00
validate_fiscal_year ( self . get ( date_field ) , self . fiscal_year , self . company ,
2015-02-19 09:21:58 +00:00
self . meta . get_label ( date_field ) , self )
2014-04-08 14:40:03 +00:00
2014-08-27 11:16:33 +00:00
def validate_due_date ( self ) :
2018-07-06 06:58:58 +00:00
if self . get ( ' is_pos ' ) : return
2014-08-27 11:16:33 +00:00
from erpnext . accounts . party import validate_due_date
if self . doctype == " Sales Invoice " :
2015-07-22 12:17:49 +00:00
if not self . due_date :
frappe . throw ( _ ( " Due Date is mandatory " ) )
2015-08-03 10:43:33 +00:00
2018-01-29 10:37:21 +00:00
validate_due_date ( self . posting_date , self . due_date , " Customer " , self . customer , self . company )
2014-08-27 11:16:33 +00:00
elif self . doctype == " Purchase Invoice " :
2018-02-16 07:35:21 +00:00
validate_due_date ( self . posting_date , self . due_date , " Supplier " , self . supplier , self . company , self . bill_date )
2014-08-27 11:16:33 +00:00
2013-10-17 11:31:14 +00:00
def set_price_list_currency ( self , buying_or_selling ) :
2016-12-08 08:39:23 +00:00
if self . meta . get_field ( " posting_date " ) :
2016-12-08 10:06:23 +00:00
transaction_date = self . posting_date
2016-12-08 08:39:23 +00:00
else :
2016-12-08 10:06:23 +00:00
transaction_date = self . transaction_date
2017-01-16 11:27:53 +00:00
2013-08-09 12:41:35 +00:00
if self . meta . get_field ( " currency " ) :
2013-09-17 04:51:20 +00:00
# price list part
2018-05-15 11:29:20 +00:00
if buying_or_selling . lower ( ) == " selling " :
fieldname = " selling_price_list "
args = " for_selling "
else :
fieldname = " buying_price_list "
args = " for_buying "
2014-03-28 08:25:00 +00:00
if self . meta . get_field ( fieldname ) and self . get ( fieldname ) :
self . price_list_currency = frappe . db . get_value ( " Price List " ,
self . get ( fieldname ) , " currency " )
2014-04-08 14:40:03 +00:00
2015-08-28 13:54:22 +00:00
if self . price_list_currency == self . company_currency :
2014-03-28 08:25:00 +00:00
self . plc_conversion_rate = 1.0
2013-09-24 09:06:55 +00:00
2014-03-28 08:25:00 +00:00
elif not self . plc_conversion_rate :
2017-01-16 11:27:53 +00:00
self . plc_conversion_rate = get_exchange_rate ( self . price_list_currency ,
2018-05-15 11:29:20 +00:00
self . company_currency , transaction_date , args )
2014-04-08 14:40:03 +00:00
2013-09-17 04:51:20 +00:00
# currency
2014-03-28 08:25:00 +00:00
if not self . currency :
self . currency = self . price_list_currency
self . conversion_rate = self . plc_conversion_rate
2015-08-28 13:54:22 +00:00
elif self . currency == self . company_currency :
2014-03-28 08:25:00 +00:00
self . conversion_rate = 1.0
elif not self . conversion_rate :
2014-07-01 12:15:15 +00:00
self . conversion_rate = get_exchange_rate ( self . currency ,
2018-05-15 11:29:20 +00:00
self . company_currency , transaction_date , args )
2013-09-17 04:51:20 +00:00
2016-10-05 11:45:43 +00:00
def set_missing_item_details ( self , for_validate = False ) :
2013-05-24 13:55:01 +00:00
""" set missing item values """
2014-02-11 10:44:52 +00:00
from erpnext . stock . get_item_details import get_item_details
2017-07-18 06:44:42 +00:00
from erpnext . stock . doctype . serial_no . serial_no import get_serial_nos
2016-01-15 11:29:26 +00:00
2014-12-26 07:45:21 +00:00
if hasattr ( self , " items " ) :
2014-06-20 10:29:49 +00:00
parent_dict = { }
2014-04-10 12:23:30 +00:00
for fieldname in self . meta . get_valid_columns ( ) :
parent_dict [ fieldname ] = self . get ( fieldname )
2016-03-05 09:40:25 +00:00
if self . doctype in [ " Quotation " , " Sales Order " , " Delivery Note " , " Sales Invoice " ] :
document_type = " {} Item " . format ( self . doctype )
2016-01-18 10:58:21 +00:00
parent_dict . update ( { " document_type " : document_type } )
2014-12-26 07:45:21 +00:00
for item in self . get ( " items " ) :
2014-04-03 09:00:42 +00:00
if item . get ( " item_code " ) :
2014-06-20 10:29:49 +00:00
args = parent_dict . copy ( )
args . update ( item . as_dict ( ) )
2016-01-15 11:29:26 +00:00
2016-01-19 10:15:49 +00:00
args [ " doctype " ] = self . doctype
args [ " name " ] = self . name
2016-01-15 11:29:26 +00:00
2015-03-09 09:24:37 +00:00
if not args . get ( " transaction_date " ) :
args [ " transaction_date " ] = args . get ( " posting_date " )
2015-03-20 08:48:09 +00:00
if self . get ( " is_subcontracted " ) :
args [ " is_subcontracted " ] = self . is_subcontracted
2014-04-03 09:00:42 +00:00
ret = get_item_details ( args )
2014-05-28 07:19:20 +00:00
2014-04-03 09:00:42 +00:00
for fieldname , value in ret . items ( ) :
2015-11-16 13:35:46 +00:00
if item . meta . get_field ( fieldname ) and value is not None :
if ( item . get ( fieldname ) is None or fieldname in force_item_fields ) :
2014-04-03 09:00:42 +00:00
item . set ( fieldname , value )
2014-04-08 14:40:03 +00:00
2017-04-03 08:47:08 +00:00
elif fieldname in [ ' cost_center ' , ' conversion_factor ' ] and not item . get ( fieldname ) :
2015-11-16 13:35:46 +00:00
item . set ( fieldname , value )
2017-04-03 08:47:08 +00:00
elif fieldname == " serial_no " :
2018-07-06 09:06:52 +00:00
item_qty = abs ( item . get ( " qty " ) ) * item . get ( " conversion_factor " )
if item_qty != len ( get_serial_nos ( item . get ( ' serial_no ' ) ) ) :
2017-04-03 08:47:08 +00:00
item . set ( fieldname , value )
2014-12-30 13:03:52 +00:00
2014-05-28 07:19:20 +00:00
if ret . get ( " pricing_rule " ) :
2016-01-19 14:21:11 +00:00
# if user changed the discount percentage then set user's discount percentage ?
2016-03-05 09:40:25 +00:00
item . set ( " discount_percentage " , ret . get ( " discount_percentage " ) )
2015-08-13 07:55:43 +00:00
if ret . get ( " pricing_rule_for " ) == " Price " :
item . set ( " pricing_list_rate " , ret . get ( " pricing_list_rate " ) )
2015-09-11 10:52:37 +00:00
2015-09-22 09:56:15 +00:00
if item . price_list_rate :
item . rate = flt ( item . price_list_rate *
2015-09-28 11:57:02 +00:00
( 1.0 - ( flt ( item . discount_percentage ) / 100.0 ) ) , item . precision ( " rate " ) )
2016-01-15 11:29:26 +00:00
2016-04-18 10:24:01 +00:00
if self . doctype == " Purchase Invoice " :
2016-10-05 11:45:43 +00:00
self . set_expense_account ( for_validate )
2014-05-28 07:19:20 +00:00
2015-05-12 09:37:02 +00:00
def set_taxes ( self ) :
if not self . meta . get_field ( " taxes " ) :
2013-05-24 13:55:01 +00:00
return
2014-04-08 14:40:03 +00:00
2015-05-12 09:37:02 +00:00
tax_master_doctype = self . meta . get_field ( " taxes_and_charges " ) . options
2014-04-08 14:40:03 +00:00
2018-04-24 13:49:47 +00:00
if ( self . is_new ( ) or self . is_pos_profile_changed ( ) ) and not self . get ( " taxes " ) :
2017-12-11 09:22:28 +00:00
if self . company and not self . get ( " taxes_and_charges " ) :
2013-05-24 13:55:01 +00:00
# get the default tax master
2017-12-11 09:22:28 +00:00
self . taxes_and_charges = frappe . db . get_value ( tax_master_doctype ,
{ " is_default " : 1 , ' company ' : self . company } )
2014-04-08 14:40:03 +00:00
2015-05-12 09:37:02 +00:00
self . append_taxes_from_master ( tax_master_doctype )
2014-04-08 14:40:03 +00:00
2018-04-24 13:49:47 +00:00
def is_pos_profile_changed ( self ) :
if ( self . doctype == ' Sales Invoice ' and self . is_pos and
self . pos_profile != frappe . db . get_value ( ' Sales Invoice ' , self . name , ' pos_profile ' ) ) :
return True
2015-05-12 09:37:02 +00:00
def append_taxes_from_master ( self , tax_master_doctype = None ) :
if self . get ( " taxes_and_charges " ) :
2013-07-04 11:43:53 +00:00
if not tax_master_doctype :
2015-05-12 09:37:02 +00:00
tax_master_doctype = self . meta . get_field ( " taxes_and_charges " ) . options
2014-04-08 14:40:03 +00:00
2015-05-12 09:37:02 +00:00
self . extend ( " taxes " , get_taxes_and_charges ( tax_master_doctype , self . get ( " taxes_and_charges " ) ) )
2014-01-30 08:26:57 +00:00
2014-04-17 20:00:14 +00:00
def set_other_charges ( self ) :
2014-12-25 10:31:55 +00:00
self . set ( " taxes " , [ ] )
2015-05-12 09:37:02 +00:00
self . set_taxes ( )
2014-04-08 14:40:03 +00:00
2014-10-06 07:50:53 +00:00
def validate_enabled_taxes_and_charges ( self ) :
taxes_and_charges_doctype = self . meta . get_options ( " taxes_and_charges " )
if frappe . db . get_value ( taxes_and_charges_doctype , self . taxes_and_charges , " disabled " ) :
frappe . throw ( _ ( " {0} ' {1} ' is disabled " ) . format ( taxes_and_charges_doctype , self . taxes_and_charges ) )
2018-01-15 12:15:46 +00:00
def validate_tax_account_company ( self ) :
for d in self . get ( " taxes " ) :
if d . account_head :
tax_account_company = frappe . db . get_value ( " Account " , d . account_head , " company " )
if tax_account_company != self . company :
frappe . throw ( _ ( " Row # {0} : Account {1} does not belong to company {2} " )
. format ( d . idx , d . account_head , self . company ) )
2015-08-19 08:19:10 +00:00
def get_gl_dict ( self , args , account_currency = None ) :
2013-01-29 06:04:39 +00:00
""" this method populates the common properties of a gl entry record """
2016-06-15 11:15:03 +00:00
2018-05-10 20:26:05 +00:00
posting_date = args . get ( ' posting_date ' ) or self . get ( ' posting_date ' )
fiscal_years = get_fiscal_years ( posting_date , company = self . company )
2016-03-29 07:44:17 +00:00
if len ( fiscal_years ) > 1 :
2018-05-10 20:26:05 +00:00
frappe . throw ( _ ( " Multiple fiscal years exist for the date {0} . Please set company in Fiscal Year " ) . format ( formatdate ( posting_date ) ) )
2016-03-29 07:44:17 +00:00
else :
fiscal_year = fiscal_years [ 0 ] [ 0 ]
2016-06-15 11:15:03 +00:00
2014-02-14 10:17:51 +00:00
gl_dict = frappe . _dict ( {
2014-04-08 14:40:03 +00:00
' company ' : self . company ,
2018-05-10 20:26:05 +00:00
' posting_date ' : posting_date ,
2016-03-29 07:44:17 +00:00
' fiscal_year ' : fiscal_year ,
2014-03-28 08:25:00 +00:00
' voucher_type ' : self . doctype ,
' voucher_no ' : self . name ,
2014-04-07 06:32:57 +00:00
' remarks ' : self . get ( " remarks " ) ,
2013-01-29 06:04:39 +00:00
' debit ' : 0 ,
' credit ' : 0 ,
2015-08-19 08:19:10 +00:00
' debit_in_account_currency ' : 0 ,
' credit_in_account_currency ' : 0 ,
2014-03-28 08:25:00 +00:00
' is_opening ' : self . get ( " is_opening " ) or " No " ,
2014-08-29 05:48:32 +00:00
' party_type ' : None ,
2016-05-26 12:11:39 +00:00
' party ' : None ,
' project ' : self . get ( " project " )
2013-08-28 13:23:11 +00:00
} )
2013-01-29 06:04:39 +00:00
gl_dict . update ( args )
2015-09-11 10:52:37 +00:00
2015-08-20 09:25:39 +00:00
if not account_currency :
2015-09-28 08:01:17 +00:00
account_currency = get_account_currency ( gl_dict . account )
2015-09-11 10:52:37 +00:00
2017-10-17 07:00:34 +00:00
if gl_dict . account and self . doctype not in [ " Journal Entry " ,
2017-06-15 05:39:27 +00:00
" Period Closing Voucher " , " Payment Entry " ] :
2017-10-17 07:00:34 +00:00
2015-09-17 15:37:04 +00:00
self . validate_account_currency ( gl_dict . account , account_currency )
2016-02-18 12:53:31 +00:00
set_balance_in_account_currency ( gl_dict , account_currency , self . get ( " conversion_rate " ) , self . company_currency )
2015-09-11 10:52:37 +00:00
2015-08-19 13:52:34 +00:00
return gl_dict
2015-09-11 10:52:37 +00:00
2015-08-20 09:25:39 +00:00
def validate_account_currency ( self , account , account_currency = None ) :
2015-08-28 13:54:22 +00:00
valid_currency = [ self . company_currency ]
if self . get ( " currency " ) and self . currency != self . company_currency :
valid_currency . append ( self . currency )
2015-08-20 09:25:39 +00:00
if account_currency not in valid_currency :
2015-08-27 06:58:36 +00:00
frappe . throw ( _ ( " Account {0} is invalid. Account Currency must be {1} " )
2015-09-11 10:52:37 +00:00
. format ( account , _ ( " or " ) . join ( valid_currency ) ) )
2013-02-06 12:03:46 +00:00
def clear_unallocated_advances ( self , childtype , parentfield ) :
2014-04-02 12:39:34 +00:00
self . set ( parentfield , self . get ( parentfield , { " allocated_amount " : [ " not in " , [ 0 , None , " " ] ] } ) )
2014-04-08 14:40:03 +00:00
frappe . db . sql ( """ delete from `tab %s ` where parentfield= %s and parent = %s
2015-11-16 13:35:46 +00:00
and allocated_amount = 0 """ % (childtype, ' %s ' , ' %s ' ), (parentfield, self.name))
2014-04-08 14:40:03 +00:00
2016-06-27 12:11:39 +00:00
def set_advances ( self ) :
2015-08-10 11:34:07 +00:00
""" Returns list of advances against Account, Party, Reference """
2016-07-07 08:32:26 +00:00
2016-06-27 12:11:39 +00:00
res = self . get_advance_entries ( )
2015-08-10 11:34:07 +00:00
2016-06-27 12:11:39 +00:00
self . set ( " advances " , [ ] )
for d in res :
self . append ( " advances " , {
" doctype " : self . doctype + " Advance " ,
" reference_type " : d . reference_type ,
" reference_name " : d . reference_name ,
" reference_row " : d . reference_row ,
" remarks " : d . remarks ,
" advance_amount " : flt ( d . amount ) ,
" allocated_amount " : flt ( d . amount ) if d . against_order else 0
} )
2016-07-07 08:32:26 +00:00
2017-11-17 09:01:09 +00:00
def apply_shipping_rule ( self ) :
if self . shipping_rule :
shipping_rule = frappe . get_doc ( " Shipping Rule " , self . shipping_rule )
shipping_rule . apply ( self )
self . calculate_taxes_and_totals ( )
def get_shipping_address ( self ) :
''' Returns Address object from shipping address fields if present '''
# shipping address fields can be `shipping_address_name` or `shipping_address`
# try getting value from both
for fieldname in ( ' shipping_address_name ' , ' shipping_address ' ) :
shipping_field = self . meta . get_field ( fieldname )
if shipping_field and shipping_field . fieldtype == ' Link ' :
if self . get ( fieldname ) :
return frappe . get_doc ( ' Address ' , self . get ( fieldname ) )
return { }
2016-06-27 12:11:39 +00:00
def get_advance_entries ( self , include_unallocated = True ) :
if self . doctype == " Sales Invoice " :
party_account = self . debit_to
party_type = " Customer "
party = self . customer
amount_field = " credit_in_account_currency "
order_field = " sales_order "
order_doctype = " Sales Order "
else :
party_account = self . credit_to
party_type = " Supplier "
party = self . supplier
amount_field = " debit_in_account_currency "
order_field = " purchase_order "
order_doctype = " Purchase Order "
2016-07-07 08:32:26 +00:00
order_list = list ( set ( [ d . get ( order_field )
2016-06-27 12:11:39 +00:00
for d in self . get ( " items " ) if d . get ( order_field ) ] ) )
2016-07-07 08:32:26 +00:00
journal_entries = get_advance_journal_entries ( party_type , party , party_account ,
2016-06-27 12:11:39 +00:00
amount_field , order_doctype , order_list , include_unallocated )
2016-07-07 08:32:26 +00:00
payment_entries = get_advance_payment_entries ( party_type , party , party_account ,
2016-06-27 12:11:39 +00:00
order_doctype , order_list , include_unallocated )
2016-07-07 08:32:26 +00:00
2016-06-27 12:11:39 +00:00
res = journal_entries + payment_entries
2016-07-07 08:32:26 +00:00
2016-06-27 12:11:39 +00:00
return res
2018-01-08 09:50:15 +00:00
def is_inclusive_tax ( self ) :
is_inclusive = cint ( frappe . db . get_single_value ( " Accounts Settings " ,
" show_inclusive_tax_in_print " ) )
if is_inclusive :
is_inclusive = 0
if self . get ( " taxes " , filters = { " included_in_print_rate " : 1 } ) :
is_inclusive = 1
return is_inclusive
2016-06-27 12:11:39 +00:00
def validate_advance_entries ( self ) :
2016-06-28 14:12:19 +00:00
order_field = " sales_order " if self . doctype == " Sales Invoice " else " purchase_order "
2016-07-07 08:32:26 +00:00
order_list = list ( set ( [ d . get ( order_field )
2016-06-28 14:12:19 +00:00
for d in self . get ( " items " ) if d . get ( order_field ) ] ) )
2016-07-07 08:32:26 +00:00
2016-06-28 14:12:19 +00:00
if not order_list : return
2016-07-07 08:32:26 +00:00
2016-06-27 12:11:39 +00:00
advance_entries = self . get_advance_entries ( include_unallocated = False )
2016-07-07 08:32:26 +00:00
2016-06-27 12:11:39 +00:00
if advance_entries :
advance_entries_against_si = [ d . reference_name for d in self . get ( " advances " ) ]
for d in advance_entries :
if not advance_entries_against_si or d . reference_name not in advance_entries_against_si :
frappe . msgprint ( _ ( " Payment Entry {0} is linked against Order {1} , check if it should be pulled as advance in this invoice. " )
. format ( d . reference_name , d . against_order ) )
2016-07-07 08:32:26 +00:00
2016-06-27 12:11:39 +00:00
def update_against_document_in_jv ( self ) :
"""
Links invoice and advance voucher :
1. cancel advance voucher
2. split into multiple rows if partially adjusted , assign against voucher
3. submit advance voucher
"""
2016-07-07 08:32:26 +00:00
2016-06-27 12:11:39 +00:00
if self . doctype == " Sales Invoice " :
2016-07-04 06:11:14 +00:00
party_type = " Customer "
2016-06-27 12:11:39 +00:00
party = self . customer
party_account = self . debit_to
dr_or_cr = " credit_in_account_currency "
else :
2016-07-04 06:11:14 +00:00
party_type = " Supplier "
2016-06-27 12:11:39 +00:00
party = self . supplier
party_account = self . credit_to
dr_or_cr = " debit_in_account_currency "
lst = [ ]
for d in self . get ( ' advances ' ) :
if flt ( d . allocated_amount ) > 0 :
args = frappe . _dict ( {
' voucher_type ' : d . reference_type ,
' voucher_no ' : d . reference_name ,
' voucher_detail_no ' : d . reference_row ,
' against_voucher_type ' : self . doctype ,
' against_voucher ' : self . name ,
' account ' : party_account ,
2016-07-04 06:11:14 +00:00
' party_type ' : party_type ,
2016-06-27 12:11:39 +00:00
' party ' : party ,
' is_advance ' : ' Yes ' ,
' dr_or_cr ' : dr_or_cr ,
' unadjusted_amount ' : flt ( d . advance_amount ) ,
' allocated_amount ' : flt ( d . allocated_amount ) ,
2016-07-07 08:32:26 +00:00
' exchange_rate ' : ( self . conversion_rate
2016-06-27 12:11:39 +00:00
if self . party_account_currency != self . company_currency else 1 ) ,
2016-07-07 08:32:26 +00:00
' grand_total ' : ( self . base_grand_total
2016-06-27 12:11:39 +00:00
if self . party_account_currency == self . company_currency else self . grand_total ) ,
' outstanding_amount ' : self . outstanding_amount
} )
lst . append ( args )
2016-07-07 08:32:26 +00:00
2016-06-27 12:11:39 +00:00
if lst :
from erpnext . accounts . utils import reconcile_against_document
reconcile_against_document ( lst )
2014-04-08 14:40:03 +00:00
2013-07-29 13:05:39 +00:00
def validate_multiple_billing ( self , ref_dt , item_ref_dn , based_on , parentfield ) :
2014-01-23 10:03:30 +00:00
from erpnext . controllers . status_updater import get_tolerance_for
2014-01-03 12:13:19 +00:00
item_tolerance = { }
global_tolerance = None
2014-04-08 14:40:03 +00:00
2014-12-25 12:49:39 +00:00
for item in self . get ( " items " ) :
2014-03-28 08:25:00 +00:00
if item . get ( item_ref_dn ) :
2014-04-08 14:40:03 +00:00
ref_amt = flt ( frappe . db . get_value ( ref_dt + " Item " ,
2014-03-31 18:07:40 +00:00
item . get ( item_ref_dn ) , based_on ) , self . precision ( based_on , item ) )
2014-01-07 07:11:09 +00:00
if not ref_amt :
2014-04-14 13:50:45 +00:00
frappe . msgprint ( _ ( " Warning: System will not check overbilling since amount for Item {0} in {1} is zero " ) . format ( item . item_code , ref_dt ) )
2014-01-07 07:11:09 +00:00
else :
2014-04-08 14:40:03 +00:00
already_billed = frappe . db . sql ( """ select sum( %s ) from `tab %s `
where % s = % s and docstatus = 1 and parent != % s """ %
2014-12-26 07:45:21 +00:00
( based_on , self . doctype + " Item " , item_ref_dn , ' %s ' , ' %s ' ) ,
2014-03-31 18:07:40 +00:00
( item . get ( item_ref_dn ) , self . name ) ) [ 0 ] [ 0 ]
2014-04-08 14:40:03 +00:00
total_billed_amt = flt ( flt ( already_billed ) + flt ( item . get ( based_on ) ) ,
2014-01-07 07:11:09 +00:00
self . precision ( based_on , item ) )
2014-04-08 14:40:03 +00:00
tolerance , item_tolerance , global_tolerance = get_tolerance_for ( item . item_code ,
2014-01-07 07:11:09 +00:00
item_tolerance , global_tolerance )
2014-04-08 14:40:03 +00:00
2014-01-07 07:11:09 +00:00
max_allowed_amt = flt ( ref_amt * ( 100 + tolerance ) / 100 )
2014-04-08 14:40:03 +00:00
2014-01-07 07:11:09 +00:00
if total_billed_amt - max_allowed_amt > 0.01 :
2018-01-23 05:30:58 +00:00
frappe . throw ( _ ( " Cannot overbill for Item {0} in row {1} more than {2} . To allow over-billing, please set in Stock Settings " ) . format ( item . item_code , item . idx , max_allowed_amt ) )
2014-04-08 14:40:03 +00:00
2013-03-25 05:36:00 +00:00
def get_company_default ( self , fieldname ) :
2013-12-12 13:42:19 +00:00
from erpnext . accounts . utils import get_company_default
2014-03-28 08:25:00 +00:00
return get_company_default ( self . company , fieldname )
2014-04-08 14:40:03 +00:00
2013-08-02 09:20:12 +00:00
def get_stock_items ( self ) :
stock_items = [ ]
2014-12-26 07:45:21 +00:00
item_codes = list ( set ( item . item_code for item in self . get ( " items " ) ) )
2013-08-02 09:20:12 +00:00
if item_codes :
2014-02-26 07:05:33 +00:00
stock_items = [ r [ 0 ] for r in frappe . db . sql ( """ select name
2015-07-24 09:46:25 +00:00
from ` tabItem ` where name in ( % s ) and is_stock_item = 1 """ % \
2013-08-02 09:20:12 +00:00
( " , " . join ( ( [ " %s " ] * len ( item_codes ) ) ) , ) , item_codes ) ]
2014-04-08 14:40:03 +00:00
2013-08-02 09:20:12 +00:00
return stock_items
2014-04-08 14:40:03 +00:00
2014-09-12 09:48:53 +00:00
def set_total_advance_paid ( self ) :
if self . doctype == " Sales Order " :
2015-08-28 13:54:22 +00:00
dr_or_cr = " credit_in_account_currency "
2016-01-27 10:13:12 +00:00
party = self . customer
2014-09-12 09:48:53 +00:00
else :
2015-08-28 13:54:22 +00:00
dr_or_cr = " debit_in_account_currency "
2016-01-27 10:13:12 +00:00
party = self . supplier
2014-09-12 09:48:53 +00:00
2016-01-27 10:13:12 +00:00
advance = frappe . db . sql ( """
2014-09-12 09:48:53 +00:00
select
2016-01-27 10:13:12 +00:00
account_currency , sum ( { dr_or_cr } ) as amount
2014-09-12 09:48:53 +00:00
from
2016-06-25 20:07:21 +00:00
` tabGL Entry `
2014-09-12 09:48:53 +00:00
where
2016-06-25 20:07:21 +00:00
against_voucher_type = % s and against_voucher = % s and party = % s
and docstatus = 1
2016-01-27 10:13:12 +00:00
""" .format(dr_or_cr=dr_or_cr), (self.doctype, self.name, party), as_dict=1)
if advance :
2016-01-29 06:46:24 +00:00
advance = advance [ 0 ]
advance_paid = flt ( advance . amount , self . precision ( " advance_paid " ) )
formatted_advance_paid = fmt_money ( advance_paid , precision = self . precision ( " advance_paid " ) ,
currency = advance . account_currency )
frappe . db . set_value ( self . doctype , self . name , " party_account_currency " ,
advance . account_currency )
if advance . account_currency == self . currency :
2016-01-27 10:13:12 +00:00
order_total = self . grand_total
2016-01-29 06:46:24 +00:00
formatted_order_total = fmt_money ( order_total , precision = self . precision ( " grand_total " ) ,
currency = advance . account_currency )
2016-01-27 10:13:12 +00:00
else :
order_total = self . base_grand_total
2016-01-29 06:46:24 +00:00
formatted_order_total = fmt_money ( order_total , precision = self . precision ( " base_grand_total " ) ,
currency = advance . account_currency )
2016-06-30 07:07:53 +00:00
if self . currency == self . company_currency and advance_paid > order_total :
2016-01-27 10:13:12 +00:00
frappe . throw ( _ ( " Total advance ( {0} ) against Order {1} cannot be greater than the Grand Total ( {2} ) " )
2016-01-29 06:46:24 +00:00
. format ( formatted_advance_paid , self . name , formatted_order_total ) )
2016-07-07 08:32:26 +00:00
2016-06-29 12:34:37 +00:00
frappe . db . set_value ( self . doctype , self . name , " advance_paid " , advance_paid )
2014-09-12 09:48:53 +00:00
2014-08-26 08:59:06 +00:00
@property
def company_abbr ( self ) :
if not hasattr ( self , " _abbr " ) :
self . _abbr = frappe . db . get_value ( " Company " , self . company , " abbr " )
return self . _abbr
2015-07-17 06:40:12 +00:00
def validate_party ( self ) :
2015-08-27 06:58:36 +00:00
party_type , party = self . get_party ( )
2016-01-26 09:26:52 +00:00
validate_party_frozen_disabled ( party_type , party )
2015-09-11 10:52:37 +00:00
2015-08-27 06:58:36 +00:00
def get_party ( self ) :
2015-07-20 07:33:48 +00:00
party_type = None
2016-01-25 12:00:49 +00:00
if self . doctype in ( " Opportunity " , " Quotation " , " Sales Order " , " Delivery Note " , " Sales Invoice " ) :
2016-01-26 09:26:52 +00:00
party_type = ' Customer '
2016-01-25 12:00:49 +00:00
elif self . doctype in ( " Supplier Quotation " , " Purchase Order " , " Purchase Receipt " , " Purchase Invoice " ) :
2016-01-26 09:26:52 +00:00
party_type = ' Supplier '
2016-01-25 12:00:49 +00:00
elif self . meta . get_field ( " customer " ) :
party_type = " Customer "
2015-07-17 06:40:12 +00:00
2015-07-20 07:33:48 +00:00
elif self . meta . get_field ( " supplier " ) :
2016-01-25 12:00:49 +00:00
party_type = " Supplier "
2015-09-11 10:52:37 +00:00
2015-08-27 06:58:36 +00:00
party = self . get ( party_type . lower ( ) ) if party_type else None
2015-09-11 10:52:37 +00:00
2015-08-27 06:58:36 +00:00
return party_type , party
2015-09-11 10:52:37 +00:00
2015-08-27 06:58:36 +00:00
def validate_currency ( self ) :
2015-08-28 13:54:22 +00:00
if self . get ( " currency " ) :
2015-08-27 06:58:36 +00:00
party_type , party = self . get_party ( )
2015-08-28 13:54:22 +00:00
if party_type and party :
2015-09-25 10:47:50 +00:00
party_account_currency = get_party_account_currency ( party_type , party , self . company )
2015-09-11 10:52:37 +00:00
2015-10-01 13:25:25 +00:00
if ( party_account_currency
and party_account_currency != self . company_currency
and self . currency != party_account_currency ) :
2015-09-03 04:58:08 +00:00
frappe . throw ( _ ( " Accounting Entry for {0} : {1} can only be made in currency: {2} " )
. format ( party_type , party , party_account_currency ) , InvalidCurrency )
2015-09-11 10:52:37 +00:00
2016-01-25 12:00:49 +00:00
# Note: not validating with gle account because we don't have the account
# at quotation / sales order level and we shouldn't stop someone
# from creating a sales invoice if sales order is already created
2016-06-15 11:15:03 +00:00
2016-04-07 13:02:37 +00:00
def validate_fixed_asset ( self ) :
for d in self . get ( " items " ) :
if d . is_fixed_asset :
2018-05-16 18:27:28 +00:00
# if d.qty > 1:
# frappe.throw(_("Row #{0}: Qty must be 1, as item is a fixed asset. Please use separate row for multiple qty.").format(d.idx))
2016-06-15 11:15:03 +00:00
2018-04-26 13:48:29 +00:00
if d . meta . get_field ( " asset " ) and d . asset :
asset = frappe . get_doc ( " Asset " , d . asset )
if asset . company != self . company :
frappe . throw ( _ ( " Row # {0} : Asset {1} does not belong to company {2} " )
. format ( d . idx , d . asset , self . company ) )
elif asset . item_code != d . item_code :
frappe . throw ( _ ( " Row # {0} : Asset {1} does not linked to Item {2} " )
. format ( d . idx , d . asset , d . item_code ) )
2018-05-17 05:48:34 +00:00
# elif asset.docstatus != 1:
# frappe.throw(_("Row #{0}: Asset {1} must be submitted").format(d.idx, d.asset))
2018-04-26 13:48:29 +00:00
elif self . doctype == " Purchase Invoice " :
2018-05-16 12:46:08 +00:00
# if asset.status != "Submitted":
# frappe.throw(_("Row #{0}: Asset {1} is already {2}")
# .format(d.idx, d.asset, asset.status))
if getdate ( asset . purchase_date ) != getdate ( self . posting_date ) :
2018-04-26 13:48:29 +00:00
frappe . throw ( _ ( " Row # {0} : Posting Date must be same as purchase date {1} of asset {2} " ) . format ( d . idx , asset . purchase_date , d . asset ) )
elif asset . is_existing_asset :
frappe . throw ( _ ( " Row # {0} : Purchase Invoice cannot be made against an existing asset {1} " ) . format ( d . idx , d . asset ) )
elif self . docstatus == " Sales Invoice " and self . docstatus == 1 :
if self . update_stock :
frappe . throw ( _ ( " ' Update Stock ' cannot be checked for fixed asset sale " ) )
elif asset . status in ( " Scrapped " , " Cancelled " , " Sold " ) :
frappe . throw ( _ ( " Row # {0} : Asset {1} cannot be submitted, it is already {2} " )
. format ( d . idx , d . asset , asset . status ) )
2016-04-09 09:01:09 +00:00
2016-11-23 10:28:51 +00:00
def delink_advance_entries ( self , linked_doc_name ) :
total_allocated_amount = 0
for adv in self . advances :
consider_for_total_advance = True
if adv . reference_name == linked_doc_name :
frappe . db . sql ( """ delete from `tab {0} Advance`
where name = % s """ .format(self.doctype), adv.name)
consider_for_total_advance = False
if consider_for_total_advance :
total_allocated_amount + = flt ( adv . allocated_amount , adv . precision ( " allocated_amount " ) )
2017-01-16 11:27:53 +00:00
frappe . db . set_value ( self . doctype , self . name , " total_advance " ,
2016-11-23 10:28:51 +00:00
total_allocated_amount , update_modified = False )
2016-04-09 09:01:09 +00:00
2016-11-18 08:27:53 +00:00
def group_similar_items ( self ) :
group_item_qty = { }
group_item_amount = { }
for item in self . items :
group_item_qty [ item . item_code ] = group_item_qty . get ( item . item_code , 0 ) + item . qty
group_item_amount [ item . item_code ] = group_item_amount . get ( item . item_code , 0 ) + item . amount
duplicate_list = [ ]
for item in self . items :
if item . item_code in group_item_qty :
item . qty = group_item_qty [ item . item_code ]
item . amount = group_item_amount [ item . item_code ]
2018-05-28 12:11:09 +00:00
item . rate = flt ( flt ( item . amount ) / flt ( item . qty ) , item . precision ( " rate " ) )
2016-11-18 08:27:53 +00:00
del group_item_qty [ item . item_code ]
else :
duplicate_list . append ( item )
for item in duplicate_list :
self . remove ( item )
2017-09-07 05:52:15 +00:00
def set_payment_schedule ( self ) :
2018-04-02 05:02:00 +00:00
if self . doctype == ' Sales Invoice ' and self . is_pos :
self . payment_terms_template = ' '
return
2018-03-01 05:02:29 +00:00
2018-01-17 10:53:04 +00:00
posting_date = self . get ( " bill_date " ) or self . get ( " posting_date " ) or self . get ( " transaction_date " )
2017-09-11 10:02:57 +00:00
date = self . get ( " due_date " )
due_date = date or posting_date
2017-11-21 14:28:16 +00:00
grand_total = self . get ( " rounded_total " ) or self . grand_total
2017-12-20 06:54:59 +00:00
if self . doctype in ( " Sales Invoice " , " Purchase Invoice " ) :
grand_total = grand_total - flt ( self . write_off_amount )
2017-09-08 14:35:59 +00:00
2017-11-21 14:28:16 +00:00
if not self . get ( " payment_schedule " ) :
if self . get ( " payment_terms_template " ) :
data = get_payment_terms ( self . payment_terms_template , posting_date , grand_total )
for item in data :
self . append ( " payment_schedule " , item )
else :
data = dict ( due_date = due_date , invoice_portion = 100 , payment_amount = grand_total )
self . append ( " payment_schedule " , data )
else :
for d in self . get ( " payment_schedule " ) :
2017-12-20 06:54:59 +00:00
if d . invoice_portion :
d . payment_amount = grand_total * flt ( d . invoice_portion ) / 100
2017-08-18 10:59:30 +00:00
2017-08-19 07:21:44 +00:00
def set_due_date ( self ) :
2017-11-21 14:28:16 +00:00
due_dates = [ d . due_date for d in self . get ( " payment_schedule " ) if d . due_date ]
if due_dates :
self . due_date = max ( due_dates )
2017-09-22 14:16:38 +00:00
def validate_payment_schedule_dates ( self ) :
2017-09-22 22:12:55 +00:00
dates = [ ]
2017-09-25 09:19:35 +00:00
li = [ ]
2017-08-18 10:59:30 +00:00
2018-03-01 05:02:29 +00:00
if self . doctype == ' Sales Invoice ' and self . is_pos : return
2017-08-18 10:59:30 +00:00
for d in self . get ( " payment_schedule " ) :
2017-12-20 06:54:59 +00:00
if self . doctype == " Sales Order " and getdate ( d . due_date ) < getdate ( self . transaction_date ) :
2017-08-18 10:59:30 +00:00
frappe . throw ( _ ( " Row {0} : Due Date cannot be before posting date " ) . format ( d . idx ) )
2017-09-22 22:12:55 +00:00
elif d . due_date in dates :
2018-05-22 03:38:50 +00:00
li . append ( _ ( " {0} in row {1} " ) . format ( d . due_date , d . idx ) )
2017-09-25 09:19:35 +00:00
dates . append ( d . due_date )
if li :
duplicates = ' <br> ' + ' <br> ' . join ( li )
2017-11-21 14:28:16 +00:00
frappe . throw ( _ ( " Rows with duplicate due dates in other rows were found: {list} " )
. format ( list = duplicates ) )
2017-08-18 10:59:30 +00:00
2017-09-22 14:16:38 +00:00
def validate_payment_schedule_amount ( self ) :
2018-03-01 05:02:29 +00:00
if self . doctype == ' Sales Invoice ' and self . is_pos : return
2017-11-21 14:28:16 +00:00
if self . get ( " payment_schedule " ) :
total = 0
for d in self . get ( " payment_schedule " ) :
2017-12-19 06:01:34 +00:00
total + = flt ( d . payment_amount )
2017-12-19 06:09:20 +00:00
total = flt ( total , self . precision ( " grand_total " ) )
2017-08-18 10:59:30 +00:00
2017-12-19 06:09:20 +00:00
grand_total = flt ( self . get ( " rounded_total " ) or self . grand_total , self . precision ( ' grand_total ' ) )
2017-12-21 06:16:30 +00:00
if self . doctype in ( " Sales Invoice " , " Purchase Invoice " ) :
grand_total = grand_total - flt ( self . write_off_amount )
2017-11-21 14:28:16 +00:00
if total != grand_total :
frappe . throw ( _ ( " Total Payment Amount in Payment Schedule must be equal to Grand / Rounded Total " ) )
2017-08-18 10:59:30 +00:00
2017-11-17 06:57:43 +00:00
def is_rounded_total_disabled ( self ) :
if self . meta . get_field ( " disable_rounded_total " ) :
return self . disable_rounded_total
else :
return frappe . db . get_single_value ( " Global Defaults " , " disable_rounded_total " )
2014-02-14 10:17:51 +00:00
@frappe.whitelist ( )
2013-10-10 11:05:09 +00:00
def get_tax_rate ( account_head ) :
2016-06-15 11:15:03 +00:00
return frappe . db . get_value ( " Account " , account_head , [ " tax_rate " , " account_name " ] , as_dict = True )
2015-06-01 11:45:42 +00:00
2015-05-12 09:37:02 +00:00
@frappe.whitelist ( )
2018-01-15 12:15:46 +00:00
def get_default_taxes_and_charges ( master_doctype , tax_template = None , company = None ) :
2017-12-11 09:22:28 +00:00
if not company : return { }
2018-01-15 12:15:46 +00:00
if tax_template and company :
tax_template_company = frappe . db . get_value ( master_doctype , tax_template , " company " )
if tax_template_company == company :
return
default_tax = frappe . db . get_value ( master_doctype , { " is_default " : 1 , " company " : company } )
2017-12-11 09:22:28 +00:00
2017-11-14 04:02:32 +00:00
return {
' taxes_and_charges ' : default_tax ,
' taxes ' : get_taxes_and_charges ( master_doctype , default_tax )
}
2014-05-02 10:15:10 +00:00
@frappe.whitelist ( )
2015-05-12 09:37:02 +00:00
def get_taxes_and_charges ( master_doctype , master_name ) :
2015-05-16 06:44:39 +00:00
if not master_name :
return
2014-05-02 10:15:10 +00:00
from frappe . model import default_fields
tax_master = frappe . get_doc ( master_doctype , master_name )
taxes_and_charges = [ ]
2015-05-12 09:37:02 +00:00
for i , tax in enumerate ( tax_master . get ( " taxes " ) ) :
2014-05-02 10:15:10 +00:00
tax = tax . as_dict ( )
for fieldname in default_fields :
if fieldname in tax :
del tax [ fieldname ]
taxes_and_charges . append ( tax )
return taxes_and_charges
2014-09-29 06:47:03 +00:00
def validate_conversion_rate ( currency , conversion_rate , conversion_rate_label , company ) :
""" common validation for currency and price list currency """
2015-04-27 07:43:38 +00:00
company_currency = frappe . db . get_value ( " Company " , company , " default_currency " , cache = True )
2014-09-29 06:47:03 +00:00
if not conversion_rate :
throw ( _ ( " {0} is mandatory. Maybe Currency Exchange record is not created for {1} to {2} . " ) . format (
conversion_rate_label , currency , company_currency ) )
2015-02-23 06:28:15 +00:00
def validate_taxes_and_charges ( tax ) :
2015-02-23 10:31:33 +00:00
if tax . charge_type in [ ' Actual ' , ' On Net Total ' ] and tax . row_id :
2015-02-23 06:28:15 +00:00
frappe . throw ( _ ( " Can refer row only if the charge type is ' On Previous Row Amount ' or ' Previous Row Total ' " ) )
2015-02-23 10:31:33 +00:00
elif tax . charge_type in [ ' On Previous Row Amount ' , ' On Previous Row Total ' ] :
2015-02-23 06:28:15 +00:00
if cint ( tax . idx ) == 1 :
frappe . throw ( _ ( " Cannot select charge type as ' On Previous Row Amount ' or ' On Previous Row Total ' for first row " ) )
elif not tax . row_id :
frappe . throw ( _ ( " Please specify a valid Row ID for row {0} in table {1} " . format ( tax . idx , _ ( tax . doctype ) ) ) )
elif tax . row_id and cint ( tax . row_id ) > = cint ( tax . idx ) :
frappe . throw ( _ ( " Cannot refer row number greater than or equal to current row number for this Charge type " ) )
2015-02-25 09:38:42 +00:00
if tax . charge_type == " Actual " :
2015-02-27 18:10:56 +00:00
tax . rate = None
2015-02-25 09:38:42 +00:00
2015-02-23 06:28:15 +00:00
def validate_inclusive_tax ( tax , doc ) :
def _on_previous_row_error ( row_range ) :
throw ( _ ( " To include tax in row {0} in Item rate, taxes in rows {1} must also be included " ) . format ( tax . idx ,
row_range ) )
if cint ( getattr ( tax , " included_in_print_rate " , None ) ) :
if tax . charge_type == " Actual " :
# inclusive tax cannot be of type Actual
throw ( _ ( " Charge of type ' Actual ' in row {0} cannot be included in Item Rate " ) . format ( tax . idx ) )
elif tax . charge_type == " On Previous Row Amount " and \
not cint ( doc . get ( " taxes " ) [ cint ( tax . row_id ) - 1 ] . included_in_print_rate ) :
# referred row should also be inclusive
_on_previous_row_error ( tax . row_id )
elif tax . charge_type == " On Previous Row Total " and \
not all ( [ cint ( t . included_in_print_rate ) for t in doc . get ( " taxes " ) [ : cint ( tax . row_id ) - 1 ] ] ) :
# all rows about the reffered tax should be inclusive
_on_previous_row_error ( " 1 - %d " % ( tax . row_id , ) )
2015-02-24 11:38:34 +00:00
elif tax . get ( " category " ) == " Valuation " :
2015-02-24 10:35:19 +00:00
frappe . throw ( _ ( " Valuation type charges can not marked as Inclusive " ) )
2016-02-18 10:32:05 +00:00
2016-02-18 11:18:35 +00:00
def set_balance_in_account_currency ( gl_dict , account_currency = None , conversion_rate = None , company_currency = None ) :
2016-02-18 10:32:05 +00:00
if ( not conversion_rate ) and ( account_currency != company_currency ) :
frappe . throw ( _ ( " Account: {0} with currency: {1} can not be selected " )
. format ( gl_dict . account , account_currency ) )
gl_dict [ " account_currency " ] = company_currency if account_currency == company_currency \
else account_currency
# set debit/credit in account currency if not provided
if flt ( gl_dict . debit ) and not flt ( gl_dict . debit_in_account_currency ) :
gl_dict . debit_in_account_currency = gl_dict . debit if account_currency == company_currency \
else flt ( gl_dict . debit / conversion_rate , 2 )
if flt ( gl_dict . credit ) and not flt ( gl_dict . credit_in_account_currency ) :
gl_dict . credit_in_account_currency = gl_dict . credit if account_currency == company_currency \
else flt ( gl_dict . credit / conversion_rate , 2 )
2016-06-27 14:39:05 +00:00
2016-07-07 08:32:26 +00:00
def get_advance_journal_entries ( party_type , party , party_account , amount_field ,
2016-06-27 14:39:05 +00:00
order_doctype , order_list , include_unallocated = True ) :
2016-07-07 08:32:26 +00:00
2016-06-27 14:39:05 +00:00
dr_or_cr = " credit_in_account_currency " if party_type == " Customer " else " debit_in_account_currency "
2016-07-07 08:32:26 +00:00
2016-06-27 14:39:05 +00:00
conditions = [ ]
if include_unallocated :
conditions . append ( " ifnull(t2.reference_name, ' ' )= ' ' " )
if order_list :
order_condition = ' , ' . join ( [ ' %s ' ] * len ( order_list ) )
conditions . append ( " (t2.reference_type = ' {0} ' and ifnull(t2.reference_name, ' ' ) in ( {1} )) " \
. format ( order_doctype , order_condition ) )
2016-07-07 08:32:26 +00:00
2016-06-27 14:39:05 +00:00
reference_condition = " and ( " + " or " . join ( conditions ) + " ) " if conditions else " "
2017-01-16 11:27:53 +00:00
2016-06-27 14:39:05 +00:00
journal_entries = frappe . db . sql ( """
select
2016-07-07 08:32:26 +00:00
" Journal Entry " as reference_type , t1 . name as reference_name ,
2016-06-27 14:39:05 +00:00
t1 . remark as remarks , t2 . { 0 } as amount , t2 . name as reference_row ,
t2 . reference_name as against_order
from
` tabJournal Entry ` t1 , ` tabJournal Entry Account ` t2
where
t1 . name = t2 . parent and t2 . account = % s
and t2 . party_type = % s and t2 . party = % s
and t2 . is_advance = ' Yes ' and t1 . docstatus = 1
2016-09-05 10:46:53 +00:00
and { 1 } > 0 { 2 }
2016-06-27 14:39:05 +00:00
order by t1 . posting_date """ .format(amount_field, dr_or_cr, reference_condition),
[ party_account , party_type , party ] + order_list , as_dict = 1 )
2016-07-07 08:32:26 +00:00
2016-06-27 14:39:05 +00:00
return list ( journal_entries )
2016-07-07 08:32:26 +00:00
def get_advance_payment_entries ( party_type , party , party_account ,
2016-06-27 14:39:05 +00:00
order_doctype , order_list = None , include_unallocated = True , against_all_orders = False ) :
party_account_field = " paid_from " if party_type == " Customer " else " paid_to "
payment_type = " Receive " if party_type == " Customer " else " Pay "
payment_entries_against_order , unallocated_payment_entries = [ ] , [ ]
2016-07-04 06:11:14 +00:00
2016-06-27 14:39:05 +00:00
if order_list or against_all_orders :
if order_list :
2016-07-04 06:11:14 +00:00
reference_condition = " and t2.reference_name in ( {0} ) " \
2016-06-27 14:39:05 +00:00
. format ( ' , ' . join ( [ ' %s ' ] * len ( order_list ) ) )
else :
reference_condition = " "
order_list = [ ]
2016-07-07 08:32:26 +00:00
2016-06-27 14:39:05 +00:00
payment_entries_against_order = frappe . db . sql ( """
select
" Payment Entry " as reference_type , t1 . name as reference_name ,
t1 . remarks , t2 . allocated_amount as amount , t2 . name as reference_row ,
t2 . reference_name as against_order , t1 . posting_date
2016-07-07 08:32:26 +00:00
from ` tabPayment Entry ` t1 , ` tabPayment Entry Reference ` t2
2016-06-27 14:39:05 +00:00
where
t1 . name = t2 . parent and t1 . { 0 } = % s and t1 . payment_type = % s
and t1 . party_type = % s and t1 . party = % s and t1 . docstatus = 1
and t2 . reference_doctype = % s { 1 }
2018-02-16 07:44:20 +00:00
order by t1 . posting_date
2016-07-07 08:32:26 +00:00
""" .format(party_account_field, reference_condition),
2016-06-27 14:39:05 +00:00
[ party_account , payment_type , party_type , party , order_doctype ] + order_list , as_dict = 1 )
2016-07-07 08:32:26 +00:00
2016-06-27 14:39:05 +00:00
if include_unallocated :
unallocated_payment_entries = frappe . db . sql ( """
2016-07-07 08:32:26 +00:00
select " Payment Entry " as reference_type , name as reference_name ,
2016-06-27 14:39:05 +00:00
remarks , unallocated_amount as amount
from ` tabPayment Entry `
where
{ 0 } = % s and party_type = % s and party = % s and payment_type = % s
and docstatus = 1 and unallocated_amount > 0
2018-02-16 07:44:20 +00:00
order by posting_date
2016-06-27 14:39:05 +00:00
""" .format(party_account_field), (party_account, party_type, party, payment_type), as_dict=1)
2016-07-07 08:32:26 +00:00
2016-11-08 07:09:33 +00:00
return list ( payment_entries_against_order ) + list ( unallocated_payment_entries )
def update_invoice_status ( ) :
# Daily update the status of the invoices
2017-01-16 11:27:53 +00:00
frappe . db . sql ( """ update `tabSales Invoice` set status = ' Overdue '
2016-11-08 07:09:33 +00:00
where due_date < CURDATE ( ) and docstatus = 1 and outstanding_amount > 0 """ )
2017-01-16 11:27:53 +00:00
frappe . db . sql ( """ update `tabPurchase Invoice` set status = ' Overdue '
2017-08-15 02:53:51 +00:00
where due_date < CURDATE ( ) and docstatus = 1 and outstanding_amount > 0 """ )
@frappe.whitelist ( )
2018-02-16 07:35:21 +00:00
def get_payment_terms ( terms_template , posting_date = None , grand_total = None , bill_date = None ) :
2017-08-15 02:53:51 +00:00
if not terms_template :
return
terms_doc = frappe . get_doc ( " Payment Terms Template " , terms_template )
schedule = [ ]
2017-08-19 14:01:40 +00:00
for d in terms_doc . get ( " terms " ) :
2018-02-16 07:35:21 +00:00
term_details = get_payment_term_details ( d , posting_date , grand_total , bill_date )
2017-08-15 02:53:51 +00:00
schedule . append ( term_details )
return schedule
@frappe.whitelist ( )
2018-02-16 07:35:21 +00:00
def get_payment_term_details ( term , posting_date = None , grand_total = None , bill_date = None ) :
2017-08-15 02:53:51 +00:00
term_details = frappe . _dict ( )
2018-03-08 07:25:41 +00:00
if isinstance ( term , text_type ) :
2017-08-15 02:53:51 +00:00
term = frappe . get_doc ( " Payment Term " , term )
else :
term_details . payment_term = term . payment_term
term_details . description = term . description
term_details . invoice_portion = term . invoice_portion
2017-11-27 06:13:52 +00:00
term_details . payment_amount = flt ( term . invoice_portion ) * flt ( grand_total ) / 100
2018-02-16 07:35:21 +00:00
if bill_date :
term_details . due_date = get_due_date ( term , bill_date )
elif posting_date :
term_details . due_date = get_due_date ( term , posting_date )
if getdate ( term_details . due_date ) < getdate ( posting_date ) :
term_details . due_date = posting_date
2018-03-09 06:53:53 +00:00
term_details . mode_of_payment = term . mode_of_payment
2018-02-16 07:35:21 +00:00
2017-08-15 02:53:51 +00:00
return term_details
2018-02-16 07:35:21 +00:00
def get_due_date ( term , posting_date = None , bill_date = None ) :
2017-08-15 02:53:51 +00:00
due_date = None
2018-02-16 07:35:21 +00:00
date = bill_date or posting_date
2017-08-15 02:53:51 +00:00
if term . due_date_based_on == " Day(s) after invoice date " :
2018-02-16 07:35:21 +00:00
due_date = add_days ( date , term . credit_days )
2017-08-15 02:53:51 +00:00
elif term . due_date_based_on == " Day(s) after the end of the invoice month " :
2018-02-16 07:35:21 +00:00
due_date = add_days ( get_last_day ( date ) , term . credit_days )
2017-08-15 02:53:51 +00:00
elif term . due_date_based_on == " Month(s) after the end of the invoice month " :
2018-02-16 07:35:21 +00:00
due_date = add_months ( get_last_day ( date ) , term . credit_months )
2017-09-08 14:35:59 +00:00
return due_date
2018-05-16 06:01:41 +00:00
def get_supplier_block_status ( party_name ) :
"""
Returns a dict containing the values of ` on_hold ` , ` release_date ` and ` hold_type ` of
a ` Supplier `
"""
supplier = frappe . get_doc ( ' Supplier ' , party_name )
info = {
' on_hold ' : supplier . on_hold ,
' release_date ' : supplier . release_date ,
' hold_type ' : supplier . hold_type
}
return info