Merge branch 'develop' into version-12

This commit is contained in:
Sahil Khan
2019-10-02 16:36:54 +05:30
76 changed files with 4590 additions and 6136 deletions

View File

@@ -5,7 +5,7 @@ import frappe
from erpnext.hooks import regional_overrides from erpnext.hooks import regional_overrides
from frappe.utils import getdate from frappe.utils import getdate
__version__ = '12.1.5' __version__ = '12.1.6'
def get_default_company(user=None): def get_default_company(user=None):
'''Get default company for user''' '''Get default company for user'''

View File

@@ -0,0 +1,809 @@
{
"country_code": "sv",
"name": "El Salvador Standard",
"tree": {
"100000 - ACTIVOS - xmC": {
"11000000 - ACTIVOS CORRIENTES - xmC": {
"11010000 - EFECTIVO Y EQUIVALENTES AL EFECTIVO - xmC": {
"11010100 - Caja general - xmC": {
"account_number": "11010100",
"account_type": "Cash"
},
"Caja chica": {
"account_number": "11010200",
"account_type": "Cash",
"is_group": 1
},
"Efectivo en bancos": {
"account_number": "11010300",
"account_type": "Bank",
"is_group": 1
},
"account_number": "11010000",
"account_type": "Cash"
},
"CUENTAS POR COBRAR ARRENDAMIENTOS FINANCIEROS": {
"Arrendamientos financieros por cobrar": {
"account_number": "11040100",
"is_group": 1
},
"Estimaci\u00f3n para cuentas de cobro dudoso (CR)": {
"account_number": "11040200",
"is_group": 1
},
"account_number": "11040000"
},
"DEUDORES COMERCIALES Y OTRAS CUENTAS POR COBRAR": {
"11030100 - Deudores comerciales - xmC": {
"account_number": "11030100",
"account_type": "Receivable"
},
"Estimaci\u00f3n para cuentas de cobro dudoso (CR)": {
"account_number": "11030200",
"account_type": "Receivable",
"is_group": 1
},
"Otras cuentas por cobrar no comerciales": {
"account_number": "11030300",
"account_type": "Receivable",
"is_group": 1
},
"account_number": "11030000",
"account_type": "Receivable"
},
"GASTOS PAGADOS POR ANTICIPADO": {
"Adelantos a empleados": {
"account_number": "110904",
"account_type": "Temporary",
"is_group": 1
},
"Papeler\u00eda y \u00fatiles en existencia": {
"account_number": "11090300",
"account_type": "Temporary",
"is_group": 1
},
"Primas de seguros aun no vendidas": {
"account_number": "11090100",
"account_type": "Temporary",
"is_group": 1
},
"Rentas aun no corridas por los inmuebles": {
"account_number": "11090200",
"account_type": "Temporary",
"is_group": 1
},
"account_number": "11090000",
"account_type": "Temporary"
},
"INVENTARIOS": {
"11050100 - Inventarios en bodega al costo - xmC": {
"account_number": "11050100",
"account_type": "Stock"
},
"11050300 - Pedidos en transito - xmC": {
"account_number": "11050300",
"account_type": "Stock Received But Not Billed"
},
"Estimaci\u00f3n para obsolescencia de inventarios o de lento movimiento (CR)": {
"account_number": "11050200",
"account_type": "Stock",
"is_group": 1
},
"Mercader\u00edas en consignaci\u00f3n": {
"account_number": "11050400",
"account_type": "Stock",
"is_group": 1
},
"account_number": "11050000",
"account_type": "Stock"
},
"INVERSIONES TEMPORALES": {
"Acciones": {
"account_number": "11020100",
"account_type": "Temporary",
"is_group": 1
},
"Bonos": {
"account_number": "11020200",
"account_type": "Temporary",
"is_group": 1
},
"C\u00e9dulas hipotecarias": {
"account_number": "11020300",
"account_type": "Temporary",
"is_group": 1
},
"account_number": "11020000",
"account_type": "Temporary"
},
"IVA CREDITO FISCAL": {
"IVA compras locales": {
"account_number": "11060100",
"account_type": "Tax",
"is_group": 1,
"tax_rate": 13.0
},
"IVA importaciones": {
"account_number": "11060200",
"account_type": "Tax",
"is_group": 1,
"tax_rate": 13.0
},
"account_number": "11060000",
"account_type": "Tax",
"tax_rate": 13.0
},
"IVA PERCIBIDO": {
"account_number": "IVA PERCIBIDO",
"account_type": "Tax",
"is_group": 1,
"tax_rate": 1.0
},
"IVA RETENIDO": {
"account_number": "11070000",
"account_type": "Tax",
"is_group": 1,
"tax_rate": 1.0
},
"account_number": "11000000"
},
"12000000 - ACTIVOS NO CORRIENTES - xmC": {
"ACTIVOS INTANGIBLES": {
"Concesiones y franquicias": {
"account_number": "12080300",
"account_type": "Fixed Asset",
"is_group": 1
},
"Derechos de llave": {
"account_number": "12080100",
"account_type": "Fixed Asset",
"is_group": 1
},
"Patentes y marcas de fabrica": {
"account_number": "12080200",
"account_type": "Fixed Asset",
"is_group": 1
},
"Software": {
"account_number": "12080400",
"account_type": "Fixed Asset",
"is_group": 1
},
"account_number": "12080000",
"account_type": "Fixed Asset"
},
"CUENTAS POR COBRAR ARRENDAMIENTOS FINANCIEROS A LARGO PLAZO": {
"Arrendamientos financieros por cobrar a largo plazo": {
"account_number": "12060100",
"account_type": "Receivable",
"is_group": 1
},
"Estimaci\u00f3n para cuentas de cobro dudoso a largo plazo (CR)": {
"account_number": "12060200",
"account_type": "Receivable",
"is_group": 1
},
"account_number": "12060000",
"account_type": "Receivable"
},
"DEPRECIACION ACUMULUDA (CR)": {
"12030100 - Deprec. acumulada de propiedades, planta y equipo propio al costo - xmC": {
"account_number": "12030100",
"account_type": "Accumulated Depreciation"
},
"Deprec. acumulada de prop., planta y equipo bajo arrend. Financiero": {
"account_number": "12030200",
"account_type": "Accumulated Depreciation",
"is_group": 1
},
"Deprec. acumulada de revaluaos de propiedades, planta y equipo": {
"account_number": "12030300",
"account_type": "Accumulated Depreciation",
"is_group": 1
},
"account_number": "12030000",
"account_type": "Accumulated Depreciation"
},
"DEUDORES COMERCIALES Y OTRAS CUENTAS POR COBRAR A LARGO PLAZO": {
"12050100 - Deudores comerciales a largo plazo - xmC": {
"account_number": "12050100",
"account_type": "Receivable",
"is_group": 1
},
"Cuentas por cobrar no comerciales a largo plazo": {
"account_number": "12050300",
"account_type": "Receivable",
"is_group": 1
},
"Estimaci\u00f3n para cuentas de cobro dudoso a largo plazo (CR)": {
"account_number": "12050200",
"account_type": "Receivable",
"is_group": 1
},
"account_number": "12050000",
"account_type": "Receivable"
},
"IMPUESTO SOBRE LA RENTA DIFERIDO-ACTIVO": {
"12070200 - Pago anticipados de impuestos sobre la renta - xmC": {
"account_number": "12070200",
"account_type": "Temporary",
"is_group": 1
},
"Cr\u00e9dito impuestos sobre la renta de a\u00f1os anteriores": {
"account_number": "12070100",
"account_type": "Tax",
"is_group": 1
},
"account_number": "12070000",
"account_type": "Tax"
},
"INVERSIONES PERMANENTES": {
"Inversiones en asociadas": {
"account_number": "12040200",
"account_type": "Fixed Asset",
"is_group": 1
},
"Inversiones en negocios conjuntos": {
"account_number": "12040300",
"account_type": "Fixed Asset",
"is_group": 1
},
"Inversiones en subsidiarias": {
"account_number": "12040100",
"account_type": "Fixed Asset",
"is_group": 1
},
"account_number": "12040000",
"account_type": "Fixed Asset"
},
"PROPIEDADES, PLANTA Y EQUIPO": {
"12010300 - Maquinarias y equipos - xmC": {
"account_number": "12010300",
"account_type": "Fixed Asset"
},
"Edificios": {
"account_number": "12010200",
"account_type": "Fixed Asset",
"is_group": 1
},
"Mobiliario y equipo": {
"account_number": "12010400",
"account_type": "Fixed Asset",
"is_group": 1
},
"Terrenos": {
"account_number": "12010100",
"account_type": "Fixed Asset",
"is_group": 1
},
"Veh\u00edculos": {
"account_number": "12010500",
"account_type": "Fixed Asset",
"is_group": 1
},
"account_number": "12010000",
"account_type": "Fixed Asset"
},
"REVALUACIONES DE PROPIEDADES, PLANTA Y EQUIPO": {
"Revaluaci\u00f3n de edificios": {
"account_number": "12020200",
"account_type": "Fixed Asset",
"is_group": 1
},
"Revaluaci\u00f3n de maquinarias y equipo": {
"account_number": "12020300",
"account_type": "Fixed Asset",
"is_group": 1
},
"Revaluaci\u00f3n de mobiliario y equipo": {
"account_number": "12020400",
"account_type": "Fixed Asset",
"is_group": 1
},
"Revaluaci\u00f3n de terrenos": {
"account_number": "12020100",
"account_type": "Fixed Asset",
"is_group": 1
},
"Revaluaci\u00f3n de veh\u00edculos": {
"account_number": "12020500",
"account_type": "Fixed Asset",
"is_group": 1
},
"account_number": "12020000",
"account_type": "Fixed Asset"
},
"account_number": "12000000",
"account_type": "Fixed Asset"
},
"account_number": "100000",
"root_type": "Asset"
},
"20000000 - PASIVOS - xmC": {
"PASIVOS CORRIENTES": {
"ACREEDORES COMERCIALES Y OTRAS CUENTAS POR PAGAR": {
"21020100 - Cuentas por pagar comerciales - xmC": {
"account_number": "21020100",
"account_type": "Payable"
},
"Documentos por pagar comerciales": {
"account_number": "21020200",
"account_type": "Payable",
"is_group": 1
},
"account_number": "21020000",
"account_type": "Payable"
},
"BENEFICIOS A EMPLEADOS POR PAGAR": {
"Beneficios a corto plazo por pagar": {
"account_number": "21050100",
"account_type": "Payable",
"is_group": 1
},
"Beneficios post empleo por pagar": {
"account_number": "21050200",
"account_type": "Payable",
"is_group": 1
},
"account_number": "21050000",
"account_type": "Payable"
},
"DIVIDENDOS POR PAGAR": {
"account_number": "21080000",
"account_type": "Payable",
"is_group": 1
},
"IMPUESTO SOBRE LA RENTA CORRIENTE POR PAGAR": {
"account_number": "21070000",
"account_type": "Tax",
"is_group": 1
},
"IVA DEBITOS FISCALES": {
"Por ventas a consumidores": {
"account_number": "21060200",
"account_type": "Tax",
"is_group": 1
},
"Por ventas a contribuyentes": {
"account_number": "21060100",
"account_type": "Tax",
"is_group": 1
},
"account_number": "21060000",
"account_type": "Tax"
},
"OBLIGACIONES BAJO ARRENDAMIENTOS FINANCIEROS PORCION CORRIENTE": {
"Contratos bajo arrendamientos financieros": {
"account_number": "21030100",
"account_type": "Payable",
"is_group": 1
},
"account_number": "21030000",
"account_type": "Payable"
},
"OTROS ACREEDORES, RETENCIONES Y DESCUENTOS": {
"Cuentas por pagar a accionistas": {
"account_number": "21040400",
"account_type": "Payable",
"is_group": 1
},
"Descuentos": {
"account_number": "21040300",
"account_type": "Payable",
"is_group": 1
},
"Otros acreedores": {
"account_number": "21040100",
"account_type": "Payable",
"is_group": 1
},
"Retenciones": {
"account_number": "21040200",
"account_type": "Payable",
"is_group": 1
},
"account_number": "21040000",
"account_type": "Payable"
},
"PRESTAMOS Y SOBREGIROS BANCARIOS": {
"Porci\u00f3n corriente de prestamos bancarios a largo plazo": {
"account_number": "21010300",
"is_group": 1
},
"Prestamos bancarios a corto plazo": {
"account_number": "21010100",
"is_group": 1
},
"Sobregiros bancarios": {
"account_number": "21010200",
"is_group": 1
},
"account_number": "21010000"
},
"account_number": "21000000"
},
"PASIVOS NO CORRIENTES": {
"ANTICIPOS Y GARANTIAS DE CLIENTES": {
"Anticipos de clientes": {
"account_number": "22040100",
"account_type": "Temporary",
"is_group": 1
},
"Garant\u00edas de clientes": {
"account_number": "22040200",
"account_type": "Temporary",
"is_group": 1
},
"account_number": "22040000",
"account_type": "Temporary"
},
"INTERES MINOTARIO": {
"Inter\u00e9s de accionista minotarios": {
"account_number": "22050100",
"is_group": 1
},
"account_number": "22050000"
},
"OBLIGACIONES BAJO ARRENDAMIENTOS FINANCIEROS A LARGO PLAZO": {
"Contratos bajo arrendamientos financieros": {
"account_number": "22030100",
"is_group": 1
},
"account_number": "22030000"
},
"OTROS PRESTAMOS A LARGO PLAZO": {
"account_number": "22020000",
"is_group": 1
},
"PRESTAMOS BANCARIOS A LARGO PLAZO": {
"account_number": "22010000",
"is_group": 1
},
"account_number": "22000000"
},
"PROVISIONES": {
"IMPUESTOS SOBRE LA RENTA COMPLEMENTARIO": {
"Ejercicios anteriores": {
"account_number": "23010100",
"account_type": "Tax",
"is_group": 1
},
"account_number": "23010000",
"account_type": "Tax"
},
"PROVISION PARA OBLIGACIONES LABORALES": {
"Indemnizaci\u00f3n": {
"account_number": "23020100",
"is_group": 1
},
"account_number": "23020000"
},
"account_number": "23000000"
},
"account_number": "20000000",
"root_type": "Liability"
},
"30000000 - PATRIMONIO - xmC": {
"CAPITAL, RESERVAS Y RESULTADOS": {
"CAPITAL SOCIAL": {
"Capital Social M\u00ednimo": {
"Capital Social M\u00ednimo NO Pagado": {
"account_number": "31010102",
"account_type": "Equity",
"is_group": 1
},
"Capital Social M\u00ednimo Suscrito": {
"account_number": "31010101",
"account_type": "Equity",
"is_group": 1
},
"account_number": "31010100",
"account_type": "Equity"
},
"Capital Social Variable": {
"Capital Social Variable NO Pagado": {
"account_number": "31010202",
"account_type": "Equity",
"is_group": 1
},
"Capital Social Variable Suscrito": {
"account_number": "31010201",
"account_type": "Equity",
"is_group": 1
},
"account_number": "31010200",
"account_type": "Equity"
},
"account_number": "31010000",
"account_type": "Equity"
},
"DIVIDENDOS PAGADOS": {
"account_number": "31060000",
"account_type": "Equity",
"is_group": 1
},
"GANANCIAS NO DISTRIBUIDAS": {
"De ejercicios anteriores": {
"account_number": "31020100",
"account_type": "Equity",
"is_group": 1
},
"Del presente ejercicio": {
"account_number": "31020200",
"account_type": "Equity",
"is_group": 1
},
"account_number": "31020000",
"account_type": "Equity"
},
"GANANCIAS RESTRINGIDAS": {
"Reserva legal": {
"account_number": "31030100",
"account_type": "Equity",
"is_group": 1
},
"account_number": "31030000",
"account_type": "Equity"
},
"PERDIDAS ACUMULADAS (CR)": {
"De ejercicios anteriores": {
"account_number": "31050100",
"account_type": "Equity",
"is_group": 1
},
"Del presente ejercicio": {
"account_number": "31050200",
"account_type": "Equity",
"is_group": 1
},
"account_number": "31050000",
"account_type": "Equity"
},
"SUPERAVIT POR REVALUACIONES": {
"Super\u00e1vit por revaluaci\u00f3n de activos": {
"account_number": "31040100",
"account_type": "Equity",
"is_group": 1
},
"account_number": "31040000",
"account_type": "Equity"
},
"account_number": "31000000",
"account_type": "Equity"
},
"account_number": "30000000",
"root_type": "Equity"
},
"40000000 - CUENTAS DE RESULTADO DEUDORAS - xmC": {
"COSTOS Y GASTOS DE OPERACI\u00d3N": {
"41010000 - COSTO DE LAS VENTAS - xmC": {
"account_number": "41010000",
"account_type": "Cost of Goods Sold"
},
"GASTOS DE DEPARTAMENTO DE OPERACIONES": {
"41020600 - Depreciaciones y amortizaciones - xmC": {
"account_number": "41020600",
"account_type": "Depreciation"
},
"41020900 - Redondeos - xmC": {
"account_number": "41020900",
"account_type": "Round Off"
},
"Gastos operativos": {
"account_number": "41020700",
"account_type": "Chargeable",
"is_group": 1
},
"Mantenimientos": {
"account_number": "41020400",
"account_type": "Chargeable",
"is_group": 1
},
"Materiales y suministros": {
"Ajustes de Inventario": {
"account_number": "41020201",
"account_type": "Stock Adjustment"
},
"account_number": "41020200",
"account_type": "Chargeable"
},
"Seguros": {
"account_number": "41020500",
"account_type": "Chargeable",
"is_group": 1
},
"Servicios b\u00e1sicos": {
"account_number": "41020300",
"account_type": "Chargeable",
"is_group": 1
},
"Servicios y honorarios profesionales": {
"account_number": "41020800",
"account_type": "Chargeable",
"is_group": 1
},
"Sueldos y prestaciones laborales": {
"account_number": "41020100",
"account_type": "Chargeable",
"is_group": 1
},
"account_number": "41020000",
"account_type": "Expense Account"
},
"GASTOS DE VENTAS Y DISTRIBUCION": {
"Depreciaciones y amortizaciones": {
"account_number": "41030600",
"account_type": "Chargeable",
"is_group": 1
},
"Gastos operativos": {
"account_number": "41030700",
"account_type": "Chargeable",
"is_group": 1
},
"Mantenimientos": {
"account_number": "41030400",
"account_type": "Chargeable",
"is_group": 1
},
"Materiales y suministros": {
"account_number": "41030200",
"account_type": "Chargeable",
"is_group": 1
},
"Seguros": {
"account_number": "41030500",
"account_type": "Chargeable",
"is_group": 1
},
"Servicios b\u00e1sicos": {
"account_number": "41030300",
"account_type": "Chargeable",
"is_group": 1
},
"Servicios y honorarios profesionales": {
"account_number": "41030800",
"account_type": "Chargeable",
"is_group": 1
},
"Sueldos y prestaciones laborales": {
"account_number": "41030100",
"account_type": "Chargeable",
"is_group": 1
},
"account_number": "41030000",
"account_type": "Expense Account"
},
"account_number": "41000000",
"account_type": "Expense Account"
},
"GASTOS DE IMPUESTO SOBRE LA RENTA": {
"GASTOS DE IMPUESTO SOBRE LA RENTA CORRIENTE": {
"account_number": "43010000",
"account_type": "Expense Account",
"is_group": 1
},
"account_number": "43000000",
"account_type": "Expense Account"
},
"GASTOS DE NO OPERACI\u00d3N": {
"42020000 - GASTOS NO DEDUCIBLES - xmC": {
"Garant\u00eda por venta de productos": {
"account_number": "42020200",
"account_type": "Expense Account",
"is_group": 1
},
"Impuesto sobre la renta complementario": {
"account_number": "42020100",
"account_type": "Tax",
"is_group": 1
},
"account_number": "42020000",
"account_type": "Expense Account"
},
"GASTOS FINANCIEROS": {
"42010100 - Intereses - xmC": {
"account_number": "42010100",
"account_type": "Expense Account",
"is_group": 1
},
"42010300 - Diferenciales cambiarios - xmC": {
"account_number": "42010300",
"account_type": "Expense Account",
"is_group": 1
},
"Comisiones bancarias": {
"account_number": "42010200",
"account_type": "Expense Account",
"is_group": 1
},
"account_number": "42010000",
"account_type": "Expense Account"
},
"account_number": "42000000",
"account_type": "Expense Account"
},
"account_number": "40000000",
"root_type": "Expense"
},
"50000000 - CUENTAS DE RESULTADO ACREEDORAS - xmC": {
"INGRESOS DE NO OPERACI\u00d3N": {
"DIVIDENDOS GANADOS": {
"Dividendos devengados por inversiones": {
"account_number": "52020100",
"account_type": "Income Account",
"is_group": 1
},
"account_number": "52020000",
"account_type": "Income Account"
},
"INGRESOS FINANCIEROS": {
"Comisiones": {
"account_number": "52010200",
"account_type": "Income Account",
"is_group": 1
},
"Diferenciales cambiarios": {
"account_number": "52010300",
"account_type": "Income Account",
"is_group": 1
},
"Intereses": {
"account_number": "52010100",
"account_type": "Income Account",
"is_group": 1
},
"account_number": "52010000",
"account_type": "Income Account"
},
"OTROS INGRESOS": {
"Ingresos por activos dados en arrendamientos financieros": {
"account_number": "52030200",
"account_type": "Income Account",
"is_group": 1
},
"Ingresos por conversi\u00f3n": {
"account_number": "52030100",
"account_type": "Income Account",
"is_group": 1
},
"Reintegros de seguros": {
"account_number": "52030300",
"account_type": "Income Account",
"is_group": 1
},
"account_number": "52030000",
"account_type": "Income Account"
},
"account_number": "52000000",
"account_type": "Income Account"
},
"INGRESOS POR OPERACIONES CONTINUAS": {
"51010000 - VENTAS DE BIENES - xmC": {
"account_number": "51010000",
"account_type": "Income Account"
},
"VENTAS DE SERVICIOS": {
"account_number": "51020000",
"account_type": "Income Account",
"is_group": 1
},
"account_number": "51000000",
"account_type": "Income Account"
},
"account_number": "50000000",
"root_type": "Income"
},
"60000000 - CUENTA LIQUIDADORA DE RESULTADOS - xmC": {
"CUENTA DE CIERRE": {
"PERDIDAS Y GANANCIAS": {
"account_number": "61010000",
"is_group": 1
},
"account_number": "61000000"
},
"account_number": "60000000",
"root_type": "Income"
}
}
}

View File

@@ -38,13 +38,13 @@
"read_only": 1 "read_only": 1
}, },
{ {
"fetch_from": "sales_invoice.grand_total", "fetch_from": "sales_invoice.outstanding_amount",
"fetch_if_empty": 1,
"fieldname": "outstanding_amount", "fieldname": "outstanding_amount",
"fieldtype": "Currency", "fieldtype": "Currency",
"in_list_view": 1, "in_list_view": 1,
"label": "Outstanding Amount", "label": "Outstanding Amount",
"options": "Company:company:default_currency", "options": "Company:company:default_currency"
"read_only": 1
}, },
{ {
"fieldname": "column_break_3", "fieldname": "column_break_3",
@@ -60,7 +60,7 @@
} }
], ],
"istable": 1, "istable": 1,
"modified": "2019-08-07 15:13:55.808349", "modified": "2019-09-26 11:05:36.016772",
"modified_by": "Administrator", "modified_by": "Administrator",
"module": "Accounts", "module": "Accounts",
"name": "Discounted Invoice", "name": "Discounted Invoice",

View File

@@ -97,7 +97,6 @@ frappe.ui.form.on('Invoice Discounting', {
} }
frm.set_value("total_amount", total_amount); frm.set_value("total_amount", total_amount);
}, },
get_invoices: (frm) => { get_invoices: (frm) => {
var d = new frappe.ui.Dialog({ var d = new frappe.ui.Dialog({
title: __('Get Invoices based on Filters'), title: __('Get Invoices based on Filters'),

View File

@@ -26,14 +26,20 @@ class InvoiceDiscounting(AccountsController):
frappe.throw(_("Loan Start Date and Loan Period are mandatory to save the Invoice Discounting")) frappe.throw(_("Loan Start Date and Loan Period are mandatory to save the Invoice Discounting"))
def validate_invoices(self): def validate_invoices(self):
discounted_invoices = [record.sales_invoice for record in discounted_invoices = [record.sales_invoice for record in
frappe.get_all("Discounted Invoice",fields = ["sales_invoice"], filters= {"docstatus":1})] frappe.get_all("Discounted Invoice",fields=["sales_invoice"], filters={"docstatus":1})]
for record in self.invoices: for record in self.invoices:
if record.sales_invoice in discounted_invoices: if record.sales_invoice in discounted_invoices:
frappe.throw("Row({0}): {1} is already discounted in {2}" frappe.throw(_("Row({0}): {1} is already discounted in {2}")
.format(record.idx, frappe.bold(record.sales_invoice), frappe.bold(record.parent))) .format(record.idx, frappe.bold(record.sales_invoice), frappe.bold(record.parent)))
actual_outstanding = frappe.db.get_value("Sales Invoice", record.sales_invoice,"outstanding_amount")
if record.outstanding_amount > actual_outstanding :
frappe.throw(_
("Row({0}): Outstanding Amount cannot be greater than actual Outstanding Amount {1} in {2}").format(
record.idx, frappe.bold(actual_outstanding), frappe.bold(record.sales_invoice)))
def calculate_total_amount(self): def calculate_total_amount(self):
self.total_amount = sum([flt(d.outstanding_amount) for d in self.invoices]) self.total_amount = sum([flt(d.outstanding_amount) for d in self.invoices])

View File

@@ -827,10 +827,10 @@ def get_opening_accounts(company):
accounts = frappe.db.sql_list("""select accounts = frappe.db.sql_list("""select
name from tabAccount name from tabAccount
where where
is_group=0 and report_type='Balance Sheet' and company=%s and is_group=0 and report_type='Balance Sheet' and company={0} and
name not in(select distinct account from tabWarehouse where name not in (select distinct account from tabWarehouse where
account is not null and account != '') account is not null and account != '')
order by name asc""", frappe.db.escape(company)) order by name asc""".format(frappe.db.escape(company)))
return [{"account": a, "balance": get_balance_on(a)} for a in accounts] return [{"account": a, "balance": get_balance_on(a)} for a in accounts]

File diff suppressed because it is too large Load Diff

View File

@@ -364,7 +364,7 @@ class PurchaseInvoice(BuyingController):
update_outstanding = "No" if (cint(self.is_paid) or self.write_off_account) else "Yes" update_outstanding = "No" if (cint(self.is_paid) or self.write_off_account) else "Yes"
make_gl_entries(gl_entries, cancel=(self.docstatus == 2), make_gl_entries(gl_entries, cancel=(self.docstatus == 2),
update_outstanding=update_outstanding, merge_entries=False) update_outstanding=update_outstanding, merge_entries=False, from_repost=from_repost)
if update_outstanding == "No": if update_outstanding == "No":
update_outstanding_amt(self.credit_to, "Supplier", self.supplier, update_outstanding_amt(self.credit_to, "Supplier", self.supplier,

View File

@@ -591,7 +591,6 @@
"oldfieldname": "purchase_order", "oldfieldname": "purchase_order",
"oldfieldtype": "Link", "oldfieldtype": "Link",
"options": "Purchase Order", "options": "Purchase Order",
"print_hide": 1,
"read_only": 1, "read_only": 1,
"search_index": 1 "search_index": 1
}, },
@@ -607,6 +606,7 @@
"fieldname": "include_exploded_items", "fieldname": "include_exploded_items",
"fieldtype": "Check", "fieldtype": "Check",
"label": "Include Exploded Items", "label": "Include Exploded Items",
"print_hide": 1,
"read_only": 1 "read_only": 1
}, },
{ {
@@ -758,7 +758,7 @@
], ],
"idx": 1, "idx": 1,
"istable": 1, "istable": 1,
"modified": "2019-06-02 06:36:17.078419", "modified": "2019-09-17 22:32:05.984240",
"modified_by": "Administrator", "modified_by": "Administrator",
"module": "Accounts", "module": "Accounts",
"name": "Purchase Invoice Item", "name": "Purchase Invoice Item",

View File

@@ -238,7 +238,7 @@ def get_contacts(customers):
customers = [frappe._dict({'name': customers})] customers = [frappe._dict({'name': customers})]
for data in customers: for data in customers:
contact = frappe.db.sql(""" select email_id, phone from `tabContact` contact = frappe.db.sql(""" select email_id, phone, mobile_no from `tabContact`
where is_primary_contact=1 and name in where is_primary_contact=1 and name in
(select parent from `tabDynamic Link` where link_doctype = 'Customer' and link_name = %s (select parent from `tabDynamic Link` where link_doctype = 'Customer' and link_name = %s
and parenttype = 'Contact')""", data.name, as_dict=1) and parenttype = 'Contact')""", data.name, as_dict=1)

View File

@@ -698,7 +698,7 @@ class SalesInvoice(SellingController):
cint(self.redeem_loyalty_points)) else "Yes" cint(self.redeem_loyalty_points)) else "Yes"
make_gl_entries(gl_entries, cancel=(self.docstatus == 2), make_gl_entries(gl_entries, cancel=(self.docstatus == 2),
update_outstanding=update_outstanding, merge_entries=False) update_outstanding=update_outstanding, merge_entries=False, from_repost=from_repost)
if update_outstanding == "No": if update_outstanding == "No":
from erpnext.accounts.doctype.gl_entry.gl_entry import update_outstanding_amt from erpnext.accounts.doctype.gl_entry.gl_entry import update_outstanding_amt

View File

@@ -817,6 +817,7 @@ erpnext.pos.PointOfSale = erpnext.taxes_and_totals.extend({
if(reg.test(data.name.toLowerCase()) if(reg.test(data.name.toLowerCase())
|| reg.test(data.customer_name.toLowerCase()) || reg.test(data.customer_name.toLowerCase())
|| (contact && reg.test(contact["phone"])) || (contact && reg.test(contact["phone"]))
|| (contact && reg.test(contact["mobile_no"]))
|| (data.customer_group && reg.test(data.customer_group.toLowerCase()))){ || (data.customer_group && reg.test(data.customer_group.toLowerCase()))){
return data; return data;
} }
@@ -833,6 +834,7 @@ erpnext.pos.PointOfSale = erpnext.taxes_and_totals.extend({
if(contact && !c['phone']) { if(contact && !c['phone']) {
c["phone"] = contact["phone"]; c["phone"] = contact["phone"];
c["email_id"] = contact["email_id"]; c["email_id"] = contact["email_id"];
c["mobile_no"] = contact["mobile_no"];
} }
me.customers_mapper.push({ me.customers_mapper.push({
@@ -842,9 +844,10 @@ erpnext.pos.PointOfSale = erpnext.taxes_and_totals.extend({
customer_group: c.customer_group, customer_group: c.customer_group,
territory: c.territory, territory: c.territory,
phone: contact ? contact["phone"] : '', phone: contact ? contact["phone"] : '',
mobile_no: contact ? contact["mobile_no"] : '',
email_id: contact ? contact["email_id"] : '', email_id: contact ? contact["email_id"] : '',
searchtext: ['customer_name', 'customer_group', 'name', 'value', searchtext: ['customer_name', 'customer_group', 'name', 'value',
'label', 'email_id', 'phone'] 'label', 'email_id', 'phone', 'mobile_no']
.map(key => c[key]).join(' ') .map(key => c[key]).join(' ')
.toLowerCase() .toLowerCase()
}); });

View File

@@ -59,7 +59,6 @@ class ReceivablePayableReport(object):
self.invoices = set() self.invoices = set()
def get_data(self): def get_data(self):
t1 = now()
self.get_gl_entries() self.get_gl_entries()
self.voucher_balance = OrderedDict() self.voucher_balance = OrderedDict()
self.init_voucher_balance() # invoiced, paid, credit_note, outstanding self.init_voucher_balance() # invoiced, paid, credit_note, outstanding
@@ -73,6 +72,9 @@ class ReceivablePayableReport(object):
# fetch future payments against invoices # fetch future payments against invoices
self.get_future_payments() self.get_future_payments()
# Get return entries
self.get_return_entries()
self.data = [] self.data = []
for gle in self.gl_entries: for gle in self.gl_entries:
self.update_voucher_balance(gle) self.update_voucher_balance(gle)
@@ -91,6 +93,7 @@ class ReceivablePayableReport(object):
party = gle.party, party = gle.party,
posting_date = gle.posting_date, posting_date = gle.posting_date,
remarks = gle.remarks, remarks = gle.remarks,
account_currency = gle.account_currency,
invoiced = 0.0, invoiced = 0.0,
paid = 0.0, paid = 0.0,
credit_note = 0.0, credit_note = 0.0,
@@ -106,7 +109,6 @@ class ReceivablePayableReport(object):
# get the row where this balance needs to be updated # get the row where this balance needs to be updated
# if its a payment, it will return the linked invoice or will be considered as advance # if its a payment, it will return the linked invoice or will be considered as advance
row = self.get_voucher_balance(gle) row = self.get_voucher_balance(gle)
# gle_balance will be the total "debit - credit" for receivable type reports and # gle_balance will be the total "debit - credit" for receivable type reports and
# and vice-versa for payable type reports # and vice-versa for payable type reports
gle_balance = self.get_gle_balance(gle) gle_balance = self.get_gle_balance(gle)
@@ -131,7 +133,18 @@ class ReceivablePayableReport(object):
if gle.against_voucher: if gle.against_voucher:
# find invoice # find invoice
voucher_balance = self.voucher_balance.get((gle.against_voucher_type, gle.against_voucher, gle.party)) against_voucher = gle.against_voucher
# If payment is made against credit note
# and credit note is made against a Sales Invoice
# then consider the payment against original sales invoice.
if gle.against_voucher_type in ('Sales Invoice', 'Purchase Invoice'):
if gle.against_voucher in self.return_entries:
return_against = self.return_entries.get(gle.against_voucher)
if return_against:
against_voucher = return_against
voucher_balance = self.voucher_balance.get((gle.against_voucher_type, against_voucher, gle.party))
if not voucher_balance: if not voucher_balance:
# no invoice, this is an invoice / stand-alone payment / credit note # no invoice, this is an invoice / stand-alone payment / credit note
@@ -258,7 +271,6 @@ class ReceivablePayableReport(object):
# customer / supplier name # customer / supplier name
party_details = self.get_party_details(row.party) party_details = self.get_party_details(row.party)
row.update(party_details) row.update(party_details)
if self.filters.get(scrub(self.filters.party_type)): if self.filters.get(scrub(self.filters.party_type)):
row.currency = row.account_currency row.currency = row.account_currency
else: else:
@@ -423,6 +435,19 @@ class ReceivablePayableReport(object):
if row.future_ref: if row.future_ref:
row.future_ref = ', '.join(row.future_ref) row.future_ref = ', '.join(row.future_ref)
def get_return_entries(self):
doctype = "Sales Invoice" if self.party_type == "Customer" else "Purchase Invoice"
filters={
'is_return': 1,
'docstatus': 1
}
party_field = scrub(self.filters.party_type)
if self.filters.get(party_field):
filters.update({party_field: self.filters.get(party_field)})
self.return_entries = frappe._dict(
frappe.get_all(doctype, filters, ['name', 'return_against'], as_list=1)
)
def set_ageing(self, row): def set_ageing(self, row):
if self.filters.ageing_based_on == "Due Date": if self.filters.ageing_based_on == "Due Date":
entry_date = row.due_date entry_date = row.due_date
@@ -689,11 +714,11 @@ class ReceivablePayableReport(object):
def get_chart_data(self): def get_chart_data(self):
rows = [] rows = []
for row in self.data: for row in self.data:
rows.append( values = [row.range1, row.range2, row.range3, row.range4, row.range5]
{ precision = cint(frappe.db.get_default("float_precision")) or 2
'values': [row.range1, row.range2, row.range3, row.range4, row.range5] rows.append({
} 'values': [flt(val, precision) for val in values]
) })
self.chart = { self.chart = {
"data": { "data": {

View File

@@ -3,7 +3,7 @@
from __future__ import unicode_literals from __future__ import unicode_literals
import frappe import frappe
from frappe import _ from frappe import _, scrub
from frappe.utils import flt, cint from frappe.utils import flt, cint
from erpnext.accounts.party import get_partywise_advanced_payment_amount from erpnext.accounts.party import get_partywise_advanced_payment_amount
from erpnext.accounts.report.accounts_receivable.accounts_receivable import ReceivablePayableReport from erpnext.accounts.report.accounts_receivable.accounts_receivable import ReceivablePayableReport
@@ -40,7 +40,7 @@ class AccountsReceivableSummary(ReceivablePayableReport):
row.party = party row.party = party
if self.party_naming_by == "Naming Series": if self.party_naming_by == "Naming Series":
row.party_name = frappe.get_cached_value(self.party_type, party, [self.party_type + "_name"]) row.party_name = frappe.get_cached_value(self.party_type, party, scrub(self.party_type) + "_name")
row.update(party_dict) row.update(party_dict)

View File

@@ -1,6 +1,7 @@
{ {
"add_total_row": 0, "add_total_row": 1,
"creation": "2018-08-21 11:32:30.874923", "creation": "2018-08-21 11:32:30.874923",
"disable_prepared_report": 0,
"disabled": 0, "disabled": 0,
"docstatus": 0, "docstatus": 0,
"doctype": "Report", "doctype": "Report",

View File

@@ -70,7 +70,7 @@ def get_result(filters):
rate = [i.tax_withholding_rate for i in tds_doc.rates rate = [i.tax_withholding_rate for i in tds_doc.rates
if i.fiscal_year == gle_map[d][0].fiscal_year] if i.fiscal_year == gle_map[d][0].fiscal_year]
if rate and len(rate) > 0: if rate and len(rate) > 0 and tds_deducted:
rate = rate[0] rate = rate[0]
if getdate(filters.from_date) <= gle_map[d][0].posting_date \ if getdate(filters.from_date) <= gle_map[d][0].posting_date \
@@ -164,7 +164,7 @@ def get_columns(filters):
{ {
"label": _("TDS Rate %"), "label": _("TDS Rate %"),
"fieldname": "tds_rate", "fieldname": "tds_rate",
"fieldtype": "Float", "fieldtype": "Percent",
"width": 90 "width": 90
}, },
{ {

View File

@@ -0,0 +1,30 @@
// Copyright (c) 2016, Frappe Technologies Pvt. Ltd. and contributors
// For license information, please see license.txt
/* eslint-disable */
frappe.query_reports["Fixed Asset Register"] = {
"filters": [
{
fieldname:"company",
label: __("Company"),
fieldtype: "Link",
options: "Company",
default: frappe.defaults.get_user_default("Company"),
reqd: 1
},
{
fieldname:"status",
label: __("Status"),
fieldtype: "Select",
options: "In Store\nDisposed",
default: 'In Store',
reqd: 1
},
{
fieldname:"finance_book",
label: __("Finance Book"),
fieldtype: "Link",
options: "Finance Book"
},
]
};

View File

@@ -0,0 +1,27 @@
{
"add_total_row": 0,
"creation": "2019-09-23 16:35:02.836134",
"disable_prepared_report": 0,
"disabled": 0,
"docstatus": 0,
"doctype": "Report",
"idx": 0,
"is_standard": "Yes",
"modified": "2019-09-23 16:35:02.836134",
"modified_by": "Administrator",
"module": "Assets",
"name": "Fixed Asset Register",
"owner": "Administrator",
"prepared_report": 0,
"ref_doctype": "Asset",
"report_name": "Fixed Asset Register",
"report_type": "Script Report",
"roles": [
{
"role": "Accounts User"
},
{
"role": "Quality Manager"
}
]
}

View File

@@ -0,0 +1,172 @@
# Copyright (c) 2013, Frappe Technologies Pvt. Ltd. and contributors
# For license information, please see license.txt
from __future__ import unicode_literals
import frappe
from frappe import _
def execute(filters=None):
filters = frappe._dict(filters or {})
columns = get_columns(filters)
data = get_data(filters)
return columns, data
def get_columns(filters):
return [
{
"label": _("Asset Id"),
"fieldtype": "Link",
"fieldname": "asset_id",
"options": "Asset",
"width": 100
},
{
"label": _("Asset Name"),
"fieldtype": "Data",
"fieldname": "asset_name",
"width": 140
},
{
"label": _("Asset Category"),
"fieldtype": "Link",
"fieldname": "asset_category",
"options": "Asset Category",
"width": 100
},
{
"label": _("Status"),
"fieldtype": "Data",
"fieldname": "status",
"width": 90
},
{
"label": _("Cost Center"),
"fieldtype": "Link",
"fieldname": "cost_center",
"options": "Cost Center",
"width": 100
},
{
"label": _("Department"),
"fieldtype": "Link",
"fieldname": "department",
"options": "Department",
"width": 100
},
{
"label": _("Location"),
"fieldtype": "Link",
"fieldname": "location",
"options": "Location",
"width": 100
},
{
"label": _("Purchase Date"),
"fieldtype": "Date",
"fieldname": "purchase_date",
"width": 90
},
{
"label": _("Gross Purchase Amount"),
"fieldname": "gross_purchase_amount",
"options": "Currency",
"width": 90
},
{
"label": _("Vendor Name"),
"fieldtype": "Data",
"fieldname": "vendor_name",
"width": 100
},
{
"label": _("Available For Use Date"),
"fieldtype": "Date",
"fieldname": "available_for_use_date",
"width": 90
},
{
"label": _("Current Value"),
"fieldname": "current_value",
"options": "Currency",
"width": 90
},
]
def get_conditions(filters):
conditions = {'docstatus': 1}
status = filters.status
if filters.company:
conditions["company"] = filters.company
# In Store assets are those that are not sold or scrapped
operand = 'not in'
if status not in 'In Store':
operand = 'in'
conditions['status'] = (operand, ['Sold', 'Scrapped'])
return conditions
def get_data(filters):
data = []
conditions = get_conditions(filters)
current_value_map = get_finance_book_value_map(filters.finance_book)
pr_supplier_map = get_purchase_receipt_supplier_map()
pi_supplier_map = get_purchase_invoice_supplier_map()
assets_record = frappe.db.get_all("Asset",
filters=conditions,
fields=["name", "asset_name", "department", "cost_center", "purchase_receipt",
"asset_category", "purchase_date", "gross_purchase_amount", "location",
"available_for_use_date", "status", "purchase_invoice"])
for asset in assets_record:
if current_value_map.get(asset.name) is not None:
row = {
"asset_id": asset.name,
"asset_name": asset.asset_name,
"status": asset.status,
"department": asset.department,
"cost_center": asset.cost_center,
"vendor_name": pr_supplier_map.get(asset.purchase_receipt) or pi_supplier_map.get(asset.purchase_invoice),
"gross_purchase_amount": asset.gross_purchase_amount,
"available_for_use_date": asset.available_for_use_date,
"location": asset.location,
"asset_category": asset.asset_category,
"purchase_date": asset.purchase_date,
"current_value": current_value_map.get(asset.name)
}
data.append(row)
return data
def get_finance_book_value_map(finance_book=''):
return frappe._dict(frappe.db.sql(''' Select
parent, value_after_depreciation
FROM `tabAsset Finance Book`
WHERE
parentfield='finance_books'
AND finance_book=%s''', (finance_book)))
def get_purchase_receipt_supplier_map():
return frappe._dict(frappe.db.sql(''' Select
pr.name, pr.supplier
FROM `tabPurchase Receipt` pr, `tabPurchase Receipt Item` pri
WHERE
pri.parent = pr.name
AND pri.is_fixed_asset=1
AND pr.docstatus=1
AND pr.is_return=0'''))
def get_purchase_invoice_supplier_map():
return frappe._dict(frappe.db.sql(''' Select
pi.name, pi.supplier
FROM `tabPurchase Invoice` pi, `tabPurchase Invoice Item` pii
WHERE
pii.parent = pi.name
AND pii.is_fixed_asset=1
AND pi.docstatus=1
AND pi.is_return=0'''))

View File

@@ -267,6 +267,7 @@
"fieldtype": "Currency", "fieldtype": "Currency",
"label": "Last Purchase Rate", "label": "Last Purchase Rate",
"options": "currency", "options": "currency",
"print_hide": 1,
"read_only": 1 "read_only": 1
}, },
{ {
@@ -561,7 +562,8 @@
"depends_on": "eval:parent.is_subcontracted == 'Yes'", "depends_on": "eval:parent.is_subcontracted == 'Yes'",
"fieldname": "include_exploded_items", "fieldname": "include_exploded_items",
"fieldtype": "Check", "fieldtype": "Check",
"label": "Include Exploded Items" "label": "Include Exploded Items",
"print_hide": 1
}, },
{ {
"fieldname": "section_break_56", "fieldname": "section_break_56",
@@ -701,7 +703,7 @@
], ],
"idx": 1, "idx": 1,
"istable": 1, "istable": 1,
"modified": "2019-06-23 20:03:13.818917", "modified": "2019-09-17 22:32:34.703923",
"modified_by": "Administrator", "modified_by": "Administrator",
"module": "Buying", "module": "Buying",
"name": "Purchase Order Item", "name": "Purchase Order Item",
@@ -712,4 +714,4 @@
"sort_field": "modified", "sort_field": "modified",
"sort_order": "DESC", "sort_order": "DESC",
"track_changes": 1 "track_changes": 1
} }

View File

@@ -143,7 +143,7 @@ class AccountsController(TransactionBase):
if not self.cash_bank_account: if not self.cash_bank_account:
# show message that the amount is not paid # show message that the amount is not paid
frappe.throw(_("Note: Payment Entry will not be created since 'Cash or Bank Account' was not specified")) frappe.throw(_("Note: Payment Entry will not be created since 'Cash or Bank Account' was not specified"))
if cint(self.is_return) and self.grand_total > self.paid_amount: if cint(self.is_return) and self.grand_total > self.paid_amount:
self.paid_amount = flt(flt(self.grand_total), self.precision("paid_amount")) self.paid_amount = flt(flt(self.grand_total), self.precision("paid_amount"))
@@ -1195,8 +1195,10 @@ def update_child_qty_rate(parent_doctype, trans_items, parent_doctype_name, chil
child_item.rate = flt(d.get("rate")) child_item.rate = flt(d.get("rate"))
if flt(child_item.price_list_rate): if flt(child_item.price_list_rate):
child_item.discount_percentage = flt((1 - flt(child_item.rate) / flt(child_item.price_list_rate)) * 100.0, \ discount = flt((1 - flt(child_item.rate) / flt(child_item.price_list_rate)) * 100.0,
child_item.precision("discount_percentage")) child_item.precision("discount_percentage"))
if discount > 0:
child_item.discount_percentage = discount
child_item.flags.ignore_validate_update_after_submit = True child_item.flags.ignore_validate_update_after_submit = True
if new_child_flag: if new_child_flag:
@@ -1220,7 +1222,6 @@ def update_child_qty_rate(parent_doctype, trans_items, parent_doctype_name, chil
parent.update_status_updater() parent.update_status_updater()
else: else:
parent.check_credit_limit() parent.check_credit_limit()
parent.save() parent.save()
if parent_doctype == 'Purchase Order': if parent_doctype == 'Purchase Order':

View File

@@ -598,6 +598,7 @@ class BuyingController(StockController):
'item_code': d.item_code, 'item_code': d.item_code,
'via_stock_ledger': False, 'via_stock_ledger': False,
'company': self.company, 'company': self.company,
'supplier': self.supplier,
'actual_qty': d.qty, 'actual_qty': d.qty,
'purchase_document_type': self.doctype, 'purchase_document_type': self.doctype,
'purchase_document_no': self.name, 'purchase_document_no': self.name,
@@ -625,6 +626,7 @@ class BuyingController(StockController):
'asset_category': item_data.get('asset_category'), 'asset_category': item_data.get('asset_category'),
'location': row.asset_location, 'location': row.asset_location,
'company': self.company, 'company': self.company,
'supplier': self.supplier,
'purchase_date': self.posting_date, 'purchase_date': self.posting_date,
'calculate_depreciation': 1, 'calculate_depreciation': 1,
'purchase_receipt_amount': purchase_amount, 'purchase_receipt_amount': purchase_amount,

View File

@@ -283,7 +283,7 @@ def copy_attributes_to_variant(item, variant):
if 'description' not in allow_fields: if 'description' not in allow_fields:
if not variant.description: if not variant.description:
variant.description = "" variant.description = ""
else:
if item.variant_based_on=='Item Attribute': if item.variant_based_on=='Item Attribute':
if variant.attributes: if variant.attributes:
attributes_description = item.description + " " attributes_description = item.description + " "
@@ -291,7 +291,7 @@ def copy_attributes_to_variant(item, variant):
attributes_description += "<div>" + d.attribute + ": " + cstr(d.attribute_value) + "</div>" attributes_description += "<div>" + d.attribute + ": " + cstr(d.attribute_value) + "</div>"
if attributes_description not in variant.description: if attributes_description not in variant.description:
variant.description += attributes_description variant.description = attributes_description
def make_variant_item_code(template_item_code, template_item_name, variant): def make_variant_item_code(template_item_code, template_item_name, variant):
"""Uses template's item code and abbreviations to make variant's item code""" """Uses template's item code and abbreviations to make variant's item code"""

File diff suppressed because it is too large Load Diff

View File

@@ -1,5 +1,6 @@
{ {
"allow_import": 1, "allow_import": 1,
"allow_rename": 1,
"autoname": "naming_series:", "autoname": "naming_series:",
"creation": "2013-03-07 18:50:30", "creation": "2013-03-07 18:50:30",
"description": "Potential Sales Deal", "description": "Potential Sales Deal",
@@ -411,7 +412,7 @@
], ],
"icon": "fa fa-info-sign", "icon": "fa fa-info-sign",
"idx": 195, "idx": 195,
"modified": "2019-09-12 09:37:30.127901", "modified": "2019-09-30 12:58:37.385400",
"modified_by": "Administrator", "modified_by": "Administrator",
"module": "CRM", "module": "CRM",
"name": "Opportunity", "name": "Opportunity",

View File

@@ -53,7 +53,7 @@ class TestOpportunity(unittest.TestCase):
"link_name": customer.name "link_name": customer.name
}] }]
}) })
contact.add_email(new_lead_email_id) contact.add_email(new_lead_email_id, is_primary=True)
contact.insert(ignore_permissions=True) contact.insert(ignore_permissions=True)
opp_doc = frappe.get_doc(args).insert(ignore_permissions=True) opp_doc = frappe.get_doc(args).insert(ignore_permissions=True)

View File

@@ -19,6 +19,7 @@ def store_request_data(order=None, event=None):
dump_request_data(order, event) dump_request_data(order, event)
def sync_sales_order(order, request_id=None): def sync_sales_order(order, request_id=None):
frappe.set_user('Administrator')
shopify_settings = frappe.get_doc("Shopify Settings") shopify_settings = frappe.get_doc("Shopify Settings")
frappe.flags.request_id = request_id frappe.flags.request_id = request_id
@@ -33,6 +34,7 @@ def sync_sales_order(order, request_id=None):
make_shopify_log(status="Success") make_shopify_log(status="Success")
def prepare_sales_invoice(order, request_id=None): def prepare_sales_invoice(order, request_id=None):
frappe.set_user('Administrator')
shopify_settings = frappe.get_doc("Shopify Settings") shopify_settings = frappe.get_doc("Shopify Settings")
frappe.flags.request_id = request_id frappe.flags.request_id = request_id
@@ -45,6 +47,7 @@ def prepare_sales_invoice(order, request_id=None):
make_shopify_log(status="Error", exception=True) make_shopify_log(status="Error", exception=True)
def prepare_delivery_note(order, request_id=None): def prepare_delivery_note(order, request_id=None):
frappe.set_user('Administrator')
shopify_settings = frappe.get_doc("Shopify Settings") shopify_settings = frappe.get_doc("Shopify Settings")
frappe.flags.request_id = request_id frappe.flags.request_id = request_id
@@ -137,6 +140,7 @@ def create_sales_invoice(shopify_order, shopify_settings, so):
si.naming_series = shopify_settings.sales_invoice_series or "SI-Shopify-" si.naming_series = shopify_settings.sales_invoice_series or "SI-Shopify-"
si.flags.ignore_mandatory = True si.flags.ignore_mandatory = True
set_cost_center(si.items, shopify_settings.cost_center) set_cost_center(si.items, shopify_settings.cost_center)
si.insert(ignore_mandatory=True)
si.submit() si.submit()
make_payament_entry_against_sales_invoice(si, shopify_settings) make_payament_entry_against_sales_invoice(si, shopify_settings)
frappe.db.commit() frappe.db.commit()
@@ -151,6 +155,7 @@ def make_payament_entry_against_sales_invoice(doc, shopify_settings):
payemnt_entry.flags.ignore_mandatory = True payemnt_entry.flags.ignore_mandatory = True
payemnt_entry.reference_no = doc.name payemnt_entry.reference_no = doc.name
payemnt_entry.reference_date = nowdate() payemnt_entry.reference_date = nowdate()
payemnt_entry.insert(ignore_permissions=True)
payemnt_entry.submit() payemnt_entry.submit()
def create_delivery_note(shopify_order, shopify_settings, so): def create_delivery_note(shopify_order, shopify_settings, so):
@@ -168,6 +173,7 @@ def create_delivery_note(shopify_order, shopify_settings, so):
dn.items = get_fulfillment_items(dn.items, fulfillment.get("line_items"), shopify_settings) dn.items = get_fulfillment_items(dn.items, fulfillment.get("line_items"), shopify_settings)
dn.flags.ignore_mandatory = True dn.flags.ignore_mandatory = True
dn.save() dn.save()
dn.submit()
frappe.db.commit() frappe.db.commit()
def get_fulfillment_items(dn_items, fulfillment_items, shopify_settings): def get_fulfillment_items(dn_items, fulfillment_items, shopify_settings):
@@ -200,7 +206,7 @@ def get_order_items(order_items, shopify_settings):
"rate": shopify_item.get("price"), "rate": shopify_item.get("price"),
"delivery_date": nowdate(), "delivery_date": nowdate(),
"qty": shopify_item.get("quantity"), "qty": shopify_item.get("quantity"),
"stock_uom": shopify_item.get("sku"), "stock_uom": shopify_item.get("uom") or _("Nos"),
"warehouse": shopify_settings.warehouse "warehouse": shopify_settings.warehouse
}) })
else: else:

View File

@@ -21,33 +21,22 @@ class ShopifySettings(Document):
else: else:
self.unregister_webhooks() self.unregister_webhooks()
self.validate_app_type()
def validate_access_credentials(self): def validate_access_credentials(self):
if self.app_type == "Private": if not (self.get_password(raise_exception=False) and self.api_key and self.shopify_url):
if not (self.get_password(raise_exception=False) and self.api_key and self.shopify_url): frappe.msgprint(_("Missing value for Password, API Key or Shopify URL"), raise_exception=frappe.ValidationError)
frappe.msgprint(_("Missing value for Password, API Key or Shopify URL"), raise_exception=frappe.ValidationError)
else:
if not (self.access_token and self.shopify_url):
frappe.msgprint(_("Access token or Shopify URL missing"), raise_exception=frappe.ValidationError)
def validate_app_type(self):
if self.app_type == "Public":
frappe.throw(_("Support for public app is deprecated. Please setup private app, for more details refer user manual"))
def register_webhooks(self): def register_webhooks(self):
webhooks = ["orders/create", "orders/paid", "orders/fulfilled"] webhooks = ["orders/create", "orders/paid", "orders/fulfilled"]
# url = get_shopify_url('admin/webhooks.json', self)
url = get_shopify_url('admin/webhooks.json', self)
created_webhooks = [d.method for d in self.webhooks] created_webhooks = [d.method for d in self.webhooks]
url = get_shopify_url('admin/api/2019-04/webhooks.json', self)
print('url', url)
for method in webhooks: for method in webhooks:
if method in created_webhooks: print('method', method)
continue
session = get_request_session() session = get_request_session()
print('session', session)
try: try:
print(get_header(self))
d = session.post(url, data=json.dumps({ d = session.post(url, data=json.dumps({
"webhook": { "webhook": {
"topic": method, "topic": method,
@@ -55,6 +44,7 @@ class ShopifySettings(Document):
"format": "json" "format": "json"
} }
}), headers=get_header(self)) }), headers=get_header(self))
print('d', d.json())
d.raise_for_status() d.raise_for_status()
self.update_webhook_table(method, d.json()) self.update_webhook_table(method, d.json())
except Exception as e: except Exception as e:
@@ -65,7 +55,7 @@ class ShopifySettings(Document):
deleted_webhooks = [] deleted_webhooks = []
for d in self.webhooks: for d in self.webhooks:
url = get_shopify_url('admin/webhooks/{0}.json'.format(d.webhook_id), self) url = get_shopify_url('admin/api/2019-04/webhooks.json'.format(d.webhook_id), self)
try: try:
res = session.delete(url, headers=get_header(self)) res = session.delete(url, headers=get_header(self))
res.raise_for_status() res.raise_for_status()
@@ -77,6 +67,7 @@ class ShopifySettings(Document):
self.remove(d) self.remove(d)
def update_webhook_table(self, method, res): def update_webhook_table(self, method, res):
print('update')
self.append("webhooks", { self.append("webhooks", {
"webhook_id": res['webhook']['id'], "webhook_id": res['webhook']['id'],
"method": method "method": method
@@ -84,6 +75,7 @@ class ShopifySettings(Document):
def get_shopify_url(path, settings): def get_shopify_url(path, settings):
if settings.app_type == "Private": if settings.app_type == "Private":
print(settings.api_key, settings.get_password('password'), settings.shopify_url, path)
return 'https://{}:{}@{}/{}'.format(settings.api_key, settings.get_password('password'), settings.shopify_url, path) return 'https://{}:{}@{}/{}'.format(settings.api_key, settings.get_password('password'), settings.shopify_url, path)
else: else:
return 'https://{}/{}'.format(settings.shopify_url, path) return 'https://{}/{}'.format(settings.shopify_url, path)
@@ -91,11 +83,7 @@ def get_shopify_url(path, settings):
def get_header(settings): def get_header(settings):
header = {'Content-Type': 'application/json'} header = {'Content-Type': 'application/json'}
if settings.app_type == "Private": return header;
return header
else:
header["X-Shopify-Access-Token"] = settings.access_token
return header
@frappe.whitelist() @frappe.whitelist()
def get_series(): def get_series():

View File

@@ -21,7 +21,7 @@ def create_customer(shopify_customer, shopify_settings):
"customer_type": _("Individual") "customer_type": _("Individual")
}) })
customer.flags.ignore_mandatory = True customer.flags.ignore_mandatory = True
customer.insert() customer.insert(ignore_permissions=True)
if customer: if customer:
create_customer_address(customer, shopify_customer) create_customer_address(customer, shopify_customer)

View File

@@ -1,13 +1,14 @@
from __future__ import unicode_literals from __future__ import unicode_literals
import frappe import frappe
from frappe import _ from frappe import _
from erpnext import get_default_company
from frappe.utils import cstr, cint, get_request_session from frappe.utils import cstr, cint, get_request_session
from erpnext.erpnext_integrations.doctype.shopify_settings.shopify_settings import get_shopify_url, get_header from erpnext.erpnext_integrations.doctype.shopify_settings.shopify_settings import get_shopify_url, get_header
shopify_variants_attr_list = ["option1", "option2", "option3"] shopify_variants_attr_list = ["option1", "option2", "option3"]
def sync_item_from_shopify(shopify_settings, item): def sync_item_from_shopify(shopify_settings, item):
url = get_shopify_url("/admin/products/{0}.json".format(item.get("product_id")), shopify_settings) url = get_shopify_url("admin/api/2019-04/products/{0}.json".format(item.get("product_id")), shopify_settings)
session = get_request_session() session = get_request_session()
try: try:
@@ -107,7 +108,12 @@ def create_item(shopify_item, warehouse, has_variant=0, attributes=None,variant_
"image": get_item_image(shopify_item), "image": get_item_image(shopify_item),
"weight_uom": shopify_item.get("weight_unit"), "weight_uom": shopify_item.get("weight_unit"),
"weight_per_unit": shopify_item.get("weight"), "weight_per_unit": shopify_item.get("weight"),
"default_supplier": get_supplier(shopify_item) "default_supplier": get_supplier(shopify_item),
"item_defaults": [
{
"company": get_default_company()
}
]
} }
if not is_item_exists(item_dict, attributes, variant_of=variant_of): if not is_item_exists(item_dict, attributes, variant_of=variant_of):
@@ -116,7 +122,7 @@ def create_item(shopify_item, warehouse, has_variant=0, attributes=None,variant_
if not item_details: if not item_details:
new_item = frappe.get_doc(item_dict) new_item = frappe.get_doc(item_dict)
new_item.insert() new_item.insert(ignore_permissions=True, ignore_mandatory=True)
name = new_item.name name = new_item.name
if not name: if not name:

View File

@@ -40,6 +40,7 @@ class ShopifySettings(unittest.TestCase):
"price_list": "_Test Price List", "price_list": "_Test Price List",
"warehouse": "_Test Warehouse - _TC", "warehouse": "_Test Warehouse - _TC",
"cash_bank_account": "Cash - _TC", "cash_bank_account": "Cash - _TC",
"account": "Cash - _TC",
"customer_group": "_Test Customer Group", "customer_group": "_Test Customer Group",
"cost_center": "Main - _TC", "cost_center": "Main - _TC",
"taxes": [ "taxes": [

View File

@@ -40,4 +40,4 @@ def get_webhook_address(connector_name, method, exclude_uri=False):
server_url = '{uri.scheme}://{uri.netloc}/api/method/{endpoint}'.format(uri=urlparse(url), endpoint=endpoint) server_url = '{uri.scheme}://{uri.netloc}/api/method/{endpoint}'.format(uri=urlparse(url), endpoint=endpoint)
return server_url return server_url

View File

@@ -347,13 +347,25 @@
"icon": "fa fa-user", "icon": "fa fa-user",
"image_field": "image", "image_field": "image",
"max_attachments": 50, "max_attachments": 50,
"modified": "2019-09-23 16:01:39.811633", "modified": "2019-09-25 23:30:49.905893",
"modified_by": "Administrator", "modified_by": "Administrator",
"module": "Healthcare", "module": "Healthcare",
"name": "Patient", "name": "Patient",
"name_case": "Title Case", "name_case": "Title Case",
"owner": "Administrator", "owner": "Administrator",
"permissions": [ "permissions": [
{
"create": 1,
"delete": 1,
"email": 1,
"export": 1,
"print": 1,
"read": 1,
"report": 1,
"role": "System Manager",
"share": 1,
"write": 1
},
{ {
"create": 1, "create": 1,
"delete": 1, "delete": 1,

View File

@@ -42,6 +42,8 @@ notification_config = "erpnext.startup.notifications.get_notification_config"
get_help_messages = "erpnext.utilities.activation.get_help_messages" get_help_messages = "erpnext.utilities.activation.get_help_messages"
get_user_progress_slides = "erpnext.utilities.user_progress.get_user_progress_slides" get_user_progress_slides = "erpnext.utilities.user_progress.get_user_progress_slides"
update_and_get_user_progress = "erpnext.utilities.user_progress_utils.update_default_domain_actions_and_get_state" update_and_get_user_progress = "erpnext.utilities.user_progress_utils.update_default_domain_actions_and_get_state"
leaderboards = "erpnext.startup.leaderboard.get_leaderboards"
on_session_creation = "erpnext.shopping_cart.utils.set_cart_count" on_session_creation = "erpnext.shopping_cart.utils.set_cart_count"
on_logout = "erpnext.shopping_cart.utils.clear_cart_count" on_logout = "erpnext.shopping_cart.utils.clear_cart_count"
@@ -352,3 +354,53 @@ user_privacy_documents = [
'personal_fields': ['contact_mobile', 'contact_display', 'customer_name'], 'personal_fields': ['contact_mobile', 'contact_display', 'customer_name'],
} }
] ]
global_search_doctypes = [
{"doctype": "Customer", "index": 0},
{"doctype": "Supplier", "index": 1},
{"doctype": "Item", "index": 2},
{"doctype": "Warehouse", "index": 3},
{"doctype": "Account", "index": 4},
{"doctype": "Employee", "index": 5},
{"doctype": "BOM", "index": 6},
{"doctype": "Sales Invoice", "index": 7},
{"doctype": "Sales Order", "index": 8},
{"doctype": "Quotation", "index": 9},
{"doctype": "Work Order", "index": 10},
{"doctype": "Purchase Receipt", "index": 11},
{"doctype": "Purchase Invoice", "index": 12},
{"doctype": "Delivery Note", "index": 13},
{"doctype": "Stock Entry", "index": 14},
{"doctype": "Material Request", "index": 15},
{"doctype": "Delivery Trip", "index": 16},
{"doctype": "Pick List", "index": 17},
{"doctype": "Salary Slip", "index": 18},
{"doctype": "Leave Application", "index": 19},
{"doctype": "Expense Claim", "index": 20},
{"doctype": "Payment Entry", "index": 21},
{"doctype": "Lead", "index": 22},
{"doctype": "Opportunity", "index": 23},
{"doctype": "Item Price", "index": 24},
{"doctype": "Purchase Taxes and Charges Template", "index": 25},
{"doctype": "Sales Taxes and Charges", "index": 26},
{"doctype": "Asset", "index": 27},
{"doctype": "Project", "index": 28},
{"doctype": "Task", "index": 29},
{"doctype": "Timesheet", "index": 30},
{"doctype": "Issue", "index": 31},
{"doctype": "Serial No", "index": 32},
{"doctype": "Batch", "index": 33},
{"doctype": "Branch", "index": 34},
{"doctype": "Department", "index": 35},
{"doctype": "Employee Grade", "index": 36},
{"doctype": "Designation", "index": 37},
{"doctype": "Job Opening", "index": 38},
{"doctype": "Job Applicant", "index": 39},
{"doctype": "Job Offer", "index": 40},
{"doctype": "Salary Structure Assignment", "index": 41},
{"doctype": "Appraisal", "index": 42},
{"doctype": "Loan", "index": 43},
{"doctype": "Maintenance Schedule", "index": 44},
{"doctype": "Maintenance Visit", "index": 45},
{"doctype": "Warranty Claim", "index": 46},
]

View File

@@ -2,7 +2,7 @@
"allow_copy": 0, "allow_copy": 0,
"allow_guest_to_view": 0, "allow_guest_to_view": 0,
"allow_import": 0, "allow_import": 0,
"allow_rename": 0, "allow_rename": 1,
"autoname": "HR-APP-.YYYY.-.#####", "autoname": "HR-APP-.YYYY.-.#####",
"beta": 0, "beta": 0,
"creation": "2013-01-29 19:25:37", "creation": "2013-01-29 19:25:37",
@@ -346,7 +346,7 @@
"issingle": 0, "issingle": 0,
"istable": 0, "istable": 0,
"max_attachments": 0, "max_attachments": 0,
"modified": "2019-06-21 16:15:43.552049", "modified": "2019-07-21 16:15:43.552049",
"modified_by": "Administrator", "modified_by": "Administrator",
"module": "HR", "module": "HR",
"name": "Job Applicant", "name": "Job Applicant",

View File

@@ -60,7 +60,10 @@ def get_data(filters, leave_types):
data = [] data = []
for employee in active_employees: for employee in active_employees:
leave_approvers = department_approver_map.get(employee.department_name, []).append(employee.leave_approver) leave_approvers = department_approver_map.get(employee.department_name, [])
if employee.leave_approver:
leave_approvers.append(employee.leave_approver)
if (len(leave_approvers) and user in leave_approvers) or (user in ["Administrator", employee.user_id]) or ("HR Manager" in frappe.get_roles(user)): if (len(leave_approvers) and user in leave_approvers) or (user in ["Administrator", employee.user_id]) or ("HR Manager" in frappe.get_roles(user)):
row = [employee.name, employee.employee_name, employee.department] row = [employee.name, employee.employee_name, employee.department]

View File

@@ -73,7 +73,7 @@ def make_contact(supplier):
{'link_doctype': 'Supplier', 'link_name': supplier.supplier_name} {'link_doctype': 'Supplier', 'link_name': supplier.supplier_name}
] ]
}) })
contact.add_email(supplier.supplier_email) contact.add_email(supplier.supplier_email, is_primary=True)
contact.insert() contact.insert()
else: else:
contact = frappe.get_doc('Contact', contact_name) contact = frappe.get_doc('Contact', contact_name)

View File

@@ -195,6 +195,7 @@ class ProductionPlan(Document):
for data in self.po_items: for data in self.po_items:
if data.name == production_plan_item: if data.name == production_plan_item:
data.produced_qty = produced_qty data.produced_qty = produced_qty
data.pending_qty = data.planned_qty - data.produced_qty
data.db_update() data.db_update()
self.calculate_total_produced_qty() self.calculate_total_produced_qty()

View File

@@ -545,11 +545,14 @@ erpnext.work_order = {
get_max_transferable_qty: (frm, purpose) => { get_max_transferable_qty: (frm, purpose) => {
let max = 0; let max = 0;
if (frm.doc.skip_transfer) return max; if (frm.doc.skip_transfer) {
if (purpose === 'Manufacture') { max = flt(frm.doc.qty) - flt(frm.doc.produced_qty);
max = flt(frm.doc.material_transferred_for_manufacturing) - flt(frm.doc.produced_qty);
} else { } else {
max = flt(frm.doc.qty) - flt(frm.doc.material_transferred_for_manufacturing); if (purpose === 'Manufacture') {
max = flt(frm.doc.material_transferred_for_manufacturing) - flt(frm.doc.produced_qty);
} else {
max = flt(frm.doc.qty) - flt(frm.doc.material_transferred_for_manufacturing);
}
} }
return flt(max, precision('qty')); return flt(max, precision('qty'));
}, },

View File

@@ -213,6 +213,9 @@ class WorkOrder(Document):
self.db_set(fieldname, qty) self.db_set(fieldname, qty)
from erpnext.selling.doctype.sales_order.sales_order import update_produced_qty_in_so_item
update_produced_qty_in_so_item(self.sales_order_item)
if self.production_plan: if self.production_plan:
self.update_production_plan_status() self.update_production_plan_status()

View File

@@ -636,4 +636,7 @@ erpnext.patches.v12_0.generate_leave_ledger_entries
erpnext.patches.v12_0.move_credit_limit_to_customer_credit_limit erpnext.patches.v12_0.move_credit_limit_to_customer_credit_limit
erpnext.patches.v12_0.add_variant_of_in_item_attribute_table erpnext.patches.v12_0.add_variant_of_in_item_attribute_table
erpnext.patches.v12_0.rename_bank_account_field_in_journal_entry_account erpnext.patches.v12_0.rename_bank_account_field_in_journal_entry_account
erpnext.patches.v12_0.create_default_energy_point_rules erpnext.patches.v12_0.create_default_energy_point_rules
erpnext.patches.v12_0.set_produced_qty_field_in_sales_order_for_work_order
erpnext.patches.v12_0.generate_leave_ledger_entries
erpnext.patches.v12_0.set_default_shopify_app_type

View File

@@ -0,0 +1,6 @@
from __future__ import unicode_literals
import frappe
def execute():
frappe.reload_doc('erpnext_integrations', 'doctype', 'shopify_settings')
frappe.db.set_value('Shopify Settings', None, 'app_type', 'Private')

View File

@@ -3,6 +3,7 @@ import frappe
def execute(): def execute():
frappe.reload_doc("support", "doctype", "issue_priority") frappe.reload_doc("support", "doctype", "issue_priority")
frappe.reload_doc("support", "doctype", "service_level_priority") frappe.reload_doc("support", "doctype", "service_level_priority")
frappe.reload_doc('support', 'doctype', 'issue')
set_issue_priority() set_issue_priority()
set_priority_for_issue() set_priority_for_issue()

View File

@@ -0,0 +1,10 @@
import frappe
from frappe.utils import flt
from erpnext.selling.doctype.sales_order.sales_order import update_produced_qty_in_so_item
def execute():
frappe.reload_doctype('Sales Order Item')
frappe.reload_doctype('Sales Order')
sales_order_items = frappe.db.get_all('Sales Order Item', ['name'])
for so_item in sales_order_items:
update_produced_qty_in_so_item(so_item.get('name'))

View File

@@ -108,7 +108,7 @@
"fieldname": "percent_complete_method", "fieldname": "percent_complete_method",
"fieldtype": "Select", "fieldtype": "Select",
"label": "% Complete Method", "label": "% Complete Method",
"options": "Task Completion\nTask Progress\nTask Weight" "options": "Manual\nTask Completion\nTask Progress\nTask Weight"
}, },
{ {
"bold": 1, "bold": 1,
@@ -123,6 +123,7 @@
"fieldtype": "Column Break" "fieldtype": "Column Break"
}, },
{ {
"allow_in_quick_entry": 1,
"fieldname": "project_template", "fieldname": "project_template",
"fieldtype": "Link", "fieldtype": "Link",
"label": "From Template", "label": "From Template",
@@ -443,7 +444,7 @@
"icon": "fa fa-puzzle-piece", "icon": "fa fa-puzzle-piece",
"idx": 29, "idx": 29,
"max_attachments": 4, "max_attachments": 4,
"modified": "2019-07-16 11:11:12.343658", "modified": "2019-09-24 15:02:50.208301",
"modified_by": "Administrator", "modified_by": "Administrator",
"module": "Projects", "module": "Projects",
"name": "Project", "name": "Project",

View File

@@ -87,6 +87,11 @@ class Project(Document):
frappe.db.set_value("Sales Order", self.sales_order, "project", self.name) frappe.db.set_value("Sales Order", self.sales_order, "project", self.name)
def update_percent_complete(self): def update_percent_complete(self):
if self.percent_complete_method == "Manual":
if self.status == "Completed":
self.percent_complete = 100
return
total = frappe.db.count('Task', dict(project=self.name)) total = frappe.db.count('Task', dict(project=self.name))
if not total: if not total:

View File

@@ -19,6 +19,9 @@
{% if(contact_list[i].phone) { %} {% if(contact_list[i].phone) { %}
{%= __("Phone") %}: {%= contact_list[i].phone %}<span class="text-muted"> ({%= __("Primary") %})</span><br> {%= __("Phone") %}: {%= contact_list[i].phone %}<span class="text-muted"> ({%= __("Primary") %})</span><br>
{% endif %} {% endif %}
{% if(contact_list[i].mobile_no) { %}
{%= __("Mobile No") %}: {%= contact_list[i].mobile_no %}<span class="text-muted"> ({%= __("Primary") %})</span><br>
{% endif %}
{% if(contact_list[i].phone_nos) { %} {% if(contact_list[i].phone_nos) { %}
{% for(var j=0, k=contact_list[i].phone_nos.length; j<k; j++) { %} {% for(var j=0, k=contact_list[i].phone_nos.length; j<k; j++) { %}
{%= __("Phone") %}: {%= contact_list[i].phone_nos[j].phone %}<br> {%= __("Phone") %}: {%= contact_list[i].phone_nos[j].phone %}<br>

View File

@@ -350,8 +350,10 @@ def make_contact(args, is_primary_contact=1):
'link_name': args.get('name') 'link_name': args.get('name')
}] }]
}) })
contact.add_email(args.get('email_id')) if args.get('email_id'):
contact.add_phone(args.get('mobile_no')) contact.add_email(args.get('email_id'), is_primary=True)
if args.get('mobile_no'):
contact.add_phone(args.get('mobile_no'), is_primary_mobile_no=True)
contact.insert() contact.insert()
return contact return contact

View File

@@ -34,11 +34,11 @@
"fieldname": "bypass_credit_limit_check", "fieldname": "bypass_credit_limit_check",
"fieldtype": "Check", "fieldtype": "Check",
"in_list_view": 1, "in_list_view": 1,
"label": "Bypass credit limit_check" "label": "Bypass Credit Limit Check"
} }
], ],
"istable": 1, "istable": 1,
"modified": "2019-08-29 20:46:36.073953", "modified": "2019-09-24 15:05:26.069911",
"modified_by": "Administrator", "modified_by": "Administrator",
"module": "Selling", "module": "Selling",
"name": "Customer Credit Limit", "name": "Customer Credit Limit",

View File

@@ -1023,3 +1023,15 @@ def create_pick_list(source_name, target_doc=None):
doc.set_item_locations() doc.set_item_locations()
return doc return doc
def update_produced_qty_in_so_item(sales_order_item):
#for multiple work orders against same sales order item
linked_wo_with_so_item = frappe.db.get_all('Work Order', ['produced_qty'], {
'sales_order_item': sales_order_item,
'docstatus': 1
})
if len(linked_wo_with_so_item) > 0:
total_produced_qty = 0
for wo in linked_wo_with_so_item:
total_produced_qty += flt(wo.get('produced_qty'))
frappe.db.set_value('Sales Order Item', sales_order_item, 'produced_qty', total_produced_qty)

View File

@@ -110,6 +110,7 @@
"read_only": 1 "read_only": 1
}, },
{ {
"default": "0",
"fieldname": "ensure_delivery_based_on_produced_serial_no", "fieldname": "ensure_delivery_based_on_produced_serial_no",
"fieldtype": "Check", "fieldtype": "Check",
"label": "Ensure Delivery Based on Produced Serial No" "label": "Ensure Delivery Based on Produced Serial No"
@@ -381,6 +382,7 @@
"read_only": 1 "read_only": 1
}, },
{ {
"default": "0",
"fieldname": "is_free_item", "fieldname": "is_free_item",
"fieldtype": "Check", "fieldtype": "Check",
"label": "Is Free Item", "label": "Is Free Item",
@@ -436,6 +438,7 @@
"print_hide": 1 "print_hide": 1
}, },
{ {
"default": "0",
"fieldname": "delivered_by_supplier", "fieldname": "delivered_by_supplier",
"fieldtype": "Check", "fieldtype": "Check",
"label": "Supplier delivers to Customer", "label": "Supplier delivers to Customer",
@@ -662,6 +665,7 @@
}, },
{ {
"allow_on_submit": 1, "allow_on_submit": 1,
"default": "0",
"fieldname": "page_break", "fieldname": "page_break",
"fieldtype": "Check", "fieldtype": "Check",
"label": "Page Break", "label": "Page Break",
@@ -689,7 +693,6 @@
"description": "For Production", "description": "For Production",
"fieldname": "produced_qty", "fieldname": "produced_qty",
"fieldtype": "Float", "fieldtype": "Float",
"hidden": 1,
"label": "Produced Quantity", "label": "Produced Quantity",
"oldfieldname": "produced_qty", "oldfieldname": "produced_qty",
"oldfieldtype": "Currency", "oldfieldtype": "Currency",
@@ -740,7 +743,7 @@
], ],
"idx": 1, "idx": 1,
"istable": 1, "istable": 1,
"modified": "2019-05-01 17:52:32.810797", "modified": "2019-09-13 12:18:54.903107",
"modified_by": "Administrator", "modified_by": "Administrator",
"module": "Selling", "module": "Selling",
"name": "Sales Order Item", "name": "Sales Order Item",

View File

@@ -31,7 +31,7 @@ class SMSCenter(Document):
self.sales_partner.replace("'", "\'") or " and ifnull(dl.link_name, '') != ''" self.sales_partner.replace("'", "\'") or " and ifnull(dl.link_name, '') != ''"
if self.send_to in ['All Contact', 'All Customer Contact', 'All Supplier Contact', 'All Sales Partner Contact']: if self.send_to in ['All Contact', 'All Customer Contact', 'All Supplier Contact', 'All Sales Partner Contact']:
rec = frappe.db.sql("""select CONCAT(ifnull(c.first_name,''), ' ', ifnull(c.last_name,'')), rec = frappe.db.sql("""select CONCAT(ifnull(c.first_name,''), ' ', ifnull(c.last_name,'')),
c.phone from `tabContact` c, `tabDynamic Link` dl where ifnull(c.phone,'')!='' and c.mobile_no from `tabContact` c, `tabDynamic Link` dl where ifnull(c.mobile_no,'')!='' and
c.docstatus != 2 and dl.parent = c.name%s""" % where_clause) c.docstatus != 2 and dl.parent = c.name%s""" % where_clause)
elif self.send_to == 'All Lead (Open)': elif self.send_to == 'All Lead (Open)':

View File

@@ -64,16 +64,19 @@ class Company(NestedSet):
}) })
def validate_default_accounts(self): def validate_default_accounts(self):
for field in ["default_bank_account", "default_cash_account", accounts = [
"default_bank_account", "default_cash_account",
"default_receivable_account", "default_payable_account", "default_receivable_account", "default_payable_account",
"default_expense_account", "default_income_account", "default_expense_account", "default_income_account",
"stock_received_but_not_billed", "stock_adjustment_account", "stock_received_but_not_billed", "stock_adjustment_account",
"expenses_included_in_valuation", "default_payroll_payable_account"]: "expenses_included_in_valuation", "default_payroll_payable_account"
if self.get(field): ]
for_company = frappe.db.get_value("Account", self.get(field), "company")
if for_company != self.name: for field in accounts:
frappe.throw(_("Account {0} does not belong to company: {1}") if self.get(field):
.format(self.get(field), self.name)) for_company = frappe.db.get_value("Account", self.get(field), "company")
if for_company != self.name:
frappe.throw(_("Account {0} does not belong to company: {1}").format(self.get(field), self.name))
def validate_currency(self): def validate_currency(self):
if self.is_new(): if self.is_new():
@@ -180,21 +183,29 @@ class Company(NestedSet):
self.existing_company = self.parent_company self.existing_company = self.parent_company
def set_default_accounts(self): def set_default_accounts(self):
self._set_default_account("default_cash_account", "Cash") default_accounts = {
self._set_default_account("default_bank_account", "Bank") "default_cash_account": "Cash",
self._set_default_account("round_off_account", "Round Off") "default_bank_account": "Bank",
self._set_default_account("accumulated_depreciation_account", "Accumulated Depreciation") "round_off_account": "Round Off",
self._set_default_account("depreciation_expense_account", "Depreciation") "accumulated_depreciation_account": "Accumulated Depreciation",
self._set_default_account("capital_work_in_progress_account", "Capital Work in Progress") "depreciation_expense_account": "Depreciation",
self._set_default_account("asset_received_but_not_billed", "Asset Received But Not Billed") "capital_work_in_progress_account": "Capital Work in Progress",
self._set_default_account("expenses_included_in_asset_valuation", "Expenses Included In Asset Valuation") "asset_received_but_not_billed": "Asset Received But Not Billed",
"expenses_included_in_asset_valuation": "Expenses Included In Asset Valuation"
}
if self.enable_perpetual_inventory: if self.enable_perpetual_inventory:
self._set_default_account("stock_received_but_not_billed", "Stock Received But Not Billed") default_accounts.update({
self._set_default_account("default_inventory_account", "Stock") "stock_received_but_not_billed": "Stock Received But Not Billed",
self._set_default_account("stock_adjustment_account", "Stock Adjustment") "default_inventory_account": "Stock",
self._set_default_account("expenses_included_in_valuation", "Expenses Included In Valuation") "stock_adjustment_account": "Stock Adjustment",
self._set_default_account("default_expense_account", "Cost of Goods Sold") "expenses_included_in_valuation": "Expenses Included In Valuation",
"default_expense_account": "Cost of Goods Sold"
})
for default_account in default_accounts:
if self.is_new() or frappe.flags.in_test:
self._set_default_account(default_account, default_accounts.get(default_account))
if not self.default_income_account: if not self.default_income_account:
income_account = frappe.db.get_value("Account", income_account = frappe.db.get_value("Account",
@@ -243,8 +254,7 @@ class Company(NestedSet):
if self.get(fieldname): if self.get(fieldname):
return return
account = frappe.db.get_value("Account", {"account_type": account_type, account = frappe.db.get_value("Account", {"account_type": account_type, "is_group": 0, "company": self.name})
"is_group": 0, "company": self.name})
if account: if account:
self.db_set(fieldname, account) self.db_set(fieldname, account)

View File

@@ -9,6 +9,7 @@ from frappe import _
from frappe.desk.page.setup_wizard.setup_wizard import make_records from frappe.desk.page.setup_wizard.setup_wizard import make_records
from frappe.utils import cstr, getdate from frappe.utils import cstr, getdate
from erpnext.accounts.doctype.account.account import RootNotEditable from erpnext.accounts.doctype.account.account import RootNotEditable
from frappe.desk.doctype.global_search_settings.global_search_settings import update_global_search_doctypes
default_lead_sources = ["Existing Customer", "Reference", "Advertisement", default_lead_sources = ["Existing Customer", "Reference", "Advertisement",
"Cold Calling", "Exhibition", "Supplier Reference", "Mass Mailing", "Cold Calling", "Exhibition", "Supplier Reference", "Mass Mailing",
@@ -274,6 +275,8 @@ def install(country=None):
set_more_defaults() set_more_defaults()
update_global_search_doctypes()
# path = frappe.get_app_path('erpnext', 'regional', frappe.scrub(country)) # path = frappe.get_app_path('erpnext', 'regional', frappe.scrub(country))
# if os.path.exists(path.encode("utf-8")): # if os.path.exists(path.encode("utf-8")):
# frappe.get_attr("erpnext.regional.{0}.setup.setup_company_independent_fixtures".format(frappe.scrub(country)))() # frappe.get_attr("erpnext.regional.{0}.setup.setup_company_independent_fixtures".format(frappe.scrub(country)))()

View File

@@ -55,8 +55,6 @@ def place_order():
cart_settings = frappe.db.get_value("Shopping Cart Settings", None, cart_settings = frappe.db.get_value("Shopping Cart Settings", None,
["company", "allow_items_not_in_stock"], as_dict=1) ["company", "allow_items_not_in_stock"], as_dict=1)
quotation.company = cart_settings.company quotation.company = cart_settings.company
if not quotation.get("customer_address"):
throw(_("{0} is required").format(_(quotation.meta.get_label("customer_address"))))
quotation.flags.ignore_permissions = True quotation.flags.ignore_permissions = True
quotation.submit() quotation.submit()

View File

@@ -0,0 +1,177 @@
from __future__ import unicode_literals, print_function
import frappe
from frappe.utils import cint
def get_leaderboards():
leaderboards = {
"Customer": {
"fields": [
{'fieldname': 'total_sales_amount', 'fieldtype': 'Currency'},
'total_qty_sold',
{'fieldname': 'outstanding_amount', 'fieldtype': 'Currency'}
],
"method": "erpnext.startup.leaderboard.get_all_customers",
},
"Item": {
"fields": [
{'fieldname': 'total_sales_amount', 'fieldtype': 'Currency'},
'total_qty_sold',
{'fieldname': 'total_purchase_amount', 'fieldtype': 'Currency'},
'total_qty_purchased',
'available_stock_qty',
{'fieldname': 'available_stock_value', 'fieldtype': 'Currency'}
],
"method": "erpnext.startup.leaderboard.get_all_items",
},
"Supplier": {
"fields": [
{'fieldname': 'total_purchase_amount', 'fieldtype': 'Currency'},
'total_qty_purchased',
{'fieldname': 'outstanding_amount', 'fieldtype': 'Currency'}
],
"method": "erpnext.startup.leaderboard.get_all_suppliers",
},
"Sales Partner": {
"fields": [
{'fieldname': 'total_sales_amount', 'fieldtype': 'Currency'},
{'fieldname': 'total_commission', 'fieldtype': 'Currency'}
],
"method": "erpnext.startup.leaderboard.get_all_sales_partner",
},
"Sales Person": {
"fields": [
{'fieldname': 'total_sales_amount', 'fieldtype': 'Currency'}
],
"method": "erpnext.startup.leaderboard.get_all_sales_person",
}
}
return leaderboards
@frappe.whitelist()
def get_all_customers(from_date, company, field, limit = None):
if field == "outstanding_amount":
filters = [['docstatus', '=', '1'], ['company', '=', company]]
if from_date:
filters.append(['posting_date', '>=', from_date])
return frappe.db.get_all('Sales Invoice',
fields = ['customer as name', 'sum(outstanding_amount) as value'],
filters = filters,
group_by = 'customer',
order_by = 'value desc',
limit = limit
)
else:
if field == "total_sales_amount":
select_field = "sum(so_item.base_net_amount)"
elif field == "total_qty_sold":
select_field = "sum(so_item.stock_qty)"
return frappe.db.sql("""
select so.customer as name, {0} as value
FROM `tabSales Order` as so JOIN `tabSales Order Item` as so_item
ON so.name = so_item.parent
where so.docstatus = 1 and so.transaction_date >= %s and so.company = %s
group by so.customer
order by value DESC
limit %s
""".format(select_field), (from_date, company, cint(limit)), as_dict=1) #nosec
@frappe.whitelist()
def get_all_items(from_date, company, field, limit = None):
if field in ("available_stock_qty", "available_stock_value"):
select_field = "sum(actual_qty)" if field=="available_stock_qty" else "sum(stock_value)"
return frappe.db.get_all('Bin',
fields = ['item_code as name', '{0} as value'.format(select_field)],
group_by = 'item_code',
order_by = 'value desc',
limit = limit
)
else:
if field == "total_sales_amount":
select_field = "sum(order_item.base_net_amount)"
select_doctype = "Sales Order"
elif field == "total_purchase_amount":
select_field = "sum(order_item.base_net_amount)"
select_doctype = "Purchase Order"
elif field == "total_qty_sold":
select_field = "sum(order_item.stock_qty)"
select_doctype = "Sales Order"
elif field == "total_qty_purchased":
select_field = "sum(order_item.stock_qty)"
select_doctype = "Purchase Order"
return frappe.db.sql("""
select order_item.item_code as name, {0} as value
from `tab{1}` sales_order join `tab{1} Item` as order_item
on sales_order.name = order_item.parent
where sales_order.docstatus = 1
and sales_order.company = %s and sales_order.transaction_date >= %s
group by order_item.item_code
order by value desc
limit %s
""".format(select_field, select_doctype), (company, from_date, cint(limit)), as_dict=1) #nosec
@frappe.whitelist()
def get_all_suppliers(from_date, company, field, limit = None):
if field == "outstanding_amount":
filters = [['docstatus', '=', '1'], ['company', '=', company]]
if from_date:
filters.append(['posting_date', '>=', from_date])
return frappe.db.get_all('Purchase Invoice',
fields = ['supplier as name', 'sum(outstanding_amount) as value'],
filters = filters,
group_by = 'supplier',
order_by = 'value desc',
limit = limit
)
else:
if field == "total_purchase_amount":
select_field = "sum(purchase_order_item.base_net_amount)"
elif field == "total_qty_purchased":
select_field = "sum(purchase_order_item.stock_qty)"
return frappe.db.sql("""
select purchase_order.supplier as name, {0} as value
FROM `tabPurchase Order` as purchase_order LEFT JOIN `tabPurchase Order Item`
as purchase_order_item ON purchase_order.name = purchase_order_item.parent
where purchase_order.docstatus = 1 and purchase_order.modified >= %s
and purchase_order.company = %s
group by purchase_order.supplier
order by value DESC
limit %s""".format(select_field), (from_date, company, cint(limit)), as_dict=1) #nosec
@frappe.whitelist()
def get_all_sales_partner(from_date, company, field, limit = None):
if field == "total_sales_amount":
select_field = "sum(`base_net_total`)"
elif field == "total_commission":
select_field = "sum(`total_commission`)"
filters = {
'sales_partner': ['!=', ''],
'docstatus': 1,
'company': company
}
if from_date:
filters['transaction_date'] = ['>=', from_date]
return frappe.get_list('Sales Order', fields=[
'`sales_partner` as name',
'{} as value'.format(select_field),
], filters=filters, group_by='sales_partner', order_by='value DESC', limit=limit)
@frappe.whitelist()
def get_all_sales_person(from_date, company, field = None, limit = 0):
return frappe.db.sql("""
select sales_team.sales_person as name, sum(sales_order.base_net_total) as value
from `tabSales Order` as sales_order join `tabSales Team` as sales_team
on sales_order.name = sales_team.parent and sales_team.parenttype = 'Sales Order'
where sales_order.docstatus = 1
and sales_order.transaction_date >= %s
and sales_order.company = %s
group by sales_team.sales_person
order by value DESC
limit %s
""", (from_date, company, cint(limit)), as_dict=1)

View File

@@ -85,26 +85,13 @@ frappe.ui.form.on("Delivery Note", {
refresh: function(frm) { refresh: function(frm) {
if (frm.doc.docstatus === 1 && frm.doc.is_return === 1 && frm.doc.per_billed !== 100) { if (frm.doc.docstatus === 1 && frm.doc.is_return === 1 && frm.doc.per_billed !== 100) {
frm.add_custom_button(__('Credit Note'), function() { frm.add_custom_button(__('Credit Note'), function() {
frappe.confirm(__("Are you sure you want to make credit note?"), frappe.model.open_mapped_doc({
function() { method: "erpnext.stock.doctype.delivery_note.delivery_note.make_sales_invoice",
frm.trigger("make_credit_note"); frm: cur_frm,
} })
);
}, __('Create')); }, __('Create'));
frm.page.set_inner_btn_group_as_primary(__('Create')); frm.page.set_inner_btn_group_as_primary(__('Create'));
} }
},
make_credit_note: function(frm) {
frm.call({
method: "make_return_invoice",
doc: frm.doc,
freeze: true,
callback: function() {
frm.reload_doc();
}
});
} }
}); });
@@ -123,6 +110,9 @@ erpnext.stock.DeliveryNoteController = erpnext.selling.SellingController.extend(
setup: function(doc) { setup: function(doc) {
this.setup_posting_date_time_check(); this.setup_posting_date_time_check();
this._super(doc); this._super(doc);
this.frm.make_methods = {
'Delivery Trip': this.make_delivery_trip,
};
}, },
refresh: function(doc, dt, dn) { refresh: function(doc, dt, dn) {
var me = this; var me = this;
@@ -239,7 +229,7 @@ erpnext.stock.DeliveryNoteController = erpnext.selling.SellingController.extend(
make_delivery_trip: function() { make_delivery_trip: function() {
frappe.model.open_mapped_doc({ frappe.model.open_mapped_doc({
method: "erpnext.stock.doctype.delivery_note.delivery_note.make_delivery_trip", method: "erpnext.stock.doctype.delivery_note.delivery_note.make_delivery_trip",
frm: this.frm frm: cur_frm
}) })
}, },

View File

@@ -458,6 +458,9 @@ def make_sales_invoice(source_name, target_doc=None):
doc = get_mapped_doc("Delivery Note", source_name, { doc = get_mapped_doc("Delivery Note", source_name, {
"Delivery Note": { "Delivery Note": {
"doctype": "Sales Invoice", "doctype": "Sales Invoice",
"field_map": {
"is_return": "is_return"
},
"validation": { "validation": {
"docstatus": ["=", 1] "docstatus": ["=", 1]
} }

View File

@@ -41,28 +41,15 @@ frappe.ui.form.on("Purchase Receipt", {
if (frm.doc.docstatus === 1 && frm.doc.is_return === 1 && frm.doc.per_billed !== 100) { if (frm.doc.docstatus === 1 && frm.doc.is_return === 1 && frm.doc.per_billed !== 100) {
frm.add_custom_button(__('Debit Note'), function() { frm.add_custom_button(__('Debit Note'), function() {
frappe.confirm(__("Are you sure you want to make debit note?"), frappe.model.open_mapped_doc({
function() { method: "erpnext.stock.doctype.purchase_receipt.purchase_receipt.make_purchase_invoice",
frm.trigger("make_debit_note"); frm: cur_frm,
} })
);
}, __('Create')); }, __('Create'));
frm.page.set_inner_btn_group_as_primary(__('Create')); frm.page.set_inner_btn_group_as_primary(__('Create'));
} }
}, },
make_debit_note: function(frm) {
frm.call({
method: "make_return_invoice",
doc: frm.doc,
freeze: true,
callback: function() {
frm.reload_doc();
}
});
},
company: function(frm) { company: function(frm) {
frm.trigger("toggle_display_account_head"); frm.trigger("toggle_display_account_head");
}, },
@@ -128,12 +115,12 @@ erpnext.stock.PurchaseReceiptController = erpnext.buying.BuyingController.extend
cur_frm.add_custom_button(__("Close"), this.close_purchase_receipt, __("Status")) cur_frm.add_custom_button(__("Close"), this.close_purchase_receipt, __("Status"))
} }
cur_frm.add_custom_button(__('Return'), this.make_purchase_return, __('Create')); cur_frm.add_custom_button(__('Purchase Return'), this.make_purchase_return, __('Create'));
cur_frm.add_custom_button(__('Make Stock Entry'), cur_frm.cscript['Make Stock Entry'], __('Create')); cur_frm.add_custom_button(__('Make Stock Entry'), cur_frm.cscript['Make Stock Entry'], __('Create'));
if(flt(this.frm.doc.per_billed) < 100) { if(flt(this.frm.doc.per_billed) < 100) {
cur_frm.add_custom_button(__('Invoice'), this.make_purchase_invoice, __('Create')); cur_frm.add_custom_button(__('Purchase Invoice'), this.make_purchase_invoice, __('Create'));
} }
cur_frm.add_custom_button(__('Retention Stock Entry'), this.make_retention_stock_entry, __('Create')); cur_frm.add_custom_button(__('Retention Stock Entry'), this.make_retention_stock_entry, __('Create'));

View File

@@ -426,16 +426,6 @@ class PurchaseReceipt(BuyingController):
self.load_from_db() self.load_from_db()
def make_return_invoice(self):
return_invoice = make_purchase_invoice(self.name)
return_invoice.is_return = True
return_invoice.save()
return_invoice.submit()
debit_note_link = frappe.utils.get_link_to_form('Purchase Invoice', return_invoice.name)
frappe.msgprint(_("Debit Note {0} has been created automatically").format(debit_note_link))
def update_billed_amount_based_on_po(po_detail, update_modified=True): def update_billed_amount_based_on_po(po_detail, update_modified=True):
# Billed against Sales Order directly # Billed against Sales Order directly
billed_against_po = frappe.db.sql("""select sum(amount) from `tabPurchase Invoice Item` billed_against_po = frappe.db.sql("""select sum(amount) from `tabPurchase Invoice Item`
@@ -510,7 +500,8 @@ def make_purchase_invoice(source_name, target_doc=None):
"Purchase Receipt": { "Purchase Receipt": {
"doctype": "Purchase Invoice", "doctype": "Purchase Invoice",
"field_map": { "field_map": {
"supplier_warehouse":"supplier_warehouse" "supplier_warehouse":"supplier_warehouse",
"is_return": "is_return"
}, },
"validation": { "validation": {
"docstatus": ["=", 1], "docstatus": ["=", 1],

View File

@@ -362,6 +362,7 @@ def auto_make_serial_nos(args):
sr.batch_no = args.get('batch_no') sr.batch_no = args.get('batch_no')
sr.location = args.get('location') sr.location = args.get('location')
sr.company = args.get('company') sr.company = args.get('company')
sr.supplier = args.get('supplier')
if sr.sales_order and args.get('voucher_type') == "Stock Entry" \ if sr.sales_order and args.get('voucher_type') == "Stock Entry" \
and not args.get('actual_qty', 0) > 0: and not args.get('actual_qty', 0) > 0:
sr.sales_order = None sr.sales_order = None
@@ -396,10 +397,12 @@ def make_serial_no(serial_no, args):
sr.via_stock_ledger = args.get('via_stock_ledger') or True sr.via_stock_ledger = args.get('via_stock_ledger') or True
sr.asset = args.get('asset') sr.asset = args.get('asset')
sr.location = args.get('location') sr.location = args.get('location')
if args.get('purchase_document_type'): if args.get('purchase_document_type'):
sr.purchase_document_type = args.get('purchase_document_type') sr.purchase_document_type = args.get('purchase_document_type')
sr.purchase_document_no = args.get('purchase_document_no') sr.purchase_document_no = args.get('purchase_document_no')
sr.supplier = args.get('supplier')
sr.insert() sr.insert()
if args.get('warehouse'): if args.get('warehouse'):

View File

@@ -539,6 +539,21 @@ class StockEntry(StockController):
total_allowed = required_qty + (required_qty * (qty_allowance/100)) total_allowed = required_qty + (required_qty * (qty_allowance/100))
if not required_qty:
bom_no = frappe.db.get_value("Purchase Order Item",
{"parent": self.purchase_order, "item_code": se_item.subcontracted_item},
"bom")
allow_alternative_item = frappe.get_value("BOM", bom_no, "allow_alternative_item")
if allow_alternative_item:
original_item_code = frappe.get_value("Item Alternative", {"alternative_item_code": item_code}, "item_code")
required_qty = sum([flt(d.required_qty) for d in purchase_order.supplied_items \
if d.rm_item_code == original_item_code])
total_allowed = required_qty + (required_qty * (qty_allowance/100))
if not required_qty: if not required_qty:
frappe.throw(_("Item {0} not found in 'Raw Materials Supplied' table in Purchase Order {1}") frappe.throw(_("Item {0} not found in 'Raw Materials Supplied' table in Purchase Order {1}")
.format(se_item.item_code, self.purchase_order)) .format(se_item.item_code, self.purchase_order))

View File

@@ -107,7 +107,7 @@ def get_item_details(items, sl_entries, include_uom):
if include_uom: if include_uom:
cf_field = ", ucd.conversion_factor" cf_field = ", ucd.conversion_factor"
cf_join = "left join `tabUOM Conversion Detail` ucd on ucd.parent=item.name and ucd.uom='%s'" \ cf_join = "left join `tabUOM Conversion Detail` ucd on ucd.parent=item.name and ucd.uom='%s'" \
% frappe.db.escape(include_uom) % (include_uom)
res = frappe.db.sql(""" res = frappe.db.sql("""
select select

View File

@@ -3,10 +3,10 @@
from __future__ import print_function, unicode_literals from __future__ import print_function, unicode_literals
import frappe import frappe
from frappe.utils import flt, cstr, nowdate, nowtime from frappe.utils import flt, cstr, nowdate, nowtime
from erpnext.stock.utils import update_bin from erpnext.stock.utils import update_bin
from erpnext.stock.stock_ledger import update_entries_after from erpnext.stock.stock_ledger import update_entries_after
from erpnext.controllers.stock_controller import update_gl_entries_after
def repost(only_actual=False, allow_negative_stock=False, allow_zero_rate=False, only_bin=False): def repost(only_actual=False, allow_negative_stock=False, allow_zero_rate=False, only_bin=False):
""" """
@@ -18,23 +18,29 @@ def repost(only_actual=False, allow_negative_stock=False, allow_zero_rate=False,
existing_allow_negative_stock = frappe.db.get_value("Stock Settings", None, "allow_negative_stock") existing_allow_negative_stock = frappe.db.get_value("Stock Settings", None, "allow_negative_stock")
frappe.db.set_value("Stock Settings", None, "allow_negative_stock", 1) frappe.db.set_value("Stock Settings", None, "allow_negative_stock", 1)
for d in frappe.db.sql("""select distinct item_code, warehouse from item_warehouses = frappe.db.sql("""
(select item_code, warehouse from tabBin select distinct item_code, warehouse
union from
select item_code, warehouse from `tabStock Ledger Entry`) a"""): (select item_code, warehouse from tabBin
try: union
repost_stock(d[0], d[1], allow_zero_rate, only_actual, only_bin) select item_code, warehouse from `tabStock Ledger Entry`) a
frappe.db.commit() """)
except: for d in item_warehouses:
frappe.db.rollback() try:
repost_stock(d[0], d[1], allow_zero_rate, only_actual, only_bin, allow_negative_stock)
frappe.db.commit()
except:
frappe.db.rollback()
if allow_negative_stock: if allow_negative_stock:
frappe.db.set_value("Stock Settings", None, "allow_negative_stock", existing_allow_negative_stock) frappe.db.set_value("Stock Settings", None, "allow_negative_stock", existing_allow_negative_stock)
frappe.db.auto_commit_on_many_writes = 0 frappe.db.auto_commit_on_many_writes = 0
def repost_stock(item_code, warehouse, allow_zero_rate=False, only_actual=False, only_bin=False): def repost_stock(item_code, warehouse, allow_zero_rate=False,
only_actual=False, only_bin=False, allow_negative_stock=False):
if not only_bin: if not only_bin:
repost_actual_qty(item_code, warehouse, allow_zero_rate) repost_actual_qty(item_code, warehouse, allow_zero_rate, allow_negative_stock)
if item_code and warehouse and not only_actual: if item_code and warehouse and not only_actual:
qty_dict = { qty_dict = {
@@ -50,11 +56,8 @@ def repost_stock(item_code, warehouse, allow_zero_rate=False, only_actual=False,
update_bin_qty(item_code, warehouse, qty_dict) update_bin_qty(item_code, warehouse, qty_dict)
def repost_actual_qty(item_code, warehouse, allow_zero_rate=False): def repost_actual_qty(item_code, warehouse, allow_zero_rate=False, allow_negative_stock=False): update_entries_after({ "item_code": item_code, "warehouse": warehouse },
try: allow_zero_rate=allow_zero_rate, allow_negative_stock=allow_negative_stock)
update_entries_after({ "item_code": item_code, "warehouse": warehouse }, allow_zero_rate)
except:
pass
def get_balance_qty_from_sle(item_code, warehouse): def get_balance_qty_from_sle(item_code, warehouse):
balance_qty = frappe.db.sql("""select qty_after_transaction from `tabStock Ledger Entry` balance_qty = frappe.db.sql("""select qty_after_transaction from `tabStock Ledger Entry`
@@ -227,39 +230,14 @@ def reset_serial_no_status_and_warehouse(serial_nos=None):
except: except:
pass pass
def repost_all_stock_vouchers(): def repost_gle_for_stock_transactions(posting_date=None, posting_time=None, for_warehouses=None):
warehouses_with_account = frappe.db.sql_list("""select warehouse from tabAccount frappe.db.auto_commit_on_many_writes = 1
where ifnull(account_type, '') = 'Stock' and (warehouse is not null and warehouse != '')
and is_group=0""")
vouchers = frappe.db.sql("""select distinct voucher_type, voucher_no if not posting_date:
from `tabStock Ledger Entry` sle posting_date = "1900-01-01"
where voucher_type != "Serial No" and sle.warehouse in (%s) if not posting_time:
order by posting_date, posting_time, creation""" % posting_time = "00:00"
', '.join(['%s']*len(warehouses_with_account)), tuple(warehouses_with_account))
rejected = [] update_gl_entries_after(posting_date, posting_time, for_warehouses=for_warehouses)
i = 0
for voucher_type, voucher_no in vouchers:
i+=1
print(i, "/", len(vouchers), voucher_type, voucher_no)
try:
for dt in ["Stock Ledger Entry", "GL Entry"]:
frappe.db.sql("""delete from `tab%s` where voucher_type=%s and voucher_no=%s"""%
(dt, '%s', '%s'), (voucher_type, voucher_no))
doc = frappe.get_doc(voucher_type, voucher_no) frappe.db.auto_commit_on_many_writes = 0
if voucher_type=="Stock Entry" and doc.purpose in ["Manufacture", "Repack"]:
doc.calculate_rate_and_amount(force=1)
elif voucher_type=="Purchase Receipt" and doc.is_subcontracted == "Yes":
doc.validate()
doc.update_stock_ledger()
doc.make_gl_entries(repost_future_gle=False)
frappe.db.commit()
except Exception:
print(frappe.get_traceback())
rejected.append([voucher_type, voucher_no])
frappe.db.rollback()
print(rejected)

View File

@@ -253,31 +253,40 @@ def update_included_uom_in_report(columns, result, include_uom, conversion_facto
return return
convertible_cols = {} convertible_cols = {}
for col_idx in reversed(range(0, len(columns))):
col = columns[col_idx] is_dict_obj = False
if isinstance(col, dict) and col.get("convertible") in ['rate', 'qty']: if isinstance(result[0], dict):
convertible_cols[col_idx] = col['convertible'] is_dict_obj = True
columns.insert(col_idx+1, col.copy())
columns[col_idx+1]['fieldname'] += "_alt" convertible_columns = {}
if convertible_cols[col_idx] == 'rate': for idx, d in enumerate(columns):
columns[col_idx+1]['label'] += " (per {})".format(include_uom) key = d.get("fieldname") if is_dict_obj else idx
else: if d.get("convertible"):
columns[col_idx+1]['label'] += " ({})".format(include_uom) convertible_columns.setdefault(key, d.get("convertible"))
# Add new column to show qty/rate as per the selected UOM
columns.insert(idx+1, {
'label': "{0} (per {1})".format(d.get("label"), include_uom),
'fieldname': "{0}_{1}".format(d.get("fieldname"), frappe.scrub(include_uom)),
'fieldtype': 'Currency' if d.get("convertible") == 'rate' else 'Float'
})
for row_idx, row in enumerate(result): for row_idx, row in enumerate(result):
new_row = [] data = row.items() if is_dict_obj else enumerate(row)
for col_idx, d in enumerate(row): for key, value in data:
new_row.append(d) if not key in convertible_columns or not conversion_factors[row_idx]:
if col_idx in convertible_cols: continue
if conversion_factors[row_idx]:
if convertible_cols[col_idx] == 'rate':
new_row.append(flt(d) * conversion_factors[row_idx])
else:
new_row.append(flt(d) / conversion_factors[row_idx])
else:
new_row.append(None)
result[row_idx] = new_row if convertible_columns.get(key) == 'rate':
new_value = flt(value) * conversion_factors[row_idx]
else:
new_value = flt(value) / conversion_factors[row_idx]
if not is_dict_obj:
row.insert(key+1, new_value)
else:
new_key = "{0}_{1}".format(key, frappe.scrub(include_uom))
row[new_key] = new_value
def get_available_serial_nos(item_code, warehouse): def get_available_serial_nos(item_code, warehouse):
return frappe.get_all("Serial No", filters = {'item_code': item_code, return frappe.get_all("Serial No", filters = {'item_code': item_code,

View File

@@ -1,3 +1,18 @@
{% set domains = frappe.get_doc("Domain Settings").active_domains %} {% set domains = frappe.get_doc("Domain Settings").active_domains %}
{% set links = {
'Manufacturing': '/manufacturing',
'Services': '/services',
'Retail': '/retail',
'Distribution': '/distribution',
'Non Profit': '/non-profit',
'Education': '/education',
'Healthcare': '/healthcare',
'Agriculture': '/agriculture',
'Hospitality': ''
} %}
{% set link = '' %}
{% if domains %}
{% set link = links[domains[0].domain] %}
{% endif %}
<a href="https://erpnext.com?source=website_footer" target="_blank" class="text-muted">Powered by ERPNext - ERP Software {{ ('for ' + domains[0].domain + ' Companies') if domains else '' }}</a> <a href="https://erpnext.com{{ link }}?source=website_footer" target="_blank" class="text-muted">Powered by ERPNext - {{ '' if domains else 'Open Source' }} ERP Software {{ ('for ' + domains[0].domain + ' Companies') if domains else '' }}</a>

View File

@@ -10,27 +10,32 @@ def create_test_contact_and_address():
frappe.db.sql('delete from tabAddress') frappe.db.sql('delete from tabAddress')
frappe.db.sql('delete from `tabDynamic Link`') frappe.db.sql('delete from `tabDynamic Link`')
frappe.get_doc(dict( frappe.get_doc({
doctype='Address', "doctype": "Address",
address_title='_Test Address for Customer', "address_title": "_Test Address for Customer",
address_type='Office', "address_type": "Office",
address_line1='Station Road', "address_line1": "Station Road",
city='_Test City', "city": "_Test City",
state='Test State', "state": "Test State",
country='India', "country": "India",
links = [dict( "links": [
link_doctype='Customer', {
link_name='_Test Customer' "link_doctype": "Customer",
)] "link_name": "_Test Customer"
)).insert() }
]
}).insert()
frappe.get_doc(dict( contact = frappe.get_doc({
doctype='Contact', "doctype": 'Contact',
email_id='test_contact_customer@example.com', "first_name": "_Test Contact for _Test Customer",
phone='+91 0000000000', "links": [
first_name='_Test Contact for _Test Customer', {
links = [dict( "link_doctype": "Customer",
link_doctype='Customer', "link_name": "_Test Customer"
link_name='_Test Customer' }
)] ]
)).insert() })
contact.add_email("test_contact_customer@example.com", is_primary=True)
contact.add_phone("+91 0000000000", is_primary_phone=True)
contact.insert()

View File

@@ -1,54 +0,0 @@
.list-filters {
overflow-y: hidden;
padding: 5px
}
.list-filter-item {
min-width: 150px;
float: left;
margin:5px;
}
.list-item_content{
flex: 1;
padding-right: 15px;
align-items: center;
}
.select-time, .select-doctype, .select-filter, .select-sort {
background: #f0f4f7;
}
.select-time:focus, .select-doctype:focus, .select-filter:focus, .select-sort:focus {
background: #f0f4f7;
}
.header-btn-base{
border:none;
outline:0;
vertical-align:middle;
overflow:hidden;
text-decoration:none;
color:inherit;
background-color:inherit;
cursor:pointer;
white-space:nowrap;
}
.header-btn-grey,.header-btn-grey:hover{
color:#000!important;
background-color:#bbb!important
}
.header-btn-round{
border-radius:4px
}
.item-title-bold{
font-weight: bold;
}
/*
.header-btn-base:hover {
box-shadow:0 8px 16px 0 rgba(0,0,0,0.2),0 6px 20px 0 rgba(0,0,0,0.19)
}*/

View File

@@ -1,307 +0,0 @@
frappe.pages["leaderboard"].on_page_load = function (wrapper) {
frappe.leaderboard = new frappe.Leaderboard(wrapper);
}
frappe.Leaderboard = Class.extend({
init: function (parent) {
frappe.ui.make_app_page({
parent: parent,
title: "Leaderboard",
single_column: false
});
this.parent = parent;
this.page = this.parent.page;
this.page.sidebar.html(`<ul class="module-sidebar-nav overlay-sidebar nav nav-pills nav-stacked"></ul>`);
this.$sidebar_list = this.page.sidebar.find('ul');
// const list of doctypes
this.doctypes = ["Customer", "Item", "Supplier", "Sales Partner","Sales Person"];
this.timespans = ["Week", "Month", "Quarter", "Year"];
this.filters = {
"Customer": ["total_sales_amount", "total_qty_sold", "outstanding_amount", ],
"Item": ["total_sales_amount", "total_qty_sold", "total_purchase_amount",
"total_qty_purchased", "available_stock_qty", "available_stock_value"],
"Supplier": ["total_purchase_amount", "total_qty_purchased", "outstanding_amount"],
"Sales Partner": ["total_sales_amount", "total_commission"],
"Sales Person": ["total_sales_amount"],
};
// for saving current selected filters
// TODO: revert to 0 index for doctype and timespan, and remove preset down
const _initial_doctype = this.doctypes[0];
const _initial_timespan = this.timespans[0];
const _initial_filter = this.filters[_initial_doctype];
this.options = {
selected_doctype: _initial_doctype,
selected_filter: _initial_filter,
selected_filter_item: _initial_filter[0],
selected_timespan: _initial_timespan,
};
this.message = null;
this.make();
},
make: function () {
var me = this;
var $container = $(`<div class="leaderboard page-main-content">
<div class="leaderboard-graph"></div>
<div class="leaderboard-list"></div>
</div>`).appendTo(this.page.main);
this.$graph_area = $container.find('.leaderboard-graph');
this.doctypes.map(doctype => {
this.get_sidebar_item(doctype).appendTo(this.$sidebar_list);
});
this.company_select = this.page.add_field({
fieldname: 'company',
label: __('Company'),
fieldtype:'Link',
options:'Company',
default:frappe.defaults.get_default('company'),
reqd: 1,
change: function() {
me.options.selected_company = this.value;
me.make_request($container);
}
});
this.timespan_select = this.page.add_select(__("Timespan"),
this.timespans.map(d => {
return {"label": __(d), value: d }
})
);
this.type_select = this.page.add_select(__("Type"),
me.options.selected_filter.map(d => {
return {"label": __(frappe.model.unscrub(d)), value: d }
})
);
this.$sidebar_list.on('click', 'li', function(e) {
let $li = $(this);
let doctype = $li.find('span').attr("doctype-value");
me.options.selected_company = frappe.defaults.get_default('company');
me.options.selected_doctype = doctype;
me.options.selected_filter = me.filters[doctype];
me.options.selected_filter_item = me.filters[doctype][0];
me.type_select.empty().add_options(
me.options.selected_filter.map(d => {
return {"label": __(frappe.model.unscrub(d)), value: d }
})
);
me.$sidebar_list.find('li').removeClass('active');
$li.addClass('active');
me.make_request($container);
});
this.timespan_select.on("change", function() {
me.options.selected_timespan = this.value;
me.make_request($container);
});
this.type_select.on("change", function() {
me.options.selected_filter_item = this.value
me.make_request($container);
});
// now get leaderboard
this.$sidebar_list.find('li:first').trigger('click');
},
make_request: function ($container) {
var me = this;
frappe.model.with_doctype(me.options.selected_doctype, function () {
me.get_leaderboard(me.get_leaderboard_data, $container);
});
},
get_leaderboard: function (notify, $container) {
var me = this;
if(!me.options.selected_company) {
frappe.throw(__("Please select Company"));
}
frappe.call({
method: "erpnext.utilities.page.leaderboard.leaderboard.get_leaderboard",
args: {
doctype: me.options.selected_doctype,
timespan: me.options.selected_timespan,
company: me.options.selected_company,
field: me.options.selected_filter_item,
},
callback: function (r) {
let results = r.message || [];
let graph_items = results.slice(0, 10);
me.$graph_area.show().empty();
let args = {
data: {
datasets: [
{
values: graph_items.map(d=>d.value)
}
],
labels: graph_items.map(d=>d.name)
},
colors: ['light-green'],
format_tooltip_x: d=>d[me.options.selected_filter_item],
type: 'bar',
height: 140
};
new frappe.Chart('.leaderboard-graph', args);
notify(me, r, $container);
}
});
},
get_leaderboard_data: function (me, res, $container) {
if (res && res.message) {
me.message = null;
$container.find(".leaderboard-list").html(me.render_list_view(res.message));
} else {
me.$graph_area.hide();
me.message = __("No items found.");
$container.find(".leaderboard-list").html(me.render_list_view());
}
},
render_list_view: function (items = []) {
var me = this;
var html =
`${me.render_message()}
<div class="result" style="${me.message ? "display:none;" : ""}">
${me.render_result(items)}
</div>`;
return $(html);
},
render_result: function (items) {
var me = this;
var html =
`${me.render_list_header()}
${me.render_list_result(items)}`;
return html;
},
render_list_header: function () {
var me = this;
const _selected_filter = me.options.selected_filter
.map(i => frappe.model.unscrub(i));
const fields = ['name', me.options.selected_filter_item];
const html =
`<div class="list-headers">
<div class="list-item list-item--head" data-list-renderer="${"List"}">
${
fields.map(filter => {
const col = frappe.model.unscrub(filter);
return (
`<div class="leaderboard-item list-item_content ellipsis text-muted list-item__content--flex-2
header-btn-base
${(col && _selected_filter.indexOf(col) !== -1) ? "text-right" : ""}">
<span class="list-col-title ellipsis">
${col}
</span>
</div>`);
}).join("")
}
</div>
</div>`;
return html;
},
render_list_result: function (items) {
var me = this;
let _html = items.map((item, index) => {
const $value = $(me.get_item_html(item));
let item_class = "";
if(index == 0) {
item_class = "first";
} else if (index == 1) {
item_class = "second";
} else if(index == 2) {
item_class = "third";
}
const $item_container = $(`<div class="list-item-container ${item_class}">`).append($value);
return $item_container[0].outerHTML;
}).join("");
let html =
`<div class="result-list">
<div class="list-items">
${_html}
</div>
</div>`;
return html;
},
render_message: function () {
var me = this;
let html =
`<div class="no-result text-center" style="${me.message ? "" : "display:none;"}">
<div class="msg-box no-border">
<p>No Item found</p>
</div>
</div>`;
return html;
},
get_item_html: function (item) {
var me = this;
const company = me.options.selected_company;
const currency = frappe.get_doc(":Company", company).default_currency;
const fields = ['name','value'];
const html =
`<div class="list-item">
${
fields.map(col => {
let val = item[col];
if(col=="name") {
var formatted_value = `<a class="grey list-id ellipsis"
href="#Form/${me.options.selected_doctype}/${item["name"]}"> ${val} </a>`
} else {
var formatted_value = `<span class="text-muted ellipsis">
${(me.options.selected_filter_item.indexOf('qty') == -1) ? format_currency(val, currency) : val}</span>`
}
return (
`<div class="list-item_content ellipsis list-item__content--flex-2
${(col == "value") ? "text-right" : ""}">
${formatted_value}
</div>`);
}).join("")
}
</div>`;
return html;
},
get_sidebar_item: function(item) {
return $(`<li class="strong module-sidebar-item">
<a class="module-link">
<span doctype-value="${item}">${ __(item) }</span></a>
</li>`);
}
});

View File

@@ -1,19 +0,0 @@
{
"content": null,
"creation": "2017-06-06 02:54:24.785360",
"docstatus": 0,
"doctype": "Page",
"idx": 0,
"modified": "2017-09-12 14:05:26.422064",
"modified_by": "Administrator",
"module": "Utilities",
"name": "leaderboard",
"owner": "Administrator",
"page_name": "leaderboard",
"roles": [],
"script": null,
"standard": "Yes",
"style": null,
"system_page": 0,
"title": "Leaderboard"
}

View File

@@ -1,153 +0,0 @@
# Copyright (c) 2017, Frappe Technologies Pvt. Ltd. and Contributors
# MIT License. See license.txt
from __future__ import unicode_literals, print_function
import frappe
from frappe.utils import add_to_date
@frappe.whitelist()
def get_leaderboard(doctype, timespan, company, field):
"""return top 10 items for that doctype based on conditions"""
from_date = get_from_date(timespan)
records = []
if doctype == "Customer":
records = get_all_customers(from_date, company, field)
elif doctype == "Item":
records = get_all_items(from_date, company, field)
elif doctype == "Supplier":
records = get_all_suppliers(from_date, company, field)
elif doctype == "Sales Partner":
records = get_all_sales_partner(from_date, company, field)
elif doctype == "Sales Person":
records = get_all_sales_person(from_date, company)
return records
def get_all_customers(from_date, company, field):
if field == "outstanding_amount":
return frappe.db.sql("""
select customer as name, sum(outstanding_amount) as value
FROM `tabSales Invoice`
where docstatus = 1 and posting_date >= %s and company = %s
group by customer
order by value DESC
limit 20
""", (from_date, company), as_dict=1)
else:
if field == "total_sales_amount":
select_field = "sum(so_item.base_net_amount)"
elif field == "total_qty_sold":
select_field = "sum(so_item.stock_qty)"
return frappe.db.sql("""
select so.customer as name, {0} as value
FROM `tabSales Order` as so JOIN `tabSales Order Item` as so_item
ON so.name = so_item.parent
where so.docstatus = 1 and so.transaction_date >= %s and so.company = %s
group by so.customer
order by value DESC
limit 20
""".format(select_field), (from_date, company), as_dict=1)
def get_all_items(from_date, company, field):
if field in ("available_stock_qty", "available_stock_value"):
return frappe.db.sql("""
select item_code as name, {0} as value
from tabBin
group by item_code
order by value desc
limit 20
""".format("sum(actual_qty)" if field=="available_stock_qty" else "sum(stock_value)"), as_dict=1)
else:
if field == "total_sales_amount":
select_field = "sum(order_item.base_net_amount)"
select_doctype = "Sales Order"
elif field == "total_purchase_amount":
select_field = "sum(order_item.base_net_amount)"
select_doctype = "Purchase Order"
elif field == "total_qty_sold":
select_field = "sum(order_item.stock_qty)"
select_doctype = "Sales Order"
elif field == "total_qty_purchased":
select_field = "sum(order_item.stock_qty)"
select_doctype = "Purchase Order"
return frappe.db.sql("""
select order_item.item_code as name, {0} as value
from `tab{1}` sales_order join `tab{1} Item` as order_item
on sales_order.name = order_item.parent
where sales_order.docstatus = 1
and sales_order.company = %s and sales_order.transaction_date >= %s
group by order_item.item_code
order by value desc
limit 20
""".format(select_field, select_doctype), (company, from_date), as_dict=1)
def get_all_suppliers(from_date, company, field):
if field == "outstanding_amount":
return frappe.db.sql("""
select supplier as name, sum(outstanding_amount) as value
FROM `tabPurchase Invoice`
where docstatus = 1 and posting_date >= %s and company = %s
group by supplier
order by value DESC
limit 20""", (from_date, company), as_dict=1)
else:
if field == "total_purchase_amount":
select_field = "sum(purchase_order_item.base_net_amount)"
elif field == "total_qty_purchased":
select_field = "sum(purchase_order_item.stock_qty)"
return frappe.db.sql("""
select purchase_order.supplier as name, {0} as value
FROM `tabPurchase Order` as purchase_order LEFT JOIN `tabPurchase Order Item`
as purchase_order_item ON purchase_order.name = purchase_order_item.parent
where purchase_order.docstatus = 1 and purchase_order.modified >= %s
and purchase_order.company = %s
group by purchase_order.supplier
order by value DESC
limit 20""".format(select_field), (from_date, company), as_dict=1)
def get_all_sales_partner(from_date, company, field):
if field == "total_sales_amount":
select_field = "sum(base_net_total)"
elif field == "total_commission":
select_field = "sum(total_commission)"
return frappe.db.sql("""
select sales_partner as name, {0} as value
from `tabSales Order`
where ifnull(sales_partner, '') != '' and docstatus = 1
and transaction_date >= %s and company = %s
group by sales_partner
order by value DESC
limit 20
""".format(select_field), (from_date, company), as_dict=1)
def get_all_sales_person(from_date, company):
return frappe.db.sql("""
select sales_team.sales_person as name, sum(sales_order.base_net_total) as value
from `tabSales Order` as sales_order join `tabSales Team` as sales_team
on sales_order.name = sales_team.parent and sales_team.parenttype = 'Sales Order'
where sales_order.docstatus = 1
and sales_order.transaction_date >= %s
and sales_order.company = %s
group by sales_team.sales_person
order by value DESC
limit 20
""", (from_date, company), as_dict=1)
def get_from_date(seleted_timespan):
"""return string for ex:this week as date:string"""
days = months = years = 0
if "month" == seleted_timespan.lower():
months = -1
elif "quarter" == seleted_timespan.lower():
months = -3
elif "year" == seleted_timespan.lower():
years = -1
else:
days = -7
return add_to_date(None, years=years, months=months, days=days,
as_string=True, as_datetime=True)