Files
schuetz3-erpnext/erpnext/controllers/subcontracting_controller.py
venkat102 f94dffe568 fix: validate negative qty
(cherry picked from commit 329d14957b)

# Conflicts:
#	.editorconfig
#	.git-blame-ignore-revs
#	.github/helper/documentation.py
#	.github/helper/install.sh
#	.github/stale.yml
#	.github/workflows/linters.yml
#	.github/workflows/patch.yml
#	.github/workflows/release.yml
#	.github/workflows/release_notes.yml
#	.github/workflows/server-tests-mariadb.yml
#	.github/workflows/server-tests-postgres.yml
#	.gitignore
#	.mergify.yml
#	.releaserc
#	CODEOWNERS
#	README.md
#	erpnext/__init__.py
#	erpnext/accounts/deferred_revenue.py
#	erpnext/accounts/doctype/account/account.json
#	erpnext/accounts/doctype/account/account.py
#	erpnext/accounts/doctype/account/account_tree.js
#	erpnext/accounts/doctype/account/chart_of_accounts/chart_of_accounts.py
#	erpnext/accounts/doctype/account/chart_of_accounts/verified/de_kontenplan_SKR04.json
#	erpnext/accounts/doctype/account/chart_of_accounts/verified/hu_chart_of_accounts_for_microenterprises_with_account_number.json
#	erpnext/accounts/doctype/account/chart_of_accounts/verified/in_standard_chart_of_accounts.json
#	erpnext/accounts/doctype/account/chart_of_accounts/verified/ni_catalogo_de_cuentas.json
#	erpnext/accounts/doctype/account/chart_of_accounts/verified/standard_chart_of_accounts.py
#	erpnext/accounts/doctype/account/chart_of_accounts/verified/standard_chart_of_accounts_with_account_number.py
#	erpnext/accounts/doctype/account/test_account.py
#	erpnext/accounts/doctype/account_closing_balance/account_closing_balance.json
#	erpnext/accounts/doctype/account_closing_balance/test_account_closing_balance.py
#	erpnext/accounts/doctype/accounting_dimension/accounting_dimension.json
#	erpnext/accounts/doctype/accounting_dimension/test_accounting_dimension.py
#	erpnext/accounts/doctype/accounting_dimension_detail/accounting_dimension_detail.json
#	erpnext/accounts/doctype/accounting_dimension_filter/accounting_dimension_filter.json
#	erpnext/accounts/doctype/accounting_dimension_filter/accounting_dimension_filter.py
#	erpnext/accounts/doctype/accounting_dimension_filter/test_accounting_dimension_filter.py
#	erpnext/accounts/doctype/accounting_period/accounting_period.json
#	erpnext/accounts/doctype/accounting_period/test_accounting_period.py
#	erpnext/accounts/doctype/accounts_settings/accounts_settings.json
#	erpnext/accounts/doctype/accounts_settings/test_accounts_settings.py
#	erpnext/accounts/doctype/advance_payment_ledger_entry/test_advance_payment_ledger_entry.py
#	erpnext/accounts/doctype/advance_tax/advance_tax.json
#	erpnext/accounts/doctype/advance_taxes_and_charges/advance_taxes_and_charges.json
#	erpnext/accounts/doctype/advance_taxes_and_charges/advance_taxes_and_charges.py
#	erpnext/accounts/doctype/allowed_dimension/allowed_dimension.json
#	erpnext/accounts/doctype/allowed_to_transact_with/allowed_to_transact_with.json
#	erpnext/accounts/doctype/applicable_on_account/applicable_on_account.json
#	erpnext/accounts/doctype/bank/bank.json
#	erpnext/accounts/doctype/bank/test_bank.py
#	erpnext/accounts/doctype/bank_account/bank_account.json
#	erpnext/accounts/doctype/bank_account/bank_account.py
#	erpnext/accounts/doctype/bank_account/test_bank_account.py
#	erpnext/accounts/doctype/bank_account_subtype/bank_account_subtype.json
#	erpnext/accounts/doctype/bank_account_subtype/test_bank_account_subtype.py
#	erpnext/accounts/doctype/bank_account_type/bank_account_type.json
#	erpnext/accounts/doctype/bank_account_type/test_bank_account_type.py
#	erpnext/accounts/doctype/bank_clearance/bank_clearance.json
#	erpnext/accounts/doctype/bank_clearance/test_bank_clearance.py
#	erpnext/accounts/doctype/bank_clearance_detail/bank_clearance_detail.json
#	erpnext/accounts/doctype/bank_guarantee/bank_guarantee.json
#	erpnext/accounts/doctype/bank_guarantee/bank_guarantee.py
#	erpnext/accounts/doctype/bank_guarantee/test_bank_guarantee.py
#	erpnext/accounts/doctype/bank_reconciliation_tool/bank_reconciliation_tool.json
#	erpnext/accounts/doctype/bank_reconciliation_tool/test_bank_reconciliation_tool.py
#	erpnext/accounts/doctype/bank_statement_import/bank_statement_import.js
#	erpnext/accounts/doctype/bank_statement_import/bank_statement_import.json
#	erpnext/accounts/doctype/bank_statement_import/bank_statement_import.py
#	erpnext/accounts/doctype/bank_statement_import/test_bank_statement_import.py
#	erpnext/accounts/doctype/bank_transaction/auto_match_party.py
#	erpnext/accounts/doctype/bank_transaction/bank_transaction.py
#	erpnext/accounts/doctype/bank_transaction/test_auto_match_party.py
#	erpnext/accounts/doctype/bank_transaction/test_bank_transaction.py
#	erpnext/accounts/doctype/bank_transaction_mapping/bank_transaction_mapping.json
#	erpnext/accounts/doctype/bank_transaction_payments/bank_transaction_payments.json
#	erpnext/accounts/doctype/bisect_accounting_statements/bisect_accounting_statements.json
#	erpnext/accounts/doctype/bisect_accounting_statements/bisect_accounting_statements.py
#	erpnext/accounts/doctype/bisect_accounting_statements/test_bisect_accounting_statements.py
#	erpnext/accounts/doctype/bisect_nodes/bisect_nodes.json
#	erpnext/accounts/doctype/bisect_nodes/test_bisect_nodes.py
#	erpnext/accounts/doctype/budget/budget.json
#	erpnext/accounts/doctype/budget/test_budget.py
#	erpnext/accounts/doctype/budget_account/budget_account.json
#	erpnext/accounts/doctype/campaign_item/campaign_item.json
#	erpnext/accounts/doctype/cashier_closing/cashier_closing.json
#	erpnext/accounts/doctype/cashier_closing/test_cashier_closing.py
#	erpnext/accounts/doctype/cashier_closing_payments/cashier_closing_payments.json
#	erpnext/accounts/doctype/chart_of_accounts_importer/chart_of_accounts_importer.json
#	erpnext/accounts/doctype/chart_of_accounts_importer/test_chart_of_accounts_importer.py
#	erpnext/accounts/doctype/cheque_print_template/cheque_print_template.json
#	erpnext/accounts/doctype/cheque_print_template/test_cheque_print_template.py
#	erpnext/accounts/doctype/closed_document/closed_document.json
#	erpnext/accounts/doctype/cost_center/cost_center.json
#	erpnext/accounts/doctype/cost_center/test_cost_center.py
#	erpnext/accounts/doctype/cost_center_allocation/cost_center_allocation.json
#	erpnext/accounts/doctype/cost_center_allocation/test_cost_center_allocation.py
#	erpnext/accounts/doctype/cost_center_allocation_percentage/cost_center_allocation_percentage.json
#	erpnext/accounts/doctype/coupon_code/coupon_code.json
#	erpnext/accounts/doctype/coupon_code/coupon_code.py
#	erpnext/accounts/doctype/coupon_code/test_coupon_code.py
#	erpnext/accounts/doctype/currency_exchange_settings/currency_exchange_settings.json
#	erpnext/accounts/doctype/currency_exchange_settings/test_currency_exchange_settings.py
#	erpnext/accounts/doctype/currency_exchange_settings_details/currency_exchange_settings_details.json
#	erpnext/accounts/doctype/currency_exchange_settings_result/currency_exchange_settings_result.json
#	erpnext/accounts/doctype/customer_group_item/customer_group_item.json
#	erpnext/accounts/doctype/customer_item/customer_item.json
#	erpnext/accounts/doctype/discounted_invoice/discounted_invoice.json
#	erpnext/accounts/doctype/dunning/dunning.json
#	erpnext/accounts/doctype/dunning/dunning.py
#	erpnext/accounts/doctype/dunning/test_dunning.py
#	erpnext/accounts/doctype/dunning_letter_text/dunning_letter_text.json
#	erpnext/accounts/doctype/dunning_type/dunning_type.json
#	erpnext/accounts/doctype/dunning_type/test_dunning_type.py
#	erpnext/accounts/doctype/exchange_rate_revaluation/exchange_rate_revaluation.js
#	erpnext/accounts/doctype/exchange_rate_revaluation/exchange_rate_revaluation.json
#	erpnext/accounts/doctype/exchange_rate_revaluation/exchange_rate_revaluation.py
#	erpnext/accounts/doctype/exchange_rate_revaluation/test_exchange_rate_revaluation.py
#	erpnext/accounts/doctype/exchange_rate_revaluation_account/exchange_rate_revaluation_account.json
#	erpnext/accounts/doctype/finance_book/finance_book.json
#	erpnext/accounts/doctype/finance_book/test_finance_book.py
#	erpnext/accounts/doctype/fiscal_year/fiscal_year.json
#	erpnext/accounts/doctype/fiscal_year/test_fiscal_year.py
#	erpnext/accounts/doctype/fiscal_year_company/fiscal_year_company.json
#	erpnext/accounts/doctype/gl_entry/test_gl_entry.py
#	erpnext/accounts/doctype/invoice_discounting/invoice_discounting.js
#	erpnext/accounts/doctype/invoice_discounting/invoice_discounting.json
#	erpnext/accounts/doctype/invoice_discounting/test_invoice_discounting.py
#	erpnext/accounts/doctype/item_tax_template/item_tax_template.json
#	erpnext/accounts/doctype/item_tax_template/test_item_tax_template.py
#	erpnext/accounts/doctype/item_tax_template_detail/item_tax_template_detail.json
#	erpnext/accounts/doctype/journal_entry/journal_entry.js
#	erpnext/accounts/doctype/journal_entry/journal_entry.json
#	erpnext/accounts/doctype/journal_entry/journal_entry.py
#	erpnext/accounts/doctype/journal_entry/journal_entry_list.js
#	erpnext/accounts/doctype/journal_entry/test_journal_entry.py
#	erpnext/accounts/doctype/journal_entry_account/journal_entry_account.json
#	erpnext/accounts/doctype/journal_entry_template/journal_entry_template.json
#	erpnext/accounts/doctype/journal_entry_template/test_journal_entry_template.py
#	erpnext/accounts/doctype/journal_entry_template_account/journal_entry_template_account.json
#	erpnext/accounts/doctype/ledger_health/test_ledger_health.py
#	erpnext/accounts/doctype/ledger_health_monitor/test_ledger_health_monitor.py
#	erpnext/accounts/doctype/ledger_merge/ledger_merge.json
#	erpnext/accounts/doctype/ledger_merge/test_ledger_merge.py
#	erpnext/accounts/doctype/ledger_merge_accounts/ledger_merge_accounts.json
#	erpnext/accounts/doctype/loyalty_point_entry/loyalty_point_entry.json
#	erpnext/accounts/doctype/loyalty_point_entry/loyalty_point_entry.py
#	erpnext/accounts/doctype/loyalty_point_entry/test_loyalty_point_entry.py
#	erpnext/accounts/doctype/loyalty_point_entry_redemption/loyalty_point_entry_redemption.json
#	erpnext/accounts/doctype/loyalty_program/loyalty_program.json
#	erpnext/accounts/doctype/loyalty_program/loyalty_program.py
#	erpnext/accounts/doctype/loyalty_program/test_loyalty_program.py
#	erpnext/accounts/doctype/loyalty_program_collection/loyalty_program_collection.json
#	erpnext/accounts/doctype/mode_of_payment/mode_of_payment.json
#	erpnext/accounts/doctype/mode_of_payment/test_mode_of_payment.py
#	erpnext/accounts/doctype/mode_of_payment_account/mode_of_payment_account.json
#	erpnext/accounts/doctype/monthly_distribution/monthly_distribution.json
#	erpnext/accounts/doctype/monthly_distribution/test_monthly_distribution.py
#	erpnext/accounts/doctype/monthly_distribution_percentage/monthly_distribution_percentage.json
#	erpnext/accounts/doctype/opening_invoice_creation_tool/opening_invoice_creation_tool.json
#	erpnext/accounts/doctype/opening_invoice_creation_tool/opening_invoice_creation_tool.py
#	erpnext/accounts/doctype/opening_invoice_creation_tool/test_opening_invoice_creation_tool.py
#	erpnext/accounts/doctype/opening_invoice_creation_tool_item/opening_invoice_creation_tool_item.json
#	erpnext/accounts/doctype/overdue_payment/overdue_payment.json
#	erpnext/accounts/doctype/party_account/party_account.json
#	erpnext/accounts/doctype/party_link/party_link.json
#	erpnext/accounts/doctype/party_link/test_party_link.py
#	erpnext/accounts/doctype/payment_entry/payment_entry.js
#	erpnext/accounts/doctype/payment_entry/payment_entry.json
#	erpnext/accounts/doctype/payment_entry/payment_entry.py
#	erpnext/accounts/doctype/payment_entry/test_payment_entry.py
#	erpnext/accounts/doctype/payment_entry_deduction/payment_entry_deduction.json
#	erpnext/accounts/doctype/payment_entry_reference/payment_entry_reference.json
#	erpnext/accounts/doctype/payment_gateway_account/payment_gateway_account.json
#	erpnext/accounts/doctype/payment_gateway_account/test_payment_gateway_account.py
#	erpnext/accounts/doctype/payment_ledger_entry/payment_ledger_entry.json
#	erpnext/accounts/doctype/payment_ledger_entry/test_payment_ledger_entry.py
#	erpnext/accounts/doctype/payment_order/payment_order.json
#	erpnext/accounts/doctype/payment_order/test_payment_order.py
#	erpnext/accounts/doctype/payment_order_reference/payment_order_reference.json
#	erpnext/accounts/doctype/payment_reconciliation/payment_reconciliation.json
#	erpnext/accounts/doctype/payment_reconciliation/payment_reconciliation.py
#	erpnext/accounts/doctype/payment_reconciliation/test_payment_reconciliation.py
#	erpnext/accounts/doctype/payment_reconciliation_allocation/payment_reconciliation_allocation.json
#	erpnext/accounts/doctype/payment_reconciliation_invoice/payment_reconciliation_invoice.json
#	erpnext/accounts/doctype/payment_reconciliation_payment/payment_reconciliation_payment.json
#	erpnext/accounts/doctype/payment_request/payment_request.js
#	erpnext/accounts/doctype/payment_request/payment_request.json
#	erpnext/accounts/doctype/payment_request/payment_request.py
#	erpnext/accounts/doctype/payment_request/payment_request_list.js
#	erpnext/accounts/doctype/payment_request/test_payment_request.py
#	erpnext/accounts/doctype/payment_schedule/payment_schedule.json
#	erpnext/accounts/doctype/payment_term/payment_term.json
#	erpnext/accounts/doctype/payment_term/test_payment_term.py
#	erpnext/accounts/doctype/payment_terms_template/payment_terms_template.json
#	erpnext/accounts/doctype/payment_terms_template/test_payment_terms_template.py
#	erpnext/accounts/doctype/payment_terms_template_detail/payment_terms_template_detail.json
#	erpnext/accounts/doctype/period_closing_voucher/period_closing_voucher.json
#	erpnext/accounts/doctype/period_closing_voucher/test_period_closing_voucher.py
#	erpnext/accounts/doctype/pos_closing_entry/pos_closing_entry.json
#	erpnext/accounts/doctype/pos_closing_entry/test_pos_closing_entry.py
#	erpnext/accounts/doctype/pos_closing_entry_detail/pos_closing_entry_detail.json
#	erpnext/accounts/doctype/pos_closing_entry_taxes/pos_closing_entry_taxes.json
#	erpnext/accounts/doctype/pos_customer_group/pos_customer_group.json
#	erpnext/accounts/doctype/pos_field/pos_field.json
#	erpnext/accounts/doctype/pos_invoice/pos_invoice.js
#	erpnext/accounts/doctype/pos_invoice/pos_invoice.json
#	erpnext/accounts/doctype/pos_invoice/pos_invoice.py
#	erpnext/accounts/doctype/pos_invoice/test_pos_invoice.py
#	erpnext/accounts/doctype/pos_invoice_item/pos_invoice_item.json
#	erpnext/accounts/doctype/pos_invoice_item/pos_invoice_item.py
#	erpnext/accounts/doctype/pos_invoice_merge_log/pos_invoice_merge_log.json
#	erpnext/accounts/doctype/pos_invoice_merge_log/pos_invoice_merge_log.py
#	erpnext/accounts/doctype/pos_invoice_merge_log/test_pos_invoice_merge_log.py
#	erpnext/accounts/doctype/pos_invoice_reference/pos_invoice_reference.json
#	erpnext/accounts/doctype/pos_item_group/pos_item_group.json
#	erpnext/accounts/doctype/pos_opening_entry/pos_opening_entry.json
#	erpnext/accounts/doctype/pos_opening_entry/test_pos_opening_entry.py
#	erpnext/accounts/doctype/pos_opening_entry_detail/pos_opening_entry_detail.json
#	erpnext/accounts/doctype/pos_payment_method/pos_payment_method.json
#	erpnext/accounts/doctype/pos_profile/pos_profile.json
#	erpnext/accounts/doctype/pos_profile/pos_profile.py
#	erpnext/accounts/doctype/pos_profile/test_pos_profile.py
#	erpnext/accounts/doctype/pos_profile_user/pos_profile_user.json
#	erpnext/accounts/doctype/pos_profile_user/test_pos_profile_user.py
#	erpnext/accounts/doctype/pos_search_fields/pos_search_fields.json
#	erpnext/accounts/doctype/pos_settings/pos_settings.json
#	erpnext/accounts/doctype/pos_settings/test_pos_settings.py
#	erpnext/accounts/doctype/pricing_rule/pricing_rule.json
#	erpnext/accounts/doctype/pricing_rule/pricing_rule.py
#	erpnext/accounts/doctype/pricing_rule/test_pricing_rule.py
#	erpnext/accounts/doctype/pricing_rule/utils.py
#	erpnext/accounts/doctype/pricing_rule_brand/pricing_rule_brand.json
#	erpnext/accounts/doctype/pricing_rule_detail/pricing_rule_detail.json
#	erpnext/accounts/doctype/pricing_rule_item_code/pricing_rule_item_code.json
#	erpnext/accounts/doctype/pricing_rule_item_group/pricing_rule_item_group.json
#	erpnext/accounts/doctype/process_deferred_accounting/process_deferred_accounting.json
#	erpnext/accounts/doctype/process_deferred_accounting/test_process_deferred_accounting.py
#	erpnext/accounts/doctype/process_payment_reconciliation/process_payment_reconciliation.json
#	erpnext/accounts/doctype/process_payment_reconciliation/process_payment_reconciliation.py
#	erpnext/accounts/doctype/process_payment_reconciliation/test_process_payment_reconciliation.py
#	erpnext/accounts/doctype/process_payment_reconciliation_log/process_payment_reconciliation_log.json
#	erpnext/accounts/doctype/process_payment_reconciliation_log/test_process_payment_reconciliation_log.py
#	erpnext/accounts/doctype/process_payment_reconciliation_log_allocations/process_payment_reconciliation_log_allocations.json
#	erpnext/accounts/doctype/process_statement_of_accounts/process_statement_of_accounts.html
#	erpnext/accounts/doctype/process_statement_of_accounts/process_statement_of_accounts.json
#	erpnext/accounts/doctype/process_statement_of_accounts/process_statement_of_accounts_accounts_receivable.html
#	erpnext/accounts/doctype/process_statement_of_accounts/test_process_statement_of_accounts.py
#	erpnext/accounts/doctype/process_statement_of_accounts_customer/process_statement_of_accounts_customer.json
#	erpnext/accounts/doctype/process_subscription/process_subscription.json
#	erpnext/accounts/doctype/process_subscription/test_process_subscription.py
#	erpnext/accounts/doctype/promotional_scheme/promotional_scheme.json
#	erpnext/accounts/doctype/promotional_scheme/test_promotional_scheme.py
#	erpnext/accounts/doctype/promotional_scheme_price_discount/promotional_scheme_price_discount.json
#	erpnext/accounts/doctype/promotional_scheme_product_discount/promotional_scheme_product_discount.json
#	erpnext/accounts/doctype/psoa_cost_center/psoa_cost_center.json
#	erpnext/accounts/doctype/psoa_project/psoa_project.json
#	erpnext/accounts/doctype/purchase_invoice/purchase_invoice.js
#	erpnext/accounts/doctype/purchase_invoice/purchase_invoice.json
#	erpnext/accounts/doctype/purchase_invoice/purchase_invoice.py
#	erpnext/accounts/doctype/purchase_invoice/test_purchase_invoice.py
#	erpnext/accounts/doctype/purchase_invoice_advance/purchase_invoice_advance.json
#	erpnext/accounts/doctype/purchase_invoice_item/purchase_invoice_item.json
#	erpnext/accounts/doctype/purchase_invoice_item/purchase_invoice_item.py
#	erpnext/accounts/doctype/purchase_taxes_and_charges/purchase_taxes_and_charges.json
#	erpnext/accounts/doctype/purchase_taxes_and_charges/purchase_taxes_and_charges.py
#	erpnext/accounts/doctype/purchase_taxes_and_charges_template/purchase_taxes_and_charges_template.json
#	erpnext/accounts/doctype/purchase_taxes_and_charges_template/test_purchase_taxes_and_charges_template.py
#	erpnext/accounts/doctype/repost_accounting_ledger/repost_accounting_ledger.json
#	erpnext/accounts/doctype/repost_accounting_ledger/test_repost_accounting_ledger.py
#	erpnext/accounts/doctype/repost_accounting_ledger_items/repost_accounting_ledger_items.json
#	erpnext/accounts/doctype/repost_accounting_ledger_settings/repost_accounting_ledger_settings.json
#	erpnext/accounts/doctype/repost_accounting_ledger_settings/test_repost_accounting_ledger_settings.py
#	erpnext/accounts/doctype/repost_allowed_types/repost_allowed_types.json
#	erpnext/accounts/doctype/repost_payment_ledger/repost_payment_ledger.json
#	erpnext/accounts/doctype/repost_payment_ledger/test_repost_payment_ledger.py
#	erpnext/accounts/doctype/repost_payment_ledger_items/repost_payment_ledger_items.json
#	erpnext/accounts/doctype/sales_invoice/sales_invoice.js
#	erpnext/accounts/doctype/sales_invoice/sales_invoice.json
#	erpnext/accounts/doctype/sales_invoice/sales_invoice.py
#	erpnext/accounts/doctype/sales_invoice/sales_invoice_list.js
#	erpnext/accounts/doctype/sales_invoice/test_sales_invoice.py
#	erpnext/accounts/doctype/sales_invoice_advance/sales_invoice_advance.json
#	erpnext/accounts/doctype/sales_invoice_item/sales_invoice_item.json
#	erpnext/accounts/doctype/sales_invoice_item/sales_invoice_item.py
#	erpnext/accounts/doctype/sales_invoice_payment/sales_invoice_payment.json
#	erpnext/accounts/doctype/sales_invoice_timesheet/sales_invoice_timesheet.json
#	erpnext/accounts/doctype/sales_partner_item/sales_partner_item.json
#	erpnext/accounts/doctype/sales_taxes_and_charges/sales_taxes_and_charges.json
#	erpnext/accounts/doctype/sales_taxes_and_charges/sales_taxes_and_charges.py
#	erpnext/accounts/doctype/sales_taxes_and_charges_template/sales_taxes_and_charges_template.json
#	erpnext/accounts/doctype/sales_taxes_and_charges_template/sales_taxes_and_charges_template.py
#	erpnext/accounts/doctype/sales_taxes_and_charges_template/test_sales_taxes_and_charges_template.py
#	erpnext/accounts/doctype/share_balance/share_balance.json
#	erpnext/accounts/doctype/share_transfer/share_transfer.json
#	erpnext/accounts/doctype/share_transfer/test_share_transfer.py
#	erpnext/accounts/doctype/share_type/share_type.json
#	erpnext/accounts/doctype/share_type/test_share_type.py
#	erpnext/accounts/doctype/shareholder/shareholder.json
#	erpnext/accounts/doctype/shareholder/test_shareholder.py
#	erpnext/accounts/doctype/shipping_rule/shipping_rule.json
#	erpnext/accounts/doctype/shipping_rule/shipping_rule.py
#	erpnext/accounts/doctype/shipping_rule/test_shipping_rule.py
#	erpnext/accounts/doctype/shipping_rule_condition/shipping_rule_condition.json
#	erpnext/accounts/doctype/shipping_rule_country/shipping_rule_country.json
#	erpnext/accounts/doctype/south_africa_vat_account/south_africa_vat_account.json
#	erpnext/accounts/doctype/subscription/subscription.json
#	erpnext/accounts/doctype/subscription/subscription.py
#	erpnext/accounts/doctype/subscription/subscription_list.js
#	erpnext/accounts/doctype/subscription/test_subscription.py
#	erpnext/accounts/doctype/subscription_invoice/subscription_invoice.json
#	erpnext/accounts/doctype/subscription_invoice/test_subscription_invoice.py
#	erpnext/accounts/doctype/subscription_plan/subscription_plan.json
#	erpnext/accounts/doctype/subscription_plan/test_subscription_plan.py
#	erpnext/accounts/doctype/subscription_plan_detail/subscription_plan_detail.json
#	erpnext/accounts/doctype/subscription_settings/subscription_settings.json
#	erpnext/accounts/doctype/subscription_settings/test_subscription_settings.py
#	erpnext/accounts/doctype/supplier_group_item/supplier_group_item.json
#	erpnext/accounts/doctype/supplier_item/supplier_item.json
#	erpnext/accounts/doctype/tax_category/tax_category.json
#	erpnext/accounts/doctype/tax_category/test_tax_category.py
#	erpnext/accounts/doctype/tax_rule/tax_rule.json
#	erpnext/accounts/doctype/tax_rule/tax_rule.py
#	erpnext/accounts/doctype/tax_rule/test_tax_rule.py
#	erpnext/accounts/doctype/tax_withheld_vouchers/tax_withheld_vouchers.json
#	erpnext/accounts/doctype/tax_withholding_account/tax_withholding_account.json
#	erpnext/accounts/doctype/tax_withholding_category/tax_withholding_category.json
#	erpnext/accounts/doctype/tax_withholding_category/test_tax_withholding_category.py
#	erpnext/accounts/doctype/tax_withholding_rate/tax_withholding_rate.json
#	erpnext/accounts/doctype/territory_item/territory_item.json
#	erpnext/accounts/doctype/transaction_deletion_record_details/transaction_deletion_record_details.json
#	erpnext/accounts/doctype/unreconcile_payment/test_unreconcile_payment.py
#	erpnext/accounts/doctype/unreconcile_payment/unreconcile_payment.json
#	erpnext/accounts/doctype/unreconcile_payment/unreconcile_payment.py
#	erpnext/accounts/doctype/unreconcile_payment_entries/unreconcile_payment_entries.json
#	erpnext/accounts/general_ledger.py
#	erpnext/accounts/notification/notification_for_new_fiscal_year/notification_for_new_fiscal_year.json
#	erpnext/accounts/party.py
#	erpnext/accounts/print_format/dunning_letter/dunning_letter.json
#	erpnext/accounts/print_format/sales_invoice_return/sales_invoice_return.html
#	erpnext/accounts/report/account_balance/account_balance.js
#	erpnext/accounts/report/account_balance/account_balance.py
#	erpnext/accounts/report/account_balance/test_account_balance.py
#	erpnext/accounts/report/accounts_payable/accounts_payable.js
#	erpnext/accounts/report/accounts_payable/test_accounts_payable.py
#	erpnext/accounts/report/accounts_receivable/accounts_receivable.js
#	erpnext/accounts/report/accounts_receivable/accounts_receivable.py
#	erpnext/accounts/report/accounts_receivable/test_accounts_receivable.py
#	erpnext/accounts/report/accounts_receivable_summary/accounts_receivable_summary.py
#	erpnext/accounts/report/accounts_receivable_summary/test_accounts_receivable_summary.py
#	erpnext/accounts/report/balance_sheet/test_balance_sheet.py
#	erpnext/accounts/report/bank_reconciliation_statement/test_bank_reconciliation_statement.py
#	erpnext/accounts/report/cash_flow/cash_flow.py
#	erpnext/accounts/report/consolidated_financial_statement/consolidated_financial_statement.py
#	erpnext/accounts/report/customer_ledger_summary/customer_ledger_summary.py
#	erpnext/accounts/report/deferred_revenue_and_expense/deferred_revenue_and_expense.py
#	erpnext/accounts/report/deferred_revenue_and_expense/test_deferred_revenue_and_expense.py
#	erpnext/accounts/report/general_and_payment_ledger_comparison/test_general_and_payment_ledger_comparison.py
#	erpnext/accounts/report/general_ledger/general_ledger.html
#	erpnext/accounts/report/general_ledger/general_ledger.py
#	erpnext/accounts/report/general_ledger/test_general_ledger.py
#	erpnext/accounts/report/gross_and_net_profit_report/gross_and_net_profit_report.py
#	erpnext/accounts/report/gross_profit/test_gross_profit.py
#	erpnext/accounts/report/item_wise_purchase_register/test_item_wise_purchase_register.py
#	erpnext/accounts/report/item_wise_sales_register/item_wise_sales_register.js
#	erpnext/accounts/report/item_wise_sales_register/item_wise_sales_register.py
#	erpnext/accounts/report/item_wise_sales_register/test_item_wise_sales_register.py
#	erpnext/accounts/report/payment_ledger/test_payment_ledger.py
#	erpnext/accounts/report/profit_and_loss_statement/test_profit_and_loss_statement.py
#	erpnext/accounts/report/purchase_register/test_purchase_register.py
#	erpnext/accounts/report/sales_payment_summary/test_sales_payment_summary.py
#	erpnext/accounts/report/sales_register/test_sales_register.py
#	erpnext/accounts/report/tax_withholding_details/tax_withholding_details.py
#	erpnext/accounts/report/tax_withholding_details/test_tax_withholding_details.py
#	erpnext/accounts/report/trial_balance/test_trial_balance.py
#	erpnext/accounts/test/accounts_mixin.py
#	erpnext/accounts/test/test_reports.py
#	erpnext/accounts/test/test_utils.py
#	erpnext/accounts/test_party.py
#	erpnext/accounts/utils.py
#	erpnext/assets/doctype/asset/asset.js
#	erpnext/assets/doctype/asset/asset.json
#	erpnext/assets/doctype/asset/asset.py
#	erpnext/assets/doctype/asset/depreciation.py
#	erpnext/assets/doctype/asset/test_asset.py
#	erpnext/assets/doctype/asset_activity/asset_activity.json
#	erpnext/assets/doctype/asset_activity/test_asset_activity.py
#	erpnext/assets/doctype/asset_capitalization/asset_capitalization.js
#	erpnext/assets/doctype/asset_capitalization/asset_capitalization.json
#	erpnext/assets/doctype/asset_capitalization/asset_capitalization.py
#	erpnext/assets/doctype/asset_capitalization/test_asset_capitalization.py
#	erpnext/assets/doctype/asset_capitalization_asset_item/asset_capitalization_asset_item.json
#	erpnext/assets/doctype/asset_capitalization_service_item/asset_capitalization_service_item.json
#	erpnext/assets/doctype/asset_capitalization_stock_item/asset_capitalization_stock_item.json
#	erpnext/assets/doctype/asset_category/asset_category.json
#	erpnext/assets/doctype/asset_category/test_asset_category.py
#	erpnext/assets/doctype/asset_category_account/asset_category_account.json
#	erpnext/assets/doctype/asset_depreciation_schedule/asset_depreciation_schedule.json
#	erpnext/assets/doctype/asset_depreciation_schedule/test_asset_depreciation_schedule.py
#	erpnext/assets/doctype/asset_finance_book/asset_finance_book.json
#	erpnext/assets/doctype/asset_maintenance/asset_maintenance.json
#	erpnext/assets/doctype/asset_maintenance/asset_maintenance.py
#	erpnext/assets/doctype/asset_maintenance/test_asset_maintenance.py
#	erpnext/assets/doctype/asset_maintenance_log/asset_maintenance_log.json
#	erpnext/assets/doctype/asset_maintenance_log/test_asset_maintenance_log.py
#	erpnext/assets/doctype/asset_maintenance_task/asset_maintenance_task.json
#	erpnext/assets/doctype/asset_maintenance_team/asset_maintenance_team.json
#	erpnext/assets/doctype/asset_maintenance_team/test_asset_maintenance_team.py
#	erpnext/assets/doctype/asset_movement/asset_movement.json
#	erpnext/assets/doctype/asset_movement/asset_movement.py
#	erpnext/assets/doctype/asset_movement/test_asset_movement.py
#	erpnext/assets/doctype/asset_movement_item/asset_movement_item.json
#	erpnext/assets/doctype/asset_repair/asset_repair.js
#	erpnext/assets/doctype/asset_repair/asset_repair.json
#	erpnext/assets/doctype/asset_repair/asset_repair.py
#	erpnext/assets/doctype/asset_repair/test_asset_repair.py
#	erpnext/assets/doctype/asset_repair_consumed_item/asset_repair_consumed_item.json
#	erpnext/assets/doctype/asset_shift_allocation/asset_shift_allocation.json
#	erpnext/assets/doctype/asset_shift_allocation/test_asset_shift_allocation.py
#	erpnext/assets/doctype/asset_shift_factor/asset_shift_factor.json
#	erpnext/assets/doctype/asset_shift_factor/test_asset_shift_factor.py
#	erpnext/assets/doctype/asset_value_adjustment/asset_value_adjustment.js
#	erpnext/assets/doctype/asset_value_adjustment/asset_value_adjustment.json
#	erpnext/assets/doctype/asset_value_adjustment/asset_value_adjustment.py
#	erpnext/assets/doctype/asset_value_adjustment/test_asset_value_adjustment.py
#	erpnext/assets/doctype/depreciation_schedule/depreciation_schedule.json
#	erpnext/assets/doctype/linked_location/linked_location.json
#	erpnext/assets/doctype/location/location.json
#	erpnext/assets/doctype/location/test_location.py
#	erpnext/assets/doctype/maintenance_team_member/maintenance_team_member.json
#	erpnext/assets/doctype/maintenance_team_member/test_maintenance_team_member.py
#	erpnext/assets/report/fixed_asset_register/fixed_asset_register.py
#	erpnext/bulk_transaction/doctype/bulk_transaction_log/bulk_transaction_log.json
#	erpnext/bulk_transaction/doctype/bulk_transaction_log/test_bulk_transaction_log.py
#	erpnext/bulk_transaction/doctype/bulk_transaction_log_detail/bulk_transaction_log_detail.json
#	erpnext/bulk_transaction/doctype/bulk_transaction_log_detail/test_bulk_transaction_log_detail.py
#	erpnext/buying/doctype/buying_settings/buying_settings.json
#	erpnext/buying/doctype/buying_settings/test_buying_settings.py
#	erpnext/buying/doctype/purchase_order/purchase_order.js
#	erpnext/buying/doctype/purchase_order/purchase_order.json
#	erpnext/buying/doctype/purchase_order/purchase_order.py
#	erpnext/buying/doctype/purchase_order/purchase_order_list.js
#	erpnext/buying/doctype/purchase_order/test_purchase_order.py
#	erpnext/buying/doctype/purchase_order_item/purchase_order_item.json
#	erpnext/buying/doctype/purchase_order_item/purchase_order_item.py
#	erpnext/buying/doctype/purchase_order_item_supplied/purchase_order_item_supplied.json
#	erpnext/buying/doctype/purchase_receipt_item_supplied/purchase_receipt_item_supplied.json
#	erpnext/buying/doctype/request_for_quotation/request_for_quotation.js
#	erpnext/buying/doctype/request_for_quotation/request_for_quotation.json
#	erpnext/buying/doctype/request_for_quotation/request_for_quotation.py
#	erpnext/buying/doctype/request_for_quotation/test_request_for_quotation.py
#	erpnext/buying/doctype/request_for_quotation_item/request_for_quotation_item.json
#	erpnext/buying/doctype/request_for_quotation_supplier/request_for_quotation_supplier.json
#	erpnext/buying/doctype/supplier/supplier.json
#	erpnext/buying/doctype/supplier/supplier_dashboard.py
#	erpnext/buying/doctype/supplier/test_supplier.py
#	erpnext/buying/doctype/supplier_quotation/supplier_quotation.js
#	erpnext/buying/doctype/supplier_quotation/supplier_quotation.json
#	erpnext/buying/doctype/supplier_quotation/supplier_quotation.py
#	erpnext/buying/doctype/supplier_quotation/test_supplier_quotation.py
#	erpnext/buying/doctype/supplier_quotation_item/supplier_quotation_item.json
#	erpnext/buying/doctype/supplier_quotation_item/supplier_quotation_item.py
#	erpnext/buying/doctype/supplier_scorecard/supplier_scorecard.json
#	erpnext/buying/doctype/supplier_scorecard/supplier_scorecard.py
#	erpnext/buying/doctype/supplier_scorecard/test_supplier_scorecard.py
#	erpnext/buying/doctype/supplier_scorecard_criteria/supplier_scorecard_criteria.json
#	erpnext/buying/doctype/supplier_scorecard_criteria/test_supplier_scorecard_criteria.py
#	erpnext/buying/doctype/supplier_scorecard_period/supplier_scorecard_period.json
#	erpnext/buying/doctype/supplier_scorecard_period/test_supplier_scorecard_period.py
#	erpnext/buying/doctype/supplier_scorecard_scoring_criteria/supplier_scorecard_scoring_criteria.json
#	erpnext/buying/doctype/supplier_scorecard_scoring_standing/supplier_scorecard_scoring_standing.json
#	erpnext/buying/doctype/supplier_scorecard_scoring_variable/supplier_scorecard_scoring_variable.json
#	erpnext/buying/doctype/supplier_scorecard_standing/supplier_scorecard_standing.json
#	erpnext/buying/doctype/supplier_scorecard_standing/test_supplier_scorecard_standing.py
#	erpnext/buying/doctype/supplier_scorecard_variable/supplier_scorecard_variable.json
#	erpnext/buying/doctype/supplier_scorecard_variable/supplier_scorecard_variable.py
#	erpnext/buying/doctype/supplier_scorecard_variable/test_supplier_scorecard_variable.py
#	erpnext/buying/report/procurement_tracker/test_procurement_tracker.py
#	erpnext/buying/report/purchase_order_analysis/purchase_order_analysis.js
#	erpnext/buying/report/purchase_order_analysis/purchase_order_analysis.py
#	erpnext/buying/report/requested_items_to_order_and_receive/test_requested_items_to_order_and_receive.py
#	erpnext/buying/report/subcontracted_item_to_be_received/test_subcontracted_item_to_be_received.py
#	erpnext/buying/report/subcontracted_raw_materials_to_be_transferred/test_subcontracted_raw_materials_to_be_transferred.py
#	erpnext/buying/utils.py
#	erpnext/communication/doctype/communication_medium/communication_medium.json
#	erpnext/communication/doctype/communication_medium_timeslot/communication_medium_timeslot.json
#	erpnext/controllers/accounts_controller.py
#	erpnext/controllers/buying_controller.py
#	erpnext/controllers/queries.py
#	erpnext/controllers/sales_and_purchase_return.py
#	erpnext/controllers/selling_controller.py
#	erpnext/controllers/status_updater.py
#	erpnext/controllers/stock_controller.py
#	erpnext/controllers/subcontracting_controller.py
#	erpnext/controllers/taxes_and_totals.py
#	erpnext/controllers/tests/test_accounts_controller.py
#	erpnext/controllers/tests/test_item_variant.py
#	erpnext/controllers/tests/test_mapper.py
#	erpnext/controllers/tests/test_qty_based_taxes.py
#	erpnext/controllers/tests/test_queries.py
#	erpnext/controllers/tests/test_subcontracting_controller.py
#	erpnext/controllers/tests/test_transaction_base.py
#	erpnext/controllers/website_list_for_contact.py
#	erpnext/crm/dashboard_chart/lead_source/lead_source.json
#	erpnext/crm/dashboard_chart/opportunities_via_campaigns/opportunities_via_campaigns.json
#	erpnext/crm/doctype/appointment/appointment.json
#	erpnext/crm/doctype/appointment/appointment.py
#	erpnext/crm/doctype/appointment/test_appointment.py
#	erpnext/crm/doctype/appointment_booking_settings/appointment_booking_settings.json
#	erpnext/crm/doctype/appointment_booking_settings/test_appointment_booking_settings.py
#	erpnext/crm/doctype/appointment_booking_slots/appointment_booking_slots.json
#	erpnext/crm/doctype/availability_of_slots/availability_of_slots.json
#	erpnext/crm/doctype/campaign/campaign.js
#	erpnext/crm/doctype/campaign/campaign.json
#	erpnext/crm/doctype/campaign/campaign.py
#	erpnext/crm/doctype/campaign/test_campaign.py
#	erpnext/crm/doctype/campaign_email_schedule/campaign_email_schedule.json
#	erpnext/crm/doctype/competitor/competitor.json
#	erpnext/crm/doctype/competitor/test_competitor.py
#	erpnext/crm/doctype/competitor_detail/competitor_detail.json
#	erpnext/crm/doctype/contract/contract.json
#	erpnext/crm/doctype/contract/test_contract.py
#	erpnext/crm/doctype/contract_fulfilment_checklist/contract_fulfilment_checklist.json
#	erpnext/crm/doctype/contract_fulfilment_checklist/test_contract_fulfilment_checklist.py
#	erpnext/crm/doctype/contract_template/contract_template.json
#	erpnext/crm/doctype/contract_template/test_contract_template.py
#	erpnext/crm/doctype/contract_template_fulfilment_terms/contract_template_fulfilment_terms.json
#	erpnext/crm/doctype/crm_note/crm_note.json
#	erpnext/crm/doctype/crm_settings/crm_settings.json
#	erpnext/crm/doctype/crm_settings/test_crm_settings.py
#	erpnext/crm/doctype/email_campaign/email_campaign.json
#	erpnext/crm/doctype/email_campaign/email_campaign.py
#	erpnext/crm/doctype/email_campaign/test_email_campaign.py
#	erpnext/crm/doctype/lead/lead.json
#	erpnext/crm/doctype/lead/lead.py
#	erpnext/crm/doctype/lead/test_lead.py
#	erpnext/crm/doctype/lost_reason_detail/lost_reason_detail.json
#	erpnext/crm/doctype/market_segment/market_segment.json
#	erpnext/crm/doctype/market_segment/test_market_segment.py
#	erpnext/crm/doctype/opportunity/opportunity.js
#	erpnext/crm/doctype/opportunity/opportunity.json
#	erpnext/crm/doctype/opportunity/opportunity.py
#	erpnext/crm/doctype/opportunity/test_opportunity.py
#	erpnext/crm/doctype/opportunity_item/opportunity_item.json
#	erpnext/crm/doctype/opportunity_lost_reason/opportunity_lost_reason.json
#	erpnext/crm/doctype/opportunity_lost_reason_detail/opportunity_lost_reason_detail.json
#	erpnext/crm/doctype/opportunity_type/opportunity_type.json
#	erpnext/crm/doctype/opportunity_type/test_opportunity_type.py
#	erpnext/crm/doctype/prospect/prospect.json
#	erpnext/crm/doctype/prospect/test_prospect.py
#	erpnext/crm/doctype/prospect_lead/prospect_lead.json
#	erpnext/crm/doctype/prospect_opportunity/prospect_opportunity.json
#	erpnext/crm/doctype/sales_stage/sales_stage.json
#	erpnext/crm/doctype/sales_stage/test_sales_stage.py
#	erpnext/crm/doctype/utils.py
#	erpnext/crm/report/campaign_efficiency/campaign_efficiency.py
#	erpnext/crm/report/opportunity_summary_by_sales_stage/opportunity_summary_by_sales_stage.js
#	erpnext/crm/report/opportunity_summary_by_sales_stage/opportunity_summary_by_sales_stage.py
#	erpnext/crm/report/opportunity_summary_by_sales_stage/test_opportunity_summary_by_sales_stage.py
#	erpnext/crm/report/sales_pipeline_analytics/sales_pipeline_analytics.js
#	erpnext/crm/report/sales_pipeline_analytics/sales_pipeline_analytics.py
#	erpnext/crm/report/sales_pipeline_analytics/test_sales_pipeline_analytics.py
#	erpnext/crm/workspace/crm/crm.json
#	erpnext/edi/doctype/code_list/code_list_import.py
#	erpnext/erpnext_integrations/doctype/plaid_settings/plaid_settings.json
#	erpnext/erpnext_integrations/doctype/plaid_settings/test_plaid_settings.py
#	erpnext/erpnext_integrations/doctype/quickbooks_migrator/quickbooks_migrator.json
#	erpnext/erpnext_integrations/doctype/quickbooks_migrator/test_quickbooks_migrator.py
#	erpnext/erpnext_integrations/doctype/tally_migration/tally_migration.json
#	erpnext/erpnext_integrations/doctype/tally_migration/tally_migration.py
#	erpnext/erpnext_integrations/doctype/tally_migration/test_tally_migration.py
#	erpnext/erpnext_integrations/utils.py
#	erpnext/hooks.py
#	erpnext/maintenance/doctype/maintenance_schedule/maintenance_schedule.json
#	erpnext/maintenance/doctype/maintenance_schedule/maintenance_schedule.py
#	erpnext/maintenance/doctype/maintenance_schedule/test_maintenance_schedule.py
#	erpnext/maintenance/doctype/maintenance_schedule_detail/maintenance_schedule_detail.json
#	erpnext/maintenance/doctype/maintenance_schedule_item/maintenance_schedule_item.json
#	erpnext/maintenance/doctype/maintenance_visit/maintenance_visit.json
#	erpnext/maintenance/doctype/maintenance_visit/maintenance_visit.py
#	erpnext/maintenance/doctype/maintenance_visit/test_maintenance_visit.py
#	erpnext/maintenance/doctype/maintenance_visit_purpose/maintenance_visit_purpose.json
#	erpnext/manufacturing/doctype/blanket_order/blanket_order.json
#	erpnext/manufacturing/doctype/blanket_order/blanket_order.py
#	erpnext/manufacturing/doctype/blanket_order/test_blanket_order.py
#	erpnext/manufacturing/doctype/blanket_order_item/blanket_order_item.json
#	erpnext/manufacturing/doctype/bom/bom.js
#	erpnext/manufacturing/doctype/bom/bom.json
#	erpnext/manufacturing/doctype/bom/bom.py
#	erpnext/manufacturing/doctype/bom/test_bom.py
#	erpnext/manufacturing/doctype/bom_creator/bom_creator.js
#	erpnext/manufacturing/doctype/bom_creator/bom_creator.json
#	erpnext/manufacturing/doctype/bom_creator/bom_creator.py
#	erpnext/manufacturing/doctype/bom_creator/test_bom_creator.py
#	erpnext/manufacturing/doctype/bom_creator_item/bom_creator_item.json
#	erpnext/manufacturing/doctype/bom_creator_item/bom_creator_item.py
#	erpnext/manufacturing/doctype/bom_explosion_item/bom_explosion_item.json
#	erpnext/manufacturing/doctype/bom_item/bom_item.json
#	erpnext/manufacturing/doctype/bom_item/bom_item.py
#	erpnext/manufacturing/doctype/bom_operation/bom_operation.json
#	erpnext/manufacturing/doctype/bom_operation/bom_operation.py
#	erpnext/manufacturing/doctype/bom_scrap_item/bom_scrap_item.json
#	erpnext/manufacturing/doctype/bom_update_batch/bom_update_batch.json
#	erpnext/manufacturing/doctype/bom_update_log/bom_update_log.json
#	erpnext/manufacturing/doctype/bom_update_log/bom_update_log.py
#	erpnext/manufacturing/doctype/bom_update_log/test_bom_update_log.py
#	erpnext/manufacturing/doctype/bom_update_tool/bom_update_tool.json
#	erpnext/manufacturing/doctype/bom_update_tool/test_bom_update_tool.py
#	erpnext/manufacturing/doctype/bom_website_item/bom_website_item.json
#	erpnext/manufacturing/doctype/bom_website_operation/bom_website_operation.json
#	erpnext/manufacturing/doctype/downtime_entry/downtime_entry.json
#	erpnext/manufacturing/doctype/downtime_entry/test_downtime_entry.py
#	erpnext/manufacturing/doctype/job_card/job_card.js
#	erpnext/manufacturing/doctype/job_card/job_card.json
#	erpnext/manufacturing/doctype/job_card/job_card.py
#	erpnext/manufacturing/doctype/job_card/job_card_dashboard.py
#	erpnext/manufacturing/doctype/job_card/test_job_card.py
#	erpnext/manufacturing/doctype/job_card_item/job_card_item.json
#	erpnext/manufacturing/doctype/job_card_operation/job_card_operation.json
#	erpnext/manufacturing/doctype/job_card_scheduled_time/job_card_scheduled_time.json
#	erpnext/manufacturing/doctype/job_card_scrap_item/job_card_scrap_item.json
#	erpnext/manufacturing/doctype/job_card_time_log/job_card_time_log.json
#	erpnext/manufacturing/doctype/manufacturing_settings/manufacturing_settings.json
#	erpnext/manufacturing/doctype/manufacturing_settings/test_manufacturing_settings.py
#	erpnext/manufacturing/doctype/material_request_plan_item/material_request_plan_item.json
#	erpnext/manufacturing/doctype/material_request_plan_item/test_material_request_plan_item.py
#	erpnext/manufacturing/doctype/operation/operation.json
#	erpnext/manufacturing/doctype/operation/test_operation.py
#	erpnext/manufacturing/doctype/plant_floor/plant_floor.js
#	erpnext/manufacturing/doctype/plant_floor/plant_floor.json
#	erpnext/manufacturing/doctype/plant_floor/test_plant_floor.py
#	erpnext/manufacturing/doctype/production_plan/production_plan.json
#	erpnext/manufacturing/doctype/production_plan/production_plan.py
#	erpnext/manufacturing/doctype/production_plan/test_production_plan.py
#	erpnext/manufacturing/doctype/production_plan_item/production_plan_item.json
#	erpnext/manufacturing/doctype/production_plan_item_reference/production_plan_item_reference.json
#	erpnext/manufacturing/doctype/production_plan_material_request/production_plan_material_request.json
#	erpnext/manufacturing/doctype/production_plan_material_request_warehouse/production_plan_material_request_warehouse.json
#	erpnext/manufacturing/doctype/production_plan_material_request_warehouse/test_production_plan_material_request_warehouse.py
#	erpnext/manufacturing/doctype/production_plan_sales_order/production_plan_sales_order.json
#	erpnext/manufacturing/doctype/production_plan_sub_assembly_item/production_plan_sub_assembly_item.json
#	erpnext/manufacturing/doctype/routing/routing.json
#	erpnext/manufacturing/doctype/routing/routing.py
#	erpnext/manufacturing/doctype/routing/test_routing.py
#	erpnext/manufacturing/doctype/sub_operation/sub_operation.json
#	erpnext/manufacturing/doctype/sub_operation/test_sub_operation.py
#	erpnext/manufacturing/doctype/work_order/test_work_order.py
#	erpnext/manufacturing/doctype/work_order/work_order.js
#	erpnext/manufacturing/doctype/work_order/work_order.json
#	erpnext/manufacturing/doctype/work_order/work_order.py
#	erpnext/manufacturing/doctype/work_order_item/work_order_item.json
#	erpnext/manufacturing/doctype/work_order_operation/work_order_operation.json
#	erpnext/manufacturing/doctype/work_order_operation/work_order_operation.py
#	erpnext/manufacturing/doctype/workstation/test_workstation.py
#	erpnext/manufacturing/doctype/workstation/workstation.js
#	erpnext/manufacturing/doctype/workstation/workstation.json
#	erpnext/manufacturing/doctype/workstation/workstation.py
#	erpnext/manufacturing/doctype/workstation/workstation_job_card.html
#	erpnext/manufacturing/doctype/workstation_type/test_workstation_type.py
#	erpnext/manufacturing/doctype/workstation_type/workstation_type.json
#	erpnext/manufacturing/doctype/workstation_working_hour/workstation_working_hour.json
#	erpnext/manufacturing/notification/material_request_receipt_notification/material_request_receipt_notification.json
#	erpnext/manufacturing/report/bom_stock_calculated/test_bom_stock_calculated.py
#	erpnext/manufacturing/report/bom_stock_report/test_bom_stock_report.py
#	erpnext/manufacturing/report/production_analytics/production_analytics.py
#	erpnext/manufacturing/report/quality_inspection_summary/quality_inspection_summary.py
#	erpnext/manufacturing/report/test_reports.py
#	erpnext/patches.txt
#	erpnext/patches/v11_0/create_department_records_for_each_company.py
#	erpnext/patches/v11_0/make_location_from_warehouse.py
#	erpnext/patches/v11_0/rebuild_tree_for_company.py
#	erpnext/patches/v11_0/rename_supplier_type_to_supplier_group.py
#	erpnext/patches/v11_0/update_department_lft_rgt.py
#	erpnext/patches/v13_0/modify_invalid_gain_loss_gl_entries.py
#	erpnext/patches/v14_0/migrate_crm_settings.py
#	erpnext/patches/v14_0/migrate_gl_to_payment_ledger.py
#	erpnext/patches/v15_0/create_advance_payment_ledger_records.py
#	erpnext/patches/v15_0/create_asset_depreciation_schedules_from_assets.py
#	erpnext/patches/v15_0/update_gpa_and_ndb_for_assdeprsch.py
#	erpnext/portal/doctype/website_attribute/website_attribute.json
#	erpnext/portal/doctype/website_filter_field/website_filter_field.json
#	erpnext/portal/utils.py
#	erpnext/projects/doctype/activity_cost/activity_cost.json
#	erpnext/projects/doctype/activity_cost/test_activity_cost.py
#	erpnext/projects/doctype/activity_type/activity_type.json
#	erpnext/projects/doctype/activity_type/test_activity_type.py
#	erpnext/projects/doctype/dependent_task/dependent_task.json
#	erpnext/projects/doctype/project/project.json
#	erpnext/projects/doctype/project/project.py
#	erpnext/projects/doctype/project/test_project.py
#	erpnext/projects/doctype/project_template/project_template.json
#	erpnext/projects/doctype/project_template/test_project_template.py
#	erpnext/projects/doctype/project_template_task/project_template_task.json
#	erpnext/projects/doctype/project_type/project_type.json
#	erpnext/projects/doctype/project_type/test_project_type.py
#	erpnext/projects/doctype/project_update/project_update.json
#	erpnext/projects/doctype/project_update/test_project_update.py
#	erpnext/projects/doctype/project_user/project_user.json
#	erpnext/projects/doctype/projects_settings/projects_settings.json
#	erpnext/projects/doctype/projects_settings/test_projects_settings.py
#	erpnext/projects/doctype/task/task.js
#	erpnext/projects/doctype/task/task.json
#	erpnext/projects/doctype/task/task.py
#	erpnext/projects/doctype/task/test_task.py
#	erpnext/projects/doctype/task_depends_on/task_depends_on.json
#	erpnext/projects/doctype/task_type/task_type.json
#	erpnext/projects/doctype/task_type/test_task_type.py
#	erpnext/projects/doctype/timesheet/test_timesheet.py
#	erpnext/projects/doctype/timesheet/timesheet.js
#	erpnext/projects/doctype/timesheet/timesheet.json
#	erpnext/projects/doctype/timesheet/timesheet.py
#	erpnext/projects/doctype/timesheet_detail/timesheet_detail.json
#	erpnext/projects/doctype/timesheet_detail/timesheet_detail.py
#	erpnext/projects/report/delayed_tasks_summary/delayed_tasks_summary.py
#	erpnext/projects/report/delayed_tasks_summary/test_delayed_tasks_summary.py
#	erpnext/projects/workspace/projects/projects.json
#	erpnext/public/images/erpnext-logo.svg
#	erpnext/public/js/bom_configurator/bom_configurator.bundle.js
#	erpnext/public/js/bulk_transaction_processing.js
#	erpnext/public/js/controllers/accounts.js
#	erpnext/public/js/controllers/taxes_and_totals.js
#	erpnext/public/js/controllers/transaction.js
#	erpnext/public/js/financial_statements.js
#	erpnext/public/js/plant_floor_visual/visual_plant.js
#	erpnext/public/js/projects/timer.js
#	erpnext/public/js/setup_wizard.js
#	erpnext/public/js/templates/visual_plant_floor_template.html
#	erpnext/public/js/utils.js
#	erpnext/public/js/utils/dimension_tree_filter.js
#	erpnext/public/scss/erpnext.scss
#	erpnext/quality_management/doctype/non_conformance/non_conformance.json
#	erpnext/quality_management/doctype/non_conformance/test_non_conformance.py
#	erpnext/quality_management/doctype/quality_action/quality_action.json
#	erpnext/quality_management/doctype/quality_action/test_quality_action.py
#	erpnext/quality_management/doctype/quality_action_resolution/quality_action_resolution.json
#	erpnext/quality_management/doctype/quality_feedback/quality_feedback.json
#	erpnext/quality_management/doctype/quality_feedback/test_quality_feedback.py
#	erpnext/quality_management/doctype/quality_feedback_parameter/quality_feedback_parameter.json
#	erpnext/quality_management/doctype/quality_feedback_template/quality_feedback_template.json
#	erpnext/quality_management/doctype/quality_feedback_template/test_quality_feedback_template.py
#	erpnext/quality_management/doctype/quality_feedback_template_parameter/quality_feedback_template_parameter.json
#	erpnext/quality_management/doctype/quality_goal/quality_goal.json
#	erpnext/quality_management/doctype/quality_goal/test_quality_goal.py
#	erpnext/quality_management/doctype/quality_goal_objective/quality_goal_objective.json
#	erpnext/quality_management/doctype/quality_meeting/quality_meeting.json
#	erpnext/quality_management/doctype/quality_meeting/test_quality_meeting.py
#	erpnext/quality_management/doctype/quality_meeting_agenda/quality_meeting_agenda.json
#	erpnext/quality_management/doctype/quality_meeting_agenda/test_quality_meeting_agenda.py
#	erpnext/quality_management/doctype/quality_meeting_minutes/quality_meeting_minutes.json
#	erpnext/quality_management/doctype/quality_procedure/quality_procedure.json
#	erpnext/quality_management/doctype/quality_procedure/test_quality_procedure.py
#	erpnext/quality_management/doctype/quality_procedure_process/quality_procedure_process.json
#	erpnext/quality_management/doctype/quality_review/quality_review.json
#	erpnext/quality_management/doctype/quality_review/test_quality_review.py
#	erpnext/quality_management/doctype/quality_review_objective/quality_review_objective.json
#	erpnext/regional/doctype/import_supplier_invoice/import_supplier_invoice.json
#	erpnext/regional/doctype/import_supplier_invoice/import_supplier_invoice.py
#	erpnext/regional/doctype/import_supplier_invoice/test_import_supplier_invoice.py
#	erpnext/regional/doctype/lower_deduction_certificate/lower_deduction_certificate.json
#	erpnext/regional/doctype/lower_deduction_certificate/lower_deduction_certificate.py
#	erpnext/regional/doctype/lower_deduction_certificate/test_lower_deduction_certificate.py
#	erpnext/regional/doctype/south_africa_vat_settings/south_africa_vat_settings.json
#	erpnext/regional/doctype/south_africa_vat_settings/test_south_africa_vat_settings.py
#	erpnext/regional/doctype/uae_vat_account/uae_vat_account.json
#	erpnext/regional/doctype/uae_vat_settings/test_uae_vat_settings.py
#	erpnext/regional/doctype/uae_vat_settings/uae_vat_settings.json
#	erpnext/regional/italy/utils.py
#	erpnext/regional/print_format/detailed_tax_invoice/detailed_tax_invoice.json
#	erpnext/regional/print_format/tax_invoice/tax_invoice.json
#	erpnext/regional/report/uae_vat_201/test_uae_vat_201.py
#	erpnext/regional/report/vat_audit_report/vat_audit_report.py
#	erpnext/regional/united_states/test_united_states.py
#	erpnext/selling/doctype/customer/customer.js
#	erpnext/selling/doctype/customer/customer.json
#	erpnext/selling/doctype/customer/customer.py
#	erpnext/selling/doctype/customer/customer_dashboard.py
#	erpnext/selling/doctype/customer/test_customer.py
#	erpnext/selling/doctype/customer_credit_limit/customer_credit_limit.json
#	erpnext/selling/doctype/industry_type/industry_type.json
#	erpnext/selling/doctype/industry_type/test_industry_type.py
#	erpnext/selling/doctype/installation_note/installation_note.json
#	erpnext/selling/doctype/installation_note/installation_note.py
#	erpnext/selling/doctype/installation_note/test_installation_note.py
#	erpnext/selling/doctype/installation_note_item/installation_note_item.json
#	erpnext/selling/doctype/party_specific_item/party_specific_item.json
#	erpnext/selling/doctype/party_specific_item/test_party_specific_item.py
#	erpnext/selling/doctype/product_bundle/product_bundle.js
#	erpnext/selling/doctype/product_bundle/product_bundle.json
#	erpnext/selling/doctype/product_bundle/test_product_bundle.py
#	erpnext/selling/doctype/product_bundle_item/product_bundle_item.json
#	erpnext/selling/doctype/quotation/quotation.json
#	erpnext/selling/doctype/quotation/quotation.py
#	erpnext/selling/doctype/quotation/test_quotation.py
#	erpnext/selling/doctype/quotation_item/quotation_item.json
#	erpnext/selling/doctype/quotation_item/quotation_item.py
#	erpnext/selling/doctype/sales_order/sales_order.js
#	erpnext/selling/doctype/sales_order/sales_order.json
#	erpnext/selling/doctype/sales_order/sales_order.py
#	erpnext/selling/doctype/sales_order/sales_order_list.js
#	erpnext/selling/doctype/sales_order/test_sales_order.py
#	erpnext/selling/doctype/sales_order_item/sales_order_item.json
#	erpnext/selling/doctype/sales_order_item/sales_order_item.py
#	erpnext/selling/doctype/sales_partner_type/sales_partner_type.json
#	erpnext/selling/doctype/sales_partner_type/test_sales_partner_type.py
#	erpnext/selling/doctype/sales_team/sales_team.json
#	erpnext/selling/doctype/selling_settings/selling_settings.json
#	erpnext/selling/doctype/selling_settings/selling_settings.py
#	erpnext/selling/doctype/selling_settings/test_selling_settings.py
#	erpnext/selling/doctype/sms_center/sms_center.json
#	erpnext/selling/page/point_of_sale/pos_controller.js
#	erpnext/selling/page/point_of_sale/pos_item_cart.js
#	erpnext/selling/page/point_of_sale/pos_past_order_summary.js
#	erpnext/selling/page/point_of_sale/pos_payment.js
#	erpnext/selling/page/sales_funnel/sales_funnel.js
#	erpnext/selling/page/sales_funnel/sales_funnel.py
#	erpnext/selling/report/address_and_contacts/address_and_contacts.py
#	erpnext/selling/report/customer_credit_balance/customer_credit_balance.py
#	erpnext/selling/report/payment_terms_status_for_sales_order/payment_terms_status_for_sales_order.py
#	erpnext/selling/report/payment_terms_status_for_sales_order/test_payment_terms_status_for_sales_order.py
#	erpnext/selling/report/pending_so_items_for_purchase_request/test_pending_so_items_for_purchase_request.py
#	erpnext/selling/report/sales_analytics/sales_analytics.js
#	erpnext/selling/report/sales_analytics/sales_analytics.py
#	erpnext/selling/report/sales_analytics/test_analytics.py
#	erpnext/selling/report/sales_order_analysis/sales_order_analysis.js
#	erpnext/selling/report/sales_order_analysis/sales_order_analysis.py
#	erpnext/selling/report/sales_order_analysis/test_sales_order_analysis.py
#	erpnext/selling/report/sales_partner_target_variance_based_on_item_group/test_sales_partner_target_variance_based_on_item_group.py
#	erpnext/selling/report/sales_person_target_variance_based_on_item_group/test_sales_person_target_variance_based_on_item_group.py
#	erpnext/selling/workspace/selling/selling.json
#	erpnext/setup/doctype/authorization_control/authorization_control.json
#	erpnext/setup/doctype/authorization_control/authorization_control.py
#	erpnext/setup/doctype/authorization_rule/authorization_rule.json
#	erpnext/setup/doctype/authorization_rule/test_authorization_rule.py
#	erpnext/setup/doctype/branch/branch.json
#	erpnext/setup/doctype/branch/test_branch.py
#	erpnext/setup/doctype/brand/brand.json
#	erpnext/setup/doctype/brand/test_brand.py
#	erpnext/setup/doctype/company/company.js
#	erpnext/setup/doctype/company/company.json
#	erpnext/setup/doctype/company/company.py
#	erpnext/setup/doctype/company/test_company.py
#	erpnext/setup/doctype/currency_exchange/test_currency_exchange.py
#	erpnext/setup/doctype/customer_group/customer_group.json
#	erpnext/setup/doctype/customer_group/customer_group.py
#	erpnext/setup/doctype/customer_group/test_customer_group.py
#	erpnext/setup/doctype/department/department.json
#	erpnext/setup/doctype/department/department.py
#	erpnext/setup/doctype/department/test_department.py
#	erpnext/setup/doctype/designation/designation.json
#	erpnext/setup/doctype/designation/test_designation.py
#	erpnext/setup/doctype/driver/driver.json
#	erpnext/setup/doctype/driver/driver.py
#	erpnext/setup/doctype/driver/test_driver.py
#	erpnext/setup/doctype/driving_license_category/driving_license_category.json
#	erpnext/setup/doctype/email_digest/email_digest.json
#	erpnext/setup/doctype/email_digest/email_digest.py
#	erpnext/setup/doctype/email_digest/test_email_digest.py
#	erpnext/setup/doctype/email_digest_recipient/email_digest_recipient.json
#	erpnext/setup/doctype/employee/employee.json
#	erpnext/setup/doctype/employee/employee.py
#	erpnext/setup/doctype/employee/test_employee.py
#	erpnext/setup/doctype/employee_education/employee_education.json
#	erpnext/setup/doctype/employee_external_work_history/employee_external_work_history.json
#	erpnext/setup/doctype/employee_group/employee_group.json
#	erpnext/setup/doctype/employee_group/test_employee_group.py
#	erpnext/setup/doctype/employee_group_table/employee_group_table.json
#	erpnext/setup/doctype/employee_internal_work_history/employee_internal_work_history.json
#	erpnext/setup/doctype/global_defaults/global_defaults.json
#	erpnext/setup/doctype/global_defaults/test_global_defaults.py
#	erpnext/setup/doctype/holiday/holiday.json
#	erpnext/setup/doctype/holiday_list/holiday_list.json
#	erpnext/setup/doctype/holiday_list/test_holiday_list.py
#	erpnext/setup/doctype/incoterm/test_incoterm.py
#	erpnext/setup/doctype/item_group/item_group.json
#	erpnext/setup/doctype/item_group/test_item_group.py
#	erpnext/setup/doctype/party_type/party_type.json
#	erpnext/setup/doctype/party_type/test_party_type.py
#	erpnext/setup/doctype/print_heading/print_heading.json
#	erpnext/setup/doctype/print_heading/test_print_heading.py
#	erpnext/setup/doctype/quotation_lost_reason/quotation_lost_reason.json
#	erpnext/setup/doctype/quotation_lost_reason/test_quotation_lost_reason.py
#	erpnext/setup/doctype/quotation_lost_reason_detail/quotation_lost_reason_detail.json
#	erpnext/setup/doctype/sales_partner/sales_partner.json
#	erpnext/setup/doctype/sales_partner/sales_partner.py
#	erpnext/setup/doctype/sales_partner/test_sales_partner.py
#	erpnext/setup/doctype/sales_person/sales_person.json
#	erpnext/setup/doctype/sales_person/test_sales_person.py
#	erpnext/setup/doctype/supplier_group/supplier_group.js
#	erpnext/setup/doctype/supplier_group/supplier_group.json
#	erpnext/setup/doctype/supplier_group/test_supplier_group.py
#	erpnext/setup/doctype/target_detail/target_detail.json
#	erpnext/setup/doctype/terms_and_conditions/terms_and_conditions.json
#	erpnext/setup/doctype/terms_and_conditions/test_terms_and_conditions.py
#	erpnext/setup/doctype/territory/territory.json
#	erpnext/setup/doctype/territory/test_territory.py
#	erpnext/setup/doctype/transaction_deletion_record/test_transaction_deletion_record.py
#	erpnext/setup/doctype/transaction_deletion_record/transaction_deletion_record.json
#	erpnext/setup/doctype/transaction_deletion_record_item/transaction_deletion_record_item.json
#	erpnext/setup/doctype/uom/test_uom.py
#	erpnext/setup/doctype/uom/uom.json
#	erpnext/setup/doctype/uom/uom.py
#	erpnext/setup/doctype/uom_conversion_factor/test_uom_conversion_factor.py
#	erpnext/setup/doctype/uom_conversion_factor/uom_conversion_factor.json
#	erpnext/setup/doctype/vehicle/test_vehicle.py
#	erpnext/setup/doctype/vehicle/vehicle.json
#	erpnext/setup/doctype/website_item_group/website_item_group.json
#	erpnext/setup/install.py
#	erpnext/setup/setup_wizard/data/country_wise_tax.json
#	erpnext/setup/setup_wizard/data/uom_data.json
#	erpnext/setup/setup_wizard/operations/defaults_setup.py
#	erpnext/setup/setup_wizard/operations/install_fixtures.py
#	erpnext/setup/utils.py
#	erpnext/startup/boot.py
#	erpnext/startup/leaderboard.py
#	erpnext/stock/__init__.py
#	erpnext/stock/deprecated_serial_batch.py
#	erpnext/stock/doctype/batch/batch.json
#	erpnext/stock/doctype/batch/test_batch.py
#	erpnext/stock/doctype/bin/bin.json
#	erpnext/stock/doctype/bin/test_bin.py
#	erpnext/stock/doctype/closing_stock_balance/closing_stock_balance.json
#	erpnext/stock/doctype/closing_stock_balance/test_closing_stock_balance.py
#	erpnext/stock/doctype/customs_tariff_number/customs_tariff_number.json
#	erpnext/stock/doctype/customs_tariff_number/test_customs_tariff_number.py
#	erpnext/stock/doctype/delivery_note/delivery_note.json
#	erpnext/stock/doctype/delivery_note/delivery_note.py
#	erpnext/stock/doctype/delivery_note/delivery_note_list.js
#	erpnext/stock/doctype/delivery_note/test_delivery_note.py
#	erpnext/stock/doctype/delivery_note_item/delivery_note_item.json
#	erpnext/stock/doctype/delivery_note_item/delivery_note_item.py
#	erpnext/stock/doctype/delivery_settings/delivery_settings.json
#	erpnext/stock/doctype/delivery_settings/test_delivery_settings.py
#	erpnext/stock/doctype/delivery_stop/delivery_stop.json
#	erpnext/stock/doctype/delivery_trip/delivery_trip.js
#	erpnext/stock/doctype/delivery_trip/delivery_trip.json
#	erpnext/stock/doctype/delivery_trip/delivery_trip.py
#	erpnext/stock/doctype/delivery_trip/test_delivery_trip.py
#	erpnext/stock/doctype/inventory_dimension/inventory_dimension.json
#	erpnext/stock/doctype/inventory_dimension/inventory_dimension.py
#	erpnext/stock/doctype/inventory_dimension/test_inventory_dimension.py
#	erpnext/stock/doctype/item/item.js
#	erpnext/stock/doctype/item/item.json
#	erpnext/stock/doctype/item/item.py
#	erpnext/stock/doctype/item/item_list.js
#	erpnext/stock/doctype/item/test_item.py
#	erpnext/stock/doctype/item_alternative/item_alternative.json
#	erpnext/stock/doctype/item_alternative/test_item_alternative.py
#	erpnext/stock/doctype/item_attribute/item_attribute.json
#	erpnext/stock/doctype/item_attribute/item_attribute.py
#	erpnext/stock/doctype/item_attribute/test_item_attribute.py
#	erpnext/stock/doctype/item_attribute_value/item_attribute_value.json
#	erpnext/stock/doctype/item_barcode/item_barcode.json
#	erpnext/stock/doctype/item_customer_detail/item_customer_detail.json
#	erpnext/stock/doctype/item_default/item_default.json
#	erpnext/stock/doctype/item_manufacturer/item_manufacturer.json
#	erpnext/stock/doctype/item_manufacturer/test_item_manufacturer.py
#	erpnext/stock/doctype/item_price/item_price.json
#	erpnext/stock/doctype/item_price/item_price.py
#	erpnext/stock/doctype/item_price/test_item_price.py
#	erpnext/stock/doctype/item_quality_inspection_parameter/item_quality_inspection_parameter.json
#	erpnext/stock/doctype/item_reorder/item_reorder.json
#	erpnext/stock/doctype/item_supplier/item_supplier.json
#	erpnext/stock/doctype/item_tax/item_tax.json
#	erpnext/stock/doctype/item_variant/item_variant.json
#	erpnext/stock/doctype/item_variant_attribute/item_variant_attribute.json
#	erpnext/stock/doctype/item_variant_settings/item_variant_settings.js
#	erpnext/stock/doctype/item_variant_settings/item_variant_settings.json
#	erpnext/stock/doctype/item_variant_settings/test_item_variant_settings.py
#	erpnext/stock/doctype/item_website_specification/item_website_specification.json
#	erpnext/stock/doctype/landed_cost_item/landed_cost_item.json
#	erpnext/stock/doctype/landed_cost_purchase_receipt/landed_cost_purchase_receipt.json
#	erpnext/stock/doctype/landed_cost_taxes_and_charges/landed_cost_taxes_and_charges.json
#	erpnext/stock/doctype/landed_cost_voucher/landed_cost_voucher.json
#	erpnext/stock/doctype/landed_cost_voucher/landed_cost_voucher.py
#	erpnext/stock/doctype/landed_cost_voucher/test_landed_cost_voucher.py
#	erpnext/stock/doctype/manufacturer/manufacturer.json
#	erpnext/stock/doctype/manufacturer/test_manufacturer.py
#	erpnext/stock/doctype/material_request/material_request.js
#	erpnext/stock/doctype/material_request/material_request.json
#	erpnext/stock/doctype/material_request/material_request.py
#	erpnext/stock/doctype/material_request/material_request_list.js
#	erpnext/stock/doctype/material_request/test_material_request.py
#	erpnext/stock/doctype/material_request_item/material_request_item.json
#	erpnext/stock/doctype/packed_item/packed_item.json
#	erpnext/stock/doctype/packed_item/packed_item.py
#	erpnext/stock/doctype/packed_item/test_packed_item.py
#	erpnext/stock/doctype/packing_slip/packing_slip.json
#	erpnext/stock/doctype/packing_slip/test_packing_slip.py
#	erpnext/stock/doctype/packing_slip_item/packing_slip_item.json
#	erpnext/stock/doctype/pick_list/pick_list.json
#	erpnext/stock/doctype/pick_list/pick_list_list.js
#	erpnext/stock/doctype/pick_list/test_pick_list.py
#	erpnext/stock/doctype/pick_list_item/pick_list_item.json
#	erpnext/stock/doctype/price_list/price_list.json
#	erpnext/stock/doctype/price_list/price_list.py
#	erpnext/stock/doctype/price_list/test_price_list.py
#	erpnext/stock/doctype/price_list_country/price_list_country.json
#	erpnext/stock/doctype/purchase_receipt/purchase_receipt.json
#	erpnext/stock/doctype/purchase_receipt/purchase_receipt.py
#	erpnext/stock/doctype/purchase_receipt/test_purchase_receipt.py
#	erpnext/stock/doctype/purchase_receipt_item/purchase_receipt_item.json
#	erpnext/stock/doctype/purchase_receipt_item/purchase_receipt_item.py
#	erpnext/stock/doctype/putaway_rule/putaway_rule.json
#	erpnext/stock/doctype/putaway_rule/putaway_rule.py
#	erpnext/stock/doctype/putaway_rule/test_putaway_rule.py
#	erpnext/stock/doctype/quality_inspection/quality_inspection.json
#	erpnext/stock/doctype/quality_inspection/test_quality_inspection.py
#	erpnext/stock/doctype/quality_inspection_parameter/quality_inspection_parameter.json
#	erpnext/stock/doctype/quality_inspection_parameter/test_quality_inspection_parameter.py
#	erpnext/stock/doctype/quality_inspection_parameter_group/quality_inspection_parameter_group.json
#	erpnext/stock/doctype/quality_inspection_parameter_group/test_quality_inspection_parameter_group.py
#	erpnext/stock/doctype/quality_inspection_reading/quality_inspection_reading.json
#	erpnext/stock/doctype/quality_inspection_template/quality_inspection_template.json
#	erpnext/stock/doctype/quality_inspection_template/test_quality_inspection_template.py
#	erpnext/stock/doctype/quick_stock_balance/quick_stock_balance.json
#	erpnext/stock/doctype/repost_item_valuation/repost_item_valuation.json
#	erpnext/stock/doctype/repost_item_valuation/repost_item_valuation.py
#	erpnext/stock/doctype/repost_item_valuation/test_repost_item_valuation.py
#	erpnext/stock/doctype/serial_and_batch_bundle/serial_and_batch_bundle.json
#	erpnext/stock/doctype/serial_and_batch_bundle/serial_and_batch_bundle.py
#	erpnext/stock/doctype/serial_and_batch_bundle/test_serial_and_batch_bundle.py
#	erpnext/stock/doctype/serial_and_batch_entry/serial_and_batch_entry.json
#	erpnext/stock/doctype/serial_no/serial_no.json
#	erpnext/stock/doctype/serial_no/test_serial_no.py
#	erpnext/stock/doctype/shipment/shipment.json
#	erpnext/stock/doctype/shipment/test_shipment.py
#	erpnext/stock/doctype/shipment_delivery_note/shipment_delivery_note.json
#	erpnext/stock/doctype/shipment_parcel/shipment_parcel.json
#	erpnext/stock/doctype/shipment_parcel_template/shipment_parcel_template.json
#	erpnext/stock/doctype/shipment_parcel_template/test_shipment_parcel_template.py
#	erpnext/stock/doctype/stock_entry/stock_entry.json
#	erpnext/stock/doctype/stock_entry/stock_entry.py
#	erpnext/stock/doctype/stock_entry/test_stock_entry.py
#	erpnext/stock/doctype/stock_entry_detail/stock_entry_detail.json
#	erpnext/stock/doctype/stock_entry_type/stock_entry_type.json
#	erpnext/stock/doctype/stock_entry_type/stock_entry_type.py
#	erpnext/stock/doctype/stock_entry_type/test_stock_entry_type.py
#	erpnext/stock/doctype/stock_ledger_entry/stock_ledger_entry.json
#	erpnext/stock/doctype/stock_ledger_entry/stock_ledger_entry.py
#	erpnext/stock/doctype/stock_ledger_entry/test_stock_ledger_entry.py
#	erpnext/stock/doctype/stock_reconciliation/stock_reconciliation.json
#	erpnext/stock/doctype/stock_reconciliation/stock_reconciliation.py
#	erpnext/stock/doctype/stock_reconciliation/test_stock_reconciliation.py
#	erpnext/stock/doctype/stock_reconciliation_item/stock_reconciliation_item.json
#	erpnext/stock/doctype/stock_reposting_settings/stock_reposting_settings.json
#	erpnext/stock/doctype/stock_reposting_settings/test_stock_reposting_settings.py
#	erpnext/stock/doctype/stock_reservation_entry/stock_reservation_entry.json
#	erpnext/stock/doctype/stock_reservation_entry/stock_reservation_entry.py
#	erpnext/stock/doctype/stock_reservation_entry/test_stock_reservation_entry.py
#	erpnext/stock/doctype/stock_settings/stock_settings.json
#	erpnext/stock/doctype/stock_settings/stock_settings.py
#	erpnext/stock/doctype/stock_settings/test_stock_settings.py
#	erpnext/stock/doctype/uom_category/test_uom_category.py
#	erpnext/stock/doctype/uom_category/uom_category.json
#	erpnext/stock/doctype/uom_conversion_detail/uom_conversion_detail.json
#	erpnext/stock/doctype/variant_field/test_variant_field.py
#	erpnext/stock/doctype/variant_field/variant_field.json
#	erpnext/stock/doctype/warehouse/test_warehouse.py
#	erpnext/stock/doctype/warehouse/warehouse.json
#	erpnext/stock/doctype/warehouse/warehouse.py
#	erpnext/stock/doctype/warehouse_type/test_warehouse_type.py
#	erpnext/stock/doctype/warehouse_type/warehouse_type.json
#	erpnext/stock/get_item_details.py
#	erpnext/stock/page/warehouse_capacity_summary/warehouse_capacity_summary.js
#	erpnext/stock/page/warehouse_capacity_summary/warehouse_capacity_summary_header.html
#	erpnext/stock/print_format/purchase_receipt_serial_and_batch_bundle_print/purchase_receipt_serial_and_batch_bundle_print.json
#	erpnext/stock/reorder_item.py
#	erpnext/stock/report/batch_wise_balance_history/batch_wise_balance_history.py
#	erpnext/stock/report/item_shortage_report/test_item_shortage_report.py
#	erpnext/stock/report/reserved_stock/test_reserved_stock.py
#	erpnext/stock/report/stock_ageing/test_stock_ageing.py
#	erpnext/stock/report/stock_analytics/test_stock_analytics.py
#	erpnext/stock/report/stock_balance/test_stock_balance.py
#	erpnext/stock/report/stock_ledger/test_stock_ledger_report.py
#	erpnext/stock/report/test_reports.py
#	erpnext/stock/serial_batch_bundle.py
#	erpnext/stock/stock_balance.py
#	erpnext/stock/stock_ledger.py
#	erpnext/stock/tests/test_get_item_details.py
#	erpnext/stock/tests/test_utils.py
#	erpnext/stock/tests/test_valuation.py
#	erpnext/stock/utils.py
#	erpnext/subcontracting/doctype/subcontracting_bom/subcontracting_bom.json
#	erpnext/subcontracting/doctype/subcontracting_bom/subcontracting_bom.py
#	erpnext/subcontracting/doctype/subcontracting_bom/test_subcontracting_bom.py
#	erpnext/subcontracting/doctype/subcontracting_order/subcontracting_order.json
#	erpnext/subcontracting/doctype/subcontracting_order/subcontracting_order.py
#	erpnext/subcontracting/doctype/subcontracting_order/subcontracting_order_list.js
#	erpnext/subcontracting/doctype/subcontracting_order/test_subcontracting_order.py
#	erpnext/subcontracting/doctype/subcontracting_order_item/subcontracting_order_item.json
#	erpnext/subcontracting/doctype/subcontracting_order_item/subcontracting_order_item.py
#	erpnext/subcontracting/doctype/subcontracting_order_service_item/subcontracting_order_service_item.json
#	erpnext/subcontracting/doctype/subcontracting_order_supplied_item/subcontracting_order_supplied_item.json
#	erpnext/subcontracting/doctype/subcontracting_receipt/subcontracting_receipt.json
#	erpnext/subcontracting/doctype/subcontracting_receipt/subcontracting_receipt.py
#	erpnext/subcontracting/doctype/subcontracting_receipt/subcontracting_receipt_list.js
#	erpnext/subcontracting/doctype/subcontracting_receipt/test_subcontracting_receipt.py
#	erpnext/subcontracting/doctype/subcontracting_receipt_item/subcontracting_receipt_item.json
#	erpnext/subcontracting/doctype/subcontracting_receipt_item/subcontracting_receipt_item.py
#	erpnext/subcontracting/doctype/subcontracting_receipt_supplied_item/subcontracting_receipt_supplied_item.json
#	erpnext/support/doctype/issue/issue.json
#	erpnext/support/doctype/issue/issue.py
#	erpnext/support/doctype/issue/test_issue.py
#	erpnext/support/doctype/issue_priority/issue_priority.json
#	erpnext/support/doctype/issue_priority/test_issue_priority.py
#	erpnext/support/doctype/issue_type/issue_type.json
#	erpnext/support/doctype/issue_type/test_issue_type.py
#	erpnext/support/doctype/pause_sla_on_status/pause_sla_on_status.json
#	erpnext/support/doctype/service_day/service_day.json
#	erpnext/support/doctype/service_level_agreement/service_level_agreement.json
#	erpnext/support/doctype/service_level_agreement/service_level_agreement.py
#	erpnext/support/doctype/service_level_agreement/test_service_level_agreement.py
#	erpnext/support/doctype/service_level_priority/service_level_priority.json
#	erpnext/support/doctype/sla_fulfilled_on_status/sla_fulfilled_on_status.json
#	erpnext/support/doctype/support_search_source/support_search_source.json
#	erpnext/support/doctype/support_settings/support_settings.json
#	erpnext/support/doctype/support_settings/test_support_settings.py
#	erpnext/support/doctype/warranty_claim/test_warranty_claim.py
#	erpnext/support/doctype/warranty_claim/warranty_claim.json
#	erpnext/support/doctype/warranty_claim/warranty_claim.py
#	erpnext/support/report/issue_analytics/test_issue_analytics.py
#	erpnext/telephony/doctype/call_log/test_call_log.py
#	erpnext/telephony/doctype/incoming_call_handling_schedule/incoming_call_handling_schedule.json
#	erpnext/telephony/doctype/incoming_call_settings/incoming_call_settings.json
#	erpnext/telephony/doctype/incoming_call_settings/test_incoming_call_settings.py
#	erpnext/telephony/doctype/telephony_call_type/telephony_call_type.json
#	erpnext/telephony/doctype/telephony_call_type/test_telephony_call_type.py
#	erpnext/telephony/doctype/voice_call_settings/test_voice_call_settings.py
#	erpnext/telephony/doctype/voice_call_settings/voice_call_settings.json
#	erpnext/templates/form_grid/includes/visible_cols.html
#	erpnext/templates/generators/sales_partner.html
#	erpnext/templates/includes/issue_row.html
#	erpnext/templates/includes/macros.html
#	erpnext/templates/includes/projects/project_row.html
#	erpnext/templates/includes/transaction_row.html
#	erpnext/templates/pages/order.html
#	erpnext/templates/pages/order.py
#	erpnext/templates/pages/timelog_info.html
#	erpnext/templates/print_formats/includes/total.html
#	erpnext/tests/test_activation.py
#	erpnext/tests/test_init.py
#	erpnext/tests/test_notifications.py
#	erpnext/tests/test_perf.py
#	erpnext/tests/test_point_of_sale.py
#	erpnext/tests/test_regional.py
#	erpnext/tests/test_webform.py
#	erpnext/tests/test_zform_loads.py
#	erpnext/utilities/activation.py
#	erpnext/utilities/bulk_transaction.py
#	erpnext/utilities/doctype/portal_user/portal_user.json
#	erpnext/utilities/doctype/rename_tool/rename_tool.json
#	erpnext/utilities/doctype/video/test_video.py
#	erpnext/utilities/doctype/video/video.json
#	erpnext/utilities/doctype/video/video.py
#	erpnext/utilities/doctype/video_settings/test_video_settings.py
#	erpnext/utilities/doctype/video_settings/video_settings.json
#	erpnext/www/support/index.py
#	pyproject.toml
2024-12-11 09:26:02 +00:00

1311 lines
42 KiB
Python

# Copyright (c) 2022, Frappe Technologies Pvt. Ltd. and contributors
# For license information, please see license.txt
import copy
import json
from collections import defaultdict
import frappe
from frappe import _
from frappe.model.mapper import get_mapped_doc
from frappe.utils import cint, flt, get_link_to_form
from erpnext.controllers.stock_controller import StockController
from erpnext.stock.doctype.serial_and_batch_bundle.serial_and_batch_bundle import (
get_voucher_wise_serial_batch_from_bundle,
)
from erpnext.stock.doctype.serial_no.serial_no import get_serial_nos
from erpnext.stock.serial_batch_bundle import SerialBatchCreation, get_serial_nos_from_bundle
from erpnext.stock.utils import get_incoming_rate
class SubcontractingController(StockController):
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
if self.get("is_old_subcontracting_flow"):
self.subcontract_data = frappe._dict(
{
"order_doctype": "Purchase Order",
"order_field": "purchase_order",
"rm_detail_field": "po_detail",
"receipt_supplied_items_field": "Purchase Receipt Item Supplied",
"order_supplied_items_field": "Purchase Order Item Supplied",
}
)
else:
self.subcontract_data = frappe._dict(
{
"order_doctype": "Subcontracting Order",
"order_field": "subcontracting_order",
"rm_detail_field": "sco_rm_detail",
"receipt_supplied_items_field": "Subcontracting Receipt Supplied Item",
"order_supplied_items_field": "Subcontracting Order Supplied Item",
}
)
def before_validate(self):
if self.doctype in ["Subcontracting Order", "Subcontracting Receipt"]:
self.remove_empty_rows()
self.set_items_conversion_factor()
def validate(self):
if self.doctype in ["Subcontracting Order", "Subcontracting Receipt"]:
self.validate_items()
self.create_raw_materials_supplied()
else:
super().validate()
def validate_rejected_warehouse(self):
for item in self.get("items"):
if flt(item.rejected_qty) and not item.rejected_warehouse:
if self.rejected_warehouse:
item.rejected_warehouse = self.rejected_warehouse
else:
frappe.throw(
_("Row #{0}: Rejected Warehouse is mandatory for the rejected Item {1}").format(
item.idx, item.item_code
)
)
if item.get("rejected_warehouse") and (item.get("rejected_warehouse") == item.get("warehouse")):
frappe.throw(
_("Row #{0}: Accepted Warehouse and Rejected Warehouse cannot be same").format(item.idx)
)
def remove_empty_rows(self):
for key in ["service_items", "items", "supplied_items"]:
if self.get(key):
idx = 1
for item in self.get(key)[:]:
if not (item.get("item_code") or item.get("main_item_code")):
self.get(key).remove(item)
else:
item.idx = idx
idx += 1
def set_items_conversion_factor(self):
for item in self.get("items"):
if not item.conversion_factor:
item.conversion_factor = 1
def validate_items(self):
for item in self.items:
is_stock_item, is_sub_contracted_item = frappe.get_value(
"Item", item.item_code, ["is_stock_item", "is_sub_contracted_item"]
)
if not is_stock_item:
frappe.throw(_("Row {0}: Item {1} must be a stock item.").format(item.idx, item.item_name))
if not item.get("is_scrap_item"):
if not is_sub_contracted_item:
frappe.throw(
_("Row {0}: Item {1} must be a subcontracted item.").format(item.idx, item.item_name)
)
if item.bom:
is_active, bom_item = frappe.get_value("BOM", item.bom, ["is_active", "item"])
if not is_active:
frappe.throw(
_("Row {0}: Please select an active BOM for Item {1}.").format(
item.idx, item.item_name
)
)
if bom_item != item.item_code:
frappe.throw(
_("Row {0}: Please select an valid BOM for Item {1}.").format(
item.idx, item.item_name
)
)
else:
frappe.throw(
_("Row {0}: Please select a BOM for Item {1}.").format(item.idx, item.item_name)
)
else:
item.bom = None
def __get_data_before_save(self):
item_dict = {}
if (
self.doctype in ["Purchase Receipt", "Purchase Invoice", "Subcontracting Receipt"]
and self._doc_before_save
):
for row in self._doc_before_save.get("items"):
item_dict[row.name] = (row.item_code, row.qty)
return item_dict
def __identify_change_in_item_table(self):
self.__changed_name = []
self.__reference_name = []
if self.doctype in ["Purchase Order", "Subcontracting Order"] or self.is_new():
self.set(self.raw_material_table, [])
return
item_dict = self.__get_data_before_save()
if not item_dict:
return True
for row in self.items:
self.__reference_name.append(row.name)
if (row.name not in item_dict) or (row.item_code, row.qty) != item_dict[row.name]:
self.__changed_name.append(row.name)
if item_dict.get(row.name):
del item_dict[row.name]
self.__changed_name.extend(item_dict.keys())
def __get_backflush_based_on(self):
self.backflush_based_on = frappe.db.get_single_value(
"Buying Settings", "backflush_raw_materials_of_subcontract_based_on"
)
def initialized_fields(self):
self.available_materials = frappe._dict()
self.__transferred_items = frappe._dict()
self.alternative_item_details = frappe._dict()
self.__get_backflush_based_on()
def __get_subcontract_orders(self):
self.subcontract_orders = []
if self.doctype in ["Purchase Order", "Subcontracting Order"]:
return
self.subcontract_orders = [
item.get(self.subcontract_data.order_field)
for item in self.items
if item.get(self.subcontract_data.order_field)
]
def __get_pending_qty_to_receive(self):
"""Get qty to be received against the subcontract order."""
self.qty_to_be_received = defaultdict(float)
if (
self.doctype != self.subcontract_data.order_doctype
and self.backflush_based_on != "BOM"
and self.subcontract_orders
):
for row in frappe.get_all(
f"{self.subcontract_data.order_doctype} Item",
fields=["item_code", "(qty - received_qty) as qty", "parent", "name"],
filters={"docstatus": 1, "parent": ("in", self.subcontract_orders)},
):
self.qty_to_be_received[(row.item_code, row.parent)] += row.qty
def __get_transferred_items(self):
se = frappe.qb.DocType("Stock Entry")
se_detail = frappe.qb.DocType("Stock Entry Detail")
query = (
frappe.qb.from_(se)
.inner_join(se_detail)
.on(se.name == se_detail.parent)
.select(
se[self.subcontract_data.order_field],
se.name.as_("voucher_no"),
se_detail.item_code.as_("rm_item_code"),
se_detail.item_name,
se_detail.description,
(
frappe.qb.terms.Case()
.when(((se.purpose == "Material Transfer") & (se.is_return == 1)), -1 * se_detail.qty)
.else_(se_detail.qty)
).as_("qty"),
se_detail.basic_rate.as_("rate"),
se_detail.amount,
se_detail.serial_no,
se_detail.serial_and_batch_bundle,
se_detail.uom,
se_detail.subcontracted_item.as_("main_item_code"),
se_detail.stock_uom,
se_detail.batch_no,
se_detail.conversion_factor,
se_detail.s_warehouse,
se_detail.t_warehouse,
se_detail.item_group,
se_detail[self.subcontract_data.rm_detail_field],
)
.where(
(se.docstatus == 1)
& (se[self.subcontract_data.order_field].isin(self.subcontract_orders))
& (
(se.purpose == "Send to Subcontractor")
| ((se.purpose == "Material Transfer") & (se.is_return == 1))
)
)
)
if self.backflush_based_on == "BOM":
query = query.select(se_detail.original_item)
return query.run(as_dict=True)
def __set_alternative_item_details(self, row):
if row.get("original_item"):
self.alternative_item_details[row.get("original_item")] = row
def __get_received_items(self, doctype):
fields = []
for field in ["name", self.subcontract_data.order_field, "parent"]:
fields.append(f"`tab{doctype} Item`.`{field}`")
filters = [
[doctype, "docstatus", "=", 1],
[f"{doctype} Item", self.subcontract_data.order_field, "in", self.subcontract_orders],
]
if doctype == "Purchase Invoice":
filters.append(["Purchase Invoice", "update_stock", "=", 1])
return frappe.get_all(f"{doctype}", fields=fields, filters=filters)
def __get_consumed_items(self, doctype, receipt_items):
fields = [
"serial_no",
"rm_item_code",
"reference_name",
"batch_no",
"consumed_qty",
"main_item_code",
"parent as voucher_no",
]
if self.subcontract_data.receipt_supplied_items_field != "Purchase Receipt Item Supplied":
fields.append("serial_and_batch_bundle")
return frappe.get_all(
self.subcontract_data.receipt_supplied_items_field,
fields=fields,
filters={"docstatus": 1, "reference_name": ("in", list(receipt_items)), "parenttype": doctype},
)
def __update_consumed_materials(self, doctype, return_consumed_items=False):
"""Deduct the consumed materials from the available materials."""
receipt_items = self.__get_received_items(doctype)
if not receipt_items:
return ([], {}) if return_consumed_items else None
receipt_items = {item.name: item.get(self.subcontract_data.order_field) for item in receipt_items}
consumed_materials = self.__get_consumed_items(doctype, receipt_items.keys())
if return_consumed_items:
return (consumed_materials, receipt_items)
if not consumed_materials:
return
voucher_nos = [d.voucher_no for d in consumed_materials if d.voucher_no]
voucher_bundle_data = (
get_voucher_wise_serial_batch_from_bundle(
voucher_no=voucher_nos,
is_outward=1,
get_subcontracted_item=("Subcontracting Receipt Supplied Item", "main_item_code"),
)
if voucher_nos
else {}
)
for row in consumed_materials:
key = (row.rm_item_code, row.main_item_code, receipt_items.get(row.reference_name))
if not self.available_materials.get(key):
continue
self.available_materials[key]["qty"] -= row.consumed_qty
bundle_key = (row.rm_item_code, row.main_item_code, self.supplier_warehouse, row.voucher_no)
consumed_bundles = voucher_bundle_data.get(bundle_key, frappe._dict())
if consumed_bundles.serial_nos:
self.available_materials[key]["serial_no"] = list(
set(self.available_materials[key]["serial_no"]) - set(consumed_bundles.serial_nos)
)
if consumed_bundles.batch_nos:
for batch_no, qty in consumed_bundles.batch_nos.items():
if qty:
# Conumed qty is negative therefore added it instead of subtracting
self.available_materials[key]["batch_no"][batch_no] += qty
consumed_bundles.batch_nos[batch_no] += abs(qty)
# Will be deprecated in v16
if row.serial_no and not consumed_bundles.serial_nos:
<<<<<<< HEAD
=======
from erpnext.deprecation_dumpster import deprecation_warning
deprecation_warning("unknown", "v16", "No instructions.")
>>>>>>> 329d14957b (fix: validate negative qty)
self.available_materials[key]["serial_no"] = list(
set(self.available_materials[key]["serial_no"]) - set(get_serial_nos(row.serial_no))
)
# Will be deprecated in v16
if row.batch_no and not consumed_bundles.batch_nos:
<<<<<<< HEAD
=======
from erpnext.deprecation_dumpster import deprecation_warning
deprecation_warning("unknown", "v16", "No instructions.")
>>>>>>> 329d14957b (fix: validate negative qty)
self.available_materials[key]["batch_no"][row.batch_no] -= row.consumed_qty
def get_available_materials(self):
"""Get the available raw materials which has been transferred to the supplier.
available_materials = {
(item_code, subcontracted_item, subcontract_order): {
'qty': 1, 'serial_no': [ABC], 'batch_no': {'batch1': 1}, 'data': item_details
}
}
"""
if not self.subcontract_orders:
return
transferred_items = self.__get_transferred_items()
voucher_nos = [row.voucher_no for row in transferred_items]
voucher_bundle_data = (
get_voucher_wise_serial_batch_from_bundle(
voucher_no=voucher_nos,
is_outward=0,
get_subcontracted_item=("Stock Entry Detail", "subcontracted_item"),
)
if voucher_nos
else {}
)
for row in transferred_items:
key = (row.rm_item_code, row.main_item_code, row.get(self.subcontract_data.order_field))
if key not in self.available_materials:
self.available_materials.setdefault(
key,
frappe._dict(
{
"qty": 0,
"serial_no": [],
"batch_no": defaultdict(float),
"item_details": row,
f"{self.subcontract_data.rm_detail_field}s": [],
}
),
)
details = self.available_materials[key]
details.qty += row.qty
details[f"{self.subcontract_data.rm_detail_field}s"].append(
row.get(self.subcontract_data.rm_detail_field)
)
if row.serial_no:
details.serial_no.extend(get_serial_nos(row.serial_no))
elif row.batch_no:
details.batch_no[row.batch_no] += row.qty
elif voucher_bundle_data:
bundle_key = (row.rm_item_code, row.main_item_code, row.t_warehouse, row.voucher_no)
bundle_data = voucher_bundle_data.get(bundle_key, frappe._dict())
if bundle_data.serial_nos:
details.serial_no.extend(bundle_data.serial_nos)
bundle_data.serial_nos = []
if bundle_data.batch_nos:
for batch_no, qty in bundle_data.batch_nos.items():
if qty < 0:
qty = abs(qty)
if qty > 0:
details.batch_no[batch_no] += qty
bundle_data.batch_nos[batch_no] -= qty
self.__set_alternative_item_details(row)
self.__transferred_items = copy.deepcopy(self.available_materials)
if self.get("is_old_subcontracting_flow"):
for doctype in ["Purchase Receipt", "Purchase Invoice"]:
self.__update_consumed_materials(doctype)
else:
self.__update_consumed_materials("Subcontracting Receipt")
def __remove_changed_rows(self):
if not self.__changed_name:
return
i = 1
self.set(self.raw_material_table, [])
for item in self._doc_before_save.supplied_items:
if item.reference_name in self.__changed_name:
self.__remove_serial_and_batch_bundle(item)
continue
if item.reference_name not in self.__reference_name:
continue
item.idx = i
self.append("supplied_items", item)
i += 1
def __remove_serial_and_batch_bundle(self, item):
if item.serial_and_batch_bundle:
frappe.delete_doc("Serial and Batch Bundle", item.serial_and_batch_bundle, force=True)
def __get_materials_from_bom(self, item_code, bom_no, exploded_item=0):
doctype = "BOM Item" if not exploded_item else "BOM Explosion Item"
fields = [f"`tab{doctype}`.`stock_qty` / `tabBOM`.`quantity` as qty_consumed_per_unit"]
alias_dict = {
"item_code": "rm_item_code",
"name": "bom_detail_no",
"source_warehouse": "reserve_warehouse",
}
for field in [
"item_code",
"name",
"rate",
"stock_uom",
"source_warehouse",
"description",
"item_name",
"stock_uom",
]:
fields.append(f"`tab{doctype}`.`{field}` As {alias_dict.get(field, field)}")
filters = [
[doctype, "parent", "=", bom_no],
[doctype, "docstatus", "=", 1],
["BOM", "item", "=", item_code],
[doctype, "sourced_by_supplier", "=", 0],
]
return frappe.get_all("BOM", fields=fields, filters=filters, order_by=f"`tab{doctype}`.`idx`") or []
def __update_reserve_warehouse(self, row, item):
if self.doctype == self.subcontract_data.order_doctype:
row.reserve_warehouse = self.set_reserve_warehouse or item.warehouse
def __set_alternative_item(self, bom_item):
if self.alternative_item_details.get(bom_item.rm_item_code):
bom_item.update(self.alternative_item_details[bom_item.rm_item_code])
def __set_serial_and_batch_bundle(self, item_row, rm_obj, qty):
key = (rm_obj.rm_item_code, item_row.item_code, item_row.get(self.subcontract_data.order_field))
if not self.available_materials.get(key):
return
if not self.available_materials[key]["serial_no"] and not self.available_materials[key]["batch_no"]:
return
serial_nos = []
batches = frappe._dict({})
if self.available_materials.get(key) and self.available_materials[key]["serial_no"]:
serial_nos = self.__get_serial_nos_for_bundle(qty, key)
elif self.available_materials.get(key) and self.available_materials[key]["batch_no"]:
batches = self.__get_batch_nos_for_bundle(qty, key)
bundle = SerialBatchCreation(
frappe._dict(
{
"company": self.company,
"item_code": rm_obj.rm_item_code,
"warehouse": self.supplier_warehouse,
"qty": qty,
"serial_nos": serial_nos,
"batches": batches,
"posting_date": self.posting_date,
"posting_time": self.posting_time,
"voucher_type": "Subcontracting Receipt",
"do_not_submit": True,
"type_of_transaction": "Outward" if qty > 0 else "Inward",
}
)
).make_serial_and_batch_bundle()
return bundle.name
def __get_batch_nos_for_bundle(self, qty, key):
available_batches = defaultdict(float)
for batch_no, batch_qty in self.available_materials[key]["batch_no"].items():
qty_to_consumed = 0
if qty > 0:
if batch_qty >= qty:
qty_to_consumed = qty
else:
qty_to_consumed = batch_qty
qty -= qty_to_consumed
if qty_to_consumed > 0:
available_batches[batch_no] += qty_to_consumed
self.available_materials[key]["batch_no"][batch_no] -= qty_to_consumed
return available_batches
def __get_serial_nos_for_bundle(self, qty, key):
available_sns = sorted(self.available_materials[key]["serial_no"])[0 : cint(qty)]
serial_nos = []
for serial_no in available_sns:
serial_nos.append(serial_no)
self.available_materials[key]["serial_no"].remove(serial_no)
return serial_nos
def __add_supplied_item(self, item_row, bom_item, qty):
bom_item.conversion_factor = item_row.conversion_factor
rm_obj = self.append(self.raw_material_table, bom_item)
if rm_obj.get("qty"):
# Qty field not exists
rm_obj.qty = 0.0
rm_obj.reference_name = item_row.name
use_serial_batch_fields = frappe.db.get_single_value("Stock Settings", "use_serial_batch_fields")
if self.doctype == self.subcontract_data.order_doctype:
rm_obj.required_qty = flt(qty, rm_obj.precision("required_qty"))
rm_obj.amount = flt(rm_obj.required_qty * rm_obj.rate, rm_obj.precision("amount"))
else:
rm_obj.consumed_qty = flt(qty, rm_obj.precision("consumed_qty"))
rm_obj.required_qty = flt(bom_item.required_qty or qty, rm_obj.precision("required_qty"))
rm_obj.serial_and_batch_bundle = None
setattr(
rm_obj, self.subcontract_data.order_field, item_row.get(self.subcontract_data.order_field)
)
if use_serial_batch_fields:
rm_obj.use_serial_batch_fields = 1
self.__set_batch_nos(bom_item, item_row, rm_obj, qty)
if self.doctype == "Subcontracting Receipt" and not use_serial_batch_fields:
rm_obj.serial_and_batch_bundle = self.__set_serial_and_batch_bundle(
item_row, rm_obj, rm_obj.consumed_qty
)
self.set_rate_for_supplied_items(rm_obj, item_row)
def update_rate_for_supplied_items(self):
if self.doctype != "Subcontracting Receipt":
return
for row in self.supplied_items:
item_row = None
if row.reference_name:
item_row = self.get_item_row(row.reference_name)
if not item_row:
continue
self.set_rate_for_supplied_items(row, item_row)
def get_item_row(self, reference_name):
for item in self.items:
if item.name == reference_name:
return item
def set_rate_for_supplied_items(self, rm_obj, item_row):
args = frappe._dict(
{
"item_code": rm_obj.rm_item_code,
"warehouse": self.supplier_warehouse,
"posting_date": self.posting_date,
"posting_time": self.posting_time,
"qty": -1 * flt(rm_obj.consumed_qty),
"actual_qty": -1 * flt(rm_obj.consumed_qty),
"voucher_type": self.doctype,
"voucher_no": self.name,
"voucher_detail_no": item_row.name,
"company": self.company,
"allow_zero_valuation": 1,
}
)
if rm_obj.serial_and_batch_bundle:
args["serial_and_batch_bundle"] = rm_obj.serial_and_batch_bundle
if rm_obj.use_serial_batch_fields:
args["batch_no"] = rm_obj.batch_no
args["serial_no"] = rm_obj.serial_no
rm_obj.rate = get_incoming_rate(args)
def __set_batch_nos(self, bom_item, item_row, rm_obj, qty):
key = (rm_obj.rm_item_code, item_row.item_code, item_row.get(self.subcontract_data.order_field))
if self.available_materials.get(key) and self.available_materials[key]["batch_no"]:
new_rm_obj = None
for batch_no, batch_qty in self.available_materials[key]["batch_no"].items():
if batch_qty >= qty or (
rm_obj.consumed_qty == 0
and self.backflush_based_on == "BOM"
and len(self.available_materials[key]["batch_no"]) == 1
):
if rm_obj.consumed_qty == 0:
self.__set_consumed_qty(rm_obj, qty)
self.__set_batch_no_as_per_qty(item_row, rm_obj, batch_no, qty)
self.available_materials[key]["batch_no"][batch_no] -= qty
return
elif qty > 0 and batch_qty > 0:
qty -= batch_qty
new_rm_obj = self.append(self.raw_material_table, bom_item)
new_rm_obj.serial_and_batch_bundle = None
new_rm_obj.use_serial_batch_fields = 1
new_rm_obj.reference_name = item_row.name
self.__set_batch_no_as_per_qty(item_row, new_rm_obj, batch_no, batch_qty)
self.available_materials[key]["batch_no"][batch_no] = 0
if new_rm_obj:
self.remove(rm_obj)
elif abs(qty) > 0:
self.__set_consumed_qty(rm_obj, qty)
else:
self.__set_consumed_qty(rm_obj, qty, bom_item.required_qty or qty)
self.__set_serial_nos(item_row, rm_obj)
def __set_consumed_qty(self, rm_obj, consumed_qty, required_qty=0):
rm_obj.required_qty = flt(required_qty, rm_obj.precision("required_qty"))
rm_obj.consumed_qty = flt(consumed_qty, rm_obj.precision("consumed_qty"))
def __set_serial_nos(self, item_row, rm_obj):
key = (rm_obj.rm_item_code, item_row.item_code, item_row.get(self.subcontract_data.order_field))
if self.available_materials.get(key) and self.available_materials[key]["serial_no"]:
used_serial_nos = self.available_materials[key]["serial_no"][0 : cint(rm_obj.consumed_qty)]
rm_obj.serial_no = "\n".join(used_serial_nos)
# Removed the used serial nos from the list
for sn in used_serial_nos:
self.available_materials[key]["serial_no"].remove(sn)
def __set_batch_no_as_per_qty(self, item_row, rm_obj, batch_no, qty):
rm_obj.update(
{
"consumed_qty": qty,
"batch_no": batch_no,
"required_qty": qty,
self.subcontract_data.order_field: item_row.get(self.subcontract_data.order_field),
}
)
self.__set_serial_nos(item_row, rm_obj)
def __get_qty_based_on_material_transfer(self, item_row, transfer_item):
key = (item_row.item_code, item_row.get(self.subcontract_data.order_field))
if self.qty_to_be_received == item_row.qty:
return transfer_item.qty
if self.qty_to_be_received:
qty = (flt(item_row.qty) * flt(transfer_item.qty)) / flt(self.qty_to_be_received.get(key, 0))
transfer_item.item_details.required_qty = transfer_item.qty
if transfer_item.serial_no or frappe.get_cached_value(
"UOM", transfer_item.item_details.stock_uom, "must_be_whole_number"
):
return frappe.utils.ceil(qty)
return qty
def __set_supplied_items(self):
self.bom_items = {}
has_supplied_items = True if self.get(self.raw_material_table) else False
for row in self.items:
if self.doctype != self.subcontract_data.order_doctype and (
(self.__changed_name and row.name not in self.__changed_name)
or (has_supplied_items and not self.__changed_name)
):
continue
if self.doctype == self.subcontract_data.order_doctype or self.backflush_based_on == "BOM":
for bom_item in self.__get_materials_from_bom(
row.item_code, row.bom, row.get("include_exploded_items")
):
qty = flt(bom_item.qty_consumed_per_unit) * flt(row.qty) * row.conversion_factor
bom_item.main_item_code = row.item_code
self.__update_reserve_warehouse(bom_item, row)
self.__set_alternative_item(bom_item)
self.__add_supplied_item(row, bom_item, qty)
elif self.backflush_based_on != "BOM":
for key, transfer_item in self.available_materials.items():
if (key[1], key[2]) == (
row.item_code,
row.get(self.subcontract_data.order_field),
) and transfer_item.qty > 0:
qty = flt(self.__get_qty_based_on_material_transfer(row, transfer_item))
transfer_item.qty -= qty
self.__add_supplied_item(row, transfer_item.get("item_details"), qty)
if self.qty_to_be_received:
self.qty_to_be_received[
(row.item_code, row.get(self.subcontract_data.order_field))
] -= row.qty
def __set_rate_for_serial_and_batch_bundle(self):
if self.doctype != "Subcontracting Receipt":
return
for row in self.get(self.raw_material_table):
if not row.get("serial_and_batch_bundle"):
continue
row.rate = frappe.get_cached_value(
"Serial and Batch Bundle", row.serial_and_batch_bundle, "avg_rate"
)
def __modify_serial_and_batch_bundle(self):
if self.is_new():
return
if self.doctype != "Subcontracting Receipt":
return
for item_row in self.items:
if self.__changed_name and item_row.name in self.__changed_name:
continue
modified_data = self.__get_bundle_to_modify(item_row.name)
if modified_data:
serial_nos = []
batches = frappe._dict({})
key = (
modified_data.rm_item_code,
item_row.item_code,
item_row.get(self.subcontract_data.order_field),
)
if self.available_materials.get(key) and self.available_materials[key]["serial_no"]:
serial_nos = self.__get_serial_nos_for_bundle(modified_data.consumed_qty, key)
elif self.available_materials.get(key) and self.available_materials[key]["batch_no"]:
batches = self.__get_batch_nos_for_bundle(modified_data.consumed_qty, key)
SerialBatchCreation(
{
"item_code": modified_data.rm_item_code,
"warehouse": self.supplier_warehouse,
"serial_and_batch_bundle": modified_data.serial_and_batch_bundle,
"type_of_transaction": "Outward",
"serial_nos": serial_nos,
"batches": batches,
"qty": modified_data.consumed_qty * -1,
}
).update_serial_and_batch_entries()
def __get_bundle_to_modify(self, name):
for row in self.get("supplied_items"):
if row.reference_name == name and row.serial_and_batch_bundle:
if row.consumed_qty != abs(
frappe.get_cached_value(
"Serial and Batch Bundle", row.serial_and_batch_bundle, "total_qty"
)
):
return row
def __prepare_supplied_items(self):
self.initialized_fields()
self.__get_subcontract_orders()
self.__get_pending_qty_to_receive()
self.get_available_materials()
self.__remove_changed_rows()
self.__set_supplied_items()
self.__modify_serial_and_batch_bundle()
self.__set_rate_for_serial_and_batch_bundle()
def __validate_batch_no(self, row, key):
if row.get("batch_no") and row.get("batch_no") not in self.__transferred_items.get(key).get(
"batch_no"
):
link = get_link_to_form(
self.subcontract_data.order_doctype, row.get(self.subcontract_data.order_field)
)
msg = f'The Batch No {frappe.bold(row.get("batch_no"))} has not supplied against the {self.subcontract_data.order_doctype} {link}'
frappe.throw(_(msg), title=_("Incorrect Batch Consumed"))
def __validate_serial_no(self, row, key):
if row.get("serial_and_batch_bundle") and self.__transferred_items.get(key).get("serial_no"):
serial_nos = get_serial_nos_from_bundle(row.get("serial_and_batch_bundle"))
incorrect_sn = set(serial_nos).difference(self.__transferred_items.get(key).get("serial_no"))
if incorrect_sn:
incorrect_sn = "\n".join(incorrect_sn)
link = get_link_to_form(
self.subcontract_data.order_doctype, row.get(self.subcontract_data.order_field)
)
msg = f"The Serial Nos {incorrect_sn} has not supplied against the {self.subcontract_data.order_doctype} {link}"
frappe.throw(_(msg), title=_("Incorrect Serial Number Consumed"))
def __validate_supplied_items(self):
if self.doctype not in ["Purchase Invoice", "Purchase Receipt", "Subcontracting Receipt"]:
return
for row in self.get(self.raw_material_table):
key = (row.rm_item_code, row.main_item_code, row.get(self.subcontract_data.order_field))
if not self.__transferred_items or not self.__transferred_items.get(key):
return
self.__validate_batch_no(row, key)
self.__validate_serial_no(row, key)
def set_materials_for_subcontracted_items(self, raw_material_table):
if self.doctype == "Purchase Invoice" and not self.update_stock:
return
self.raw_material_table = raw_material_table
self.__identify_change_in_item_table()
self.__prepare_supplied_items()
self.__validate_supplied_items()
def create_raw_materials_supplied(self, raw_material_table="supplied_items"):
self.set_materials_for_subcontracted_items(raw_material_table)
if self.doctype in ["Subcontracting Receipt", "Purchase Receipt", "Purchase Invoice"]:
for item in self.get("items"):
item.rm_supp_cost = 0.0
def __update_consumed_qty_in_subcontract_order(self, itemwise_consumed_qty):
fields = ["main_item_code", "rm_item_code", "parent", "supplied_qty", "name"]
filters = {"docstatus": 1, "parent": ("in", self.subcontract_orders)}
for row in frappe.get_all(
self.subcontract_data.order_supplied_items_field, fields=fields, filters=filters, order_by="idx"
):
key = (row.rm_item_code, row.main_item_code, row.parent)
consumed_qty = itemwise_consumed_qty.get(key, 0)
if row.supplied_qty < consumed_qty:
consumed_qty = row.supplied_qty
itemwise_consumed_qty[key] -= consumed_qty
frappe.db.set_value(
self.subcontract_data.order_supplied_items_field, row.name, "consumed_qty", consumed_qty
)
def set_consumed_qty_in_subcontract_order(self):
# Update consumed qty back in the subcontract order
if self.doctype in ["Subcontracting Order", "Subcontracting Receipt"] or self.get(
"is_old_subcontracting_flow"
):
self.__get_subcontract_orders()
itemwise_consumed_qty = defaultdict(float)
if self.get("is_old_subcontracting_flow"):
doctypes = ["Purchase Receipt", "Purchase Invoice"]
else:
doctypes = ["Subcontracting Receipt"]
for doctype in doctypes:
consumed_items, receipt_items = self.__update_consumed_materials(
doctype, return_consumed_items=True
)
for row in consumed_items:
key = (row.rm_item_code, row.main_item_code, receipt_items.get(row.reference_name))
itemwise_consumed_qty[key] += row.consumed_qty
self.__update_consumed_qty_in_subcontract_order(itemwise_consumed_qty)
def update_ordered_and_reserved_qty(self):
sco_map = {}
for item in self.get("items"):
if self.doctype == "Subcontracting Receipt" and item.subcontracting_order:
sco_map.setdefault(item.subcontracting_order, []).append(item.subcontracting_order_item)
for sco, sco_item_rows in sco_map.items():
if sco and sco_item_rows:
sco_doc = frappe.get_doc("Subcontracting Order", sco)
if sco_doc.status in ["Closed", "Cancelled"]:
frappe.throw(
_("{0} {1} is cancelled or closed").format(_("Subcontracting Order"), sco),
frappe.InvalidStatusError,
)
sco_doc.update_ordered_qty_for_subcontracting(sco_item_rows)
sco_doc.update_reserved_qty_for_subcontracting()
def make_sl_entries_for_supplier_warehouse(self, sl_entries):
if hasattr(self, "supplied_items"):
for item in self.get("supplied_items"):
# negative quantity is passed, as raw material qty has to be decreased
# when SCR is submitted and it has to be increased when SCR is cancelled
sl_entries.append(
self.get_sl_entries(
item,
{
"item_code": item.rm_item_code,
"incoming_rate": item.rate if self.is_return else 0,
"warehouse": self.supplier_warehouse,
"actual_qty": -1 * flt(item.consumed_qty, item.precision("consumed_qty")),
"dependant_sle_voucher_detail_no": item.reference_name,
},
)
)
def update_stock_ledger(self, allow_negative_stock=False, via_landed_cost_voucher=False):
self.update_ordered_and_reserved_qty()
sl_entries = []
stock_items = self.get_stock_items()
for item in self.get("items"):
if item.item_code in stock_items and item.warehouse:
scr_qty = flt(item.qty) * flt(item.conversion_factor)
if scr_qty:
sle = self.get_sl_entries(item, {"actual_qty": flt(scr_qty)})
rate_db_precision = 6 if cint(self.precision("rate", item)) <= 6 else 9
incoming_rate = flt(item.rate, rate_db_precision)
sle.update(
{
"incoming_rate": incoming_rate,
"recalculate_rate": 1,
}
)
sl_entries.append(sle)
if flt(item.rejected_qty) != 0:
sl_entries.append(
self.get_sl_entries(
item,
{
"warehouse": item.rejected_warehouse,
"serial_and_batch_bundle": item.get("rejected_serial_and_batch_bundle"),
"actual_qty": flt(item.rejected_qty) * flt(item.conversion_factor),
"incoming_rate": 0.0,
},
)
)
self.make_sl_entries_for_supplier_warehouse(sl_entries)
self.make_sl_entries(
sl_entries,
allow_negative_stock=allow_negative_stock,
via_landed_cost_voucher=via_landed_cost_voucher,
)
def get_supplied_items_cost(self, item_row_id, reset_outgoing_rate=True):
supplied_items_cost = 0.0
for item in self.get("supplied_items"):
if item.reference_name == item_row_id:
if (
self.get("is_old_subcontracting_flow")
and reset_outgoing_rate
and frappe.get_cached_value("Item", item.rm_item_code, "is_stock_item")
):
rate = get_incoming_rate(
{
"item_code": item.rm_item_code,
"warehouse": self.supplier_warehouse,
"posting_date": self.posting_date,
"posting_time": self.posting_time,
"qty": -1 * item.consumed_qty,
"voucher_detail_no": item.name,
"serial_and_batch_bundle": item.get("serial_and_batch_bundle"),
"serial_no": item.get("serial_no"),
"batch_no": item.get("batch_no"),
}
)
if rate > 0:
item.rate = rate
item.amount = flt(flt(item.consumed_qty) * flt(item.rate), item.precision("amount"))
supplied_items_cost += item.amount
return supplied_items_cost
def set_subcontracting_order_status(self):
if self.doctype == "Subcontracting Order":
self.update_status()
elif self.doctype == "Subcontracting Receipt":
self.__get_subcontract_orders
if self.subcontract_orders:
for sco in set(self.subcontract_orders):
sco_doc = frappe.get_doc("Subcontracting Order", sco)
sco_doc.update_status()
def calculate_additional_costs(self):
self.total_additional_costs = sum(flt(item.amount) for item in self.get("additional_costs"))
if self.total_additional_costs:
if self.distribute_additional_costs_based_on == "Amount":
total_amt = sum(
flt(item.amount) for item in self.get("items") if not item.get("is_scrap_item")
)
for item in self.items:
if not item.get("is_scrap_item"):
item.additional_cost_per_qty = (
(item.amount * self.total_additional_costs) / total_amt
) / item.qty
else:
total_qty = sum(flt(item.qty) for item in self.get("items") if not item.get("is_scrap_item"))
additional_cost_per_qty = self.total_additional_costs / total_qty
for item in self.items:
if not item.get("is_scrap_item"):
item.additional_cost_per_qty = additional_cost_per_qty
else:
for item in self.items:
if not item.get("is_scrap_item"):
item.additional_cost_per_qty = 0
@frappe.whitelist()
def get_current_stock(self):
if self.doctype in ["Purchase Receipt", "Subcontracting Receipt"]:
for item in self.get("supplied_items"):
if self.supplier_warehouse:
actual_qty = frappe.db.get_value(
"Bin",
{"item_code": item.rm_item_code, "warehouse": self.supplier_warehouse},
"actual_qty",
)
item.current_stock = flt(actual_qty)
@property
def sub_contracted_items(self):
if not hasattr(self, "_sub_contracted_items"):
self._sub_contracted_items = []
item_codes = list(set(item.item_code for item in self.get("items")))
if item_codes:
items = frappe.get_all(
"Item", filters={"name": ["in", item_codes], "is_sub_contracted_item": 1}
)
self._sub_contracted_items = [item.name for item in items]
return self._sub_contracted_items
def update_requested_qty(self):
material_request_map = {}
for d in self.get("items"):
if d.material_request_item:
material_request_map.setdefault(d.material_request, []).append(d.material_request_item)
for mr, mr_item_rows in material_request_map.items():
if mr and mr_item_rows:
mr_obj = frappe.get_doc("Material Request", mr)
if mr_obj.status in ["Stopped", "Cancelled"]:
frappe.throw(
_("Material Request {0} is cancelled or stopped").format(mr),
frappe.InvalidStatusError,
)
mr_obj.update_requested_qty(mr_item_rows)
def get_item_details(items):
item = frappe.qb.DocType("Item")
item_list = (
frappe.qb.from_(item)
.select(item.item_code, item.item_name, item.description, item.allow_alternative_item)
.where(item.name.isin(items))
.run(as_dict=True)
)
item_details = {}
for item in item_list:
item_details[item.item_code] = item
return item_details
@frappe.whitelist()
def make_rm_stock_entry(
subcontract_order, rm_items=None, order_doctype="Subcontracting Order", target_doc=None
):
if subcontract_order:
subcontract_order = frappe.get_doc(order_doctype, subcontract_order)
if not rm_items:
if not subcontract_order.supplied_items:
frappe.throw(_("No item available for transfer."))
rm_items = subcontract_order.supplied_items
fg_item_code_list = list(
set(item.get("main_item_code") or item.get("item_code") for item in rm_items)
)
if fg_item_code_list:
rm_item_code_list = tuple(set(item.get("rm_item_code") for item in rm_items))
item_wh = get_item_details(rm_item_code_list)
field_no_map, rm_detail_field = "purchase_order", "sco_rm_detail"
if order_doctype == "Purchase Order":
field_no_map, rm_detail_field = "subcontracting_order", "po_detail"
if target_doc and target_doc.get("items"):
target_doc.items = []
stock_entry = get_mapped_doc(
order_doctype,
subcontract_order.name,
{
order_doctype: {
"doctype": "Stock Entry",
"field_map": {
"supplier": "supplier",
"supplier_name": "supplier_name",
"supplier_address": "supplier_address",
"to_warehouse": "supplier_warehouse",
},
"field_no_map": [field_no_map],
"validation": {
"docstatus": ["=", 1],
},
},
},
target_doc,
ignore_child_tables=True,
)
stock_entry.purpose = "Send to Subcontractor"
if order_doctype == "Purchase Order":
stock_entry.purchase_order = subcontract_order.name
else:
stock_entry.subcontracting_order = subcontract_order.name
stock_entry.set_stock_entry_type()
for fg_item_code in fg_item_code_list:
for rm_item in rm_items:
if (
rm_item.get("main_item_code") == fg_item_code
or rm_item.get("item_code") == fg_item_code
):
rm_item_code = rm_item.get("rm_item_code")
items_dict = {
rm_item_code: {
rm_detail_field: rm_item.get("name"),
"item_name": rm_item.get("item_name")
or item_wh.get(rm_item_code, {}).get("item_name", ""),
"description": item_wh.get(rm_item_code, {}).get("description", ""),
"qty": rm_item.get("qty")
or max(rm_item.get("required_qty") - rm_item.get("total_supplied_qty"), 0),
"from_warehouse": rm_item.get("warehouse")
or rm_item.get("reserve_warehouse"),
"to_warehouse": subcontract_order.supplier_warehouse,
"stock_uom": rm_item.get("stock_uom"),
"serial_and_batch_bundle": rm_item.get("serial_and_batch_bundle"),
"main_item_code": fg_item_code,
"allow_alternative_item": item_wh.get(rm_item_code, {}).get(
"allow_alternative_item"
),
"use_serial_batch_fields": rm_item.get("use_serial_batch_fields"),
"serial_no": rm_item.get("serial_no")
if rm_item.get("use_serial_batch_fields")
else None,
"batch_no": rm_item.get("batch_no")
if rm_item.get("use_serial_batch_fields")
else None,
}
}
stock_entry.add_to_stock_entry_detail(items_dict)
if target_doc:
return stock_entry
else:
return stock_entry.as_dict()
else:
frappe.throw(_("No Items selected for transfer."))
def add_items_in_ste(ste_doc, row, qty, rm_details, rm_detail_field="sco_rm_detail", batch_no=None):
item = ste_doc.append("items", row.item_details)
rm_detail = list(set(row.get(f"{rm_detail_field}s")).intersection(rm_details))
item.update(
{
"qty": qty,
"batch_no": batch_no,
"basic_rate": row.item_details["rate"],
rm_detail_field: rm_detail[0] if rm_detail else "",
"s_warehouse": row.item_details["t_warehouse"],
"t_warehouse": row.item_details["s_warehouse"],
"item_code": row.item_details["rm_item_code"],
"subcontracted_item": row.item_details["main_item_code"],
"serial_no": "\n".join(row.serial_no) if row.serial_no else "",
}
)
def make_return_stock_entry_for_subcontract(
available_materials, order_doc, rm_details, order_doctype="Subcontracting Order"
):
def post_process(source_doc, target_doc):
target_doc.purpose = "Material Transfer"
if source_doc.doctype == "Purchase Order":
target_doc.purchase_order = source_doc.name
else:
target_doc.subcontracting_order = source_doc.name
target_doc.company = source_doc.company
target_doc.is_return = 1
ste_doc = get_mapped_doc(
order_doctype,
order_doc.name,
{
order_doctype: {
"doctype": "Stock Entry",
"field_no_map": ["purchase_order", "subcontracting_order"],
},
},
ignore_child_tables=True,
postprocess=post_process,
)
if order_doctype == "Purchase Order":
rm_detail_field = "po_detail"
else:
rm_detail_field = "sco_rm_detail"
for _key, value in available_materials.items():
if not value.qty:
continue
if value.batch_no:
for batch_no, qty in value.batch_no.items():
if qty > 0:
add_items_in_ste(ste_doc, value, value.qty, rm_details, rm_detail_field, batch_no)
else:
add_items_in_ste(ste_doc, value, value.qty, rm_details, rm_detail_field)
ste_doc.set_stock_entry_type()
return ste_doc
@frappe.whitelist()
def get_materials_from_supplier(subcontract_order, rm_details, order_doctype="Subcontracting Order"):
if isinstance(rm_details, str):
rm_details = json.loads(rm_details)
doc = frappe.get_cached_doc(order_doctype, subcontract_order)
doc.initialized_fields()
doc.subcontract_orders = [doc.name]
doc.get_available_materials()
if not doc.available_materials:
frappe.throw(
_("Materials are already received against the {0} {1}").format(order_doctype, subcontract_order)
)
return make_return_stock_entry_for_subcontract(doc.available_materials, doc, rm_details, order_doctype)