Esempio n. 1
0
def invoice_qol(name, payments, loyalty_card_no, loyalty_program,
                loyalty_points, cashback_receipt):
    def set_cost_center(item):
        if cost_center:
            item.cost_center = cost_center

    doc = make_sales_invoice(name)
    cost_center = (frappe.db.get_value(
        "Branch", doc.os_branch, "os_cost_center") if doc.os_branch else None)
    mapf(set_cost_center, doc.items)
    if loyalty_program and cint(loyalty_points):
        doc.redeem_loyalty_points = 1
        doc.os_loyalty_card_no = loyalty_card_no
        doc.loyalty_program = loyalty_program
        doc.loyalty_points = cint(loyalty_points)
        doc.loyalty_redemption_cost_center = cost_center
    if cashback_receipt:
        doc.os_cashback_receipt = cashback_receipt
    get_payments = compose(
        partial(filterf, lambda x: x.get("amount") != 0),
        partial(
            map,
            partial(keyfilter, lambda x: x in ["mode_of_payment", "amount"])),
        json.loads,
    )
    payments_proc = get_payments(payments)
    if payments_proc:
        doc.is_pos = 1
        mapf(lambda x: doc.append("payments", x), payments_proc)
    doc.update_stock = 0
    doc.insert(ignore_permissions=True)
    doc.submit()
    return doc.name
Esempio n. 2
0
def update_sales_orders(sales_orders, action, lab_tech=None):
    transition = compose(lambda doc: apply_workflow(doc, action),
                         partial(frappe.get_doc, "Sales Order"))
    mapf(transition, json.loads(sales_orders))
    if lab_tech and action == "Proceed to Deliver":
        update = compose(lambda x: frappe.db.set_value(
            "Sales Order", x, "os_lab_tech", lab_tech))
        mapf(update, json.loads(sales_orders))
Esempio n. 3
0
def update_sales_orders(sales_orders, action, lab_tech=None):
    workflow_name = frappe.model.meta.get_workflow_name("Sales Order")
    if not workflow_name:
        frappe.throw(NO_WORKFLOW_MSG)
    if workflow_name != "Optic Store Sales Order":
        frappe.throw(
            frappe._("Operation not allowed for Workflow: {}".format(
                frappe.bold(workflow_name))))
    transition = compose(lambda doc: apply_workflow(doc, action),
                         partial(frappe.get_doc, "Sales Order"))
    mapf(transition, json.loads(sales_orders))
    if lab_tech and action == "Proceed to Deliver":
        update = compose(lambda x: frappe.db.set_value(
            "Sales Order", x, "os_lab_tech", lab_tech))
        mapf(update, json.loads(sales_orders))
Esempio n. 4
0
def _get_customers_details(pos_profile, query_date):
    customers = compose(list, partial(pluck, "name"),
                        get_customers_list)(pos_profile)
    details = frappe.db.sql(
        """
            SELECT
                name,
                old_customer_id,
                customer_name,
                os_loyalty_card_no,
                loyalty_program,
                {customer_details_fields}
            FROM `tabCustomer` WHERE name IN %(customers)s
        """.format(customer_details_fields=", ".join(CUSTOMER_DETAILS_FIELDS)),
        values={"customers": customers},
        as_dict=1,
    )

    def add_loyalty_points(customer):
        loyalty_points = get_loyalty_details(
            customer.get("name"),
            customer.get("loyalty_program"),
            expiry_date=query_date,
        )
        return merge(customer, pick(["loyalty_points"], loyalty_points))

    return mapf(compose(add_loyalty_points, partial(valfilter, lambda x: x)),
                details)
Esempio n. 5
0
def _get_item_type(items, settings):
    groups = mapf(lambda x: x.item_group, items)
    if settings.special_order_item_group in groups:
        return "Special"
    if settings.standard_item_group in groups:
        return "Standard"
    return "Other"
Esempio n. 6
0
def _get_data(clauses, values, keys):
    items = frappe.db.sql(
        """
            SELECT
                i.brand AS brand,
                i.item_code AS item_code,
                i.item_group AS item_group,
                i.item_name AS item_name,
                ipss.price_list_rate AS standard_selling,
                SUM(b.actual_qty) AS qty,
                ipms.price_list_rate AS minimum_selling
            FROM `tabItem` AS i
            LEFT JOIN `tabBin` AS b ON {bin_clauses}
            LEFT JOIN ({standard_selling_sq}) AS ipss ON ipss.item_code = i.item_code
            LEFT JOIN ({minimum_selling_sq}) AS ipms ON ipms.item_code = i.item_code
            WHERE {clauses}
            GROUP BY i.item_code
        """.format(
            standard_selling_sq=price_sq("Standard Selling"),
            minimum_selling_sq=price_sq("Minimum Selling"),
            **clauses
        ),
        values=values,
        as_dict=1,
    )
    return mapf(partial(pick, keys), items)
Esempio n. 7
0
def _get_data(clauses, values, keys, query):
    rows = frappe.db.sql(query.format(clauses=clauses),
                         values=values,
                         as_dict=1)

    make_row = partial(pick, keys)
    return mapf(make_row, rows)
Esempio n. 8
0
def _validate_spec_parts(items):
    parts = mapf(lambda x: x.os_spec_part, items)

    def count(part):
        return compose(len, list, partial(filter, lambda x: x == part))

    for part in ["Frame", "Lens Right", "Lens Left"]:
        if count(part)(parts) > 1:
            frappe.throw(_("There can only be one row for {}".format(part)))
Esempio n. 9
0
 def get_ref_doc(pf):
     if not pf_settings.get(pf, {}).get("is_invoice_pf"):
         return [
             {"doctype": "Sales Order", "docname": sales_order, "print_format": pf}
         ]
     return mapf(
         lambda x: {"doctype": "Sales Invoice", "docname": x, "print_format": pf},
         sales_invoices,
     )
Esempio n. 10
0
def _get_branch_collections(payments, yesterday, settings):
    def make_aggregator(start, end):
        return compose(
            sum_by("amount"),
            lambda x: filter(
                lambda row: row.branch == x and
                (start <= row.posting_date <= end),
                payments,
            ),
            partial(get, "branch"),
        )

    sum_today = make_aggregator(yesterday, yesterday)
    sum_half_month = make_aggregator(*_get_half_month_dates(yesterday))
    sum_month = make_aggregator(*_get_month_dates(yesterday))
    sum_quarter = make_aggregator(*_get_quarter_dates(yesterday))
    sum_half_year = make_aggregator(*_get_half_year_dates(yesterday))
    sum_year = make_aggregator(*_get_year_dates(yesterday))

    def set_amounts(x):
        collected_mtd = sum_month(x)
        return merge(
            {
                "collected_today":
                sum_today(x),
                "half_monthly_sales":
                sum_half_month(x),
                "collected_mtd":
                collected_mtd,
                "monthly_target_remaining":
                get("monthly_target", x, 0) - collected_mtd,
            },
            {"quarterly_sales": sum_quarter(x)}
            if settings.show_quarter else {},
            {"half_yearly_sales": sum_half_year(x)}
            if settings.show_half_year else {},
            {"yearly_sales": sum_year(x)} if settings.show_year else {},
        )

    return mapf(
        lambda x: merge(x, set_amounts(x)),
        frappe.get_all(
            "Branch",
            fields=[
                "name AS branch",
                "os_half_monthly_target AS half_monthly_target",
                "os_target AS monthly_target",
                "os_quarterly_target AS quarterly_target",
                "os_half_yearly_target AS half_yearly_target",
                "os_yearly_target AS yearly_target",
            ],
            filters=[[
                "name", "in", (settings.branches_to_show or "").split("\n")
            ]],
        ),
    )
Esempio n. 11
0
def _get_mop_collections(payments, yesterday):
    get_sum_today = compose(
        sum_by("amount"),
        lambda x: filter(
            lambda row: row.mode_of_payment == x and row.posting_date == yesterday,
            payments,
        ),
        partial(get, "mop"),
    )
    return mapf(
        lambda x: merge(x, {"collected_today": get_sum_today(x)}),
        frappe.get_all("Mode of Payment", fields=["name AS mop"]),
    )
Esempio n. 12
0
def before_save(doc, method):
    settings = frappe.get_single("Optical Store Settings")
    frames = mapf(lambda x: x.item_group, settings.frames)
    lenses = mapf(lambda x: x.item_group, settings.lens)

    validate_item_group = _validate_item_group(frames, lenses)
    frame, lens_right, lens_left = get_parts(doc.items)
    for item in doc.items:
        if item.os_spec_part:
            validate_item_group(item)
        else:
            if not frame and item.item_group in frames:
                item.os_spec_part = "Frame"
                frame = item
            elif not lens_right and item.item_group in lenses:
                item.os_spec_part = "Lens Right"
                lens_right = item
            elif not lens_left and item.item_group in lenses:
                item.os_spec_part = "Lens Left"
                lens_left = item

    _validate_spec_parts(doc.items)
Esempio n. 13
0
def _get_data(data):
    def get_reference_st(stock_entry):
        return frappe.db.exists(
            "Stock Transfer", {"outgoing_stock_entry": stock_entry}
        ) or frappe.db.exists("Stock Transfer", {"outgoing_stock_entry": stock_entry})

    def add_fields(row):
        if row.voucher_type == "Stock Entry":
            purpose = frappe.db.get_value("Stock Entry", row.voucher_no, "purpose")
            return frappe._dict(
                merge(
                    row,
                    {
                        "purpose": purpose,
                        "reference_stock_transfer": get_reference_st(row.voucher_no)
                        if purpose == "Material Transfer"
                        else None,
                    },
                )
            )
        return row

    return mapf(add_fields, data)
def _get_data(clauses, values, keys):
    items = frappe.db.sql(
        """
            SELECT
                si.name AS invoice_name,
                sii.sales_order AS order_name,
                si.posting_date AS invoice_date,
                si.posting_time AS invoice_time,
                sii.brand AS brand,
                sii.item_code AS item_code,
                sii.item_group AS item_group,
                sii.description AS description,
                bp.valuation_rate AS valuation_rate,
                sii.price_list_rate AS selling_rate,
                sii.rate AS rate,
                sii.qty AS qty,
                sii.qty * IFNULL(bp.valuation_rate, 0) AS valuation_amount,
                IF(
                    sii.discount_percentage = 100,
                    sii.price_list_rate * sii.qty,
                    sii.amount * 100 / (100 - sii.discount_percentage)
                ) AS amount_before_discount,
                IF(
                    sii.discount_percentage = 100,
                    sii.price_list_rate * sii.qty,
                    sii.amount * (100 / (100 - sii.discount_percentage) - 1)
                ) AS discount_amount,
                sii.discount_percentage AS discount_percentage,
                sii.amount AS amount_after_discount,
                sii.os_minimum_selling_rate AS ms1,
                IF(
                    ABS(sii.amount) < sii.os_minimum_selling_rate * sii.qty,
                    'Yes',
                    'No'
                ) AS below_ms1,
                sii.os_minimum_selling_2_rate AS ms2,
                IF(
                    ABS(sii.amount) < sii.os_minimum_selling_2_rate,
                    'Yes',
                    'No'
                ) AS below_ms2,
                si.os_sales_person AS sales_person,
                si.os_sales_person_name AS sales_person_name,
                IF(
                    si.total = 0,
                    0,
                    si.total_commission * sii.amount / si.total
                ) AS commission_amount,
                si.customer AS customer,
                si.customer_name AS customer_name,
                si.os_notes AS notes,
                si.orx_dispensor AS dispensor,
                si.os_branch AS branch,
                IF(
                    si.update_stock = 1 OR so.workflow_state = 'Collected',
                    'Collected',
                    'Achieved'
                ) AS sales_status,
                si.update_stock AS own_delivery,
                si.is_return AS is_return,
                dn.posting_date AS delivery_date
            FROM `tabSales Invoice Item` AS sii
            LEFT JOIN `tabSales Invoice` AS si ON
                si.name = sii.parent
            LEFT JOIN `tabSales Order` AS so ON
                so.name = sii.sales_order
            LEFT JOIN (
                SELECT
                    idni.si_detail AS si_detail,
                    idn.is_return AS is_return,
                    idn.posting_date AS posting_date
                FROM `tabDelivery Note Item` AS idni
                LEFT JOIN `tabDelivery Note` AS idn ON
                    idn.name = idni.parent
            ) AS dn ON
                dn.si_detail = sii.name AND
                dn.is_return = si.is_return
            LEFT JOIN `tabBin` AS bp ON
                bp.item_code = sii.item_code AND
                bp.warehouse = sii.warehouse
            WHERE {clauses}
            ORDER BY invoice_date
        """.format(clauses=clauses),
        values=values,
        as_dict=1,
    )

    def add_collection_date(row):
        def get_collection_date(x):
            if x.sales_status == "Achieved":
                return None
            if x.own_delivery or x.is_return:
                return x.invoice_date
            return x.delivery_date

        return merge(row, {"collection_date": get_collection_date(row)})

    def add_payment_remarks(items):
        payments = _get_payments(items)

        def fn(row):
            make_remark = compose(
                lambda x: ", ".join(x),
                partial(
                    map,
                    lambda x: "{mop}: {amount}".format(mop=x[0], amount=x[1])),
                lambda x: x.items(),
                lambda lines: reduceby(
                    "mode_of_payment",
                    lambda a, x: a + get("paid_amount", x, 0),
                    lines,
                    0,
                ),
                lambda x: concatv(get(x.invoice_name, payments, []),
                                  get(x.order_name, payments, [])),
                frappe._dict,
            )

            return merge(row, {"remarks": make_remark(row)})

        return fn

    def set_null(k, v):
        if v:
            return k, v
        if k not in [
                "valuation_rate",
                "selling_rate",
                "rate",
                "qty",
                "valuation_amount",
                "amount_before_discount",
                "discount_amount",
                "discount_percentage",
                "amount_after_discount",
                "ms1",
                "ms2",
                "commission_amount",
        ]:
            return k, None
        return k, 0

    template = reduce(lambda a, x: merge(a, {x: None}), keys, {})
    make_row = compose(
        partial(pick, keys),
        partial(itemmap, lambda x: set_null(*x)),
        partial(merge, template),
        add_payment_remarks(items),
        add_collection_date,
    )

    return mapf(make_row, items)
Esempio n. 15
0
def get_items(
    start,
    page_length,
    price_list,
    item_group,
    search_value="",
    pos_profile=None,
    customer=None,
):
    debug = frappe.db.get_single_value("Optical Store Settings", "debug_query")
    search_data = (search_serial_or_batch_or_barcode_number(search_value)
                   if search_value else {})
    clauses, values = _get_conditions(
        merge(
            {
                "start": cint(start),
                "page_length": cint(page_length),
                "price_list": price_list,
                "item_group": item_group or get_root_of("Item Group"),
                "search_value": search_value,
                "pos_profile": pos_profile,
                "customer": customer,
            },
            search_data,
        ))

    items = frappe.db.sql(
        """
            SELECT
                i.name AS item_code,
                i.item_name AS item_name,
                i.image AS item_image,
                i.idx AS idx,
                i.is_stock_item AS is_stock_item,
                i.variant_of AS variant_of
            FROM `tabItem` AS i
            LEFT JOIN `tabItem Group` AS ig ON ig.name = i.item_group
            {stock_clause}
            WHERE {clauses}
            ORDER BY i.idx
            LIMIT %(start)s, %(page_length)s
        """.format(**clauses),
        values=values,
        as_dict=1,
        debug=debug,
    )

    def list_items(items):
        make_list = compose(partial(filterf, lambda x: x), unique, concatv)
        return make_list(pluck("item_code", items), pluck("variant_of", items))

    make_prices = compose(partial(valmap, partial(groupby, "item_code")),
                          partial(groupby, "price_list"))

    # better to use a second query to fetch prices because combining with items query
    # takes considerably longer
    prices = (make_prices(
        frappe.db.sql(
            """
                SELECT
                    i.name AS item_code,
                    ip.price_list AS price_list,
                    ip.price_list_rate AS price_list_rate,
                    ip.currency AS currency
                FROM `tabItem Price` AS ip
                LEFT JOIN `tabItem` AS i ON i.name = ip.item_code
                WHERE
                    ip.selling = 1 AND
                    ip.item_code IN %(items)s AND
                    IFNULL(ip.uom, '') IN (i.stock_uom, '') AND
                    IFNULL(ip.min_qty, 0) <= 1 AND
                    IFNULL(ip.customer, '') IN (%(customer)s, '') AND
                    CURDATE() BETWEEN
                        IFNULL(ip.valid_from, '2000-01-01') AND
                        IFNULL(ip.valid_upto, '2500-12-31')
            """,
            values={
                "items": list_items(items),
                "customer": customer
            },
            as_dict=1,
            debug=debug,
        )) if items else [])

    def add_price(prices):
        def make_price(item_code):
            return compose(
                excepts(StopIteration, first, lambda x: {}),
                partial(get, item_code, default=[]),
                lambda x: get(x, prices, {}),
            )

        def fn(item):
            item_price = make_price(item.item_code)
            template_price = make_price(item.variant_of)
            sp = item_price(price_list) or template_price(price_list)
            ms1 = item_price("Minimum Selling") or template_price(
                "Minimum Selling")
            ms2 = item_price("Minimum Selling 2") or template_price(
                "Minimum Selling 2")
            return merge(
                item,
                {
                    "price_list_rate": get("price_list_rate", sp, 0),
                    "currency": get("currency", sp, 0),
                    "os_minimum_selling_rate": get("price_list_rate", ms1, 0),
                    "os_minimum_selling_2_rate": get("price_list_rate", ms2,
                                                     0),
                },
            )

        return fn

    make_item = compose(
        partial(
            pick,
            [
                "item_code",
                "item_name",
                "idx",
                "is_stock_item",
                "price_list_rate",
                "currency",
                "os_minimum_selling_rate",
                "os_minimum_selling_2_rate",
            ],
        ),
        add_price(prices),
    )

    return merge(
        {"items": mapf(make_item, items)},
        pick(["barcode", "serial_no", "batch_no"], search_data),
    )
Esempio n. 16
0
def update_prices(item_code, prices):
    price_list_rates = json.loads(prices)
    mapf(lambda x: _update_price(item_code, **x), price_list_rates)