2015-03-03 09:25:30 +00:00
# Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors
2014-01-28 12:13:10 +00:00
# License: GNU General Public License v3. See license.txt
from __future__ import unicode_literals
2014-02-14 10:17:51 +00:00
import frappe
2015-07-09 11:05:46 +00:00
import datetime
2014-09-10 11:41:30 +00:00
from frappe import _ , msgprint , scrub
2014-05-27 03:09:35 +00:00
from frappe . defaults import get_user_permissions
2015-08-18 06:01:02 +00:00
from frappe . utils import add_days , getdate , formatdate , get_first_day , date_diff
2014-02-03 10:44:56 +00:00
from erpnext . utilities . doctype . address . address import get_address_display
from erpnext . utilities . doctype . contact . contact import get_contact_details
2014-01-28 12:13:10 +00:00
2015-09-03 10:33:07 +00:00
class InvalidCurrency ( frappe . ValidationError ) : pass
class InvalidAccountCurrency ( frappe . ValidationError ) : pass
2014-02-14 10:17:51 +00:00
@frappe.whitelist ( )
2014-04-10 13:10:57 +00:00
def get_party_details ( party = None , account = None , party_type = " Customer " , company = None ,
2014-06-18 11:10:27 +00:00
posting_date = None , price_list = None , currency = None , doctype = None ) :
2014-02-20 09:51:53 +00:00
2015-04-01 18:08:13 +00:00
if not party :
return { }
2015-06-08 06:56:52 +00:00
if not frappe . db . exists ( party_type , party ) :
frappe . throw ( _ ( " {0} : {1} does not exists " ) . format ( party_type , party ) )
2015-04-01 18:08:13 +00:00
2014-06-18 11:10:27 +00:00
return _get_party_details ( party , account , party_type ,
company , posting_date , price_list , currency , doctype )
2014-02-20 09:51:53 +00:00
2014-04-10 13:10:57 +00:00
def _get_party_details ( party = None , account = None , party_type = " Customer " , company = None ,
2014-06-18 11:10:27 +00:00
posting_date = None , price_list = None , currency = None , doctype = None , ignore_permissions = False ) :
2015-06-08 06:56:52 +00:00
2014-06-18 11:10:27 +00:00
out = frappe . _dict ( set_account_and_due_date ( party , account , party_type , company , posting_date , doctype ) )
2014-04-10 13:10:57 +00:00
2014-02-03 10:44:56 +00:00
party = out [ party_type . lower ( ) ]
2014-02-20 09:51:53 +00:00
if not ignore_permissions and not frappe . has_permission ( party_type , " read " , party ) :
2014-04-15 11:00:55 +00:00
frappe . throw ( _ ( " Not permitted " ) , frappe . PermissionError )
2014-02-03 10:44:56 +00:00
2014-04-03 12:08:54 +00:00
party = frappe . get_doc ( party_type , party )
2014-02-03 10:44:56 +00:00
2014-02-19 12:13:24 +00:00
set_address_details ( out , party , party_type )
set_contact_details ( out , party , party_type )
2014-02-03 10:44:56 +00:00
set_other_values ( out , party , party_type )
2014-04-03 12:08:54 +00:00
set_price_list ( out , party , party_type , price_list )
2014-04-10 13:10:57 +00:00
2014-02-03 10:44:56 +00:00
if not out . get ( " currency " ) :
out [ " currency " ] = currency
2014-04-10 13:10:57 +00:00
2014-02-03 10:44:56 +00:00
# sales team
2014-01-28 12:13:10 +00:00
if party_type == " Customer " :
2014-02-03 10:44:56 +00:00
out [ " sales_team " ] = [ {
2014-04-10 13:10:57 +00:00
" sales_person " : d . sales_person ,
2014-05-02 10:15:10 +00:00
" sales_designation " : d . sales_designation ,
" allocated_percentage " : d . allocated_percentage
2014-04-03 12:08:54 +00:00
} for d in party . get ( " sales_team " ) ]
2014-04-10 13:10:57 +00:00
2014-02-03 10:44:56 +00:00
return out
2014-02-19 12:13:24 +00:00
def set_address_details ( out , party , party_type ) :
billing_address_field = " customer_address " if party_type == " Lead " \
else party_type . lower ( ) + " _address "
2014-04-10 13:10:57 +00:00
out [ billing_address_field ] = frappe . db . get_value ( " Address " ,
2014-02-19 12:13:24 +00:00
{ party_type . lower ( ) : party . name , " is_primary_address " : 1 } , " name " )
2014-04-10 13:10:57 +00:00
2014-02-03 10:44:56 +00:00
# address display
2014-02-19 12:13:24 +00:00
out . address_display = get_address_display ( out [ billing_address_field ] )
2014-04-10 13:10:57 +00:00
2014-02-19 12:13:24 +00:00
# shipping address
if party_type in [ " Customer " , " Lead " ] :
2014-04-10 13:10:57 +00:00
out . shipping_address_name = frappe . db . get_value ( " Address " ,
2014-02-19 12:13:24 +00:00
{ party_type . lower ( ) : party . name , " is_shipping_address " : 1 } , " name " )
out . shipping_address = get_address_display ( out [ " shipping_address_name " ] )
2014-04-10 13:10:57 +00:00
2014-02-19 12:13:24 +00:00
def set_contact_details ( out , party , party_type ) :
2014-04-10 13:10:57 +00:00
out . contact_person = frappe . db . get_value ( " Contact " ,
2014-02-19 12:13:24 +00:00
{ party_type . lower ( ) : party . name , " is_primary_contact " : 1 } , " name " )
2014-04-10 13:10:57 +00:00
if not out . contact_person :
2015-07-07 11:11:37 +00:00
out . update ( {
" contact_person " : None ,
" contact_display " : None ,
" contact_email " : None ,
" contact_mobile " : None ,
" contact_phone " : None ,
" contact_designation " : None ,
" contact_department " : None
} )
2015-07-08 12:46:51 +00:00
else :
out . update ( get_contact_details ( out . contact_person ) )
2014-02-03 10:44:56 +00:00
def set_other_values ( out , party , party_type ) :
# copy
if party_type == " Customer " :
to_copy = [ " customer_name " , " customer_group " , " territory " ]
2014-01-28 12:13:10 +00:00
else :
2014-02-03 10:44:56 +00:00
to_copy = [ " supplier_name " , " supplier_type " ]
for f in to_copy :
out [ f ] = party . get ( f )
2014-04-10 13:10:57 +00:00
2014-02-03 10:44:56 +00:00
# fields prepended with default in Customer doctype
for f in [ ' currency ' , ' taxes_and_charges ' ] \
+ ( [ ' sales_partner ' , ' commission_rate ' ] if party_type == " Customer " else [ ] ) :
if party . get ( " default_ " + f ) :
out [ f ] = party . get ( " default_ " + f )
2014-04-03 12:08:54 +00:00
def set_price_list ( out , party , party_type , given_price_list ) :
2014-04-10 13:10:57 +00:00
# price list
2014-05-27 08:16:42 +00:00
price_list = filter ( None , get_user_permissions ( ) . get ( " Price List " , [ ] ) )
2014-02-03 10:44:56 +00:00
if isinstance ( price_list , list ) :
2014-05-27 08:16:42 +00:00
price_list = price_list [ 0 ] if len ( price_list ) == 1 else None
2014-02-03 10:44:56 +00:00
if not price_list :
price_list = party . default_price_list
2014-04-10 13:10:57 +00:00
2014-04-03 12:08:54 +00:00
if not price_list and party_type == " Customer " :
2014-04-10 13:10:57 +00:00
price_list = frappe . db . get_value ( " Customer Group " ,
2014-02-03 10:44:56 +00:00
party . customer_group , " default_price_list " )
if not price_list :
price_list = given_price_list
if price_list :
2014-02-26 07:05:33 +00:00
out . price_list_currency = frappe . db . get_value ( " Price List " , price_list , " currency " )
2014-04-10 13:10:57 +00:00
2014-02-03 10:44:56 +00:00
out [ " selling_price_list " if party . doctype == " Customer " else " buying_price_list " ] = price_list
2014-04-10 13:10:57 +00:00
2014-02-03 10:44:56 +00:00
2014-06-18 11:10:27 +00:00
def set_account_and_due_date ( party , account , party_type , company , posting_date , doctype ) :
if doctype not in [ " Sales Invoice " , " Purchase Invoice " ] :
2014-02-03 10:44:56 +00:00
# not an invoice
return {
party_type . lower ( ) : party
}
2014-04-10 13:10:57 +00:00
2014-01-28 12:13:10 +00:00
if party :
account = get_party_account ( company , party , party_type )
2014-04-10 13:10:57 +00:00
account_fieldname = " debit_to " if party_type == " Customer " else " credit_to "
2014-01-28 12:13:10 +00:00
out = {
party_type . lower ( ) : party ,
account_fieldname : account ,
2014-08-27 11:16:33 +00:00
" due_date " : get_due_date ( posting_date , party_type , party , company )
2014-02-03 10:44:56 +00:00
}
2014-01-28 12:13:10 +00:00
return out
2015-08-18 06:01:02 +00:00
2015-09-03 04:58:08 +00:00
def validate_accounting_currency ( party ) :
company_currency = get_company_currency ( )
# set party account currency
if not party . party_account_currency :
if party . default_currency :
party . party_account_currency = party . default_currency
2015-09-03 10:33:07 +00:00
elif len ( set ( company_currency . values ( ) ) ) == 1 :
party . party_account_currency = company_currency . values ( ) [ 0 ]
2015-09-03 04:58:08 +00:00
party_account_currency_in_db = frappe . db . get_value ( party . doctype , party . name , " party_account_currency " )
2015-09-03 10:33:07 +00:00
if party_account_currency_in_db != party . party_account_currency :
2015-09-03 04:58:08 +00:00
existing_gle = frappe . db . get_value ( " GL Entry " , { " party_type " : party . doctype ,
" party " : party . name } , [ " name " , " account_currency " ] , as_dict = 1 )
if existing_gle :
2015-09-03 10:33:07 +00:00
if party_account_currency_in_db :
frappe . throw ( _ ( " Accounting Currency cannot be changed, as GL Entry exists for this {0} " )
. format ( party . doctype ) , InvalidCurrency )
else :
party . party_account_currency = existing_gle . account_currency
2015-09-03 04:58:08 +00:00
2015-08-18 06:01:02 +00:00
def validate_party_account ( party ) :
2015-09-03 04:58:08 +00:00
company_currency = get_company_currency ( )
2015-09-03 10:33:07 +00:00
if party . party_account_currency :
companies_with_different_currency = [ ]
for company , currency in company_currency . items ( ) :
if currency != party . party_account_currency :
companies_with_different_currency . append ( company )
for d in party . get ( " accounts " ) :
if d . company in companies_with_different_currency :
companies_with_different_currency . remove ( d . company )
2015-09-03 13:48:00 +00:00
2015-09-03 10:33:07 +00:00
selected_account_currency = frappe . db . get_value ( " Account " , d . account , " account_currency " )
if selected_account_currency != party . party_account_currency :
frappe . throw ( _ ( " Account {0} is invalid, account currency must be {1} " )
. format ( d . account , selected_account_currency ) , InvalidAccountCurrency )
if companies_with_different_currency :
2015-09-03 04:58:08 +00:00
frappe . msgprint ( _ ( " Please mention Default {0} Account for the following companies, as accounting currency is different from company ' s default currency: {1} " )
. format (
" Receivable " if party . doctype == " Customer " else " Payable " ,
2015-09-03 10:33:07 +00:00
" \n " + " \n " . join ( companies_with_different_currency )
2015-09-03 04:58:08 +00:00
)
)
def get_company_currency ( ) :
2015-09-03 10:33:07 +00:00
company_currency = frappe . _dict ( )
for d in frappe . get_all ( " Company " , fields = [ " name " , " default_currency " ] ) :
company_currency . setdefault ( d . name , d . default_currency )
return company_currency
2015-08-18 06:01:02 +00:00
2014-09-10 11:41:30 +00:00
@frappe.whitelist ( )
2014-01-28 12:13:10 +00:00
def get_party_account ( company , party , party_type ) :
2015-02-21 09:09:35 +00:00
""" Returns the account for the given `party`.
Will first search in party ( Customer / Supplier ) record , if not found ,
will search in group ( Customer Group / Supplier Type ) ,
finally will return default . """
2014-01-28 12:13:10 +00:00
if not company :
2014-02-14 10:17:51 +00:00
frappe . throw ( _ ( " Please select company first. " ) )
2014-01-28 12:13:10 +00:00
if party :
2015-02-21 09:09:35 +00:00
account = frappe . db . get_value ( " Party Account " ,
{ " parenttype " : party_type , " parent " : party , " company " : company } , " account " )
2014-09-10 11:41:30 +00:00
if not account :
2015-02-21 09:09:35 +00:00
party_group_doctype = " Customer Group " if party_type == " Customer " else " Supplier Type "
group = frappe . db . get_value ( party_type , party , scrub ( party_group_doctype ) )
2014-09-10 11:41:30 +00:00
account = frappe . db . get_value ( " Party Account " ,
2015-02-21 09:09:35 +00:00
{ " parenttype " : party_group_doctype , " parent " : group , " company " : company } , " account " )
if not account :
default_account_name = " default_receivable_account " if party_type == " Customer " else " default_payable_account "
account = frappe . db . get_value ( " Company " , company , default_account_name )
2014-09-10 11:41:30 +00:00
return account
2014-01-28 12:13:10 +00:00
2015-07-09 11:05:46 +00:00
@frappe.whitelist ( )
2014-08-27 11:16:33 +00:00
def get_due_date ( posting_date , party_type , party , company ) :
2014-01-28 12:13:10 +00:00
""" Set Due Date = Posting Date + Credit Days """
due_date = None
2015-07-09 11:05:46 +00:00
if posting_date and party :
2015-07-09 14:18:21 +00:00
due_date = posting_date
2015-07-09 11:05:46 +00:00
if party_type == " Customer " :
credit_days_based_on , credit_days = get_credit_days ( party_type , party , company )
if credit_days_based_on == " Fixed Days " and credit_days :
due_date = add_days ( posting_date , credit_days )
elif credit_days_based_on == " Last Day of the Next Month " :
due_date = ( get_first_day ( posting_date , 0 , 2 ) + datetime . timedelta ( - 1 ) ) . strftime ( " % Y- % m- %d " )
else :
credit_days = get_credit_days ( party_type , party , company )
if credit_days :
due_date = add_days ( posting_date , credit_days )
2014-04-10 13:10:57 +00:00
return due_date
2014-01-28 12:13:10 +00:00
2014-08-27 12:13:55 +00:00
def get_credit_days ( party_type , party , company ) :
2015-07-09 11:05:46 +00:00
if party_type and party :
if party_type == " Customer " :
credit_days_based_on , credit_days , customer_group = \
frappe . db . get_value ( party_type , party , [ " credit_days_based_on " , " credit_days " , " customer_group " ] )
if not credit_days_based_on :
credit_days_based_on , credit_days = \
frappe . db . get_value ( " Customer Group " , customer_group , [ " credit_days_based_on " , " credit_days " ] ) \
or frappe . db . get_value ( " Company " , company , [ " credit_days_based_on " , " credit_days " ] )
return credit_days_based_on , credit_days
else :
credit_days , supplier_type = frappe . db . get_value ( party_type , party , [ " credit_days " , " supplier_type " ] )
if not credit_days :
credit_days = frappe . db . get_value ( " Supplier Type " , supplier_type , " credit_days " ) \
or frappe . db . get_value ( " Company " , company , " credit_days " )
return credit_days
2014-08-27 11:16:33 +00:00
def validate_due_date ( posting_date , due_date , party_type , party , company ) :
2015-07-09 11:05:46 +00:00
if getdate ( due_date ) < getdate ( posting_date ) :
2014-08-27 11:16:33 +00:00
frappe . throw ( _ ( " Due Date cannot be before Posting Date " ) )
2015-07-09 11:05:46 +00:00
else :
default_due_date = get_due_date ( posting_date , party_type , party , company )
2015-07-10 11:41:58 +00:00
if default_due_date != posting_date and getdate ( due_date ) > getdate ( default_due_date ) :
2015-07-09 11:05:46 +00:00
is_credit_controller = frappe . db . get_single_value ( " Accounts Settings " , " credit_controller " ) in frappe . get_roles ( )
if is_credit_controller :
msgprint ( _ ( " Note: Due / Reference Date exceeds allowed customer credit days by {0} day(s) " )
. format ( date_diff ( due_date , default_due_date ) ) )
else :
frappe . throw ( _ ( " Due / Reference Date cannot be after {0} " ) . format ( formatdate ( default_due_date ) ) )