コード例 #1
0
ファイル: delivery_note.py プロジェクト: erpnext-tm/erpnext
    def validate(self):
        self.validate_posting_time()
        super(DeliveryNote, self).validate()
        self.set_status()
        self.so_required()
        self.validate_proj_cust()
        self.check_sales_order_on_hold_or_close("against_sales_order")
        self.validate_warehouse()
        self.validate_uom_is_integer("stock_uom", "stock_qty")
        self.validate_uom_is_integer("uom", "qty")
        self.validate_with_previous_doc()

        from erpnext.stock.doctype.packed_item.packed_item import make_packing_list

        make_packing_list(self)

        if self._action != "submit" and not self.is_return:
            set_batch_nos(self, "warehouse", throw=True)
            set_batch_nos(self,
                          "warehouse",
                          throw=True,
                          child_table="packed_items")

        self.update_current_stock()

        if not self.installation_status:
            self.installation_status = "Not Installed"
        self.reset_default_field_value("set_warehouse", "items", "warehouse")
コード例 #2
0
    def validate(self):
        super(SalesOrder, self).validate()

        #self.validate_order_type()
        self.set_delivery_date()
        self.validate_delivery_date()
        self.validate_mandatory()
        self.validate_proj_cust()
        self.validate_po()
        self.validate_uom_is_integer("stock_uom", "qty")
        # self.validate_for_items()
        self.validate_warehouse()
        #frappe.errprint("in validate")

        from erpnext.stock.doctype.packed_item.packed_item import make_packing_list
        make_packing_list(self, 'sales_order_details')

        self.validate_with_previous_doc()

        if not self.status:
            self.status = "Draft"

        from erpnext.utilities import validate_status
        validate_status(self.status,
                        ["Draft", "Submitted", "Stopped", "Cancelled"])

        if not self.billing_status: self.billing_status = 'Not Billed'
        if not self.delivery_status: self.delivery_status = 'Not Delivered'
コード例 #3
0
    def validate(self):
        super(SalesOrder, self).validate()

        self.validate_order_type()
        self.validate_delivery_date()
        self.validate_mandatory()
        self.validate_proj_cust()
        self.validate_po()
        self.validate_uom_is_integer("stock_uom", "qty")
        self.validate_for_items()
        self.validate_warehouse()
        self.validate_drop_ship()

        from erpnext.stock.doctype.packed_item.packed_item import make_packing_list
        make_packing_list(self)

        self.validate_with_previous_doc()
        self.set_status()

        if not self.billing_status: self.billing_status = 'Not Billed'
        if not self.delivery_status: self.delivery_status = 'Not Delivered'

        #	self.rounded_total = round(self.net_total,-2)
        self.rounded_total = frappe.utils.data.rounded(self.net_total, -2)
        self.in_words = frappe.utils.data.money_in_words(self.rounded_total)
        self.base_in_words = frappe.utils.data.money_in_words(
            self.rounded_total)
コード例 #4
0
ファイル: sales_invoice.py プロジェクト: David-Duarte/erpnext
	def on_update(self):
		if cint(self.update_stock) == 1:
			# Set default warehouse from pos setting
			if cint(self.is_pos) == 1:
				w = self.get_warehouse()
				if w:
					for d in self.get('entries'):
						if not d.warehouse:
							d.warehouse = cstr(w)

			from erpnext.stock.doctype.packed_item.packed_item import make_packing_list
			make_packing_list(self, 'entries')
		else:
			self.set('packing_details', [])

		if cint(self.is_pos) == 1:
			if flt(self.paid_amount) == 0:
				if self.cash_bank_account:
					frappe.db.set(self, 'paid_amount',
						(flt(self.grand_total) - flt(self.write_off_amount)))
				else:
					# show message that the amount is not paid
					frappe.db.set(self,'paid_amount',0)
					frappe.msgprint(_("Note: Payment Entry will not be created since 'Cash or Bank Account' was not specified"))
		else:
			frappe.db.set(self,'paid_amount',0)
コード例 #5
0
    def update_packing_list(self):
        if cint(self.update_stock) == 1:
            from erpnext.stock.doctype.packed_item.packed_item import make_packing_list

            make_packing_list(self, "items")
        else:
            self.set("packed_items", [])
コード例 #6
0
    def validate(self):
        super(SalesOrder, self).validate()
        self.validate_order_type()
        self.validate_delivery_date()
        self.validate_proj_cust()
        self.validate_po()
        self.validate_uom_is_integer("stock_uom", "stock_qty")
        self.validate_uom_is_integer("uom", "qty")
        self.validate_for_items()
        self.validate_warehouse()
        self.validate_drop_ship()
        self.validate_serial_no_based_delivery()
        validate_inter_company_party(self.doctype, self.customer, self.company,
                                     self.inter_company_order_reference)

        if self.coupon_code:
            from erpnext.accounts.doctype.pricing_rule.utils import validate_coupon_code
            validate_coupon_code(self.coupon_code)

        from erpnext.stock.doctype.packed_item.packed_item import make_packing_list
        make_packing_list(self)

        self.validate_with_previous_doc()
        self.set_status()

        if not self.billing_status: self.billing_status = 'Not Billed'
        if not self.delivery_status: self.delivery_status = 'Not Delivered'
コード例 #7
0
    def validate(self):
        super(SalesOrder, self).validate()

        self.validate_order_type()
        self.validate_delivery_date()
        self.validate_proj_cust()
        self.validate_po()
        self.validate_uom_is_integer("stock_uom", "stock_qty")
        self.validate_uom_is_integer("uom", "qty")
        self.validate_for_items()
        self.validate_warehouse()
        self.validate_drop_ship()
        self.validate_serial_no_based_delivery()
        #self.total_commission_sp= 0
        #for d in self.items:
        #d.commission_amount = (d.commission_rate * d.amount) / 100
        #self.total_commission_sp += d.commission_amount
        #for t in self.sales_team:
        #t.incentives = (self.total_commission_sp * t.allocated_percentage)/100
        from erpnext.stock.doctype.packed_item.packed_item import make_packing_list
        make_packing_list(self)

        self.validate_with_previous_doc()
        self.set_status()

        if not self.billing_status: self.billing_status = 'Not Billed'
        if not self.delivery_status: self.delivery_status = 'Not Delivered'
コード例 #8
0
    def on_update(self):
        if cint(self.update_stock) == 1:
            # Set default warehouse from pos setting
            if cint(self.is_pos) == 1:
                w = self.get_warehouse()
                if w:
                    for d in self.get('entries'):
                        if not d.warehouse:
                            d.warehouse = cstr(w)

            from erpnext.stock.doctype.packed_item.packed_item import make_packing_list
            make_packing_list(self, 'entries')
        else:
            self.set('packing_details', [])

        if cint(self.is_pos) == 1:
            if flt(self.paid_amount) == 0:
                if self.cash_bank_account:
                    frappe.db.set(
                        self, 'paid_amount',
                        (flt(self.grand_total) - flt(self.write_off_amount)))
                else:
                    # show message that the amount is not paid
                    frappe.db.set(self, 'paid_amount', 0)
                    frappe.msgprint(
                        _("Note: Payment Entry will not be created since 'Cash or Bank Account' was not specified"
                          ))
        else:
            frappe.db.set(self, 'paid_amount', 0)
コード例 #9
0
	def validate(self):
		super(SalesOrder, self).validate()

		self.validate_order_type()
		self.validate_delivery_date()
		self.validate_mandatory()
		self.validate_proj_cust()
		self.validate_po()
		self.validate_uom_is_integer("stock_uom", "qty")
		self.validate_for_items()
		self.validate_warehouse()

		from erpnext.stock.doctype.packed_item.packed_item import make_packing_list
		make_packing_list(self,'items')

		self.validate_with_previous_doc()

		if not self.status:
			self.status = "Draft"

		from erpnext.controllers.status_updater import validate_status
		validate_status(self.status, ["Draft", "Submitted", "Stopped",
			"Cancelled"])

		if not self.billing_status: self.billing_status = 'Not Billed'
		if not self.delivery_status: self.delivery_status = 'Not Delivered'
コード例 #10
0
	def validate(self):
		#frappe.errprint("in the validate of sales order")
		super(SalesOrder, self).validate()

		self.validate_order_type()
		self.validate_delivery_date()
		self.validate_mandatory()
		self.validate_proj_cust()
		self.validate_po()
		self.validate_uom_is_integer("stock_uom", "qty")
		self.validate_for_items()
		self.validate_warehouse()

		from erpnext.stock.doctype.packed_item.packed_item import make_packing_list
		make_packing_list(self,'sales_order_details')

		self.validate_with_previous_doc()

		if not self.status:
			self.status = "Draft"

		from erpnext.utilities import validate_status
		validate_status(self.status, ["Draft", "Submitted", "Stopped",
			"Cancelled"])

		if not self.billing_status: self.billing_status = 'Not Billed'
		if not self.delivery_status: self.delivery_status = 'Not Delivered'
コード例 #11
0
    def validate(self):
        super(DeliveryNote, self).validate()

        from erpnext.utilities import validate_status

        validate_status(self.status, ["Draft", "Submitted", "Cancelled"])

        self.so_required()
        self.validate_proj_cust()
        self.check_stop_sales_order("against_sales_order")
        self.validate_for_items()
        self.validate_warehouse()
        self.validate_uom_is_integer("stock_uom", "qty")
        self.validate_with_previous_doc()

        from erpnext.stock.doctype.packed_item.packed_item import make_packing_list

        make_packing_list(self, "delivery_note_details")

        self.update_current_stock()

        if not self.status:
            self.status = "Draft"
        if not self.installation_status:
            self.installation_status = "Not Installed"
コード例 #12
0
ファイル: sales_order.py プロジェクト: OmarJaber/Aldaan
    def validate(self):
        super(SalesOrder, self).validate()

        self.validate_order_type()
        self.validate_delivery_date()
        self.validate_proj_cust()
        self.validate_po()
        self.validate_uom_is_integer("stock_uom", "stock_qty")
        self.validate_uom_is_integer("uom", "qty")
        self.validate_for_items()
        self.validate_warehouse()
        self.validate_drop_ship()
        self.validate_serial_no_based_delivery()
        self.refresh_customer_address()
        self.refresh_bank_account_details()
        self.refresh_in_words_arabic()

        from erpnext.stock.doctype.packed_item.packed_item import make_packing_list
        make_packing_list(self)

        self.validate_with_previous_doc()
        self.set_status()

        if not self.billing_status: self.billing_status = 'Not Billed'
        if not self.delivery_status: self.delivery_status = 'Not Delivered'
コード例 #13
0
def make_delivery_note(source_name, target_doc=None):
	def set_missing_values(source, target):
		if source.po_no:
			if target.po_no:
				target_po_no = target.po_no.split(", ")
				target_po_no.append(source.po_no)
				target.po_no = ", ".join(list(set(target_po_no))) if len(target_po_no) > 1 else target_po_no[0]
			else:
				target.po_no = source.po_no

		target.ignore_pricing_rule = 1
		target.run_method("set_missing_values")
		target.run_method("calculate_taxes_and_totals")

	def update_item(source, target, source_parent):
		target.base_amount = (flt(source.qty) - flt(source.delivered_qty)) * flt(source.base_rate)
		target.amount = (flt(source.qty) - flt(source.delivered_qty)) * flt(source.rate)
		target.qty = flt(source.qty) - flt(source.delivered_qty)

	target_doc = get_mapped_doc("Sales Order", source_name, {
		"Sales Order": {
			"doctype": "Delivery Note",
			"validation": {
				"docstatus": ["=", 1]
			}
		},
		"Sales Order Item": {
			"doctype": "Delivery Note Item",
			"field_map": {
				"rate": "rate",
				"name": "so_detail",
				"parent": "against_sales_order",
			},
			"postprocess": update_item,
			"condition": lambda doc: abs(doc.delivered_qty) < abs(doc.qty) and doc.delivered_by_supplier!=1
		},
		"Sales Taxes and Charges": {
			"doctype": "Sales Taxes and Charges",
			"add_if_empty": True
		},
		"Sales Team": {
			"doctype": "Sales Team",
			"add_if_empty": True
		}
	}, target_doc, set_missing_values)

	if not cint(frappe.db.get_default('maintain_packed_items_list')):
		if hasattr(target_doc, 'packed_items'):
			# remove packed_items suggested from sales order
			del target_doc.packed_items[0:]

		# make packed_items from product bundle
		from erpnext.stock.doctype.packed_item.packed_item import make_packing_list
		make_packing_list(target_doc)

	return target_doc
コード例 #14
0
def make_delivery_note(source_name, target_doc=None):
	def set_missing_values(source, target):
		target.ignore_pricing_rule = 1
		target.run_method("set_missing_values")
		target.run_method("calculate_taxes_and_totals")

	def update_item(source_doc, target_doc, source_parent):
		target_doc.base_amount = (flt(source_doc.qty) - flt(source_doc.delivered_qty)) * \
			flt(source_doc.base_rate)
		target_doc.amount = (flt(source_doc.qty) - flt(source_doc.delivered_qty)) * \
			flt(source_doc.rate)
		target_doc.qty = flt(source_doc.qty) - flt(source_doc.delivered_qty)

	doclist = get_mapped_doc("Sales Invoice", source_name, 	{
		"Sales Invoice": {
			"doctype": "Delivery Note",
			"validation": {
				"docstatus": ["=", 1]
			}
		},
		"Sales Invoice Item": {
			"doctype": "Delivery Note Item",
			"field_map": {
				"name": "si_detail",
				"parent": "against_sales_invoice",
				"serial_no": "serial_no",
				"sales_order": "against_sales_order",
				"so_detail": "so_detail"
			},
			"postprocess": update_item,
			"condition": lambda doc: doc.delivered_by_supplier!=1
		},
		"Sales Taxes and Charges": {
			"doctype": "Sales Taxes and Charges",
			"add_if_empty": True
		},
		"Sales Team": {
			"doctype": "Sales Team",
			"field_map": {
				"incentives": "incentives"
			},
			"add_if_empty": True
		}
	}, target_doc, set_missing_values)

	if not cint(frappe.db.get_default('maintain_packed_items_list')):
		if hasattr(target_doc, 'packed_items'):
			# remove packed_items suggested from sales order
			del target_doc.packed_items[0:]

		# make packed_items from product bundle
		from erpnext.stock.doctype.packed_item.packed_item import make_packing_list
		make_packing_list(target_doc)

	return doclist
コード例 #15
0
ファイル: quotation.py プロジェクト: MorezMartin/erpnext
    def validate(self):
        super(Quotation, self).validate()
        self.set_status()
        self.validate_uom_is_integer("stock_uom", "qty")
        self.validate_valid_till()
        self.set_customer_name()
        if self.items:
            self.with_items = 1

        from erpnext.stock.doctype.packed_item.packed_item import make_packing_list
        make_packing_list(self)
コード例 #16
0
ファイル: delivery_note.py プロジェクト: Aravinthu/erpnext
	def validate(self):
		super(DeliveryNote, self).validate()
		self.set_status()
		self.so_required()
		self.validate_proj_cust()
		self.check_close_sales_order("against_sales_order")
		self.validate_for_items()
		self.validate_warehouse()
		self.validate_uom_is_integer("stock_uom", "qty")
		self.validate_with_previous_doc()

		from erpnext.stock.doctype.packed_item.packed_item import make_packing_list
		make_packing_list(self)

		self.update_current_stock()

		if not self.installation_status: self.installation_status = 'Not Installed'
コード例 #17
0
	def validate(self):
		super(DeliveryNote, self).validate()
		self.set_status()
		self.so_required()
		self.validate_proj_cust()
		self.check_close_sales_order("against_sales_order")
		self.validate_for_items()
		self.validate_warehouse()
		self.validate_uom_is_integer("stock_uom", "qty")
		self.validate_with_previous_doc()

		from erpnext.stock.doctype.packed_item.packed_item import make_packing_list
		make_packing_list(self)

		self.update_current_stock()

		if not self.installation_status: self.installation_status = 'Not Installed'
コード例 #18
0
ファイル: sales_order.py プロジェクト: hatwar/osmosis-erpnext
	def validate(self):
		super(SalesOrder, self).validate()
		self.validate_order_type()
		self.validate_delivery_date()
		self.validate_mandatory()
		self.validate_proj_cust()
		self.validate_po()
		self.validate_uom_is_integer("stock_uom", "qty")
		self.validate_for_items()
		self.validate_warehouse()

		from erpnext.stock.doctype.packed_item.packed_item import make_packing_list
		make_packing_list(self)

		self.validate_with_previous_doc()
		self.set_status()

		if not self.billing_status: self.billing_status = 'Not Billed'
		if not self.delivery_status: self.delivery_status = 'Not Delivered'
コード例 #19
0
    def validate(self):
        super(SalesOrder, self).validate()

        self.validate_order_type()
        self.validate_delivery_date()
        self.validate_mandatory()
        self.validate_proj_cust()
        self.validate_po()
        self.validate_uom_is_integer("stock_uom", "qty")
        self.validate_for_items()
        self.validate_warehouse()

        from erpnext.stock.doctype.packed_item.packed_item import make_packing_list
        make_packing_list(self)

        self.validate_with_previous_doc()
        self.set_status()

        if not self.billing_status: self.billing_status = 'Not Billed'
        if not self.delivery_status: self.delivery_status = 'Not Delivered'
コード例 #20
0
ファイル: delivery_note.py プロジェクト: aisu-11/synergy_erp
	def validate(self):
		super(DeliveryNote, self).validate()

		from erpnext.utilities import validate_status
		validate_status(self.status, ["Draft", "Submitted", "Cancelled"])

		self.so_required()
		self.validate_proj_cust()
		self.check_stop_sales_order("against_sales_order")
		self.validate_for_items()
		self.validate_warehouse()
		self.validate_uom_is_integer("stock_uom", "qty")
		self.validate_with_previous_doc()

		from erpnext.stock.doctype.packed_item.packed_item import make_packing_list
		make_packing_list(self, 'items')

		self.update_current_stock()

		if not self.status: self.status = 'Draft'
		if not self.installation_status: self.installation_status = 'Not Installed'
コード例 #21
0
    def validate(self):
        self.validate_posting_time()
        super(DeliveryNote, self).validate()
        self.set_status()
        self.so_required()
        self.validate_proj_cust()
        self.check_sales_order_on_hold_or_close("against_sales_order")
        self.validate_warehouse()
        self.validate_uom_is_integer("stock_uom", "stock_qty")
        self.validate_uom_is_integer("uom", "qty")
        self.validate_with_previous_doc()

        if self._action != 'submit' and not self.is_return:
            set_batch_nos(self, 'warehouse', True)

        from erpnext.stock.doctype.packed_item.packed_item import make_packing_list
        make_packing_list(self)

        self.update_current_stock()

        if not self.installation_status:
            self.installation_status = 'Not Installed'
コード例 #22
0
ファイル: delivery_note.py プロジェクト: tfroehlich82/erpnext
	def validate(self):
		self.validate_posting_time()
		super(DeliveryNote, self).validate()
		self.set_status()
		self.so_required()
		self.validate_proj_cust()
		self.check_close_sales_order("against_sales_order")
		self.validate_for_items()
		self.validate_warehouse()
		self.validate_uom_is_integer("stock_uom", "stock_qty")
		self.validate_uom_is_integer("uom", "qty")
		self.validate_with_previous_doc()

		if self._action != 'submit' and not self.is_return:
			set_batch_nos(self, 'warehouse', True)

		from erpnext.stock.doctype.packed_item.packed_item import make_packing_list
		make_packing_list(self)

		self.update_current_stock()

		if not self.installation_status: self.installation_status = 'Not Installed'
コード例 #23
0
    def validate(self):
        super(SalesOrder, self).validate()

        self.validate_order_type()
        self.validate_checkout_date()
        self.validate_proj_cust()
        self.validate_po()
        self.validate_uom_is_integer("stock_uom", "stock_qty")
        self.validate_uom_is_integer("uom", "qty")
        self.validate_for_items()
        self.validate_warehouse()
        self.validate_drop_ship()
        self.validate_serial_no_based_delivery()

        from erpnext.stock.doctype.packed_item.packed_item import make_packing_list
        make_packing_list(self)

        self.validate_with_previous_doc()
        self.set_status()

        if not self.billing_status: self.billing_status = 'Not Billed'
        if not self.booking_status: self.booking_status = 'Pending'
コード例 #24
0
 def update_packing_list(self):
     from erpnext.stock.doctype.packed_item.packed_item import make_packing_list
     make_packing_list(self)
コード例 #25
0
def make_sales_invoice(source_name, target_doc=None, ignore_permissions=False):
	def postprocess(source, target):
		set_missing_values(source, target)
		#Get the advance paid Journal Entries in Sales Invoice Advance
		target.set_advances()

	def set_missing_values(source, target):
		target.is_pos = 0
		target.ignore_pricing_rule = 1
		target.flags.ignore_permissions = True
		target.run_method("set_missing_values")
		target.run_method("calculate_taxes_and_totals")

	def update_item(source, target, source_parent):
		target.amount = flt(source.amount) - flt(source.billed_amt)
		target.base_amount = target.amount * flt(source_parent.conversion_rate)
		target.qty = target.amount / flt(source.rate) if (source.rate and source.billed_amt) else source.qty
		
		item = frappe.db.get_value("Item", target.item_code, ["item_group", "selling_cost_center"], as_dict=1)
		target.cost_center = frappe.db.get_value("Project", source_parent.project, "cost_center") \
			or item.selling_cost_center \
			or frappe.db.get_value("Item Group", item.item_group, "default_cost_center")

	doclist = get_mapped_doc("Sales Order", source_name, {
		"Sales Order": {
			"doctype": "Sales Invoice",
			"field_map": {
				"party_account_currency": "party_account_currency"
			},
			"validation": {
				"docstatus": ["=", 1]
			}
		},
		"Sales Order Item": {
			"doctype": "Sales Invoice Item",
			"field_map": {
				"name": "so_detail",
				"parent": "sales_order",
			},
			"postprocess": update_item,
			"condition": lambda doc: doc.qty and (doc.base_amount==0 or abs(doc.billed_amt) < abs(doc.amount))
		},
		"Sales Taxes and Charges": {
			"doctype": "Sales Taxes and Charges",
			"add_if_empty": True
		},
		"Sales Team": {
			"doctype": "Sales Team",
			"add_if_empty": True
		}
	}, target_doc, postprocess, ignore_permissions=ignore_permissions)

	if not cint(frappe.db.get_default('maintain_packed_items_list')):
		if hasattr(doclist, 'packed_items'):
			# remove packed_items suggested from sales order
			del doclist.packed_items[0:]

		# make packed_items from product bundle
		from erpnext.stock.doctype.packed_item.packed_item import make_packing_list
		make_packing_list(doclist)

	return doclist
コード例 #26
0
	def update_packing_list(self):
		if cint(self.update_stock) == 1:
			from erpnext.stock.doctype.packed_item.packed_item import make_packing_list
			make_packing_list(self)
		else:
			self.set('packed_items', [])
コード例 #27
0
def update_child_qty_rate(parent_doctype,
                          trans_items,
                          parent_doctype_name,
                          child_docname="items"):
    def check_doc_permissions(doc, perm_type="create"):
        try:
            doc.check_permission(perm_type)
        except frappe.PermissionError:
            actions = {"create": "add", "write": "update"}

            frappe.throw(
                _("You do not have permissions to {} items in a {}.").format(
                    actions[perm_type], parent_doctype),
                title=_("Insufficient Permissions"),
            )

    def validate_workflow_conditions(doc):
        workflow = get_workflow_name(doc.doctype)
        if not workflow:
            return

        workflow_doc = frappe.get_doc("Workflow", workflow)
        current_state = doc.get(workflow_doc.workflow_state_field)
        roles = frappe.get_roles()

        transitions = []
        for transition in workflow_doc.transitions:
            if transition.next_state == current_state and transition.allowed in roles:
                if not is_transition_condition_satisfied(transition, doc):
                    continue
                transitions.append(transition.as_dict())

        if not transitions:
            frappe.throw(
                _("You are not allowed to update as per the conditions set in {} Workflow."
                  ).format(get_link_to_form("Workflow", workflow)),
                title=_("Insufficient Permissions"),
            )

    def get_new_child_item(item_row):
        child_doctype = "Sales Order Item" if parent_doctype == "Sales Order" else "Purchase Order Item"
        return set_order_defaults(parent_doctype, parent_doctype_name,
                                  child_doctype, child_docname, item_row)

    def validate_quantity(child_item, d):
        if parent_doctype == "Sales Order" and flt(d.get("qty")) < flt(
                child_item.delivered_qty):
            frappe.throw(_("Cannot set quantity less than delivered quantity"))

        if parent_doctype == "Purchase Order" and flt(d.get("qty")) < flt(
                child_item.received_qty):
            frappe.throw(_("Cannot set quantity less than received quantity"))

    data = json.loads(trans_items)

    sales_doctypes = [
        "Sales Order", "Sales Invoice", "Delivery Note", "Quotation"
    ]
    parent = frappe.get_doc(parent_doctype, parent_doctype_name)

    check_doc_permissions(parent, "write")
    validate_and_delete_children(parent, data)

    for d in data:
        new_child_flag = False

        if not d.get("item_code"):
            # ignore empty rows
            continue

        if not d.get("docname"):
            new_child_flag = True
            check_doc_permissions(parent, "create")
            child_item = get_new_child_item(d)
        else:
            check_doc_permissions(parent, "write")
            child_item = frappe.get_doc(parent_doctype + " Item",
                                        d.get("docname"))

            prev_rate, new_rate = flt(child_item.get("rate")), flt(
                d.get("rate"))
            prev_qty, new_qty = flt(child_item.get("qty")), flt(d.get("qty"))
            prev_con_fac, new_con_fac = flt(
                child_item.get("conversion_factor")), flt(
                    d.get("conversion_factor"))
            prev_uom, new_uom = child_item.get("uom"), d.get("uom")

            if parent_doctype == "Sales Order":
                prev_date, new_date = child_item.get("delivery_date"), d.get(
                    "delivery_date")
            elif parent_doctype == "Purchase Order":
                prev_date, new_date = child_item.get("schedule_date"), d.get(
                    "schedule_date")

            rate_unchanged = prev_rate == new_rate
            qty_unchanged = prev_qty == new_qty
            uom_unchanged = prev_uom == new_uom
            conversion_factor_unchanged = prev_con_fac == new_con_fac
            date_unchanged = (prev_date == getdate(new_date)
                              if prev_date and new_date else False
                              )  # in case of delivery note etc
            if (rate_unchanged and qty_unchanged
                    and conversion_factor_unchanged and uom_unchanged
                    and date_unchanged):
                continue

        validate_quantity(child_item, d)

        child_item.qty = flt(d.get("qty"))
        rate_precision = child_item.precision("rate") or 2
        conv_fac_precision = child_item.precision("conversion_factor") or 2
        qty_precision = child_item.precision("qty") or 2

        if flt(child_item.billed_amt, rate_precision) > flt(
                flt(d.get("rate"), rate_precision) *
                flt(d.get("qty"), qty_precision), rate_precision):
            frappe.throw(
                _("Row #{0}: Cannot set Rate if amount is greater than billed amount for Item {1}."
                  ).format(child_item.idx, child_item.item_code))
        else:
            child_item.rate = flt(d.get("rate"), rate_precision)

        if d.get("conversion_factor"):
            if child_item.stock_uom == child_item.uom:
                child_item.conversion_factor = 1
            else:
                child_item.conversion_factor = flt(d.get("conversion_factor"),
                                                   conv_fac_precision)

        if d.get("uom"):
            child_item.uom = d.get("uom")
            conversion_factor = flt(
                get_conversion_factor(child_item.item_code,
                                      child_item.uom).get("conversion_factor"))
            child_item.conversion_factor = (flt(d.get("conversion_factor"),
                                                conv_fac_precision)
                                            or conversion_factor)

        if d.get("delivery_date") and parent_doctype == "Sales Order":
            child_item.delivery_date = d.get("delivery_date")

        if d.get("schedule_date") and parent_doctype == "Purchase Order":
            child_item.schedule_date = d.get("schedule_date")

        if flt(child_item.price_list_rate):
            if flt(child_item.rate) > flt(child_item.price_list_rate):
                #  if rate is greater than price_list_rate, set margin
                #  or set discount
                child_item.discount_percentage = 0

                if parent_doctype in sales_doctypes:
                    child_item.margin_type = "Amount"
                    child_item.margin_rate_or_amount = flt(
                        child_item.rate - child_item.price_list_rate,
                        child_item.precision("margin_rate_or_amount"))
                    child_item.rate_with_margin = child_item.rate
            else:
                child_item.discount_percentage = flt(
                    (1 -
                     flt(child_item.rate) / flt(child_item.price_list_rate)) *
                    100.0,
                    child_item.precision("discount_percentage"),
                )
                child_item.discount_amount = flt(
                    child_item.price_list_rate) - flt(child_item.rate)

                if parent_doctype in sales_doctypes:
                    child_item.margin_type = ""
                    child_item.margin_rate_or_amount = 0
                    child_item.rate_with_margin = 0

        child_item.flags.ignore_validate_update_after_submit = True
        if new_child_flag:
            parent.load_from_db()
            child_item.idx = len(parent.items) + 1
            child_item.insert()
        else:
            child_item.save()

    parent.reload()
    parent.flags.ignore_validate_update_after_submit = True
    parent.set_qty_as_per_stock_uom()
    parent.calculate_taxes_and_totals()
    parent.set_total_in_words()
    if parent_doctype == "Sales Order":
        make_packing_list(parent)
        parent.set_gross_profit()
    frappe.get_doc("Authorization Control").validate_approving_authority(
        parent.doctype, parent.company, parent.base_grand_total)

    parent.set_payment_schedule()
    if parent_doctype == "Purchase Order":
        parent.validate_minimum_order_qty()
        parent.validate_budget()
        if parent.is_against_so():
            parent.update_status_updater()
    else:
        parent.check_credit_limit()

    # reset index of child table
    for idx, row in enumerate(parent.get(child_docname), start=1):
        row.idx = idx

    parent.save()

    if parent_doctype == "Purchase Order":
        update_last_purchase_rate(parent, is_submit=1)
        parent.update_prevdoc_status()
        parent.update_requested_qty()
        parent.update_ordered_qty()
        parent.update_ordered_and_reserved_qty()
        parent.update_receiving_percentage()
        if parent.is_subcontracted == "Yes":
            parent.update_reserved_qty_for_subcontract()
            parent.create_raw_materials_supplied("supplied_items")
            parent.save()
    else:
        parent.update_reserved_qty()
        parent.update_project()
        parent.update_prevdoc_status("submit")
        parent.update_delivery_status()

    parent.reload()
    validate_workflow_conditions(parent)

    parent.update_blanket_order()
    parent.update_billing_percentage()
    parent.set_status()