2015-03-03 14:55:30 +05:30
# Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors
2014-01-28 17:43:10 +05:30
# License: GNU General Public License v3. See license.txt
from __future__ import unicode_literals
2014-02-14 15:47:51 +05:30
import frappe
2015-07-09 16:35:46 +05:30
import datetime
2014-09-10 17:11:30 +05:30
from frappe import _ , msgprint , scrub
2014-05-27 08:39:35 +05:30
from frappe . defaults import get_user_permissions
2017-02-16 13:12:29 +05:30
from frappe . utils import add_days , getdate , formatdate , get_first_day , date_diff , add_years , get_timestamp
2017-01-13 18:53:11 +05:30
from frappe . geo . doctype . address . address import get_address_display , get_default_address
from frappe . email . doctype . contact . contact import get_contact_details , get_default_contact
2016-01-25 21:25:11 +05:30
from erpnext . exceptions import PartyFrozen , InvalidCurrency , PartyDisabled , InvalidAccountCurrency
2014-01-28 17:43:10 +05:30
2015-09-28 15:24:00 +05:30
class DuplicatePartyAccountError ( frappe . ValidationError ) : pass
2015-09-03 16:03:07 +05:30
2014-02-14 15:47:51 +05:30
@frappe.whitelist ( )
2014-04-10 18:40:57 +05:30
def get_party_details ( party = None , account = None , party_type = " Customer " , company = None ,
2016-03-03 14:00:35 +05:30
posting_date = None , price_list = None , currency = None , doctype = None , ignore_permissions = False ) :
2014-02-20 15:21:53 +05:30
2015-04-01 23:38:13 +05:30
if not party :
return { }
2015-09-18 12:59:51 +05:30
2015-06-08 12:26:52 +05:30
if not frappe . db . exists ( party_type , party ) :
frappe . throw ( _ ( " {0} : {1} does not exists " ) . format ( party_type , party ) )
2015-04-01 23:38:13 +05:30
2014-06-18 16:40:27 +05:30
return _get_party_details ( party , account , party_type ,
2016-03-03 14:00:35 +05:30
company , posting_date , price_list , currency , doctype , ignore_permissions )
2014-02-20 15:21:53 +05:30
2014-04-10 18:40:57 +05:30
def _get_party_details ( party = None , account = None , party_type = " Customer " , company = None ,
2014-06-18 16:40:27 +05:30
posting_date = None , price_list = None , currency = None , doctype = None , ignore_permissions = False ) :
2015-09-18 12:59:51 +05:30
2014-06-18 16:40:27 +05:30
out = frappe . _dict ( set_account_and_due_date ( party , account , party_type , company , posting_date , doctype ) )
2014-04-10 18:40:57 +05:30
2014-02-03 16:14:56 +05:30
party = out [ party_type . lower ( ) ]
2014-02-20 15:21:53 +05:30
if not ignore_permissions and not frappe . has_permission ( party_type , " read " , party ) :
2015-11-27 11:35:35 +05:30
frappe . throw ( _ ( " Not permitted for {0} " ) . format ( party ) , frappe . PermissionError )
2014-02-03 16:14:56 +05:30
2014-04-03 17:38:54 +05:30
party = frappe . get_doc ( party_type , party )
2014-02-03 16:14:56 +05:30
2014-02-19 17:43:24 +05:30
set_address_details ( out , party , party_type )
set_contact_details ( out , party , party_type )
2014-02-03 16:14:56 +05:30
set_other_values ( out , party , party_type )
2014-04-03 17:38:54 +05:30
set_price_list ( out , party , party_type , price_list )
2015-08-26 10:41:31 +05:30
out [ " taxes_and_charges " ] = set_taxes ( party . name , party_type , posting_date , company , out . customer_group , out . supplier_type )
2014-04-10 18:40:57 +05:30
2014-02-03 16:14:56 +05:30
if not out . get ( " currency " ) :
out [ " currency " ] = currency
2014-04-10 18:40:57 +05:30
2014-02-03 16:14:56 +05:30
# sales team
2014-01-28 17:43:10 +05:30
if party_type == " Customer " :
2014-02-03 16:14:56 +05:30
out [ " sales_team " ] = [ {
2015-10-12 16:15:52 +05:30
" sales_person " : d . sales_person ,
" allocated_percentage " : d . allocated_percentage or None
2014-04-03 17:38:54 +05:30
} for d in party . get ( " sales_team " ) ]
2014-04-10 18:40:57 +05:30
2014-02-03 16:14:56 +05:30
return out
2014-02-19 17:43:24 +05:30
def set_address_details ( out , party , party_type ) :
billing_address_field = " customer_address " if party_type == " Lead " \
else party_type . lower ( ) + " _address "
2017-01-13 18:53:11 +05:30
out [ billing_address_field ] = get_default_address ( party_type , party . name )
2014-04-10 18:40:57 +05:30
2014-02-03 16:14:56 +05:30
# address display
2014-02-19 17:43:24 +05:30
out . address_display = get_address_display ( out [ billing_address_field ] )
2014-04-10 18:40:57 +05:30
2014-02-19 17:43:24 +05:30
# shipping address
if party_type in [ " Customer " , " Lead " ] :
2017-01-13 18:53:11 +05:30
out . shipping_address_name = get_default_address ( party_type , party . name , ' is_shipping_address ' )
2014-02-19 17:43:24 +05:30
out . shipping_address = get_address_display ( out [ " shipping_address_name " ] )
2014-04-10 18:40:57 +05:30
2014-02-19 17:43:24 +05:30
def set_contact_details ( out , party , party_type ) :
2017-01-13 18:53:11 +05:30
out . contact_person = get_default_contact ( party_type , party . name )
2014-04-10 18:40:57 +05:30
if not out . contact_person :
2015-07-07 16:41:37 +05:30
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 18:16:51 +05:30
else :
out . update ( get_contact_details ( out . contact_person ) )
2014-02-03 16:14:56 +05:30
def set_other_values ( out , party , party_type ) :
# copy
if party_type == " Customer " :
2016-02-19 11:08:45 +05:30
to_copy = [ " customer_name " , " customer_group " , " territory " , " language " ]
2014-01-28 17:43:10 +05:30
else :
2016-02-19 11:08:45 +05:30
to_copy = [ " supplier_name " , " supplier_type " , " language " ]
2014-02-03 16:14:56 +05:30
for f in to_copy :
out [ f ] = party . get ( f )
2014-04-10 18:40:57 +05:30
2014-02-03 16:14:56 +05:30
# fields prepended with default in Customer doctype
2015-08-21 15:04:57 +05:30
for f in [ ' currency ' ] \
2014-02-03 16:14:56 +05:30
+ ( [ ' sales_partner ' , ' commission_rate ' ] if party_type == " Customer " else [ ] ) :
if party . get ( " default_ " + f ) :
out [ f ] = party . get ( " default_ " + f )
2015-09-17 18:29:44 +05:30
def get_default_price_list ( party ) :
""" Return default price list for party (Document object) """
if party . default_price_list :
return party . default_price_list
if party . doctype == " Customer " :
price_list = frappe . db . get_value ( " Customer Group " ,
party . customer_group , " default_price_list " )
if price_list :
return price_list
return None
2014-04-03 17:38:54 +05:30
def set_price_list ( out , party , party_type , given_price_list ) :
2014-04-10 18:40:57 +05:30
# price list
2014-05-27 13:46:42 +05:30
price_list = filter ( None , get_user_permissions ( ) . get ( " Price List " , [ ] ) )
2014-02-03 16:14:56 +05:30
if isinstance ( price_list , list ) :
2014-05-27 13:46:42 +05:30
price_list = price_list [ 0 ] if len ( price_list ) == 1 else None
2014-02-03 16:14:56 +05:30
if not price_list :
2015-09-17 18:29:44 +05:30
price_list = get_default_price_list ( party )
2014-02-03 16:14:56 +05:30
if not price_list :
price_list = given_price_list
if price_list :
2014-02-26 12:35:33 +05:30
out . price_list_currency = frappe . db . get_value ( " Price List " , price_list , " currency " )
2014-04-10 18:40:57 +05:30
2014-02-03 16:14:56 +05:30
out [ " selling_price_list " if party . doctype == " Customer " else " buying_price_list " ] = price_list
2014-04-10 18:40:57 +05:30
2014-02-03 16:14:56 +05:30
2014-06-18 16:40:27 +05:30
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 16:14:56 +05:30
# not an invoice
return {
party_type . lower ( ) : party
}
2014-04-10 18:40:57 +05:30
2014-01-28 17:43:10 +05:30
if party :
2015-09-25 16:17:50 +05:30
account = get_party_account ( party_type , party , company )
2014-01-28 17:43:10 +05:30
2014-04-10 18:40:57 +05:30
account_fieldname = " debit_to " if party_type == " Customer " else " credit_to "
2014-01-28 17:43:10 +05:30
out = {
party_type . lower ( ) : party ,
account_fieldname : account ,
2014-08-27 16:46:33 +05:30
" due_date " : get_due_date ( posting_date , party_type , party , company )
2014-02-03 16:14:56 +05:30
}
2014-01-28 17:43:10 +05:30
return out
2015-09-18 12:59:51 +05:30
2015-09-03 10:28:08 +05:30
def get_company_currency ( ) :
2015-09-03 16:03:07 +05:30
company_currency = frappe . _dict ( )
for d in frappe . get_all ( " Company " , fields = [ " name " , " default_currency " ] ) :
company_currency . setdefault ( d . name , d . default_currency )
2015-09-18 12:59:51 +05:30
2015-09-03 16:03:07 +05:30
return company_currency
2015-09-18 12:59:51 +05:30
2014-09-10 17:11:30 +05:30
@frappe.whitelist ( )
2015-09-25 16:17:50 +05:30
def get_party_account ( party_type , party , company ) :
2015-02-21 14:39:35 +05:30
""" 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 17:43:10 +05:30
if not company :
2015-09-25 16:17:50 +05:30
frappe . throw ( _ ( " Please select a Company " ) )
2014-01-28 17:43:10 +05:30
if party :
2015-02-21 14:39:35 +05:30
account = frappe . db . get_value ( " Party Account " ,
{ " parenttype " : party_type , " parent " : party , " company " : company } , " account " )
2014-09-10 17:11:30 +05:30
2017-02-01 12:02:08 +05:30
if not account and party_type in [ ' Customer ' , ' Supplier ' ] :
2015-02-21 14:39:35 +05:30
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 17:11:30 +05:30
account = frappe . db . get_value ( " Party Account " ,
2015-02-21 14:39:35 +05:30
{ " parenttype " : party_group_doctype , " parent " : group , " company " : company } , " account " )
2017-02-01 12:02:08 +05:30
if not account and party_type in [ ' Customer ' , ' Supplier ' ] :
2016-06-28 16:15:58 +05:30
default_account_name = " default_receivable_account " \
if party_type == " Customer " else " default_payable_account "
2015-02-21 14:39:35 +05:30
account = frappe . db . get_value ( " Company " , company , default_account_name )
2017-01-13 18:53:11 +05:30
2016-06-28 16:15:58 +05:30
existing_gle_currency = get_party_gle_currency ( party_type , party , company )
if existing_gle_currency :
if account :
account_currency = frappe . db . get_value ( " Account " , account , " account_currency " )
if ( account and account_currency != existing_gle_currency ) or not account :
account = get_party_gle_account ( party_type , party , company )
2014-09-10 17:11:30 +05:30
return account
2014-01-28 17:43:10 +05:30
2015-09-25 16:17:50 +05:30
def get_party_account_currency ( party_type , party , company ) :
def generator ( ) :
party_account = get_party_account ( party_type , party , company )
return frappe . db . get_value ( " Account " , party_account , " account_currency " )
return frappe . local_cache ( " party_account_currency " , ( party_type , party , company ) , generator )
2015-09-30 16:41:15 +05:30
def get_party_gle_currency ( party_type , party , company ) :
def generator ( ) :
existing_gle_currency = frappe . db . sql ( """ select account_currency from `tabGL Entry`
where docstatus = 1 and company = % ( company ) s and party_type = % ( party_type ) s and party = % ( party ) s
limit 1 """ , { " company " : company, " party_type " : party_type, " party " : party })
return existing_gle_currency [ 0 ] [ 0 ] if existing_gle_currency else None
2015-10-22 19:32:56 +05:30
return frappe . local_cache ( " party_gle_currency " , ( party_type , party , company ) , generator ,
regenerate_if_none = True )
2017-01-13 18:53:11 +05:30
2016-06-28 16:15:58 +05:30
def get_party_gle_account ( party_type , party , company ) :
def generator ( ) :
existing_gle_account = frappe . db . sql ( """ select account from `tabGL Entry`
where docstatus = 1 and company = % ( company ) s and party_type = % ( party_type ) s and party = % ( party ) s
limit 1 """ , { " company " : company, " party_type " : party_type, " party " : party })
return existing_gle_account [ 0 ] [ 0 ] if existing_gle_account else None
return frappe . local_cache ( " party_gle_account " , ( party_type , party , company ) , generator ,
regenerate_if_none = True )
2015-09-30 16:41:15 +05:30
2015-10-22 17:54:50 +05:30
def validate_party_gle_currency ( party_type , party , company , party_account_currency = None ) :
2015-09-30 16:41:15 +05:30
""" Validate party account currency with existing GL Entry ' s currency """
2015-10-22 17:54:50 +05:30
if not party_account_currency :
party_account_currency = get_party_account_currency ( party_type , party , company )
2015-09-30 16:41:15 +05:30
existing_gle_currency = get_party_gle_currency ( party_type , party , company )
if existing_gle_currency and party_account_currency != existing_gle_currency :
frappe . throw ( _ ( " Accounting Entry for {0} : {1} can only be made in currency: {2} " )
. format ( party_type , party , existing_gle_currency ) , InvalidAccountCurrency )
2015-09-28 15:13:38 +05:30
def validate_party_accounts ( doc ) :
companies = [ ]
for account in doc . get ( " accounts " ) :
if account . company in companies :
2015-10-16 13:07:45 +05:30
frappe . throw ( _ ( " There can only be 1 Account per Company in {0} {1} " )
. format ( doc . doctype , doc . name ) , DuplicatePartyAccountError )
2015-09-28 15:13:38 +05:30
else :
companies . append ( account . company )
2015-10-22 17:54:50 +05:30
2015-10-16 13:07:45 +05:30
party_account_currency = frappe . db . get_value ( " Account " , account . account , " account_currency " )
existing_gle_currency = get_party_gle_currency ( doc . doctype , doc . name , account . company )
2016-03-30 13:24:42 +05:30
company_default_currency = frappe . db . get_value ( " Company " ,
frappe . db . get_default ( " Company " ) , " default_currency " , cache = True )
2015-10-22 17:54:50 +05:30
2015-10-16 13:08:33 +05:30
if existing_gle_currency and party_account_currency != existing_gle_currency :
frappe . throw ( _ ( " Accounting entries have already been made in currency {0} for company {1} . Please select a receivable or payable account with currency {0} . " ) . format ( existing_gle_currency , account . company ) )
2015-09-28 15:13:38 +05:30
2017-02-01 12:02:08 +05:30
if doc . get ( " default_currency " ) and party_account_currency and company_default_currency :
2016-03-30 13:24:42 +05:30
if doc . default_currency != party_account_currency and doc . default_currency != company_default_currency :
2016-07-01 15:58:39 +05:30
frappe . throw ( _ ( " Billing currency must be equal to either default comapany ' s currency or party account currency " ) )
2016-03-30 13:24:42 +05:30
2015-07-09 16:35:46 +05:30
@frappe.whitelist ( )
2014-08-27 16:46:33 +05:30
def get_due_date ( posting_date , party_type , party , company ) :
2014-01-28 17:43:10 +05:30
""" Set Due Date = Posting Date + Credit Days """
due_date = None
2015-07-09 16:35:46 +05:30
if posting_date and party :
2015-07-09 19:48:21 +05:30
due_date = posting_date
2016-01-25 12:44:14 +05:30
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 " )
2015-09-18 12:59:51 +05:30
2014-04-10 18:40:57 +05:30
return due_date
2014-01-28 17:43:10 +05:30
2014-08-27 17:43:55 +05:30
def get_credit_days ( party_type , party , company ) :
2015-07-09 16:35:46 +05:30
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 " ] )
2016-01-25 12:44:14 +05:30
else :
credit_days_based_on , credit_days , supplier_type = \
frappe . db . get_value ( party_type , party , [ " credit_days_based_on " , " credit_days " , " supplier_type " ] )
2015-09-18 12:59:51 +05:30
2016-01-25 12:44:14 +05:30
if not credit_days_based_on :
if party_type == " Customer " :
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 " ] )
2015-07-09 16:35:46 +05:30
else :
2016-01-25 12:44:14 +05:30
credit_days_based_on , credit_days = \
frappe . db . get_value ( " Supplier Type " , supplier_type , [ " credit_days_based_on " , " credit_days " ] ) \
or frappe . db . get_value ( " Company " , company , [ " credit_days_based_on " , " credit_days " ] )
2015-09-18 12:59:51 +05:30
2016-01-25 12:44:14 +05:30
return credit_days_based_on , credit_days
2015-09-18 12:59:51 +05:30
2014-08-27 16:46:33 +05:30
def validate_due_date ( posting_date , due_date , party_type , party , company ) :
2015-07-09 16:35:46 +05:30
if getdate ( due_date ) < getdate ( posting_date ) :
2014-08-27 16:46:33 +05:30
frappe . throw ( _ ( " Due Date cannot be before Posting Date " ) )
2015-07-09 16:35:46 +05:30
else :
default_due_date = get_due_date ( posting_date , party_type , party , company )
2015-09-25 16:32:14 +05:30
if not default_due_date :
return
2015-07-10 17:11:58 +05:30
if default_due_date != posting_date and getdate ( due_date ) > getdate ( default_due_date ) :
2015-07-09 16:35:46 +05:30
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 :
2015-08-21 15:04:57 +05:30
frappe . throw ( _ ( " Due / Reference Date cannot be after {0} " ) . format ( formatdate ( default_due_date ) ) )
2015-09-18 12:59:51 +05:30
2015-08-26 10:41:31 +05:30
@frappe.whitelist ( )
2015-09-18 12:59:51 +05:30
def set_taxes ( party , party_type , posting_date , company , customer_group = None , supplier_type = None ,
2015-09-11 15:44:06 +05:30
billing_address = None , shipping_address = None , use_for_shopping_cart = None ) :
2015-08-21 15:04:57 +05:30
from erpnext . accounts . doctype . tax_rule . tax_rule import get_tax_template , get_party_details
args = {
2015-09-23 15:43:09 +05:30
party_type . lower ( ) : party ,
2015-08-26 10:41:31 +05:30
" customer_group " : customer_group ,
" supplier_type " : supplier_type ,
2015-08-21 15:04:57 +05:30
" company " : company
}
2015-09-18 12:59:51 +05:30
2015-08-26 10:41:31 +05:30
if billing_address or shipping_address :
2015-09-11 15:44:06 +05:30
args . update ( get_party_details ( party , party_type , { " billing_address " : billing_address , \
" shipping_address " : shipping_address } ) )
2015-08-26 10:41:31 +05:30
else :
args . update ( get_party_details ( party , party_type ) )
2015-09-18 12:59:51 +05:30
2016-04-08 17:36:10 +05:30
if party_type in ( " Customer " , " Lead " ) :
2015-08-21 15:04:57 +05:30
args . update ( { " tax_type " : " Sales " } )
2016-04-08 17:36:10 +05:30
if party_type == ' Lead ' :
args [ ' customer ' ] = None
del args [ ' lead ' ]
2015-08-21 15:04:57 +05:30
else :
args . update ( { " tax_type " : " Purchase " } )
2015-09-18 12:59:51 +05:30
2015-09-11 15:44:06 +05:30
if use_for_shopping_cart :
args . update ( { " use_for_shopping_cart " : use_for_shopping_cart } )
2015-09-18 12:59:51 +05:30
return get_tax_template ( posting_date , args )
2016-01-25 17:30:49 +05:30
2016-01-26 14:56:52 +05:30
def validate_party_frozen_disabled ( party_type , party_name ) :
2016-01-26 13:57:06 +05:30
if party_type and party_name :
2017-01-30 15:15:58 +05:30
if party_type in ( " Customer " , " Supplier " ) :
party = frappe . db . get_value ( party_type , party_name , [ " is_frozen " , " disabled " ] , as_dict = True )
if party . disabled :
frappe . throw ( _ ( " {0} {1} is disabled " ) . format ( party_type , party_name ) , PartyDisabled )
elif party . get ( " is_frozen " ) :
frozen_accounts_modifier = frappe . db . get_value ( ' Accounts Settings ' , None , ' frozen_accounts_modifier ' )
if not frozen_accounts_modifier in frappe . get_roles ( ) :
frappe . throw ( _ ( " {0} {1} is frozen " ) . format ( party_type , party_name ) , PartyFrozen )
elif party_type == " Employee " :
if frappe . db . get_value ( " Employee " , party_name , " status " ) == " Left " :
frappe . msgprint ( _ ( " {0} {1} is not active " ) . format ( party_type , party_name ) , PartyDisabled , alert = True )
2016-04-14 17:30:40 +05:30
def get_timeline_data ( doctype , name ) :
''' returns timeline data for the past one year '''
from frappe . desk . form . load import get_communication_data
2017-02-16 13:12:29 +05:30
out = { }
2016-05-20 11:20:59 +05:30
data = get_communication_data ( doctype , name ,
2017-02-16 13:12:29 +05:30
fields = ' date(creation), count(name) ' ,
2016-05-20 11:20:59 +05:30
after = add_years ( None , - 1 ) . strftime ( ' % Y- % m- %d ' ) ,
2016-04-14 17:30:40 +05:30
group_by = ' group by date(creation) ' , as_dict = False )
2017-02-16 13:12:29 +05:30
timeline_items = dict ( data )
for date , count in timeline_items . iteritems ( ) :
timestamp = get_timestamp ( date )
out . update ( { timestamp : count } )
return out