Example #1
0
class Website(Model):
    _name = "website"
    _string = "Website"
    _fields = {
        "name": fields.Char("Website Title"),
        "parent_categ_id": fields.Many2One("product.categ", "Product Category"),
        "parent_group_id": fields.Many2One("product.group", "Product Group"),
        "contact_categ_id": fields.Many2One("contact.categ", "Customer Contact Category"),
        "user_profile_id": fields.Many2One("profile", "Customer User Profile"),
        "sale_account_id": fields.Many2One("account.account", "Sales Account"),
        "sale_tax_id": fields.Many2One("account.tax.rate", "Sales Tax"),
        "account_receivable_id": fields.Many2One("account.account", "Receivable Account"),
        "news_categ_id": fields.Many2One("contact.categ", "Newsletter Contact Category"),
        "target_list_id": fields.Many2One("mkt.target.list", "Newsletter Target List"),
        "invoice_flag": fields.Boolean("Use same invoice number as sale order number"),
        "ship_product_id": fields.Many2One("product", "Shipping Product"),
        "preview_doc_categ_id": fields.Many2One("document.categ", "Preview picture document category"),
        "invoice_template_id": fields.Many2One("report.template", "Invoice Template"),
        "payment_slip_template_id": fields.Many2One("report.template", "Payment Slip Template"),
        "auto_create_account": fields.Boolean("Auto-create customer account after checkout"),
        "ga_script": fields.Text("Google Analytic script"),
        "state": fields.Selection([["active", "Active"], ["inactive", "Inactive"]], "Status", required=True),
        "theme_id": fields.Many2One("theme", "Theme"),
        "settings": fields.One2Many("website.setting","website_id","Website Settings"),
        "sale_channel_id": fields.Many2One("sale.channel","Sales Channel"),
        "bank_method_id": fields.Many2One("payment.method","Bank Transfer",condition=[["type","=","bank"]]),
        "paypal_method_id": fields.Many2One("payment.method","Paypal",condition=[["type","=","paypal"]]),
        "paysbuy_method_id": fields.Many2One("payment.method","Paysbuy",condition=[["type","=","paysbuy"]]),
        "scb_method_id": fields.Many2One("payment.method","SCB Gateway",condition=[["type","=","scb_gateway"]]),
        "url": fields.Char("Website URL"),
    }
    _order="name"
    _defaults = {
        "state": "active",
    }
Example #2
0
class ReportTaxSum(Model):
    _name = "report.tax.sum"
    _transient = True
    _fields = {
        "date_from": fields.Date("From"),
        "date_to": fields.Date("To"),
        "by_rate": fields.Boolean("Show by Tax Rate"),
        "by_comp": fields.Boolean("Show by Tax Component"),
    }

    _defaults = {
        "date_from":
        lambda *a: date.today().strftime("%Y-%m-01"),
        "date_to":
        lambda *a: (date.today() + relativedelta(day=31)).strftime("%Y-%m-%d"),
        "by_comp":
        True,
    }

    def get_report_data(self, ids, context={}):
        company_id = get_active_company()
        company_ids = get_model("company").search(
            [["id", "child_of", company_id]])
        comp = get_model("company").browse(company_id)
        if ids:
            params = self.read(ids, load_m2o=False)[0]
        else:
            params = self.default_get(load_m2o=False, context=context)
        settings = get_model("settings").browse(1)
        date_from = params.get("date_from")
        if not date_from:
            date_from = date.today().strftime("%Y-%m-01")
        date_to = params.get("date_to")
        if not date_to:
            date_to = (date.today() +
                       relativedelta(day=31)).strftime("%Y-%m-%d")
        data = {
            "company_name": comp.name,
            "date_from": date_from,
            "date_to": date_to,
            "by_rate": params.get("by_rate"),
            "by_comp": params.get("by_comp"),
        }
        db = database.get_connection()
        if params.get("by_comp"):
            res = db.query(
                "SELECT c.id AS comp_id,c.name AS comp_name,c.rate AS comp_rate,r.name AS rate_name,SUM(l.credit-l.debit) AS tax_total,SUM(l.tax_base*sign(l.credit-l.debit)) AS base_total FROM account_move_line l,account_move m,account_tax_component c,account_tax_rate r WHERE m.id=l.move_id AND m.state='posted' AND m.date>=%s AND m.date<=%s AND c.id=l.tax_comp_id AND r.id=c.tax_rate_id AND m.company_id IN %s GROUP BY comp_id,comp_name,comp_rate,rate_name ORDER BY comp_name,rate_name",
                date_from, date_to, tuple(company_ids))
            data["comp_taxes"] = [dict(r) for r in res]
        if params.get("by_rate"):
            res = db.query(
                "SELECT c.id AS comp_id,c.name AS comp_name,c.rate AS comp_rate,r.name AS rate_name,SUM(l.credit-l.debit) AS tax_total,SUM(l.tax_base*sign(l.credit-l.debit)) AS base_total FROM account_move_line l,account_move m,account_tax_component c,account_tax_rate r WHERE m.id=l.move_id AND m.state='posted' AND m.date>=%s AND m.date<=%s AND c.id=l.tax_comp_id AND r.id=c.tax_rate_id AND m.company_id IN %s GROUP BY comp_id,comp_name,comp_rate,rate_name ORDER BY rate_name,comp_name",
                date_from, date_to, tuple(company_ids))
            data["rate_taxes"] = [dict(r) for r in res]
        return data
Example #3
0
class Profile(Model):
    _name = "profile"
    _string = "Profile"
    _key = ["name"]
    _fields = {
        "name": fields.Char("Name", required=True, search=True),
        "code": fields.Char("Short Code"),
        "perms": fields.One2Many("profile.access", "profile_id", "Model Permissions"),
        "field_perms": fields.One2Many("field.access", "profile_id", "Field Permissions"),
        "menu_perms": fields.One2Many("menu.access", "profile_id", "Menu Permissions"),
        "other_perms": fields.Many2Many("permission", "Other Permissions"),
        "home_action": fields.Char("Login Action"),
        "login_company_id": fields.Many2One("company", "Login Company"),
        "prevent_login": fields.Boolean("Prevent Login"),
        "comments": fields.One2Many("message", "related_id", "Comments"),
        "default_model_perms": fields.Selection([["full", "Full Access"], ["readonly","Read-only Access"], ["no", "No Access"]], "Default Model Permissions"),
        "default_menu_access": fields.Selection([["visible", "Visible"], ["hidden", "Hidden"]], "Default Menu Access"),
    }
    _order = "name"
    _defaults = {
        "default_model_perms": "full",
    }

    def get_data(self, context={}):
        vals = {}
        perms = []
        for m in get_model("model").search_browse([]):
            perms.append({
                "model_id": [m.id, m.string],
            })
        vals["perms"] = perms
        return vals

    def copy(self, ids, context={}):
        obj = self.browse(ids)[0]
        vals = {
            "name": obj.name + " (Copy)",
            "perms": [],
            "other_perms": [("set", [p.id for p in obj.other_perms])],
            "home_action": obj.home_action,
        }
        for perm in obj.perms:
            vals["perms"].append(("create", {
                "model_id": perm.model_id.id,
                "perm_read": perm.perm_read,
                "perm_create": perm.perm_create,
                "perm_write": perm.perm_write,
                "perm_delete": perm.perm_delete,
                "view_all": perm.view_all,
                "modif_all": perm.modif_all,
            }))
        profile_id = get_model("profile").create(vals)
        return {
            "next": {
                "name": "profile",
                "mode": "form",
                "active_id": profile_id,
            },
            "flash": "New profile created",
        }
Example #4
0
class Product(Model):
    _inherit = "product"
    _fields = {
        "review":
        fields.One2Many("ecom.product.review", "product_id", "Product Review"),
        "wishlist":
        fields.One2Many("ecom.wishlist", "product_id", "Wishlist"),
        "has_sample":
        fields.Boolean("Has Sample", function="check_sample"),
        "avg_rate":
        fields.Integer("Average Rating", function="get_rating"),
    }

    def check_sample(self, ids, context={}):
        vals = {}
        for obj in self.browse(ids):
            product = obj
            sample = False
            for img in product.images:
                if img.title:
                    if "SAMPLE_OPTS_" in img.title:
                        sample = True
            vals[obj.id] = sample
        return vals

    def get_rating(self, ids, context={}):
        vals = {}
        for obj in self.browse(ids):
            score = []
            for review in get_model("ecom.product.review").search_browse(
                [["product_id", "=", obj.id], ["state", "=", "approved"]]):
                score.append(int(review.rating or 0))
            vals[obj.id] = sum(score) / len(score) if score else 0
        return vals
class CustomOption(Model):
    _name = "product.custom.option"
    _string = "Custom Option"
    _key = ["code"]
    _fields = {
        "name":
        fields.Char("Name", required=True, search=True, translate=True),
        "seq":
        fields.Char("Sequence", required=True),
        "code":
        fields.Char("Code", search=True),
        "type":
        fields.Selection([["text", "Text"], ["selection", "Selection"]],
                         "Type",
                         required=True),
        "required":
        fields.Boolean("Required"),
        "description":
        fields.Text("Description"),
        "price":
        fields.Decimal("Price"),
        "values":
        fields.One2Many("product.custom.option.value", "cust_opt_id",
                        "Values"),
    }
    _defaults = {
        "type": "text",
        "seq": '0',
    }
Example #6
0
class InlineHelp(Model):
    _name = "inline.help"
    _string = "Help Item"
    _fields = {
        "action": fields.Char("Action Name", required=True, search=True),
        "title": fields.Char("Help Title", required=True, search=True),
        "content": fields.Text("Help Content", required=True, search=True),
        "hide": fields.Boolean("Hide"),
        "create_date": fields.DateTime("Date Created"),
        "modif_date": fields.DateTime("Date Modified"),
    }
    _order = "title"
    _defaults = {
        "create_date": lambda *a: time.strftime("%Y-%m-%d %H:%M:%S"),
        "modif_date": lambda *a: time.strftime("%Y-%m-%d %H:%M:%S"),
    }

    def create(self, vals, **kw):
        res = super().create(vals, **kw)
        static.clear_translations()  # XXX: rename this
        return res

    def write(self, ids, vals, **kw):
        super().write(ids, vals, **kw)
        static.clear_translations()  # XXX: rename this

    def delete(self, ids, **kw):
        super().delete(ids, **kw)
        static.clear_translations()  # XXX: rename this
Example #7
0
class Settings(Model):
    _name = "hr.payroll.settings"
    _fields = {
        "tax_rates": fields.One2Many("hr.tax.rate", "settings_id",
                                     "Tax Rates"),
        "social_rate": fields.Decimal("Rate (%)"),
        "social_min_wage": fields.Decimal("Min Wage Per Month"),
        "social_max_wage": fields.Decimal("Max Wage Per Month"),
        "comments": fields.One2Many("message", "related_id", "Comments"),
        "social_number": fields.Char("SSO Identification No."),
        "prov_name": fields.Char("Fund Name"),
        "child_alw_limit": fields.Integer("Limit to Children"),
        "child_alw_limit": fields.Integer("Limit to Children"),
        'journal_id': fields.Many2One("account.journal", "Journal"),
        'bank_account_id': fields.Many2One("account.account", "Bank Account"),
        'sso_account_id': fields.Many2One("account.account", "SSO Account"),
        'sso_comp_support': fields.Boolean("SSO Company Support"),
        'intg_acc': fields.Boolean("Integrate to Account"),
    }
Example #8
0
class PurchaseType(Model):
    _name = "purchase.type"
    _string = "Purchase Type"
    _fields = {
        "name": fields.Char("Name", required=True),
        "parent_id": fields.Many2One("purchase.type", "Parent"),
        "description": fields.Char("Description"),
        "commission_po": fields.Boolean("Commission Purchase"),
        "comments": fields.One2Many("message", "related_id", "Comments"),
    }
    _order = "name"
Example #9
0
class BankAccount(Model):
    _name = "bank.account"
    _fields = {
        "bank_id": fields.Many2One("bank", "Bank", required=True),
        "branch": fields.Char("Branch"),
        "number": fields.Char("Account Number", required=True),
        "signatory": fields.Char("Signatory"),
        "online": fields.Boolean("Online"),
        "contact_id": fields.Many2One("contact",
                                      "Partner",
                                      on_delete="cascade"),
    }
Example #10
0
class Holiday(Model):
    _name = "hr.holiday"
    _string = "Holiday"
    _fields = {
        "name": fields.Char("Name", search=True),
        "date": fields.Date("Date", required=True, search=True),
        "description": fields.Text("Description"),
        "comments": fields.One2Many("message", "related_id", "Comments"),
        'generic': fields.Boolean("Generic"),
    }
    _defaults = {
        "date": lambda *a: time.strftime("%Y-%m-%d"),
        "comday": False,
        'generic': False,
    }
    _order = "date"

    _sql_constraints = [
        ("hr_holiday_date_uniq", "unique (date)", "Date should be unique"),
    ]

    def get_holidays(self, context={}):
        date_from = context.get('start_date', time.strftime(FMT_DAY))
        date_to = context.get('end_date', time.strftime(FMT_DAY))
        cond = [
            ['date', '>=', date_from],
            ['date', '<=', date_to],
        ]
        res = set()
        for r in self.search_read(cond, ['date']):
            res.update({r['date']})
        yearnow, month, date = time.strftime(FMT_DAY).split("-")
        cond = [['generic', '=', True]]
        for r in self.search_read(cond, ['date']):
            y, m, d = r['date'].split("-")
            date = '-'.join([yearnow, m, d])
            res.update({date})
        return list(res)

    def is_holiday(self,ds):
        d=datetime.strptime(ds,"%Y-%m-%d")
        w=d.weekday()
        if w==5 or w==6:
            return True
        res=self.search([["date","=",ds]])
        if res:
            return True
        return False
Example #11
0
class Journal(Model):
    _name = "account.journal"
    _string = "Journal"
    _key = ["code"]
    _fields = {
        "name": fields.Char("Name", required=True, search=True),
        "sequence_id": fields.Many2One("sequence",
                                       "Sequence",
                                       multi_company=True),
        "comments": fields.One2Many("message", "related_id", "Comments"),
        "active": fields.Boolean("Active"),
        "code": fields.Char("Code", search=True),
    }
    _defaults = {
        "active": True,
    }
Example #12
0
class Notification(Model):
    _name = "hr.notification"
    _fields = {
        "subject": fields.Char("Title", required=True),
        "description": fields.Text("Description"),
        'birthday_ntf': fields.Boolean("Birthday Notify"),
    }

    def birthday_notify(self, context={}):
        db = database.get_connection()
        cr_time = time.strftime("%Y-%m-%d %H:%M:%S")
        cr_yyyy = cr_time[0:4]
        cr_mm = cr_time[5:7]
        cr_dd = cr_time[8:10]
        today = "%s%s" % (cr_dd, cr_mm)
        print(cr_time, " checking birthday")
        subject = "Happy Birth Day"
        body = subject
        ntf = get_model("hr.notification").browse(1)
        if ntf:
            subject = ntf.subject
            body = ntf.description
        count = 0
        for emp in get_model("hr.employee").search_browse([['work_status', '=', 'working']]):
            if emp.birth_date:
                mm = emp.birth_date[5:7]
                dd = emp.birth_date[8:10]
                emp_date = "%s%s" % (dd, mm)
                if emp_date == today:
                    user_id = emp.user_id.id
                    sql = "select id from message where related_id='hr.employee,%s' and extract(year from create_time) = %s"
                    res = db.query(sql, emp.id, cr_yyyy)
                    if not res:
                        if emp.email:
                            self.trigger([emp.id], "birthday_notify")
                            print("happby birthday %s %s" % (emp.first_name, emp.last_name))
                        if user_id:
                            vals = {
                                'subject': subject,
                                'to_id': user_id,
                                'body': body,
                                "related_id": "hr.employee,%s" % emp.id,
                            }
                            msg_id = get_model("message").create(vals)
                            print("created message #", msg_id)
                        count += 1
        print("SEND TOTOAL: #", count)
Example #13
0
class ProfileAccess(Model):
    _name = "profile.access"
    _key = ["profile_id", "model_id"]
    _fields = {
        "profile_id": fields.Many2One("profile", "Profile", required=True, on_delete="cascade"),
        "model_id": fields.Many2One("model", "Model", required=True),
        "perm_read": fields.Boolean("Read"),
        "perm_create": fields.Boolean("Create"),
        "perm_write": fields.Boolean("Write"),
        "perm_delete": fields.Boolean("Delete"),
        "view_all": fields.Boolean("View All"),
        "modif_all": fields.Boolean("Modify All"),
    }
Example #14
0
class CreateDB(Model):
    _name = "create_db"
    _store = False
    _fields = {
        "super_password": fields.Char("Super Admin Password", required=True),
        "db_name": fields.Char("Database Name", required=True),
        "admin_password": fields.Char("Admin Password", required=True),
        "use_demo": fields.Boolean("Use demo data"),
    }

    def create_db(self, context={}):
        data = context["data"]
        if data["super_password"] != config.get("super_password"):
            raise Exception("Invalid super admin password")
        db_name = data["db_name"]
        admin_password = data["admin_password"]
        use_demo = data.get("use_demo")
        if use_demo:
            base_sql = pkg_resources.resource_string(
                "netforce_general", "data/base_demo.sql").decode()
        else:
            base_sql = pkg_resources.resource_string("netforce_general",
                                                     "data/base.sql").decode()
        print("creating db...")
        db = database.connect("template1")
        db._db.set_isolation_level(0)
        db.execute("CREATE DATABASE %s" % db_name)
        db.close()
        print("initializing db...")
        db = database.connect(db_name)
        db.execute(base_sql)
        db.execute(
            "UPDATE base_user SET name='Admin',login='******',password=%s WHERE id=1",
            admin_password)
        db.commit()
        print("done!")
        return {
            "next": {
                "name": "manage_db"
            },
            "flash": "Database created successfully",
        }
Example #15
0
class Language(Model):
    _name = "language"
    _string = "Language"
    _key = ["code"]
    _fields = {
        "name": fields.Char("Name", required=True, search=True),
        "code": fields.Char("Code", required=True),
        "num_translations": fields.Integer("Number of translations", function="get_num_translations"),
        "active": fields.Boolean("Active"),
        "comments": fields.One2Many("message", "related_id", "Comments"),
    }
    _defaults = {
        "active": True,
    }

    def create(self, vals, **kw):
        res = super().create(vals, **kw)
        static.clear_translations()
        return res

    def write(self, ids, vals, **kw):
        super().write(ids, vals, **kw)
        static.clear_translations()

    def delete(self, ids, **kw):
        super().delete(ids, **kw)
        static.clear_translations()

    def get_num_translations(self, ids, context={}):
        db = database.get_connection()
        res = db.query(
            "SELECT lang_id,COUNT(*) AS num FROM translation WHERE lang_id IN %s GROUP BY lang_id", tuple(ids))
        vals = {r.lang_id: r.num for r in res}
        return vals

    def get_active_langs(self):
        db = database.get_connection()
        res = db.query("SELECT code,name FROM language WHERE active=true ORDER BY name")
        active_langs = [dict(r) for r in res]
        return active_langs
Example #16
0
class Model(Model):
    _name = "model"
    _string = "Model"
    _name_field = "string"
    _key = ["name"]
    _fields = {
        "name": fields.Char("Name", required=True, search=True),
        "string": fields.Char("String", required=True, search=True),
        "fields": fields.One2Many("field", "model_id", "Fields"),
        "code": fields.Text("Code"),
        "order": fields.Char("Order"),
        "description": fields.Text("Description"),
        "offline": fields.Boolean("Offline"),
        "module_id": fields.Many2One("module", "Module"),
    }
    _order = "name"

    def name_search_multi(self,
                          name,
                          models,
                          condition=[],
                          limit=None,
                          context={}):
        for model in models:
            m = get_model(model)
            res = m.name_search(name,
                                condition=condition,
                                limit=limit,
                                context=context)
            if res:
                return {
                    "model": model,
                    "values": res,
                }
        return {
            "model": None,
            "values": [],
        }
Example #17
0
class TaxComponent(Model):
    _name = "account.tax.component"
    _fields = {
        "tax_rate_id":
        fields.Many2One("account.tax.rate",
                        "Tax Rate",
                        required=True,
                        on_delete="cascade"),
        "name":
        fields.Char("Name", required=True),
        "compound":
        fields.Boolean("Compound"),
        "rate":
        fields.Decimal("Rate", required=True),
        "account_id":
        fields.Many2One("account.account", "Account", multi_company=True),
        "type":
        fields.Selection(
            [["vat", "VAT"], ["vat_exempt", "VAT Exempt"],
             ["vat_defer", "Deferred VAT"], ["wht", "Withholding Tax"]],
            "Tax Type"),
        "trans_type":
        fields.Selection([["out", "Sale"], ["in", "Purchase"]],
                         "Transaction Type"),
        "description":
        fields.Text("Description"),
    }
    _defaults = {
        "rate": 0,
    }

    def name_get(self, ids, context={}):
        vals = []
        for obj in self.browse(ids):
            name = "%s - %s" % (obj.tax_rate_id.name, obj.name)
            vals.append((obj.id, name))
        return vals
Example #18
0
class Cart(Model):
    _name = "ecom2.cart"
    _string = "Cart"
    _name_field = "number"
    _audit_log = True
    _fields = {
        "number":
        fields.Char("Number", required=True, search=True),
        "date":
        fields.DateTime("Date Created", required=True, search=True),
        "customer_id":
        fields.Many2One("contact", "Customer", search=True),
        "lines":
        fields.One2Many("ecom2.cart.line", "cart_id", "Lines"),
        "ship_amount_details":
        fields.Json("Shipping Amount Details",
                    function="get_ship_amount_details"),
        "amount_ship":
        fields.Decimal("Shipping Amount", function="get_amount_ship"),
        "amount_total":
        fields.Decimal("Total Amount", function="get_total"),
        "sale_orders":
        fields.One2Many("sale.order", "related_id", "Sales Orders"),
        "delivery_date":
        fields.Date("Delivery Date"),
        "ship_address_id":
        fields.Many2One("address", "Shipping Address"),
        "bill_address_id":
        fields.Many2One("address", "Billing Address"),
        "delivery_slot_id":
        fields.Many2One("delivery.slot", "Peferred Delivery Slot"),
        "ship_method_id":
        fields.Many2One("ship.method", "Shipping Method"),
        "pay_method_id":
        fields.Many2One("payment.method", "Payment Method"),
        "logs":
        fields.One2Many("log", "related_id", "Audit Log"),
        "state":
        fields.Selection([["draft", "Draft"], ["confirmed", "Confirmed"],
                          ["canceled", "Canceled"]],
                         "Status",
                         required=True),
        "payment_methods":
        fields.Json("Payment Methods", function="get_payment_methods"),
        "delivery_delay":
        fields.Integer("Delivery Delay (Days)", function="get_delivery_delay"),
        "delivery_slots":
        fields.Json("Delivery Slots", function="get_delivery_slots"),
        "delivery_slots_str":
        fields.Text("Delivery Slots", function="get_delivery_slots_str"),
        "date_delivery_slots":
        fields.Json("Date Delivery Slots", function="get_date_delivery_slots"),
        "comments":
        fields.Text("Comments"),
        "transaction_no":
        fields.Char("Payment Transaction No.", search=True),
        "currency_id":
        fields.Many2One("currency", "Currency", required=True),
        "invoices":
        fields.One2Many("account.invoice", "related_id", "Invoices"),
        "company_id":
        fields.Many2One("company", "Company"),
        "voucher_id":
        fields.Many2One("sale.voucher", "Voucher"),
        "ship_addresses":
        fields.Json("Shipping Addresses", function="get_ship_addresses"),
        "amount_voucher":
        fields.Decimal("Voucher Amount",
                       function="get_amount_voucher",
                       function_multi=True),
        "voucher_error_message":
        fields.Text("Voucher Error Message",
                    function="get_amount_voucher",
                    function_multi=True),
        "free_ship_address":
        fields.Boolean("Is free Ship"),
    }
    _order = "date desc"

    def _get_number(self, context={}):
        seq_id = get_model("sequence").find_sequence(type="ecom_cart")
        if not seq_id:
            return None
        while 1:
            num = get_model("sequence").get_next_number(seq_id,
                                                        context=context)
            if not num:
                return None
            user_id = access.get_active_user()
            access.set_active_user(1)
            res = self.search([["number", "=", num]])
            access.set_active_user(user_id)
            if not res:
                return num
            get_model("sequence").increment_number(seq_id, context=context)

    def _get_currency(self, context={}):
        res = get_model("company").search([])  # XXX
        if not res:
            return
        company_id = res[0]
        access.set_active_company(company_id)
        settings = get_model("settings").browse(1)
        return settings.currency_id.id

    def _get_company(self, context={}):
        res = get_model("company").search([])  # XXX
        if res:
            return res[0]

    def _get_ship_method(self, context={}):
        res = get_model("ship.method").search([], order="sequence")
        if res:
            return res[0]

    _defaults = {
        "date": lambda *a: time.strftime("%Y-%m-%d %H:%M:%S"),
        "number": _get_number,
        "state": "draft",
        "currency_id": _get_currency,
        "company_id": _get_company,
        "ship_method_d": _get_ship_method,
        "free_ship_address": False,
    }

    def get_ship_amount_details(self, ids, context={}):
        print("get_ship_amount_details", ids)
        vals = {}
        for obj in self.browse(ids):
            delivs = []
            for line in obj.lines:
                date = line.delivery_date
                meth_id = line.ship_method_id.id or line.cart_id.ship_method_id.id
                addr_id = line.ship_address_id.id or line.cart_id.ship_address_id.id
                if not date or not meth_id or not addr_id:
                    continue
                delivs.append((date, meth_id, addr_id))
            delivs = list(set(delivs))
            details = []
            for date, meth_id, addr_id in delivs:
                ctx = {
                    "contact_id": obj.customer_id.id,
                    "ship_address_id": addr_id,
                }
                meth = get_model("ship.method").browse(meth_id, context=ctx)
                details.append({
                    "ship_addr_id": addr_id,
                    "date": date,
                    "ship_method_id": meth.id,
                    "ship_amount": meth.ship_amount,
                })
            vals[obj.id] = details
        return vals

    def get_amount_ship(self, ids, context={}):
        print("get_amount_ship", ids)
        vals = {}
        for obj in self.browse(ids):
            ship_amt = 0
            for d in obj.ship_amount_details:
                ship_amt += d["ship_amount"] or 0
            vals[obj.id] = ship_amt
        return vals

    def get_total(self, ids, context={}):
        vals = {}
        for obj in self.browse(ids):
            amt = 0
            for line in obj.lines:
                amt += line.amount
            vals[obj.id] = amt + obj.amount_ship - obj.amount_voucher
        return vals

    def get_payment_methods(self, ids, context={}):
        res = []
        for obj in get_model("payment.method").search_browse([]):
            res.append({
                "id": obj.id,
                "name": obj.name,
            })
        return {ids[0]: res}

    def confirm(self, ids, context={}):
        obj = self.browse(ids[0])
        user_id = context.get("user_id")  # XX: remove this
        if user_id:
            user_id = int(user_id)
            user = get_model("base.user").browse(user_id)
            if user.contact_id:
                obj.write({"customer_id": user.contact_id.id})
        access.set_active_company(1)  # XXX
        vals = {
            "contact_id": obj.customer_id.id,
            "ship_address_id": obj.ship_address_id.id,
            "ship_method_id": obj.ship_method_id.id,
            "bill_address_id": obj.bill_address_id.id,
            "due_date": obj.delivery_date,
            "lines": [],
            "related_id": "ecom2.cart,%s" % obj.id,
            "delivery_slot_id": obj.delivery_slot_id.id,
            "pay_method_id": obj.pay_method_id.id,
            "other_info": obj.comments,
            "voucher_id": obj.voucher_id.id,
            "ref": obj.comments,  # XXX
        }
        print(">>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>Ship Method Id ",
              ship_method_id)
        for line in obj.lines:
            prod = line.product_id
            if line.lot_id and line.qty_avail <= 0:
                raise Exception("Lot is out of stock (%s)" % prod.name)
            if not prod.locations:
                raise Exception("Can't find location for product %s" %
                                prod.code)
            location_id = prod.locations[0].location_id.id
            line_vals = {
                "product_id": prod.id,
                "description": prod.description,
                "qty": line.qty,
                "uom_id": line.uom_id.id,
                "unit_price": line.unit_price,
                "location_id": location_id,
                "lot_id": line.lot_id.id,
                "due_date": line.delivery_date,
                "delivery_slot_id": obj.delivery_slot_id.id,
                "ship_address_id": line.ship_address_id.id,
            }
            vals["lines"].append(("create", line_vals))
        for ship in obj.ship_amount_details:
            meth_id = ship["ship_method_id"]
            amount = ship["ship_amount"]
            meth = get_model("ship.method").browse(meth_id)
            prod = meth.product_id
            if not prod:
                raise Exception("Missing product in shipping method %s" %
                                meth.name)
            line_vals = {
                "product_id": prod.id,
                "description": prod.description,
                "qty": 1,
                "uom_id": prod.uom_id.id,
                "unit_price": amount,
            }
            vals["lines"].append(("create", line_vals))
        sale_id = get_model("sale.order").create(vals)
        sale = get_model("sale.order").browse(sale_id)
        sale.confirm()
        obj.write({"state": "confirmed"})
        obj.trigger("confirm_order")
        return {
            "sale_id": sale_id,
        }

    def cancel_order(self, ids, context={}):
        obj = self.browse(ids[0])
        for sale in obj.sale_orders:
            if sale.state == "voided":
                continue
            for inv in sale.invoices:
                if inv.state != "voided":
                    raise Exception(
                        "Can not cancel order %s because there are linked invoices"
                        % sale.number)
            for pick in sale.pickings:
                if pick.state == "voided":
                    continue
                pick.void()
            sale.void()
        for inv in obj.invoices:
            if inv.state != "voided":
                raise Exception(
                    "Can not cancel cart %s because there are linked invoices"
                    % obj.number)
        obj.write({"state": "canceled"})

    def payment_received(self, ids, context={}):
        obj = self.browse(ids[0])
        if obj.state != "waiting_payment":
            raise Exception("Cart is not waiting payment")
        res = obj.sale_orders.copy_to_invoice()
        inv_id = res["invoice_id"]
        inv = get_model("account.invoice").browse(inv_id)
        inv.write({"related_id": "ecom2.cart,%s" % obj.id})
        inv.post()
        method = obj.pay_method_id
        if not method:
            raise Exception("Missing payment method in cart %s" % obj.number)
        if not method.account_id:
            raise Exception("Missing account in payment method %s" %
                            method.name)
        transaction_no = context.get("transaction_no")
        pmt_vals = {
            "type": "in",
            "pay_type": "invoice",
            "contact_id": inv.contact_id.id,
            "account_id": method.account_id.id,
            "lines": [],
            "company_id": inv.company_id.id,
            "transaction_no": transaction_no,
        }
        line_vals = {
            "invoice_id": inv_id,
            "amount": inv.amount_total,
        }
        pmt_vals["lines"].append(("create", line_vals))
        pmt_id = get_model("account.payment").create(pmt_vals,
                                                     context={"type": "in"})
        get_model("account.payment").post([pmt_id])
        obj.write({"state": "paid"})
        for sale in obj.sale_orders:
            for pick in sale.pickings:
                if pick.state == "pending":
                    pick.approve()

    def to_draft(self, ids, context={}):
        obj = self.browse(ids[0])
        obj.write({"state": "draft"})

    def set_qty(self, ids, prod_id, qty, context={}):
        print("Cart.set_qty", ids, prod_id, qty)
        obj = self.browse(ids[0])
        line_id = None
        for line in obj.lines:
            if line.product_id.id == prod_id and not line.lot_id:
                line_id = line.id
                break
        if line_id:
            if qty == 0:
                get_model("ecom2.cart.line").delete([line_id])
            else:
                get_model("ecom2.cart.line").write([line_id], {"qty": qty})
        else:
            if qty != 0:
                get_model("ecom2.cart.line").create({
                    "cart_id": obj.id,
                    "product_id": prod_id,
                    "qty": qty
                })

    def set_date_qty(self, ids, due_date, prod_id, qty, context={}):
        print("Cart.set_date_qty", ids, due_date, prod_id, qty)
        obj = self.browse(ids[0])
        line_id = None
        for line in obj.lines:
            if line.delivery_date == due_date and line.product_id.id == prod_id and not line.lot_id:
                line_id = line.id
                break
        if line_id:
            if qty == 0:
                get_model("ecom2.cart.line").delete([line_id])
            else:
                get_model("ecom2.cart.line").write([line_id], {"qty": qty})
        else:
            if qty != 0:
                ctx = {
                    "cart_id": obj.id,
                    "delivery_date": due_date,
                    "product_id": prod_id
                }
                get_model("ecom2.cart.line").create(
                    {
                        "cart_id": obj.id,
                        "product_id": prod_id,
                        "qty": qty,
                        "delivery_date": due_date
                    },
                    context=ctx)

    def add_lot(self, ids, prod_id, lot_id, context={}):
        print("Cart.add_lot", ids, prod_id, lot_id)
        obj = self.browse(ids[0])
        line_id = None
        for line in obj.lines:
            if line.product_id.id == prod_id and line.lot_id.id == lot_id:
                line_id = line.id
                break
        if line_id:
            raise Exception("Lot already added to cart")
        get_model("ecom2.cart.line").create({
            "cart_id": obj.id,
            "product_id": prod_id,
            "lot_id": lot_id,
            "qty": 1
        })

    def remove_lot(self, ids, prod_id, lot_id, context={}):
        obj = self.browse(ids[0])
        line_id = None
        for line in obj.lines:
            if line.product_id.id == prod_id and line.lot_id.id == lot_id:
                line_id = line.id
                break
        if not line_id:
            raise Exception("Lot not found in cart")
        get_model("ecom2.cart.line").delete([line_id])

    def set_qty_auto_select_lot(self, ids, prod_id, qty, context={}):
        print("Cart.set_qty_auto_select_lot", ids, prod_id, qty)
        obj = self.browse(ids[0])
        cur_qty = 0
        for line in obj.lines:
            if line.product_id.id == prod_id:
                cur_qty += line.qty
        diff_qty = qty - cur_qty
        if diff_qty > 0:
            self.add_lots_auto_select(ids, prod_id, diff_qty, context=context)
        elif diff_qty < 0:
            self.remove_lots_auto_select(ids,
                                         prod_id,
                                         -diff_qty,
                                         context=context)

    def add_lots_auto_select(self, ids, prod_id, add_qty, context={}):
        print("Cart.add_lots_auto_select", ids, prod_id, add_qty)
        obj = self.browse(ids[0])
        exclude_lot_ids = []
        for line in obj.lines:
            if line.product_id.id == prod_id and line.lot_id:
                exclude_lot_ids.append(line.lot_id.id)
        prod = get_model("product").browse(prod_id)
        avail_qty = prod.stock_qty
        add_lot_ids = []
        for lot in prod.stock_lots:
            if len(add_lot_ids) >= avail_qty:
                break
            if lot.id not in exclude_lot_ids:
                add_lot_ids.append(lot.id)
                if len(add_lot_ids) >= add_qty:
                    break
        print("add_lot_ids", add_lot_ids)
        for lot_id in add_lot_ids:
            get_model("ecom2.cart.line").create({
                "cart_id": obj.id,
                "product_id": prod_id,
                "lot_id": lot_id,
                "qty": 1
            })
        remain_qty = add_qty - len(add_lot_ids)
        print("remain_qty", remain_qty)
        if remain_qty > 0:
            found_line = None
            for line in obj.lines:
                if line.product_id.id == prod_id and not line.lot_id:
                    found_line = line
                    break
            if found_line:
                found_line.write({"qty": found_line.qty + remain_qty})
            else:
                get_model("ecom2.cart.line").create({
                    "cart_id": obj.id,
                    "product_id": prod_id,
                    "qty": remain_qty
                })

    def remove_lots_auto_select(self, ids, prod_id, remove_qty, context={}):
        print("Cart.remove_lots_auto_select", ids, prod_id, remove_qty)
        obj = self.browse(ids[0])
        remain_qty = remove_qty
        for line in obj.lines:
            if line.product_id.id == prod_id and not line.lot_id:
                if line.qty <= remain_qty:
                    remain_qty -= line.qty
                    line.delete()
                else:
                    line.write({"qty": line.qty - remain_qty})
                    remain_qty = 0
        print("remain_qty", remain_qty)
        if remain_qty > 0:
            lots = []
            for line in obj.lines:
                if line.product_id.id == prod_id and line.lot_id:
                    d = line.lot_id.received_date or "1900-01-01"
                    lots.append((d, line.id))
            lots.sort()
            del_ids = []
            for d, line_id in lots:
                del_ids.append(line_id)
                if len(del_ids) >= remain_qty:
                    break
            print("del_ids", del_ids)
            get_model("ecom2.cart.line").delete(del_ids)

    def get_delivery_delay(self, ids, context={}):
        vals = {}
        for obj in self.browse(ids):
            vals[obj.id] = max(l.delivery_delay
                               for l in obj.lines) if obj.lines else 0
        return vals

    def get_delivery_slots(self, ids, context={}):
        print("get_delivery_slots", ids)
        obj = self.browse(ids[0])
        settings = get_model("ecom2.settings").browse(1)
        max_days = settings.delivery_max_days
        if not max_days:
            return {obj.id: []}
        min_hours = settings.delivery_min_hours or 0
        d_from = date.today() + timedelta(days=obj.delivery_delay)
        d_to = d_from + timedelta(days=max_days)
        d = d_from
        slots = []
        for slot in get_model("delivery.slot").search_browse([]):
            slots.append([slot.id, slot.name, slot.time_from])
        slot_num_sales = {}
        for sale in get_model("sale.order").search_browse(
            [["plr_order_type", "=", "grocery"],
             ["date", ">=", time.strftime("%Y-%m-%d")]]):
            k = (sale.due_date, sale.delivery_slot_id.id)
            slot_num_sales.setdefault(k, 0)
            slot_num_sales[k] += 1
        print("slot_num_sales", slot_num_sales)
        slot_caps = {}
        for cap in get_model("delivery.slot.capacity").search_browse([]):
            k = (cap.slot_id.id, int(cap.weekday))
            slot_caps[k] = cap.capacity
        print("slot_caps", slot_caps)
        delivery_weekdays = None
        for line in obj.lines:
            prod = line.product_id
            if prod.delivery_weekdays:
                days = [int(w) for w in prod.delivery_weekdays.split(",")]
                if delivery_weekdays == None:
                    delivery_weekdays = days
                else:
                    delivery_weekdays = [
                        d for d in delivery_weekdays if d in days
                    ]
        days = []
        now = datetime.now()
        tomorrow = now + timedelta(days=1)
        tomorrow_seconds = 0
        if settings.work_time_start and settings.work_time_end:
            today_end = datetime.strptime(
                date.today().strftime("%Y-%m-%d") + " " +
                settings.work_time_end, "%Y-%m-%d %H:%M")
            tomorrow_start = datetime.strptime(
                tomorrow.strftime("%Y-%m-%d") + " " + settings.work_time_start,
                "%Y-%m-%d %H:%M")
            if now < today_end and now.weekday() != 6:
                remain_seconds = (today_end - now).total_seconds()
            else:
                remain_seconds = 0
            if remain_seconds < min_hours * 3600:
                tomorrow_seconds = min_hours * 3600 - remain_seconds
        print("tomorrow_seconds=%s" % tomorrow_seconds)
        while d <= d_to:
            ds = d.strftime("%Y-%m-%d")
            res = get_model("hr.holiday").search([["date", "=", ds]])
            if res:
                d += timedelta(days=1)
                continue
            w = d.weekday()
            if w == 6 or delivery_weekdays is not None and w not in delivery_weekdays:
                d += timedelta(days=1)
                continue
            day_slots = []
            for slot_id, slot_name, from_time in slots:
                t_from = datetime.strptime(ds + " " + from_time + ":00",
                                           "%Y-%m-%d %H:%M:%S")
                capacity = slot_caps.get((slot_id, w))
                num_sales = slot_num_sales.get((ds, slot_id), 0)
                state = "avail"
                if d == now.date():
                    if t_from < now or (t_from - now).total_seconds(
                    ) < min_hours * 3600 or tomorrow_seconds:
                        state = "full"
                elif d == tomorrow.date() and tomorrow_seconds:
                    if (t_from -
                            tomorrow_start).total_seconds() < tomorrow_seconds:
                        state = "full"
                if capacity is not None and num_sales >= capacity:
                    state = "full"
                day_slots.append(
                    [slot_id, slot_name, state, num_sales, capacity])
            days.append([ds, day_slots])
            d += timedelta(days=1)
        print("days:")
        pprint(days)
        return {obj.id: days}

    def get_delivery_slots_str(self, ids, context={}):
        vals = {}
        for obj in self.browse(ids):
            s = ""
            for d, slots in obj.delivery_slots:
                s += "- Date: %s\n" % d
                for slot_id, name, state, num_sales, capacity in slots:
                    s += "    - %s: %s (%s/%s)\n" % (name, state, num_sales,
                                                     capacity or "-")
            vals[obj.id] = s
        return vals

    def get_date_delivery_slots(self, ids, context={}):
        print("get_date_delivery_slots", ids)
        obj = self.browse(ids[0])
        slots = []
        for slot in get_model("delivery.slot").search_browse([]):
            slots.append([slot.id, slot.name])
        dates = []
        for line in obj.lines:
            d = line.delivery_date
            if d:
                dates.append(d)
        dates = list(set(dates))
        date_slots = {}
        for d in dates:
            date_slots[d] = slots  # TODO: use capacity?
        return {obj.id: date_slots}

    def get_ship_addresses(self, ids, context={}):
        obj = self.browse(ids[0])
        settings = get_model("ecom2.settings").browse(1)
        contact = obj.customer_id
        addrs = []
        if contact:
            for a in contact.addresses:
                addr_vals = {
                    "id": a.id,
                    "name": a.address,
                }
                if obj.ship_method_id:  # TODO: handle general case for different shipping methods per order
                    meth_id = obj.ship_method_id.id
                    ctx = {"ship_address_id": a.id}
                    meth = get_model("ship.method").browse(meth_id,
                                                           context=ctx)
                    addr_vals["ship_amount"] = meth.ship_amount
                else:
                    addr_vals["ship_amount"] = 0
                addrs.append(addr_vals)
        for a in settings.extra_ship_addresses:
            addr_vals = {
                "id": a.id,
                "name": a.address or "",
            }
            if obj.ship_method_id:
                meth_id = obj.ship_method_id.id
                ctx = {"ship_address_id": a.id}
                meth = get_model("ship.method").browse(meth_id, context=ctx)
                addr_vals["ship_amount"] = meth.ship_amount
            else:
                addr_vals["ship_amount"] = 0
            addrs.append(addr_vals)
        return {obj.id: addrs}

    def apply_voucher_code(self, ids, voucher_code, context={}):
        obj = self.browse(ids[0])
        res = get_model("sale.voucher").search([["code", "=", voucher_code]])
        if not res:
            raise Exception("Invalid voucher code")
        voucher_id = res[0]
        obj.write({"voucher_id": voucher_id})

    def clear_voucher(self, ids, context={}):
        obj = self.browse(ids[0])
        obj.write({"voucher_id": None})

    def get_amount_voucher(self, ids, context={}):
        print("get_amount_voucher", ids)
        vals = {}
        for obj in self.browse(ids):
            voucher = obj.voucher_id
            if voucher:
                ctx = {
                    "contact_id": obj.customer_id.id,
                    "amount_total": 0,
                    "products": [],
                }
                for line in obj.lines:
                    ctx["amount_total"] += line.amount
                    ctx["products"].append({
                        "product_id": line.product_id.id,
                        "unit_price": line.unit_price,
                        "qty": line.qty,
                        "uom_id": line.uom_id.id,
                        "amount": line.amount,
                    })
                ctx["amount_total"] += obj.amount_ship
                res = voucher.apply_voucher(context=ctx)
                disc_amount = res.get("discount_amount", 0)
                error_message = res.get("error_message")
            else:
                disc_amount = 0
                error_message = None
            vals[obj.id] = {
                "amount_voucher": disc_amount,
                "voucher_error_message": error_message,
            }
        return vals

    def update_date_delivery(self, ids, date, vals, context={}):
        print("cart.update_date_delivery", ids, date, vals)
        obj = self.browse(ids[0])
        settings = get_model("ecom2.settings").browse(1)
        for line in obj.lines:
            if line.delivery_date == date:
                line.write(vals)
        #for a in settings.extra_ship_addresses:
        #if a.id == vals['ship_address_id']:
        #return {'free_ship':True}
        return {'free_ship': False}

    def empty_cart(self, ids, context={}):
        obj = self.browse(ids[0])
        obj.write({"lines": [("delete_all", )]})

    def check_due_dates(self, ids, context={}):
        obj = self.browse(ids[0])
        today = time.strftime("%Y-%m-%d")
        if obj.delivery_date and obj.delivery_date < today:
            raise Exception("Delivery date is in the past %s" %
                            obj.delivery_date)
        for line in obj.lines:
            if line.delivery_date and line.delivery_date < today:
                raise Exception(
                    "Delivery date is in the past %s for product %s" %
                    (line.delivery_date, line.product_id.name))

    def free_ship_address(self, ids, context={}):
        obj = self.browse(ids[0])
        settings = get_model("ecom2.settings").browse(1)
        data = []
        for a in settings.extra_ship_addresses:
            if obj.ship_method_id:
                data["is_free"] = False
            else:
                data["is_free"] = True
            data.append(data)
        return {obj.id: addrs}
Example #19
0
class Employee(Model):
    _name = "hr.employee"
    _string = "Employee"
    _name_field = "first_name"  # XXX
    _multi_company = True
    _key = ["code", "company_id"]
    _export_field = "code"

    _fields = {
        "code":
        fields.Char("Employee Code", search=True),
        "department_id":
        fields.Many2One("hr.department", "Department", search=True),
        "title":
        fields.Selection(
            [["mr", "Mr."], ["mrs", "Mrs."], ["miss", "Miss"], ["ms", "Ms."]],
            "Title"),
        "first_name":
        fields.Char("First Name", search=True, translate=True),
        "last_name":
        fields.Char("Last Name", required=True, search=True, translate=True),
        "hire_date":
        fields.Date("Hire Date"),
        "work_status":
        fields.Selection([["working", "Working"], ["dismissed", "Dismissed"],
                          ["resigned", "Resigned"], ["died", "Died"]],
                         "Work Status"),
        "work_type":
        fields.Selection(
            [["monthly", "Monthly"], ["daily", "Daily"], ["hourly", "Job"]],
            "Work Type"),
        "resign_date":
        fields.Date("Resign Date"),
        "position":
        fields.Char("Position", search=True),
        "birth_date":
        fields.Date("Birth Date"),
        "age":
        fields.Integer("Age", function="get_age"),
        "gender":
        fields.Selection([["male", "Male"], ["female", "Female"]], "Gender"),
        "marital_status":
        fields.Selection([["single", "Single"], ["married", "Married"],
                          ["divorced", "Divorced"], ["widowed", "Widowed"]],
                         "Marital Status"),
        "addresses":
        fields.One2Many("address", "employee_id", "Address"),
        "id_no":
        fields.Char("ID No."),
        "drive_license_type":
        fields.Selection([["car", "Car"], ['motorcycle', 'Motorcycle']],
                         "Driving License"),
        "drive_license_no":
        fields.Char("Driving License No."),
        "country_id":
        fields.Many2One("country", "Country"),
        "bank_account":
        fields.Char("Bank Account"),
        "salary":
        fields.Decimal("Salary"),
        "picture":
        fields.File("Picture"),
        "tax_no":
        fields.Char("Taxpayer ID No."),
        "spouse_first_name":
        fields.Char("Spouse First Name"),
        "spouse_last_name":
        fields.Char("Spouse Last Name"),
        "spouse_title":
        fields.Selection([["mr", "Mr."], ["ms", "Ms."]], "Spouse Title"),
        "spouse_birth_date":
        fields.Date("Spouse Birth Date"),
        "spouse_tax_no":
        fields.Char("Spouse Tax ID No"),
        "spouse_status":
        fields.Selection(
            [["married", "Married existed throughout this tax year"],
             ["married_new", "Married during this tax year"],
             ["divorced", "Divorced during tax year"],
             ["deceased", "Deceased during tax year"]], "Spouse Status"),
        "spouse_filing_status":
        fields.Selection(
            [["joint", "Has income and file joint return"],
             ["separate", "Has income and file separate tax return"],
             ["no_income", "Has no income"]], "Spouse Filing Status"),
        "num_child1":
        fields.Integer("No. of Children #1 (C3)"),
        "num_child2":
        fields.Integer("No. of Children #2 (C3)"),
        "social_no":
        fields.Char("Social No."),
        "social_register":
        fields.Boolean("Register Soc. Secur."),
        "social_calc_method":
        fields.Selection(
            [["regular", "Regular Rate"], ["none", "Not Participate"],
             ["special", "Special Rate"]], "Calc. Method"),
        "prov_fund_no":
        fields.Char("Prov. Fund No."),
        "prov_open_date":
        fields.Char("Opened Prov. Fund A/C Date"),
        "prov_rate_employer":
        fields.Decimal("Employer Contribution (%)"),
        "prov_rate_employee":
        fields.Decimal("Employee Contribution (%)"),
        "gov_pension_fund":
        fields.Decimal("Gov. Pension Fund Amount (B2)"),
        "teacher_fund":
        fields.Decimal("Teacher Aid Fund Amount (B3)"),
        "old_disabled":
        fields.Decimal("Older than 65 or disabled (personal, B4)"),
        "old_disabled_spouse":
        fields.Decimal("Older than 65 or disabled (spouse, B5)"),
        "severance_pay":
        fields.Decimal("Severance Pay (B6)"),
        "education_donation":
        fields.Decimal("Education Donations (A8)"),
        "other_donation":
        fields.Decimal("Other Donations (A10)"),
        "house_deduct":
        fields.Decimal("Exemption for home buyer (A13)"),
        "wht_amount":
        fields.Decimal("Withholding Tax Amount (A15)"),
        "father_id_no":
        fields.Char("Father ID No. (C4)"),
        "mother_id_no":
        fields.Char("Mother ID No. (C4)"),
        "spouse_father_id_no":
        fields.Char("Father of spouse ID No. (C4)"),
        "spouse_mother_id_no":
        fields.Char("Mother of spouse ID No. (C4)"),
        "disabled_support":
        fields.Decimal("Disabled person support (C5)"),
        "parent_health_insurance":
        fields.Decimal("Parent Health Insurance (C6)"),
        "life_insurance":
        fields.Decimal("Life Insurance (C7)"),
        "retirement_mutual_fund":
        fields.Decimal("Retirement Mutual Fund (C9)"),
        "long_term_equity_fund":
        fields.Decimal("Long Term Equity Fund (C10)"),
        "interest_residence":
        fields.Decimal("Interest paid for residence (C11)"),
        "other_deduct":
        fields.Decimal("Other Deductions (C12)"),
        "comments":
        fields.One2Many("message", "related_id", "Comments"),
        "active":
        fields.Boolean("Active"),
        "time_in":
        fields.DateTime("Last Sign In",
                        function="get_attend",
                        function_multi=True),
        "time_out":
        fields.DateTime("Last Sign Out",
                        function="get_attend",
                        function_multi=True),
        "attend_state":
        fields.Selection([["absent", "Absent"], ["present", "Present"]],
                         "Status",
                         function="get_attend",
                         function_multi=True),
        "user_id":
        fields.Many2One("base.user", "User", search=True),
        "payslips":
        fields.One2Many("hr.payslip", "employee_id", "Payslips"),
        "documents":
        fields.One2Many("document", "related_id", "Documents"),
        "phone":
        fields.Char("Phone", search=True),
        "approver_id":
        fields.Many2One("base.user", "Approver"),
        "company_id":
        fields.Many2One("company", "Company"),
        "leave_types":
        fields.Many2Many("hr.leave.type", "Leave Types"),
        "attendance_id":
        fields.Integer("Attendance ID"),
        "email":
        fields.Char("Email", search=True),
        'profile_id':
        fields.Many2One("hr.payitem.profile", "Pay Item Profile"),
        'schedule_id':
        fields.Many2One("hr.schedule", "Working Schedule"),
        'leaves':
        fields.One2Many('hr.leave', 'employee_id', 'Leaves'),
    }

    def _get_code(self, context={}):
        while 1:
            code = get_model("sequence").get_number("employee")
            if not code:
                return None
            res = self.search([["code", "=", code]])
            if not res:
                return code
            get_model("sequence").increment("employee")

    _defaults = {
        "active": True,
        "work_status": "working",
        "code": _get_code,
        "company_id": lambda *a: get_active_company(),
    }
    _order = "code,first_name,last_name"

    def name_get(self, ids, context={}):
        vals = []
        for obj in self.browse(ids):
            if obj.first_name:
                name = obj.first_name + " " + obj.last_name
            else:
                name = obj.last_name
            if obj.code:
                name += " [%s]" % obj.code
            vals.append((obj.id, name))
        return vals

    def name_search(self, name, condition=[], limit=None, context={}):
        cond = [[
            "or", ["first_name", "ilike", "%" + name + "%"],
            ["last_name", "ilike", "%" + name + "%"],
            ["code", "ilike", "%" + name + "%"]
        ], condition]
        ids = self.search(cond, limit=limit)
        return self.name_get(ids, context)

    def get_age(self, ids, context={}):
        vals = {}
        cr_year = int(time.strftime('%Y'))
        for obj in self.browse(ids):
            if obj.birth_date:
                age = cr_year - int(obj.birth_date[0:4])
            else:
                age = 0
            vals[obj.id] = age
        return vals

    def get_attend(self, ids, context={}):
        vals = {}
        for obj in self.browse(ids):
            # user_id=obj.user_id.id
            # if user_id:
            # db=database.get_connection()
            #res=db.get("SELECT MAX(time) AS time_in FROM hr_attendance WHERE user_id=%s AND action='sign_in'",user_id)
            # time_in=res.time_in
            #res=db.get("SELECT MAX(time) AS time_out FROM hr_attendance WHERE user_id=%s AND action='sign_out'",user_id)
            # time_out=res.time_out
            # else:
            # time_in=None
            # time_out=None
            db = database.get_connection()
            res = db.get(
                "SELECT MAX(time) AS time_in FROM hr_attendance WHERE employee_id=%s AND action='sign_in'",
                obj.id)
            time_in = res.time_in
            res = db.get(
                "SELECT MAX(time) AS time_out FROM hr_attendance WHERE employee_id=%s AND action='sign_out'",
                obj.id)
            time_out = res.time_out
            if time_in:
                if time_out and time_out > time_in:
                    state = "absent"
                else:
                    today = time.strftime("%Y-%m-%d")
                    if time_in.startswith(today):
                        state = "present"
                    else:
                        state = "absent"
                    # should not show timeout of anotherday
                    # if not time_out.startswith(today):
                    # time_out=None
            else:
                state = "absent"
            vals[obj.id] = {
                "time_in": time_in,
                "time_out": time_out,
                "attend_state": state,
            }
        return vals

    def get_address(self, ids, context={}):
        obj = self.browse(ids)[0]
        if not obj.addresses:
            return ""
        addr = obj.addresses[0]
        res = addr.get_address_text()
        return res[addr.id]

    def onchange_num_child(self, context={}):
        data = context["data"]
        setting = get_model("hr.payroll.settings").browse(1)
        child_alw_limit = setting.child_alw_limit or 0
        child_total = (data['num_child1'] or 0) + (data['num_child2'] or 0)
        if child_alw_limit and child_total > child_alw_limit:
            data['num_child1'] = 0
            data['num_child2'] = 0
        return data
class TaxRate(Model):
    _name = "account.tax.rate"
    _string = "Tax Rate"
    _key = ["name"]
    _name_field = "name"
    _fields = {
        "name":
        fields.Char("Name", required=True, search=True),
        "code":
        fields.Char("Code", search=True),
        "type":
        fields.Selection([["out", "Sale"], ["in", "Purchase"]],
                         "Type",
                         required=True,
                         search=True),  #Use For Filter
        "rate":
        fields.Decimal("Tax Rate", function="get_rate", function_multi=True),
        "wht_rate":
        fields.Decimal("WHT Rate", function="get_rate", function_multi=True),
        "components":
        fields.One2Many("account.tax.component", "tax_rate_id", "Components"),
        "uuid":
        fields.Char("UUID"),
        "active":
        fields.Boolean("Active"),
        "comments":
        fields.One2Many("message", "related_id", "Comments"),
        "payer":
        fields.Selection(
            [["wht", "Withhold at source"], ["every_time", "Pay every time"],
             ["one_time", "Pay one time"], ["other", "Others"]], "Payer"),
        "note":
        fields.Text("Note"),
    }
    _defaults = {
        "uuid": lambda *a: str(uuid.uuid4()),
        "active": True,
    }
    _order = "name"

    def get_rate(self, ids, context={}):
        vals = {}
        for obj in self.browse(ids):
            rate = Decimal(0)
            wht_rate = Decimal(0)
            for comp in obj.components:
                if comp.type == "vat":
                    rate += comp.rate
                elif comp.type == "wht":
                    wht_rate += comp.rate
            res = {"rate": rate, "wht_rate": wht_rate}
            vals[obj.id] = res
        return vals

    def update_total(self, context={}):
        data = context["data"]
        data["rate"] = 0
        for comp in data["components"]:
            data["rate"] += comp["rate"]
        return data

    # XXX: remove this
    def compute_tax(self, tax_id, amt, tax_type="tax_ex", wht=False):
        pc_factor = Decimal(100)
        if not tax_id:
            return 0
        if tax_type == "no_tax":
            return 0
        obj = self.browse(tax_id)
        vat_rate = Decimal(0)
        wht_rate = Decimal(0)
        for comp in obj.components:
            if comp.type == "wht":
                wht_rate += comp.rate or 0
            elif comp.type == "vat":
                vat_rate += comp.rate or 0
        base_amt = Decimal(0)
        if tax_type == "tax_ex":
            base_amt = amt or 0
        elif tax_type == "tax_in":
            base_amt = (amt or 0) / (1 + vat_rate / pc_factor)
        base_amt = Decimal(base_amt)
        wht_rate = Decimal(wht_rate)
        vat_rate = Decimal(vat_rate)
        if wht:
            return base_amt * wht_rate / pc_factor
        else:
            return base_amt * vat_rate / pc_factor

    # XXX: remove this
    # (not used in payment)
    def compute_components(self,
                           tax_id,
                           amt,
                           tax_type="tax_ex",
                           when="invoice"):
        assert (when != "payment")  # XXX
        if tax_type == "no_tax":
            return {}
        obj = self.browse(tax_id)
        if tax_type == "tax_in":
            base_amt = amt / (1 + obj.rate / 100)
        else:
            base_amt = amt
        has_defer = False
        for comp in obj.components:
            if comp.type == "vat_defer":
                has_defer = True
        comps = {}
        for comp in obj.components:
            if comp.type == "wht":
                continue
            if has_defer and comp.type == "vat":
                continue
            comps[comp.id] = base_amt * (comp.rate / 100)
        return comps

    def compute_base(self, tax_id, amt=0, tax_type="tax_ex"):
        if isinstance(tax_id,
                      BrowseRecord):  # XXX: for speed (use browse cache)
            obj = tax_id
        else:
            obj = self.browse(tax_id)
        amt = Decimal(amt)
        base_amt = amt  # default
        if tax_type == "tax_in":
            base_amt = amt / (1 + obj.rate / 100)
        elif tax_type == "tax_ex":
            base_amt = amt
        return base_amt

    # TODO: use this in invoice/claim
    def compute_taxes(self, tax_id, base, when="invoice"):
        if isinstance(tax_id,
                      BrowseRecord):  # XXX: for speed (use browse cache)
            obj = tax_id
        else:
            obj = self.browse(tax_id)
        has_defer = False
        for comp in obj.components:
            if comp.type == "vat_defer":
                has_defer = True
        comps = {}
        for comp in obj.components:
            if when == "invoice":
                if comp.type in ("vat", "vat_exempt") and has_defer:
                    continue
                if comp.type == "wht":
                    continue
            elif when == "invoice_payment":
                if comp.type in ("vat", "vat_exempt") and not has_defer:
                    continue
            elif when == "invoice_payment_inv":
                if comp.type != "vat_defer":
                    continue
            elif when == "invoice_payment_pmt":
                if comp.type in ("vat", "vat_exempt") and not has_defer:
                    continue
                if comp.type == "vat_defer":
                    continue
            elif when == "direct_payment":
                if comp.type == "vat_defer":
                    continue
            else:
                raise Exception("Can't compute taxes: invalid 'when'")
            if when == "invoice" and comp.type not in ("vat", "vat_exempt",
                                                       "vat_defer"):
                continue
            if when == "payment" and comp.type != "wht":
                continue
            tax = base * (comp.rate / 100)
            if comp.type == "wht":
                tax = -tax
            elif comp.type == "vat_defer" and when in ("invoice_payment",
                                                       "invoice_payment_inv"):
                tax = -tax
            comps[comp.id] = tax
        return comps

    def has_defer_vat(self, ids, context={}):
        for obj in self.browse(ids):
            for comp in obj.components:
                if comp.type == "vat_defer":
                    return True
        return False

    def copy(self, ids, context={}):
        obj = self.browse(ids)[0]
        vals = {
            "name": obj.name + "-copy",
            "code": obj.code,
            "type": obj.type,
            "payer": obj.payer,
            "note": obj.note,
            "rate": obj.rate,
            "wht_rate": obj.wht_rate,
            "components": [],
            "uuid": str(uuid.uuid4()),
            "active": True,
        }
        for comp in obj.components:
            comp_vals = {
                "name": comp.name,
                "compound": comp.compo4und,
                "rate": comp.rate,
                "account_id": comp.account_id.id,
                "type": comp.type,
                "trans_type": comp.trans_type,
                "description": comp.description,
            }
            vals["components"].append(("create", comp_vals))
        new_id = self.create(vals)
        new_obj = self.browse(new_id)
        return {
            "next": {
                "name": "tax_rate",
                "mode": "form",
                "active_id": new_id,
            },
            "flash": "Tax rate copied",
        }
Example #21
0
class ReportStockCard(Model):
    _name = "report.stock.card"
    _transient = True
    _fields = {
        "date_from":
        fields.Date("From", required=True),
        "date_to":
        fields.Date("To", required=True),
        "product_id":
        fields.Many2One("product", "Product", on_delete="cascade"),
        "categ_id":
        fields.Many2One("product.categ",
                        "Product Category",
                        on_delete="cascade"),
        "location_id":
        fields.Many2One("stock.location", "Location", on_delete="cascade"),
        "uom_id":
        fields.Many2One("uom", "UoM"),
        "lot_id":
        fields.Many2One("stock.lot", "Lot / Serial Number"),
        "invoice_id":
        fields.Many2One("account.invoice", "Invoice"),
        "show_pending":
        fields.Boolean("Show Pending"),
        "show_qty2":
        fields.Boolean("Show Secondary Qty"),
        "hide_zero":
        fields.Boolean("Hide Zero Lines"),
    }
    _defaults = {
        "date_from":
        lambda *a: date.today().strftime("%Y-%m-01"),
        "date_to":
        lambda *a: (date.today() + relativedelta(day=31)).strftime("%Y-%m-%d"),
    }

    # TODO: add uom_id support again
    def get_report_data(self, ids, context={}):
        print("stock_card.get_report_data")
        company_id = get_active_company()
        comp = get_model("company").browse(company_id)
        if ids:
            params = self.read(ids, load_m2o=False)[0]
        else:
            params = self.default_get(load_m2o=False, context=context)
        settings = get_model("settings").browse(1)
        company_address = settings.default_address_id.address_text

        location_id = params.get("location_id")
        product_id = params.get("product_id")
        if not location_id and not product_id:
            raise Exception("Please select a product or location")
        categ_id = params.get("categ_id")
        uom_id = params.get("uom_id")
        date_from = params.get("date_from")
        if not date_from:
            return
        date_to = params.get("date_to")
        if not date_to:
            return
        lot_id = params.get("lot_id")
        invoice_id = params.get("invoice_id")
        show_pending = params.get("show_pending")
        hide_zero = params.get("hide_zero")

        date_from_prev = (datetime.strptime(date_from, "%Y-%m-%d") -
                          timedelta(days=1)).strftime("%Y-%m-%d")
        t0 = time.time()
        print("before get open_totals")
        open_totals = get_totals(None,
                                 date_from_prev,
                                 product_id=product_id,
                                 location_id=location_id,
                                 show_pending=show_pending,
                                 lot_id=lot_id,
                                 categ_id=categ_id)
        t1 = time.time()
        print("get open_totals in %.3f s" % (t1 - t0))
        print("len open_totals=%s" % len(open_totals))

        prod_open_totals = {}
        for (prod_id, loc_from_id, loc_to_id), (qty, amt,
                                                qty2) in open_totals.items():
            prod_open_totals.setdefault(prod_id, {})
            prod_open_totals[prod_id][(loc_from_id, loc_to_id)] = (qty, amt,
                                                                   qty2)

        def _get_open_balance(prod_id, loc_id):
            bal_qty = 0
            bal_amt = 0
            bal_qty2 = 0
            tots = prod_open_totals.get(prod_id, {})
            for (loc_from_id, loc_to_id), (qty, amt, qty2) in tots.items():
                if loc_to_id == loc_id and loc_from_id != loc_id:
                    bal_qty += qty
                    bal_amt += amt
                    bal_qty2 += qty2
                elif loc_from_id == loc_id and loc_to_id != loc_id:
                    bal_qty -= qty
                    bal_amt -= amt
                    bal_qty2 -= qty2
            return bal_qty, bal_amt, bal_qty2

        prod_locs = {}

        db = get_connection()
        q = "SELECT m.id,m.date,m.ref,m.related_id,m.lot_id,l.number AS lot_num,m.invoice_id,i.number AS invoice_num,m.product_id,m.location_from_id,m.location_to_id,m.qty,m.uom_id,m.cost_amount,m.qty2 FROM stock_move m LEFT JOIN stock_lot l ON l.id=m.lot_id LEFT JOIN account_invoice i ON i.id=m.invoice_id LEFT JOIN product p on m.product_id=p.id WHERE m.date>=%s AND m.date<=%s"
        args = [date_from + " 00:00:00", date_to + " 23:59:59"]
        if product_id:
            q += " AND m.product_id=%s"
            args.append(product_id)
        if categ_id:
            q += " AND p.categ_id=%s"
            args.append(categ_id)
        if location_id:
            q += " AND (m.location_from_id=%s OR m.location_to_id=%s)"
            args += [location_id, location_id]
        if lot_id:
            q += " AND m.lot_id=%s"
            args.append(lot_id)
        if invoice_id:
            q += " AND m.invoice_id=%s"
            args.append(invoice_id)
        if show_pending:
            q += " AND m.state IN ('pending','done')"
        else:
            q += " AND m.state='done'"
        q += " ORDER BY m.date"
        print("q", q)
        print("args", args)
        res = db.query(q, *args)
        print("%d results" % len(res))
        t2 = time.time()
        print("get stock movements in %.3f s" % (t2 - t1))
        for r in res:
            prod_locs.setdefault((r.product_id, r.location_from_id),
                                 []).append(r)
            prod_locs.setdefault((r.product_id, r.location_to_id),
                                 []).append(r)

        loc_ids = []
        prod_ids = []
        for prod_id, loc_id in prod_locs:
            loc_ids.append(loc_id)
            prod_ids.append(prod_id)
        loc_ids = list(set(loc_ids))
        prod_ids = list(set(prod_ids))
        perm_loc_ids = get_model("stock.location").search(
            [["id", "in", loc_ids]])

        prods = {}
        for prod in get_model("product").browse(prod_ids):
            prods[prod.id] = {
                "name": prod.name,
                "code": prod.code,
                "uom_id": prod.uom_id.id,
            }
        locs = {}
        for loc in get_model("stock.location").browse(loc_ids):
            locs[loc.id] = {
                "name": loc.name,
                "code": loc.code,
                "type": loc.type,
            }
        t3 = time.time()
        print("get prod/loc info in %.3f s" % (t3 - t2))

        groups = []
        i = 0
        for (prod_id, loc_id), moves in prod_locs.items():
            i += 1
            if i % 100 == 0:
                print("%d/%d" % (i, len(prod_locs)))
            if loc_id not in perm_loc_ids:
                continue
            prod = prods[prod_id]
            loc = locs[loc_id]
            if location_id:
                if loc_id != location_id:
                    continue
            else:
                if loc["type"] != "internal":
                    continue
            bal_qty, bal_amt, bal_qty2 = _get_open_balance(prod_id, loc_id)
            bal_price = bal_qty and bal_amt / bal_qty or 0
            lines = []
            line = {
                "date": date_from,
                "bal_qty": bal_qty,
                "bal_cost_amount": bal_amt,
                "bal_cost_price": bal_price,
                "bal_qty2": bal_qty2,
            }
            lines.append(line)
            for r in moves:
                ref = r.ref
                qty = r.qty * get_model("uom").get_ratio(
                    r.uom_id) / get_model("uom").get_ratio(prod["uom_id"])
                amt = r.cost_amount or 0
                price = amt / qty if qty else 0
                qty2 = r.qty2 or 0
                if hide_zero and not qty and not qty2:
                    continue
                line = {
                    "id": r.id,
                    "date": r.date,
                    "ref": ref,
                    "lot_id": r.lot_id,
                    "lot_num": r.lot_num,
                    "invoice_id": r.invoice_id,
                    "invoice_num": r.invoice_num,
                }
                if r.location_to_id == loc_id and r.location_from_id == loc_id:
                    continue
                elif r.location_to_id == loc_id:
                    bal_qty += qty
                    bal_amt += amt
                    bal_qty2 += qty2
                    line.update({
                        "in_qty": qty,
                        "in_cost_price": price,
                        "in_amount": amt,
                        "in_qty2": qty2,
                    })
                elif r.location_from_id == loc_id:
                    bal_qty -= qty
                    bal_amt -= amt
                    bal_qty2 -= qty2
                    line.update({
                        "out_qty": qty,
                        "out_cost_price": price,
                        "out_amount": amt,
                        "out_qty2": qty2,
                    })
                bal_price = bal_qty and bal_amt / bal_qty or 0
                line.update({
                    "bal_qty": bal_qty,
                    "bal_cost_amount": bal_amt,
                    "bal_cost_price": bal_price,
                    "bal_qty2": bal_qty2,
                })
                lines.append(line)
            group = {
                "product_id": prod_id,
                "location_id": loc_id,
                "product_name": prod["name"],
                "product_code": prod["code"],
                "location_name": loc["name"],
                "location_code": loc["code"],
                "lines": lines,
                "total_in_qty": sum(l.get("in_qty", 0) for l in lines),
                "total_in_amount": sum(l.get("in_amount", 0) for l in lines),
                "total_in_qty2": sum(l.get("in_qty2", 0) for l in lines),
                "total_out_qty": sum(l.get("out_qty", 0) for l in lines),
                "total_out_amount": sum(l.get("out_amount", 0) for l in lines),
                "total_out_qty2": sum(l.get("out_qty2", 0) for l in lines),
            }
            groups.append(group)
        t4 = time.time()
        print("make lines in %.3f s" % (t3 - t2))

        groups.sort(key=lambda x: (x["product_name"], x["location_name"]))
        data = {
            "company_name": comp.name,
            "tax_no": settings.tax_no,
            "address": company_address,
            "date_from": date_from,
            "date_to": date_to,
            "groups": groups,
            "show_qty2": params.get("show_qty2"),
        }
        #pprint(data)
        return data
Example #22
0
class ReportStockSummary(Model):
    _name = "report.stock.summary"
    _transient = True
    _fields = {
        "date_from": fields.Date("From", required=True),
        "date_to": fields.Date("To", required=True),
        "location_id": fields.Many2One("stock.location", "Location", on_delete="cascade"),
        "product_id": fields.Many2One("product", "Product", on_delete="cascade"),
        "lot_id": fields.Many2One("stock.lot", "Lot / Serial Number"),
        "container_id": fields.Many2One("stock.container", "Container"),
        "prod_code": fields.Char("Product Code"),  # for enterprise package
        "prod_categ_id": fields.Many2One("product.categ", "Product Category"),
        "show_lot": fields.Boolean("Show Lot / Serial Number"),
        "show_container": fields.Boolean("Show Container"),
        "show_qty2": fields.Boolean("Show Secondary Qty"),
        "only_closing": fields.Boolean("Only Show Closing"),
        "prod_company_id": fields.Many2One("company","Product Company"),
    }
    _defaults = {
        "date_from": lambda *a: date.today().strftime("%Y-%m-01"),
        "date_to": lambda *a: (date.today() + relativedelta(day=31)).strftime("%Y-%m-%d"),
    }

    def get_report_data(self, ids, context={}):
        print("stock_summary.get_report_data")
        company_id = get_active_company()
        comp = get_model("company").browse(company_id)
        if ids:
            params = self.read(ids, load_m2o=False)[0]
        else:
            params = self.default_get(load_m2o=False, context=context)
        date_from_m1 = (datetime.strptime(params["date_from"], "%Y-%m-%d") - timedelta(days=1)).strftime("%Y-%m-%d")
        prod_company_id = params.get("prod_company_id")
        prod_company_ids = get_model("company").search([["id","child_of",prod_company_id]])
        if prod_company_ids:
            prod_company_ids = tuple(prod_company_ids)
        else:
            prod_company_ids = None
        t0 = time.time()
        totals = {
            "open": get_totals(None, date_from_m1, product_id=params.get("product_id"), lot_id=params.get("lot_id"), location_id=params.get("location_id"), container_id=params.get("container_id"), prod_categ_id=params.get("prod_categ_id"), prod_code=params.get("prod_code"), prod_company_ids=prod_company_ids),
            "period": get_totals(params["date_from"], params["date_to"], product_id=params.get("product_id"), lot_id=params.get("lot_id"), location_id=params.get("location_id"), container_id=params.get("container_id"), prod_categ_id=params.get("prod_categ_id"), prod_code=params.get("prod_code"), prod_company_ids=prod_company_ids),
            "close": get_totals(None, params["date_to"], product_id=params.get("product_id"), lot_id=params.get("lot_id"), location_id=params.get("location_id"), container_id=params.get("container_id"), prod_categ_id=params.get("prod_categ_id"), prod_code=params.get("prod_code"), prod_company_ids=prod_company_ids),
        }
        t1 = time.time()
        print("get totals in %.3f s" % (t1-t0))
        prod_locs = set([])
        for prod_id, lot_id, loc_from_id, cont_from_id, loc_to_id, cont_to_id in list(totals["open"].keys()) + list(totals["period"].keys()) + list(totals["close"].keys()):
            if not params.get("show_lot"):
                lot_id = -1
            if not params.get("show_container"):
                cont_from_id = -1
                cont_to_id = -1
            prod_locs.add((prod_id, lot_id, loc_from_id, cont_from_id))
            prod_locs.add((prod_id, lot_id, loc_to_id, cont_to_id))

        prod_totals={}
        for when in ("open","period","close"):
            tots={}
            for (prod_id, lot_id, loc_from_id, cont_from_id, loc_to_id, cont_to_id), tot in totals[when].items():
                tots.setdefault(prod_id,{})
                tots[prod_id][(lot_id,loc_from_id,cont_from_id,loc_to_id,cont_to_id)]=tot
            prod_totals[when]=tots
        def get_sum(when, prod=None, lot=-1, loc_to=-1, cont_from=-1, loc_from=-1, cont_to=-1):
            qty = 0
            amt = 0
            qty2 = 0
            tots=prod_totals[when].get(prod,{})
            for (lot_id, loc_from_id, cont_from_id, loc_to_id, cont_to_id), tot in tots.items():
                if lot != -1 and lot_id != lot:
                    continue
                if loc_from != -1 and loc_from_id != loc_from:
                    continue
                if loc_to != -1 and loc_to_id != loc_to:
                    continue
                if cont_from != -1 and cont_from_id != cont_from:
                    continue
                if cont_to != -1 and cont_to_id != cont_to:
                    continue
                qty += tot[0]
                amt += tot[1]
                qty2 += tot[2]
            return (qty, amt, qty2)
        prod_ids = []
        lot_ids = []
        loc_ids = []
        cont_ids = []
        for prod_id, lot_id, loc_id, cont_id in prod_locs:
            prod_ids.append(prod_id)
            loc_ids.append(loc_id)
            if lot_id and lot_id!=-1:
                lot_ids.append(lot_id)
            if cont_id and lot_id!=-1:
                cont_ids.append(cont_id)
        prod_ids = list(set(prod_ids))
        loc_ids = list(set(loc_ids))
        perm_loc_ids=get_model("stock.location").search([["id","in",loc_ids]])
        lot_ids = list(set(lot_ids))
        cont_ids = list(set(cont_ids))
        prods = {}
        for prod in get_model("product").browse(prod_ids):
            prods[prod.id]={
                "name": prod.name,
                "code": prod.code,
                "uom_id": prod.uom_id.id,
                "uom_name": prod.uom_id.name,
            }
        locs = {}
        for loc in get_model("stock.location").browse(loc_ids):
            locs[loc.id]={
                "name": loc.name,
                "code": loc.code,
                "type": loc.type,
            }
        lots = {}
        for lot in get_model("stock.lot").browse(lot_ids):
            lots[lot.id]={
                "number": lot.number,
            }
        conts = {}
        for cont in get_model("stock.container").browse(cont_ids):
            conts[cont.id]={
                "number": cont.number,
            }
        t2 = time.time()
        print("get prod/loc/lot/cont info in %.3f s" % (t2-t1))

        lines = []
        print("num prod_locs", len(prod_locs))
        for prod_id, lot_id, loc_id, cont_id in prod_locs:
            if loc_id not in perm_loc_ids:
                continue
            if params.get("product_id") and prod_id != params["product_id"]:
                continue
            if params.get("lot_id") and lot_id != params["lot_id"]:
                continue
            if params.get("location_id") and loc_id != params["location_id"]:
                continue
            if params.get("container_id") and cont_id != params["container_id"]:
                continue
            loc = locs[loc_id]
            if loc["type"] != "internal":
                continue
            if cont_id and cont_id != -1:
                cont = conts[cont_id]
            else:
                cont = None
            prod = prods[prod_id]
            lot = lots[lot_id] if lot_id and lot_id != -1 else None
            tot_open_in = get_sum("open", prod=prod_id, lot=lot_id, loc_to=loc_id, cont_to=cont_id)
            tot_open_out = get_sum("open", prod=prod_id, lot=lot_id, loc_from=loc_id, cont_from=cont_id)
            tot_period_in = get_sum("period", prod=prod_id, lot=lot_id, loc_to=loc_id, cont_to=cont_id)
            tot_period_out = get_sum("period", prod=prod_id, lot=lot_id, loc_from=loc_id, cont_from=cont_id)
            tot_close_in = get_sum("close", prod=prod_id, lot=lot_id, loc_to=loc_id, cont_to=cont_id)
            tot_close_out = get_sum("close", prod=prod_id, lot=lot_id, loc_from=loc_id, cont_from=cont_id)
            line_vals = {
                "prod_id": prod_id,
                "prod_name": prod["name"],
                "prod_code": prod["code"],
                "lot_id": lot_id if lot_id != -1 else None,
                "lot_num": lot["number"] if lot else None,
                "uom_name": prod["uom_name"],
                "loc_id": loc_id,
                "loc_name": loc["name"],
                "cont_id": cont_id,
                "cont_name": cont["number"] if cont else "",
                "open_qty": tot_open_in[0] - tot_open_out[0],
                "open_amt": tot_open_in[1] - tot_open_out[1],
                "open_qty2": tot_open_in[2] - tot_open_out[2],
                "period_in_qty": tot_period_in[0],
                "period_in_amt": tot_period_in[1],
                "period_in_qty2": tot_period_in[2],
                "period_out_qty": tot_period_out[0],
                "period_out_amt": tot_period_out[1],
                "period_out_qty2": tot_period_out[2],
                "close_qty": tot_close_in[0] - tot_close_out[0],
                "close_amt": tot_close_in[1] - tot_close_out[1],
                "close_qty2": tot_close_in[2] - tot_close_out[2],
            }
            if params.get("only_closing") and line_vals["close_qty"] == 0:
                continue
            if not line_vals["open_qty"] and not line_vals["open_qty2"] \
                    and not line_vals["period_in_qty"] and not line_vals["period_in_qty2"] \
                    and not line_vals["period_out_qty"] and not line_vals["period_out_qty2"] \
                    and not line_vals["close_qty"] and not line_vals["close_qty2"]:
                continue
            lines.append(line_vals)
        t3 = time.time()
        print("make lines in %.3f s" % (t3-t2))
        lines.sort(
            key=lambda l: (l["prod_code"] or "", l["prod_name"], l["lot_num"] or "", l["loc_name"], l["cont_name"] or ""))
        data = {
            "company_name": comp.name,
            "date_from": params.get("date_from"),
            "date_to": params.get("date_to"),
            "show_lot": params.get("show_lot"),
            "show_container": params.get("show_container"),
            "show_qty2": params.get("show_qty2"),
            "only_closing": params.get("only_closing"),
            "lines": lines,
            "total_open_amt": sum([l["open_amt"] for l in lines]),
            "total_open_qty2": sum([l["open_qty2"] for l in lines]),
            "total_period_in_amt": sum([l["period_in_amt"] for l in lines]),
            "total_period_in_qty2": sum([l["period_in_qty2"] for l in lines]),
            "total_period_out_amt": sum([l["period_out_amt"] for l in lines]),
            "total_period_out_qty2": sum([l["period_out_qty2"] for l in lines]),
            "total_close_qty": sum([l["close_qty"] for l in lines]),
            "total_close_amt": sum([l["close_amt"] for l in lines]),
            "total_close_qty2": sum([l["close_qty2"] for l in lines]),
        }
        if params.get("location_id"):
            loc = get_model("stock.location").browse(params["location_id"])
            data["location_id"] = [loc.id, loc.name]
        if params.get("lot_id"):
            lot = get_model("stock.lot").browse(params["lot_id"])
            data["lot_id"] = params["lot_id"]
            data["lot_num"] = lot.number
        if params.get("product_id"):
            prod = get_model("product").browse(params["product_id"])
            data["product_id"] = [prod.id, prod.name_get()[0][1]]
        if params.get("container_id"):
            cont = get_model("stock.container").browse(params["container_id"])
            data["container_id"] = [cont.id, cont.name_get()[0][1]]
        return data
Example #23
0
class Move(Model):
    _name = "account.move"
    _string = "Journal Entry"
    _name_field = "number"
    _multi_company = True
    _audit_log = True
    _key = ["company_id", "number"]
    _fields = {
        "journal_id":
        fields.Many2One("account.journal",
                        "Journal",
                        required=True,
                        search=True),
        "narration":
        fields.Text("Narration", required=True, search=True),
        "date":
        fields.Date("Document Date", required=True, search=True, index=True),
        "date_posted":
        fields.Date("Posted Date", search=True, index=True),
        "state":
        fields.Selection(
            [["draft", "Draft"], ["posted", "Posted"], ["voided", "Voided"]],
            "Status",
            required=True,
            search=True),
        "lines":
        fields.One2Many("account.move.line", "move_id", "Lines"),
        "total_debit":
        fields.Decimal("Total Debit",
                       function="get_total",
                       function_multi=True),
        "total_credit":
        fields.Decimal("Total Credit",
                       function="get_total",
                       function_multi=True),
        "type":
        fields.Selection([["auto", "auto"], ["manual", "Manual"]], "Type"),
        "ref":
        fields.Char("Reference", search=True),
        "number":
        fields.Char("Number", search=True, required=True),
        "default_line_desc":
        fields.Boolean("Default narration to line description"),
        "comments":
        fields.One2Many("message", "related_id", "Comments"),
        "related_id":
        fields.Reference(
            [["account.invoice", "Invoice"], ["account.payment", "Payment"],
             ["account.transfer", "Transfer"], ["hr.expense", "Expense Claim"],
             ["service.contract", "Service Contract"], ["pawn.loan", "Loan"],
             ["landed.cost", "Landed Cost"],
             ["stock.picking", "Stock Picking"]], "Related To"),
        "company_id":
        fields.Many2One("company", "Company"),
        "track_entries":
        fields.One2Many("account.track.entry", "move_id", "Tracking Entries"),
        "difference":
        fields.Float("Difference",
                     function="get_difference",
                     function_multi=True),
    }

    def _get_journal(self, context={}):
        settings = get_model("settings").browse(1)
        return settings.general_journal_id.id

    def _get_number(self, context={}):
        journal_id = context.get("journal_id")
        if not journal_id:
            settings = get_model("settings").browse(1)
            journal_id = settings.general_journal_id.id
        if not journal_id:
            return
        journal = get_model("account.journal").browse(journal_id)
        seq_id = journal.sequence_id.id
        if not seq_id:
            return
        while 1:
            num = get_model("sequence").get_next_number(seq_id,
                                                        context=context)
            res = self.search([["number", "=", num]])
            if not res:
                return num
            get_model("sequence").increment_number(seq_id, context=context)

    _defaults = {
        "state": "draft",
        "default_line_desc": True,
        "type": "auto",
        "date": lambda *a: time.strftime("%Y-%m-%d"),
        "journal_id": _get_journal,
        "number": _get_number,
        "company_id": lambda *a: get_active_company(),
    }
    _order = "date desc,id desc"

    def get_difference(self, ids, context):
        vals = {}
        for obj in self.browse(ids):
            vals[obj.id] = {
                "difference": obj.total_debit - obj.total_credit,
            }
        return vals

    def create(self, vals, **kw):
        t0 = time.time()
        new_id = super().create(vals, **kw)
        t01 = time.time()
        dt01 = (t01 - t0) * 1000
        print("account_move.dt01", dt01)
        obj = self.browse([new_id])[0]
        line_ids = []
        rec_ids = []
        for line in obj.lines:
            line_ids.append(line.id)
            if line.reconcile_id:
                rec_ids.append(line.reconcile_id.id)
        get_model("account.move.line").function_store(line_ids)
        if rec_ids:
            get_model("account.reconcile").function_store(rec_ids)
        get_model("field.cache").clear_cache(model="account.account")
        t1 = time.time()
        dt = (t1 - t0) * 1000
        print("account_move.create <<< %d ms" % dt)
        return new_id

    def write(self, ids, vals, **kw):
        super().write(ids, vals, **kw)
        line_ids = []
        rec_ids = []
        for obj in self.browse(ids):
            for line in obj.lines:
                line_ids.append(line.id)
                if line.reconcile_id:
                    rec_ids.append(line.reconcile_id.id)
        get_model("account.move.line").function_store(line_ids)
        if rec_ids:
            get_model("account.reconcile").function_store(rec_ids)
            move_ids = []
            for rec in get_model("account.reconcile").browse(rec_ids):
                for line in rec.lines:
                    move_ids.append(line.move_id.id)
            move_ids = list(set(move_ids))
            inv_ids = get_model("account.invoice").search(
                [["move_id", "in", move_ids]])
            if inv_ids:
                get_model("account.invoice").function_store(
                    inv_ids)  # XXX: check this
        get_model("field.cache").clear_cache(model="account.account")

    def delete(self, ids, **kw):
        rec_ids = []
        for obj in self.browse(ids):
            if obj.state == "posted":
                raise Exception("Can not deleted posted journal entry")
            for line in obj.lines:
                if line.reconcile_id:
                    rec_ids.append(line.reconcile_id.id)
        super().delete(ids, **kw)
        if rec_ids:
            get_model("account.reconcile").function_store(rec_ids)
        get_model("field.cache").clear_cache(model="account.account")

    def post(self, ids, context={}):
        settings = get_model("settings").browse(1)
        for obj in self.browse(ids):
            if settings.lock_date:
                assert obj.date >= settings.lock_date, "Accounting transaction is before lock date"
            if obj.state != "draft":
                raise Exception("Journal entry is not draft")
            total_debit = 0
            total_credit = 0
            for line in obj.lines:
                acc = line.account_id
                if acc.type == "view":
                    raise Exception(
                        "Can not post to 'view' account ([%s] %s)" %
                        (acc.code, acc.name))
                if acc.company_id.id != obj.company_id.id:
                    raise Exception(
                        "Wrong company for account %s in journal entry %s (account company: %s, journal entry company %s)("
                        % (acc.code, obj.number, acc.company_id.code,
                           obj.company_id.code))
                if acc.require_contact and not line.contact_id:
                    raise Exception("Missing contact for account %s" %
                                    acc.code)
                if acc.require_tax_no and not line.tax_no:
                    raise Exception("Missing tax number for account %s" %
                                    acc.code)
                if acc.require_track and not line.track_id:
                    raise Exception(
                        "Missing tracking category for account %s" % acc.code)
                if acc.require_track2 and not line.track2_id:
                    raise Exception(
                        "Missing secondary tracking category for account %s" %
                        acc.code)
                if line.debit < 0:
                    raise Exception("Debit amount is negative (%s)" %
                                    line.debit)
                if line.credit < 0:
                    raise Exception("Credit amount is negative (%s)" %
                                    line.credit)
                if line.debit > 0 and line.credit > 0:
                    raise Exception(
                        "Debit and credit amounts can not be both non-zero (account: %s, debit: %s, credit: %s)"
                        % (line.account_id.name_get()[0][1], line.debit,
                           line.credit))
                total_debit += line.debit
                total_credit += line.credit
                if line.tax_comp_id and not line.tax_date:
                    line.write({"tax_date": line.move_id.date})
                if acc.currency_id.id != settings.currency_id.id and line.amount_cur is None:
                    raise Exception("Missing currency amount for account %s" %
                                    line.account_id.name_get()[0][1])
                if line.amount_cur is not None and acc.currency_id.id == settings.currency_id.id:
                    raise Exception(
                        "Currency amount for account %s should be empty" %
                        line.account_id.name_get()[0][1])
                if line.amount_cur is not None and line.amount_cur < 0:
                    raise Exception("Currency amount is negative (%s)" %
                                    line.amount_cur)
            if abs(total_debit - total_credit) != 0:
                print("NOT BALANCED total_debit=%s total_credit=%s" %
                      (total_debit, total_credit))
                for line in obj.lines:
                    print("  ACC: [%s] %s DR: %s CR: %s" %
                          (line.account_id.code, line.account_id.name,
                           line.debit, line.credit))
                raise Exception(
                    "Journal entry is not balanced (debit=%s, credit=%s)" %
                    (total_debit, total_credit))
            obj.write({"state": "posted"})
            if not obj.date_posted:
                date_posted = time.strftime("%Y-%m-%d")
                obj.write({"date_posted": date_posted})
            obj.create_track_entries()
            seq = 1
            for line in obj.lines:
                line.write({"sequence": seq})  # XXX
                seq += 1
        if not context.get("no_reconcile"):
            bank_ids = []
            for obj in self.browse(ids):
                for line in obj.lines:
                    acc = line.account_id
                    if acc.type in ("bank", "cash", "cheque"):
                        bank_ids.append(acc.id)
            if bank_ids:
                bank_ids = list(set(bank_ids))
                get_model("account.account").auto_bank_reconcile(bank_ids)
        get_model("account.balance").update_balances()

    def create_track_entries(self, ids, context={}):
        obj = self.browse(ids[0])
        settings = get_model("settings").browse(1)
        for line in obj.lines:
            if line.track_id:
                amt = line.credit - line.debit
                if line.track_id.currency_id:
                    amt = get_model("currency").convert(
                        amt, settings.currency_id.id,
                        line.track_id.currency_id.id)
                vals = {
                    "date": obj.date,
                    "track_id": line.track_id.id,
                    "amount": amt,
                    "description": line.description,
                    "move_id": obj.id,
                }
                get_model("account.track.entry").create(vals)
            if line.track2_id:
                amt = line.credit - line.debit
                if line.track2_id.currency_id:
                    amt = get_model("currency").convert(
                        amt, settings.currency_id.id,
                        line.track2_id.currency_id.id)
                vals = {
                    "date": obj.date,
                    "track_id": line.track2_id.id,
                    "amount": amt,
                    "description": line.description,
                    "move_id": obj.id,
                }
                get_model("account.track.entry").create(vals)

    def void(self, ids, context={}):
        obj = self.browse(ids[0])
        settings = get_model("settings").browse(1)
        if settings.lock_date:
            if obj.date < settings.lock_date:
                raise Exception("Accounting transaction is before lock date")
        obj.lines.unreconcile()
        obj.write({"state": "voided"})
        obj.delete_track_entries()
        get_model("field.cache").clear_cache(model="account.account")
        get_model("account.balance").update_balances()

    def delete_track_entries(self, ids, context={}):
        obj = self.browse(ids[0])
        obj.track_entries.delete()

    def get_total(self, ids, context):
        vals = {}
        for obj in self.browse(ids):
            total_debit = 0
            total_credit = 0
            for line in obj.lines:
                total_debit += line.debit
                total_credit += line.credit
            vals[obj.id] = {
                "total_debit": total_debit,
                "total_credit": total_credit,
            }
        return vals

    def update_amounts(self, context):
        data = context["data"]
        data["total_debit"] = 0
        data["total_credit"] = 0
        for line in data["lines"]:
            if not line:
                continue
            debit = line.get("debit") or 0
            credit = line.get("credit") or 0
            data["total_debit"] += debit
            data["total_credit"] += credit
            if line.get("debit") is not None and line.get("credit") is None:
                line["credit"] = 0
            if line.get("credit") is not None and line.get("debit") is None:
                line["debit"] = 0
        data["difference"] = data["total_debit"] - data["total_credit"]
        return data

    def get_line_desc(self, context):
        data = context["data"]
        path = context["path"]
        if not data.get("default_line_desc"):
            return
        if not get_data_path(data, path):
            set_data_path(data, path, data.get("narration"))
        return data

    def view_journal(self, ids, context={}):
        res = self.read(ids, ["related_id"])[0]["related_id"]
        rel = res and res[0] or None
        next = None
        if rel:
            model, model_id = rel.split(",")
            if model == "account.invoice":
                next = {
                    "name": "view_invoice",
                    "active_id": model_id,
                }
            elif model == "account.payment":
                next = {
                    "name": "payment",
                    "mode": "form",
                    "active_id": model_id,
                }
            elif model == "account.transfer":
                next = {
                    "name": "bank_transfer",
                    "mode": "form",
                    "active_id": model_id,
                }
            elif model == "account.claim":
                next = {
                    "name": "account_claim_edit",
                    "active_id": model_id,
                }
        if not next:
            next = {
                "name": "journal_entry",
                "mode": "form",
                "active_id": ids[0],
            }
        return {"next": next}

    def copy(self, ids, context={}):
        obj = self.browse(ids)[0]
        vals = {
            "journal_id": obj.journal_id.id,
            "ref": obj.ref,
            "default_line_desc": obj.default_line_desc,
            "narration": obj.narration,
            "lines": [],
        }
        for line in obj.lines:
            line_vals = {
                "description": line.description,
                "account_id": line.account_id.id,
                "debit": line.debit,
                "credit": line.credit,
                "tax_comp_id": line.tax_comp_id.id,
                "tax_base": line.tax_base,
                "contact_id": line.contact_id.id,
                "track_id": line.track_id.id,
                "track2_id": line.track2_id.id,
            }
            vals["lines"].append(("create", line_vals))
        new_id = self.create(vals, context={"journal_id": obj.journal_id.id})
        new_obj = self.browse(new_id)
        return {
            "next": {
                "name": "journal_entry",
                "mode": "form",
                "active_id": new_id,
            },
            "flash":
            "Journal entry %s copied to %s" % (obj.number, new_obj.number),
        }

    def to_draft(self, ids, context={}):
        obj = self.browse(ids)[0]
        for line in obj.lines:
            line.unreconcile()
        obj.write({"state": "draft"})
        obj.delete_track_entries()
        get_model("account.balance").update_balances()
        return {
            "next": {
                "name": "journal_entry",
                "mode": "form",
                "active_id": obj.id,
            },
            "flash": "Journal entry #%d set to draft" % obj.id,
        }

    def onchange_journal(self, context={}):
        data = context["data"]
        journal_id = data["journal_id"]
        date = data["date"]
        number = self._get_number(context={
            "journal_id": journal_id,
            "date": date
        })
        data["number"] = number
        return data

    def onchange_date(self, context={}):
        data = context["data"]
        journal_id = data["journal_id"]
        date = data["date"]
        number = self._get_number(context={
            "journal_id": journal_id,
            "date": date
        })
        data["number"] = number
        return data

    def get_data(self, ids, context={}):
        company_id = get_active_company()
        comp = get_model("company").browse(company_id)
        settings = get_model('settings').browse(1)
        pages = []
        for obj in self.browse(ids):
            lines = []
            for line in obj.lines:
                lines.append({
                    'description': line.description,
                    'account_code': line.account_id.code,
                    'account_name': line.account_id.name,
                    'debit': line.debit,
                    'credit': line.credit,
                    'tax_comp': line.tax_comp_id.name,
                    'tax_base': line.tax_base,
                    'track': line.track_id.name,
                    'contact': line.contact_id.name,
                })
            data = {
                "comp_name": comp.name,
                "number": obj.number,
                "date": obj.date,
                "journal": obj.journal_id.name,
                "narration": obj.narration,
                "lines": lines,
                "total_debit": obj.total_debit,
                "total_credit": obj.total_credit,
            }
            if settings.logo:
                data['logo'] = get_file_path(settings.logo)
            pages.append(data)
        if pages:
            pages[-1]["is_last_page"] = True
        return {
            "pages": pages,
            "logo":
            get_file_path(settings.logo),  # XXX: remove when render_odt fixed
        }

    def reverse(self, ids, context={}):
        obj = self.browse(ids)[0]
        vals = {
            "journal_id": obj.journal_id.id,
            "ref": obj.ref,
            "default_line_desc": obj.default_line_desc,
            "narration": obj.narration,
            "lines": [],
        }
        for line in obj.lines:
            line_vals = {
                "description": line.description,
                "account_id": line.account_id.id,
                "debit": line.credit,
                "credit": line.debit,
                "tax_comp_id": line.tax_comp_id.id,
                "tax_base": line.tax_base,
                "contact_id": line.contact_id.id,
                "track_id": line.track_id.id,
                "track2_id": line.track2_id.id,
            }
            vals["lines"].append(("create", line_vals))
        new_id = self.create(vals, context={"journal_id": obj.journal_id.id})
        self.post([new_id])
        new_obj = self.browse(new_id)
        return {
            "next": {
                "name": "journal_entry",
                "mode": "form",
                "active_id": new_id,
            },
            "flash":
            "Journal entry %s reversed to %s" % (obj.number, new_obj.number),
            "reverse_move_id":
            new_id,
        }
Example #24
0
class ReportCustInvoice(Model):
    _name = "report.cust.invoice"
    _transient = True
    _fields = {
        "date_from": fields.Date("From"),
        "date_to": fields.Date("To"),
        "contact_id": fields.Many2One("contact",
                                      "Contact",
                                      on_delete="cascade"),
        "show_details": fields.Boolean("Show Details"),
    }

    def default_get(self, field_names=None, context={}, **kw):
        defaults = context.get("defaults", {})
        contact_id = defaults.get("contact_id")
        if contact_id:
            contact_id = int(contact_id)
        date_from = defaults.get("date_from")
        date_to = defaults.get("date_to")
        return {
            "contact_id": contact_id,
            "date_from": date_from,
            "date_to": date_to,
        }

    def get_report_data(self, ids, context={}):
        company_id = get_active_company()
        company_ids = get_model("company").search(
            [["id", "child_of", company_id]])
        comp = get_model("company").browse(company_id)
        if ids:
            params = self.read(ids, load_m2o=False)[0]
        else:
            params = self.default_get(load_m2o=False, context=context)
        settings = get_model("settings").browse(1)
        company_name = comp.name
        contact_id = params.get("contact_id")
        if contact_id:
            contact_id = int(contact_id)
        date_from = params.get("date_from")
        date_to = params.get("date_to")
        show_details = params.get("show_details")
        db = get_connection()
        q = "SELECT l.id,m.date AS move_date,l.contact_id,p.name AS contact_name,m.number AS move_number,l.description,COALESCE(l.due_date,m.date) AS due_date,l.debit-l.credit AS total_amount,r.number AS reconcile_number,l.reconcile_id FROM account_move_line l JOIN account_account a ON a.id=l.account_id JOIN account_move m ON m.id=l.move_id LEFT JOIN contact p ON p.id=l.contact_id LEFT JOIN account_reconcile r ON r.id=l.reconcile_id WHERE l.move_state='posted' AND a.type='receivable' AND a.company_id IN %s"
        args = [tuple(company_ids)]
        if date_from:
            q += " AND COALESCE(l.due_date,l.move_date)>=%s"
            args.append(date_from)
        if date_to:
            q += " AND COALESCE(l.due_date,l.move_date)<=%s"
            args.append(date_to)
        if contact_id:
            q += " AND l.contact_id=%s"
            args.append(contact_id)
        else:
            q += " AND l.contact_id IS NULL"  # XXX
        if not show_details:
            q += " AND (l.reconcile_id IS NULL OR r.balance!=0)"
        q += " ORDER BY COALESCE(l.due_date,m.date),l.id"
        res = db.query(q, *args)
        lines = []
        for r in res:
            vals = dict(r)
            if vals["reconcile_number"] and not vals[
                    "reconcile_number"].endswith("*"):
                vals["due_amount"] = 0
            else:
                vals["due_amount"] = vals["total_amount"]
            lines.append(vals)
        data = {
            "company_name": company_name,
            "date_from": date_from,
            "date_to": date_to,
            "lines": lines,
            "totals": {
                "amount_total": sum(l["due_amount"] for l in lines),
            }
        }
        return data
Example #25
0
class Settings(Model):
    _name = "settings"
    _key = ["name"]
    _audit_log = True
    _fields = {
        "name":
        fields.Char("Display Name"),  # not used any more...
        "legal_name":
        fields.Char("Legal Name"),  # not used any more...
        "company_type_id":
        fields.Many2One("company.type", "Organization Type"),
        "currency_id":
        fields.Many2One("currency", "Default Currency", multi_company=True),
        "account_receivable_id":
        fields.Many2One("account.account",
                        "Account Receivable",
                        multi_company=True),
        "tax_receivable_id":
        fields.Many2One("account.tax.rate", "Account Receivable Tax"),
        "account_payable_id":
        fields.Many2One("account.account",
                        "Account Payable",
                        multi_company=True),
        "tax_payable_id":
        fields.Many2One("account.tax.rate", "Account Payable Tax"),
        "year_end_day":
        fields.Selection(_days, "Financial Year End (Day)"),
        "year_end_month":
        fields.Selection(_months, "Financial Year End (Month)"),
        "lock_date":
        fields.Date("Lock Date"),
        "nf_email":
        fields.Char("Email to Netforce"),  # XXX: deprecated
        "share_settings":
        fields.One2Many("share.access", "settings_id", "Sharing Settings"),
        "currency_gain_id":
        fields.Many2One("account.account",
                        "Currency Gain Account",
                        multi_company=True),
        "currency_loss_id":
        fields.Many2One("account.account",
                        "Currency Loss Account",
                        multi_company=True),
        "unpaid_claim_id":
        fields.Many2One("account.account",
                        "Unpaid Expense Claims Account",
                        multi_company=True),
        "retained_earnings_account_id":
        fields.Many2One("account.account",
                        "Retained Earnings Account",
                        multi_company=True),
        "logo":
        fields.File("Company Logo", multi_company=True),
        "package":
        fields.Char("Package", readonly=True),
        "version":
        fields.Char("Version"),
        "tax_no":
        fields.Char("Tax ID Number", multi_company=True),
        "branch_no":
        fields.Char("Branch Number", multi_company=True),
        "addresses":
        fields.One2Many("address",
                        "settings_id",
                        "Addresses",
                        function="get_addresses"),
        "date_format":
        fields.Char("Date Format"),
        "use_buddhist_date":
        fields.Boolean("Use Buddhist Date"),
        "phone":
        fields.Char("Phone", multi_company=True),
        "fax":
        fields.Char("Fax", multi_company=True),
        "website":
        fields.Char("Website", multi_company=True),
        "root_url":
        fields.Char("Root URL"),
        "sale_journal_id":
        fields.Many2One("account.journal", "Sales Journal"),
        "purchase_journal_id":
        fields.Many2One("account.journal", "Purchase Journal"),
        "pay_in_journal_id":
        fields.Many2One("account.journal", "Receipts Journal"),
        "pay_out_journal_id":
        fields.Many2One("account.journal", "Disbursements Journal"),
        "general_journal_id":
        fields.Many2One("account.journal", "General Journal"),
        "default_address_id":
        fields.Many2One("address",
                        "Default Address",
                        function="get_default_address"),
        "ar_revenue_id":
        fields.Many2One("account.account",
                        "Revenue Account",
                        multi_company=True),
        # XXX: rename for report
        "input_report_id":
        fields.Many2One("account.account",
                        "Input Vat Account",
                        multi_company=True),
        # XXX: rename for report
        "output_report_id":
        fields.Many2One("account.account",
                        "Output Vat Account",
                        multi_company=True),
        # XXX: rename for report
        "wht3_report_id":
        fields.Many2One("account.account", "WHT3 Account", multi_company=True),
        # XXX: rename for report
        "wht53_report_id":
        fields.Many2One("account.account", "WHT53 Account",
                        multi_company=True),
        "sale_copy_picking":
        fields.Boolean("Auto-copy sales orders to goods issue"),
        "sale_copy_invoice":
        fields.Boolean("Auto-copy sales orders to customer invoice"),
        "sale_copy_production":
        fields.Boolean("Auto-copy sales orders to production"),
        "rounding_account_id":
        fields.Many2One("account.account",
                        "Rounding Account",
                        multi_company=True),
        "production_waiting_suborder":
        fields.Boolean("Wait Sub-Order"),  # XXX: check this
        "anon_profile_id":
        fields.Many2One("profile", "Anonymous User Profile"),
        "pick_in_journal_id":
        fields.Many2One("stock.journal", "Goods Receipt Journal"),
        "pick_out_journal_id":
        fields.Many2One("stock.journal", "Goods Issue Journal"),
        "pick_internal_journal_id":
        fields.Many2One("stock.journal", "Goods Transfer Journal"),
        "stock_count_journal_id":
        fields.Many2One("stock.journal", "Stock Count Journal"),
        "landed_cost_journal_id":
        fields.Many2One("stock.journal", "Landed Cost Journal"),
        "transform_journal_id":
        fields.Many2One("stock.journal", "Transform Journal"),
        "production_journal_id":
        fields.Many2One("stock.journal", "Production Journal"),
        "product_borrow_journal_id":
        fields.Many2One("stock.journal", "Borrow Request Journal"),
        "stock_cost_mode":
        fields.Selection(
            [["periodic", "Periodic"], ["perpetual", "Perpetual"]],
            "Inventory Costing Mode"),
        "landed_cost_variance_account_id":
        fields.Many2One("account.account",
                        "Landed Cost Variance Account",
                        multi_company=True),
        "est_ship_account_id":
        fields.Many2One("account.account",
                        "Estimate Shipping Account",
                        multi_company=True),
        "est_duty_account_id":
        fields.Many2One("account.account",
                        "Estimate Duty Account",
                        multi_company=True),
        "act_ship_account_id":
        fields.Many2One("account.account",
                        "Actual Shipping Account",
                        multi_company=True),
        "act_duty_account_id":
        fields.Many2One("account.account",
                        "Actual Duty Account",
                        multi_company=True),
        "menu_icon":
        fields.File("Menu Icon"),
        "stock_cost_auto_compute":
        fields.Boolean("Auto Compute Cost"),
        "purchase_copy_picking":
        fields.Boolean("Auto-copy purchase orders to goods receipt"),
        "purchase_copy_invoice":
        fields.Boolean("Auto-copy purchase orders to supplier invoice"),
        "lot_expiry_journal_id":
        fields.Many2One("stock.journal", "Lot Expiry Journal"),
        "auto_create_delivery_order":
        fields.Boolean("Auto-create delivery orders"),
    }
    _defaults = {
        "package": "free",
    }

    def get_address_str(self, ids, context={}):
        obj = self.browse(ids[0])
        if not obj.addresses:
            return ""
        addr = obj.addresses[0]
        return addr.name_get()[0][1]

    def write(self, ids, vals, **kw):
        res = super().write(ids, vals, **kw)
        if "date_format" in vals or "use_buddhist_date" in vals:
            static.clear_translations()  # XXX: rename this

    def get_fiscal_year_end(self, date=None):
        if date:
            d0 = datetime.datetime.strptime(date, "%Y-%m-%d").date()
        else:
            d0 = datetime.date.today()
        settings = self.browse(1)
        if not settings.year_end_month or not settings.year_end_day:
            raise Exception("Missing fiscal year end")
        month = int(settings.year_end_month)
        day = int(settings.year_end_day)
        d = datetime.date(d0.year, month, day)
        if d < d0:
            d += relativedelta(years=1)
        return d.strftime("%Y-%m-%d")

    def get_fiscal_year_start(self, date=None):
        d1 = self.get_fiscal_year_end(date)
        d = datetime.datetime.strptime(d1, "%Y-%m-%d") - relativedelta(
            years=1) + datetime.timedelta(days=1)
        return d.strftime("%Y-%m-%d")

    def get_default_address(self, ids, context={}):
        vals = {}
        for obj in self.browse(ids):
            vals[obj.id] = obj.addresses and obj.addresses[0].id or None
        return vals

    def get_addresses(self, ids, context={}):
        vals = {}
        comp_id = access.get_active_company()
        for obj in self.browse(ids):
            res = get_model("address").search(
                [["settings_id", "=", obj.id],
                 [
                     "or", ["company_id", "=", None],
                     ["company_id", "child_of", comp_id]
                 ]])
            vals[obj.id] = res
        return vals
Example #26
0
class KaoProduct(Model):
    _inherit = "product"
    _fields = {
        "foodtype": fields.Boolean("Foodtype"),
        "price": fields.Decimal("Price"),
    }
Example #27
0
class ShipMethod(Model):
    _name = "ship.method"
    _string = "Shipping Method"
    _key = ["code"]
    _fields = {
        "name":
        fields.Char("Name", required=True, search=True),
        "code":
        fields.Char("Code", search=True),
        "rates":
        fields.One2Many("ship.rate", "method_id", "Shipping Rates"),
        "comments":
        fields.One2Many("message", "related_id", "Comments"),
        "sequence":
        fields.Integer("Sequence", required=True),
        "type":
        fields.Selection([], "Type"),
        "ship_product_id":
        fields.Many2One("product", "Shipping Product"),
        "exclude_ship_methods":
        fields.Many2Many("ship.method",
                         "Exclude Shipping Methods",
                         reltable="m2m_ship_method_exclude",
                         relfield="method1_id",
                         relfield_other="method2_id"),
        "active":
        fields.Boolean("Active"),
        "ship_amount":
        fields.Decimal("Shipping Amount", function="get_ship_amount"),
    }
    _order = "sequence"
    _defaults = {
        "active": True,
    }

    def create_delivery_order(self, ids, context={}):
        pass

    def get_ship_amount(self, ids, context={}):
        vals = {}
        contact_id = context.get("contact_id")
        contact = get_model("contact").browse(
            contact_id) if contact_id else None
        addr_id = context.get("ship_address_id")
        if addr_id:
            addr = get_model("address").browse(addr_id)
        else:
            addr = None
        order_amount = context.get("order_amount")
        order_weight = context.get("order_weight")
        for obj in self.browse(ids):
            if contact and contact.ship_free:
                vals[obj.id] = 0
                continue
            amt = None
            for rate in obj.rates:
                #print("try rate",rate.id)
                if rate.country_id and (
                        not addr or addr.country_id.id != rate.country_id.id):
                    #print("  skip country")
                    continue
                if rate.province_id and (not addr or addr.province_id.id !=
                                         rate.province_id.id):
                    #print("  skip province (%s / %s %s)"%(rate.province_id.id,addr.id,addr.province_id.id if addr else None))
                    continue
                if rate.district_id and (not addr or addr.district_id.id !=
                                         rate.district_id.id):
                    #print("  skip district")
                    continue
                if rate.postal_code and (not addr or
                                         addr.postal_code != rate.postal_code):
                    #print("  skip postal code")
                    continue
                if rate.address_name and (not addr
                                          or addr.name != rate.address_name):
                    #print("  skip address name")
                    continue
                if rate.min_amount and (order_amount is None
                                        or order_amount < rate.min_amount):
                    #print("  skip min amount")
                    continue
                if rate.min_weight and (order_weight is None
                                        or order_weight < rate.min_weight):
                    #print("  skip min weight")
                    continue
                #print("  OK ship_price=%s"%rate.ship_price)
                if amt is None or rate.ship_price < amt:
                    amt = rate.ship_price
            #if amt is not None:
            #print("=> shipping price found: %s"%amt)
            #else:
            #print("=> no shipping price found")
            vals[obj.id] = amt
        return vals
Example #28
0
class Activity(Model):
    _name = "activity"
    _string = "Activity"
    _name_field = "subject"
    _fields = {
        "type":
        fields.Selection([["task", "Task"], ["event", "Event"],
                          ["meeting", "Meeting"], ["call", "Call"]],
                         "Activity Type",
                         required=True,
                         search=True),
        "user_id":
        fields.Many2One("base.user", "Assigned To", search=True,
                        required=True),
        "subject":
        fields.Char("Subject", required=True, size=128, search=True),
        "date":
        fields.Date("Date", search=True),
        "due_date":
        fields.Date("Due Date"),
        "description":
        fields.Text("Description"),
        "body":
        fields.Text("Body"),
        "state":
        fields.Selection(
            [["new", "Not Started"], ["in_progress", "In Progress"],
             ["done", "Completed"], ["waiting", "Waiting on someone else"],
             ["deferred", "Deferred"]],
            "Status",
            required=True),
        "priority":
        fields.Selection(
            [["high", "High"], ["normal", "Normal"], ["low", "Low"]],
            "Priority"),
        "phone":
        fields.Char("Phone"),
        "email":
        fields.Char("Email"),
        "event_start":
        fields.DateTime("Start"),
        "event_end":
        fields.DateTime("End"),
        "location":
        fields.Char("Location"),
        "email_uid":
        fields.Char("Email UID"),
        "email_account_id":
        fields.Many2One("email.account", "Email Account"),
        "work_times":
        fields.One2Many("work.time", "activity_id", "Work Time"),
        "related_id":
        fields.Reference(
            [["contact", "Contact"], ["sale.opportunity", "Opportunity"],
             ["sale.quot", "Quotation"], ["sale.order", "Sales Order"],
             ["job", "Service Order"], ["issue", "Issue"]], "Related To"),
        "name_id":
        fields.Reference([["contact", "Contact"], ["sale.lead", "Lead"]],
                         "Name"),
        "comments":
        fields.One2Many("message", "related_id", "Comments"),
        "overdue":
        fields.Boolean("Overdue",
                       function="get_overdue",
                       function_search="search_overdue"),
    }

    def _get_name_id(self, context={}):
        defaults = context.get("defaults")
        if not defaults:
            return
        related = defaults.get("related_id")
        if not related:
            return
        model, model_id = related.split(",")
        model_id = int(model_id)
        if model == "sale.quot":
            obj = get_model("sale.quot").browse(model_id)
            return "contact,%s" % obj.contact_id.id

    _defaults = {
        "state": "new",
        "date": lambda *a: time.strftime("%Y-%m-%d"),
        "type": lambda self, ctx: ctx.get("activ_type") or "task",
        "name_id": _get_name_id,
    }
    _order = "due_date desc,date desc,id desc"

    # XXX
    def view_activity(self, ids, context={}):
        obj = self.browse(ids[0])
        return {
            "next": {
                "name": "activ",
                "mode": "form",
                "active_id": obj.id,
            }
        }

    def send_email(self, ids, context={}):
        obj = self.browse(ids)[0]
        from_addr = obj.user_id.email
        to_addr = obj.name_id.email
        if not to_addr:
            raise Exception("Email not found")
        res = get_model("email.account").search([["type", "=", "smtp"]])
        if not res:
            raise Exception("Email account not found")
        acc_id = res[0]
        acc = get_model("email.account").browse(acc_id)
        server = smtplib.SMTP(acc.host, acc.port)
        if acc.user:
            server.login(acc.user, acc.password)
        msg = "From: " + from_addr + "\r\n"
        msg += "To: " + to_addr + "\r\n"
        msg += "Subject: " + obj.subject + "\r\n\r\n"
        msg += obj.body
        server.sendmail(from_addr, [to_addr], msg)
        obj.write({"state": "done"})
        server.quit()

    # XXX: move this
    def fetch_email(self, context={}):
        print("fetch_email")
        acc_ids = get_model("email.account").search([["type", "=", "pop3"]])
        for acc in get_model("email.account").browse(acc_ids):
            print("connecting %s %s %s" % (acc.host, acc.port, acc.user))
            if acc.security == "ssl":
                serv = poplib.POP3_SSL(acc.host, acc.port)
            else:
                serv = poplib.POP3(acc.host, acc.port)
            serv.user(acc.user)
            if acc.password:
                serv.pass_(acc.password)
            try:
                resp, msg_list, size = serv.uidl()
                print("%d messages" % len(msg_list))
                for msg_info in msg_list:
                    msg_no, msg_uid = msg_info.decode().split()
                    print("msg_no", msg_no)
                    print("msg_uid", msg_uid)
                    try:
                        res = self.search_read(
                            [["email_account_id", "=", acc.id],
                             ["email_uid", "=", msg_uid]])
                        if res:
                            print("skipping %s" % msg_uid)
                            serv.dele(msg_no)
                            continue
                        print("reading %s" % msg_uid)
                        resp, lines, size = serv.retr(msg_no)
                        msg = email.message_from_bytes(b"\n".join(lines))

                        def dec_header(data):
                            dh = decode_header(data or "")
                            s = ""
                            for data, charset in dh:
                                if isinstance(data, str):
                                    s += data
                                else:
                                    s += data.decode(
                                        conv_charset(charset) or "utf-8")
                            return s

                        def dec_date(data):
                            res = parsedate(data or "")
                            if not res:
                                return ""
                            return time.strftime("%Y-%m-%d %H:%M:%S", res)

                        def get_body(m):
                            if m.get_filename():
                                return "[Attachment: %s (%s)]\n" % (dec_header(
                                    m.get_filename()), m.get_content_type())
                            else:
                                if not m.is_multipart():
                                    charset = conv_charset(
                                        m.get_content_charset())
                                    return m.get_payload(decode=True).decode(
                                        charset or "utf-8", errors="replace")
                                else:
                                    data = m.get_payload()
                                    found = False
                                    res = []
                                    for m in data:
                                        fn = m.get_filename()
                                        if not fn:
                                            if found:
                                                continue
                                            found = True
                                        res.append(get_body(m))
                                    return "\n".join(res)

                        email_vals = {
                            "account_id": acc.id,
                            "msg_uid": msg_uid,
                            "date": dec_date(msg["Date"]),
                            "from_addr": parseaddr(msg["From"])[1][:64],
                            "to_addr": parseaddr(msg["To"])[1][:64],
                            "subject": dec_header(msg["Subject"])[:128],
                            "body": get_body(msg),
                        }
                        get_model("activity").import_email(email_vals)
                    except Exception as e:
                        print("WARNING: failed to import email %s", msg_uid)
                    finally:
                        serv.dele(msg_no)
            finally:
                print("quitting")
                db = database.get_connection()
                db.commit()
                serv.quit()

    # XXX: move this
    def import_email(self, email):
        print("import_email")
        print("from=%s to=%s subject=%s" %
              (email["from_addr"], email["to_addr"], email["subject"]))
        from_user_id = None
        from_contact_id = None
        from_lead_id = None
        to_user_id = None
        to_contact_id = None
        to_lead_id = None
        user_id = None
        contact_id = None
        lead_id = None
        contact_id = None
        opport_id = None
        quot_id = None
        sale_id = None
        name_id = None
        related_id = None
        res = get_model("base.user").search(
            [["email", "=ilike", email["from_addr"]]])
        if res:
            from_user_id = res[0]
        res = get_model("contact").search(
            [["email", "=ilike", email["from_addr"]]])
        if res:
            from_contact_id = res[0]
        else:
            res = get_model("sale.lead").search(
                [["email", "=ilike", email["from_addr"]]])
            if res:
                from_lead_id = res[0]
        if not from_user_id and not from_contact_id and not from_lead_id:
            print("  => skipping (from)")
            return
        res = get_model("base.user").search(
            [["email", "=ilike", email["to_addr"]]])
        if res:
            to_user_id = res[0]
        res = get_model("contact").search(
            [["email", "=ilike", email["to_addr"]]])
        if res:
            to_contact_id = res[0]
        else:
            res = get_model("sale.lead").search(
                [["email", "=ilike", email["to_addr"]]])
            if res:
                to_lead_id = res[0]
        if (from_contact_id or from_lead_id) and to_user_id:
            if from_contact_id:
                contact_id = from_contact_id
            if from_lead_id:
                lead_id = from_lead_id
            user_id = to_user_id
        elif from_user_id and (to_contact_id or to_lead_id):
            user_id = from_user_id
            if to_contact_id:
                contact_id = to_contact_id
            if to_lead_id:
                lead_id = to_lead_id
        else:
            print("  => skipping (from/to)")
            return
        if contact_id:
            contact = get_model("contact").browse(contact_id)
            if contact.contact_id:
                contact_id = contact.contact_id.id
        if contact_id and email["subject"]:
            subj = email["subject"].lower()
            opp_ids = get_model("sale.opportunity").search(
                [["contact_id", "=", contact_id], ["state", "=", "open"]])
            for opp in get_model("sale.opportunity").browse(opp_ids):
                if subj.find(opp.name.lower()) != -1:
                    opport_id = opp.id
                    break
            quot_ids = get_model("sale.quot").search(
                [["contact_id", "=", contact_id], ["state", "=", "approved"]])
            for quot in get_model("sale.quot").browse(quot_ids):
                if subj.find(quot.number.lower()) != -1:
                    quot_id = quot.id
                    break
            sale_ids = get_model("sale.order").search(
                [["contact_id", "=", contact_id], ["state", "=", "confirmed"]])
            for sale in get_model("sale.order").browse(sale_ids):
                if subj.find(sale.number.lower()) != -1:
                    sale_id = sale.id
                    break
        if contact_id:
            name_id = "contact,%d" % contact_id
        elif lead_id:
            name_id = "sale.lead,%d" % lead_id
        if opport_id:
            related_id = "sale.opportunity,%d" % opport_id
        elif quot_id:
            related_id = "sale.quot,%d" % quot_id
        elif sale_id:
            related_id = "sale.order,%d" % sale_id
        elif contact_id:
            related_id = "contact,%d" % contact_id
        vals = {
            "type": "email",
            "subject": email["subject"],
            "state": "done",
            "user_id": user_id,
            "related_id": related_id,
            "name_id": name_id,
            "email_uid": email["msg_uid"],
            "email_account_id": email["account_id"],
            "body": email["body"],
            "date": email["date"],
        }
        act_id = get_model("activity").create(vals)
        print("  => new activity %d" % act_id)
        return act_id

    def get_overdue(self, ids, context={}):
        vals = {}
        for obj in self.browse(ids):
            if obj.due_date:
                vals[obj.id] = obj.due_date < time.strftime(
                    "%Y-%m-%d") and obj.state != "done"
            else:
                vals[obj.id] = False
        return vals

    def search_overdue(self, clause, context={}):
        return [["due_date", "<", time.strftime("%Y-%m-%d")],
                ["state", "!=", "done"]]

    def check_days_before_overdue(self,
                                  ids,
                                  days=None,
                                  days_from=None,
                                  days_to=None,
                                  context={}):
        print("Activity.check_days_before_overdue", ids, days, days_from,
              days_to)
        cond = [["state", "!=", "done"]]
        if days != None:
            d = (datetime.date.today() +
                 datetime.timedelta(days=days)).strftime("%Y-%m-%d")
            cond.append(["due_date", "=", d])
        if days_from != None:
            d = (datetime.date.today() +
                 datetime.timedelta(days=days_from)).strftime("%Y-%m-%d")
            print("XXXXXXXXXXXXXxx d", d)
            cond.append(["due_date", "<=", d])
        if days_to != None:
            d = (datetime.date.today() +
                 datetime.timedelta(days=days_to)).strftime("%Y-%m-%d")
            cond.append(["due_date", ">=", d])
        if ids:
            cond.append(["ids", "in", ids])
        ids = self.search(cond)
        return ids
Example #29
0
class PurchaseOrder(Model):
    _name = "purchase.order"
    _string = "Purchase Order"
    _audit_log = True
    _name_field = "number"
    _multi_company = True
    _key = ["company_id", "number"]
    _fields = {
        "number":
        fields.Char("Number", required=True, search=True),
        "ref":
        fields.Char("Ref", search=True),
        "contact_id":
        fields.Many2One("contact", "Supplier", required=True, search=True),
        "customer_id":
        fields.Many2One("contact", "Customer", search=True),
        "date":
        fields.Date("Date", required=True, search=True),
        "date_required":
        fields.Date("Required Date"),
        "state":
        fields.Selection([("draft", "Draft"), ("confirmed", "Confirmed"),
                          ("done", "Completed"), ("voided", "Voided")],
                         "Status",
                         required=True),
        "lines":
        fields.One2Many("purchase.order.line", "order_id", "Lines"),
        "amount_subtotal":
        fields.Decimal("Subtotal",
                       function="get_amount",
                       function_multi=True,
                       store=True),
        "amount_tax":
        fields.Decimal("Tax Amount",
                       function="get_amount",
                       function_multi=True,
                       store=True),
        "amount_total":
        fields.Decimal("Total",
                       function="get_amount",
                       function_multi=True,
                       store=True),
        "amount_total_cur":
        fields.Decimal("Total",
                       function="get_amount",
                       function_multi=True,
                       store=True),
        "amount_total_words":
        fields.Char("Total Words", function="get_amount_total_words"),
        "qty_total":
        fields.Decimal("Total Quantity", function="get_qty_total"),
        "currency_id":
        fields.Many2One("currency", "Currency", required=True),
        "tax_type":
        fields.Selection([["tax_ex", "Tax Exclusive"],
                          ["tax_in", "Tax Inclusive"], ["no_tax", "No Tax"]],
                         "Tax Type",
                         required=True),
        "invoice_lines":
        fields.One2Many("account.invoice.line", "related_id", "Invoice Lines"),
        #"stock_moves": fields.One2Many("stock.move","purch_id","Stock Moves"),
        "invoices":
        fields.Many2Many("account.invoice",
                         "Invoices",
                         function="get_invoices"),
        "pickings":
        fields.Many2Many("stock.picking",
                         "Stock Pickings",
                         function="get_pickings"),
        "is_delivered":
        fields.Boolean("Delivered", function="get_delivered"),
        "is_paid":
        fields.Boolean("Paid", function="get_paid"),
        "comments":
        fields.One2Many("message", "related_id", "Comments"),
        "location_id":
        fields.Many2One("stock.location", "Warehouse"),  # XXX: deprecated
        "delivery_date":
        fields.Date("Delivery Date"),
        "ship_method_id":
        fields.Many2One("ship.method", "Shipping Method"),  # XXX: deprecated
        "payment_terms":
        fields.Text("Payment Terms"),
        "ship_term_id":
        fields.Many2One("ship.term", "Shipping Terms"),
        "price_list_id":
        fields.Many2One("price.list",
                        "Price List",
                        condition=[["type", "=", "purchase"]]),
        "documents":
        fields.One2Many("document", "related_id", "Documents"),
        "company_id":
        fields.Many2One("company", "Company"),
        "purchase_type_id":
        fields.Many2One("purchase.type", "Purchase Type"),
        "other_info":
        fields.Text("Other Info"),
        "bill_address_id":
        fields.Many2One("address", "Billing Address"),
        "ship_address_id":
        fields.Many2One("address", "Shipping Address"),
        "sequence_id":
        fields.Many2One("sequence", "Number Sequence"),
        "stock_moves":
        fields.One2Many("stock.move", "related_id", "Stock Movements"),
        "agg_amount_total":
        fields.Decimal("Total Amount", agg_function=["sum", "amount_total"]),
        "year":
        fields.Char("Year", sql_function=["year", "date"]),
        "quarter":
        fields.Char("Quarter", sql_function=["quarter", "date"]),
        "month":
        fields.Char("Month", sql_function=["month", "date"]),
        "week":
        fields.Char("Week", sql_function=["week", "date"]),
        "agg_amount_subtotal":
        fields.Decimal("Total Amount w/o Tax",
                       agg_function=["sum", "amount_subtotal"]),
        "user_id":
        fields.Many2One("base.user", "Owner", search=True),
        "emails":
        fields.One2Many("email.message", "related_id", "Emails"),
        "related_id":
        fields.Reference([["sale.order", "Sales Order"],
                          ["stock.consign", "Consignment Stock"]],
                         "Related To"),
    }
    _order = "date desc,number desc"

    _sql_constraints = [("key_uniq", "unique (company_id, number)",
                         "The number of each company must be unique!")]

    def _get_number(self, context={}):
        seq_id = get_model("sequence").find_sequence(type="purchase_order")
        if not seq_id:
            return None
        while 1:
            num = get_model("sequence").get_next_number(seq_id)
            user_id = get_active_user()
            set_active_user(1)
            res = self.search([["number", "=", num]])
            set_active_user(user_id)
            if not res:
                return num
            get_model("sequence").increment_number(seq_id)

    def _get_currency(self, context={}):
        settings = get_model("settings").browse(1)
        return settings.currency_id.id

    _defaults = {
        "state": "draft",
        "date": lambda *a: time.strftime("%Y-%m-%d"),
        "number": _get_number,
        "currency_id": _get_currency,
        "tax_type": "tax_ex",
        "company_id": lambda *a: get_active_company(),
        "user_id": lambda *a: get_active_user(),
    }

    def create(self, vals, **kw):
        id = super(PurchaseOrder, self).create(vals, **kw)
        self.function_store([id])
        return id

    def write(self, ids, vals, **kw):
        super(PurchaseOrder, self).write(ids, vals, **kw)
        self.function_store(ids)

    def confirm(self, ids, context={}):
        settings = get_model("settings").browse(1)
        for obj in self.browse(ids):
            if obj.state != "draft":
                raise Exception("Invalid state")
            for line in obj.lines:
                prod = line.product_id
                if prod and prod.type in ("stock", "consumable",
                                          "bundle") and not line.location_id:
                    raise Exception("Missing location for product %s" %
                                    prod.code)
            obj.write({"state": "confirmed"})
            if settings.purchase_copy_picking:
                res = obj.copy_to_picking()
                picking_id = res["picking_id"]
                get_model("stock.picking").pending([picking_id])
            if settings.purchase_copy_invoice:
                obj.copy_to_invoice()
            obj.trigger("confirm")

    def done(self, ids, context={}):
        for obj in self.browse(ids):
            if obj.state != "confirmed":
                raise Exception("Invalid state")
            obj.write({"state": "done"})

    def reopen(self, ids, context={}):
        for obj in self.browse(ids):
            if obj.state != "done":
                raise Exception("Invalid state")
            obj.write({"state": "confirmed"})

    def to_draft(self, ids, context={}):
        for obj in self.browse(ids):
            obj.write({"state": "draft"})

    def get_amount(self, ids, context={}):
        settings = get_model("settings").browse(1)
        res = {}
        for obj in self.browse(ids):
            vals = {}
            subtotal = 0
            tax = 0
            for line in obj.lines:
                if line.tax_id:
                    line_tax = get_model("account.tax.rate").compute_tax(
                        line.tax_id.id, line.amount, tax_type=obj.tax_type)
                else:
                    line_tax = 0
                tax += line_tax
                if obj.tax_type == "tax_in":
                    subtotal += line.amount - line_tax
                else:
                    subtotal += line.amount
            vals["amount_subtotal"] = subtotal
            vals["amount_tax"] = tax
            vals["amount_total"] = subtotal + tax
            vals["amount_total_cur"] = get_model("currency").convert(
                vals["amount_total"], obj.currency_id.id,
                settings.currency_id.id)
            res[obj.id] = vals
        return res

    def get_qty_total(self, ids, context={}):
        res = {}
        for obj in self.browse(ids):
            qty = sum([line.qty for line in obj.lines])
            res[obj.id] = qty or 0
        return res

    def update_amounts(self, context):
        data = context["data"]
        data["amount_subtotal"] = 0
        data["amount_tax"] = 0
        tax_type = data["tax_type"]
        for line in data["lines"]:
            if not line:
                continue
            amt = Decimal(((line.get("qty") or 0) *
                           (line.get("unit_price") or 0)) -
                          (line.get("discount_amount") or 0))
            line["amount"] = amt
            tax_id = line.get("tax_id")
            if tax_id:
                tax = get_model("account.tax.rate").compute_tax(
                    tax_id, amt, tax_type=tax_type)
                data["amount_tax"] += tax
            else:
                tax = 0
            if tax_type == "tax_in":
                data["amount_subtotal"] += amt - tax
            else:
                data["amount_subtotal"] += amt
        data["amount_total"] = data["amount_subtotal"] + data["amount_tax"]
        return data

    def onchange_product(self, context):
        data = context["data"]
        path = context["path"]
        line = get_data_path(data, path, parent=True)
        prod_id = line.get("product_id")
        if not prod_id:
            return {}
        prod = get_model("product").browse(prod_id)
        line["description"] = prod.description
        line["qty"] = 1
        if prod.uom_id is not None:
            line["uom_id"] = prod.uom_id.id
        pricelist_id = data["price_list_id"]
        price = None
        if pricelist_id:
            price = get_model("price.list").get_price(pricelist_id, prod.id, 1)
            price_list = get_model("price.list").browse(pricelist_id)
            price_currency_id = price_list.currency_id.id
        if price is None:
            price = prod.purchase_price
            settings = get_model("settings").browse(1)
            price_currency_id = settings.currency_id.id
        if price is not None:
            currency_id = data["currency_id"]
            price_cur = get_model("currency").convert(price, price_currency_id,
                                                      currency_id)
            line["unit_price"] = price_cur
        if prod.purchase_tax_id is not None:
            line["tax_id"] = prod.purchase_tax_id.id
        if prod.location_id:
            line["location_id"] = prod.location_id.id
        data = self.update_amounts(context)
        return data

    def onchange_qty(self, context):
        data = context["data"]
        path = context["path"]
        line = get_data_path(data, path, parent=True)
        prod_id = line.get("product_id")
        if not prod_id:
            return {}
        prod = get_model("product").browse(prod_id)
        pricelist_id = data["price_list_id"]
        qty = line["qty"]
        price = None
        if pricelist_id:
            price = get_model("price.list").get_price(pricelist_id, prod.id,
                                                      qty)
            price_list = get_model("price.list").browse(pricelist_id)
            price_currency_id = price_list.currency_id.id
        if price is None:
            price = prod.purchase_price
            settings = get_model("settings").browse(1)
            price_currency_id = settings.currency_id.id
        if price is not None:
            currency_id = data["currency_id"]
            price_cur = get_model("currency").convert(price, price_currency_id,
                                                      currency_id)
            line["unit_price"] = price_cur
        data = self.update_amounts(context)
        return data

    def copy_to_picking(self, ids, context={}):
        settings = get_model("settings").browse(1)
        obj = self.browse(ids[0])
        contact = obj.contact_id
        pick_vals = {
            "type": "in",
            "ref": obj.number,
            "related_id": "purchase.order,%s" % obj.id,
            "contact_id": contact.id,
            "currency_id": obj.currency_id.id,
            "lines": [],
        }
        if obj.delivery_date:
            pick_vals["date"] = obj.delivery_date
        if contact and contact.pick_in_journal_id:
            pick_vals["journal_id"] = contact.pick_in_journal_id.id
        res = get_model("stock.location").search([["type", "=", "supplier"]],
                                                 order="id")
        if not res:
            raise Exception("Supplier location not found")
        supp_loc_id = res[0]
        res = get_model("stock.location").search([["type", "=", "internal"]])
        if not res:
            raise Exception("Warehouse not found")
        wh_loc_id = res[0]
        if not settings.currency_id:
            raise Exception("Missing currency in financial settings")
        for line in obj.lines:
            prod = line.product_id
            if prod.type not in ("stock", "consumable"):
                continue
            remain_qty = (line.qty_stock or line.qty) - line.qty_received
            if remain_qty <= 0:
                continue
            unit_price = line.amount / line.qty if line.qty else 0
            if obj.tax_type == "tax_in":
                if line.tax_id:
                    tax_amt = get_model("account.tax.rate").compute_tax(
                        line.tax_id.id, unit_price, tax_type=obj.tax_type)
                else:
                    tax_amt = 0
                cost_price_cur = round(unit_price - tax_amt, 2)
            else:
                cost_price_cur = unit_price
            if line.qty_stock:
                purch_uom = prod.uom_id
                if not prod.purchase_to_stock_uom_factor:
                    raise Exception(
                        "Missing purchase order to stock UoM factor for product %s"
                        % prod.code)
                cost_price_cur /= prod.purchase_to_stock_uom_factor
            else:
                purch_uom = line.uom_id
            cost_price = get_model("currency").convert(cost_price_cur,
                                                       obj.currency_id.id,
                                                       settings.currency_id.id)
            cost_amount = cost_price * remain_qty
            line_vals = {
                "product_id": prod.id,
                "qty": remain_qty,
                "uom_id": purch_uom.id,
                "cost_price_cur": cost_price_cur,
                "cost_price": cost_price,
                "cost_amount": cost_amount,
                "location_from_id": supp_loc_id,
                "location_to_id": line.location_id.id or wh_loc_id,
                "related_id": "purchase.order,%s" % obj.id,
            }
            pick_vals["lines"].append(("create", line_vals))
        if not pick_vals["lines"]:
            raise Exception("Nothing left to receive")
        pick_id = get_model("stock.picking").create(pick_vals,
                                                    {"pick_type": "in"})
        pick = get_model("stock.picking").browse(pick_id)
        return {
            "next": {
                "name": "pick_in",
                "mode": "form",
                "active_id": pick_id,
            },
            "flash":
            "Goods receipt %s created from purchase order %s" %
            (pick.number, obj.number),
            "picking_id":
            pick_id,
        }

    def copy_to_invoice(self, ids, context={}):
        id = ids[0]
        obj = self.browse(id)
        contact = obj.contact_id
        inv_vals = {
            "type": "in",
            "inv_type": "invoice",
            "ref": obj.number,
            "related_id": "purchase.order,%s" % obj.id,
            "contact_id": obj.contact_id.id,
            "currency_id": obj.currency_id.id,
            "lines": [],
            "tax_type": obj.tax_type,
        }
        if contact.purchase_journal_id:
            inv_vals["journal_id"] = contact.purchase_journal_id.id
            if contact.purchase_journal_id.sequence_id:
                inv_vals[
                    "sequence_id"] = contact.purchase_journal_id.sequence_id.id
        for line in obj.lines:
            prod = line.product_id
            remain_qty = line.qty - line.qty_invoiced
            if remain_qty <= 0:
                continue
            purch_acc_id = None
            if prod:
                purch_acc_id = prod.purchase_account_id.id
                if not purch_acc_id and prod.parent_id:
                    purch_acc_id = prod.parent_id.purchase_account_id.id
                if not purch_acc_id and prod.categ_id and prod.categ_id.purchase_account_id:
                    purch_acc_id = prod.categ_id.purchase_account_id.id
            line_vals = {
                "product_id": prod.id,
                "description": line.description,
                "qty": remain_qty,
                "uom_id": line.uom_id.id,
                "unit_price": line.unit_price,
                "account_id": prod and prod.purchase_account_id.id or None,
                "tax_id": line.tax_id.id,
                "amount": line.amount,
            }
            inv_vals["lines"].append(("create", line_vals))
        if not inv_vals["lines"]:
            raise Exception("Nothing left to invoice")
        inv_id = get_model("account.invoice").create(inv_vals, {
            "type": "in",
            "inv_type": "invoice"
        })
        inv = get_model("account.invoice").browse(inv_id)
        return {
            "next": {
                "name": "view_invoice",
                "active_id": inv_id,
            },
            "flash":
            "Invoice %s created from purchase order %s" %
            (inv.number, obj.number),
        }

    def get_delivered(self, ids, context={}):
        vals = {}
        #import pdb; pdb.set_trace()
        for obj in self.browse(ids):
            is_delivered = True
            for line in obj.lines:
                prod = line.product_id
                if prod.type not in ("stock", "consumable"):
                    continue
                remain_qty = line.qty - line.qty_received
                if remain_qty > 0:
                    is_delivered = False
                    break
            vals[obj.id] = is_delivered
        return vals

    def get_paid(self, ids, context={}):
        vals = {}
        for obj in self.browse(ids):
            amt_paid = 0
            for inv_line in obj.invoice_lines:
                inv = inv_line.invoice_id
                if inv.state != "paid":
                    continue
                amt_paid += inv_line.amount
            is_paid = amt_paid >= obj.amount_subtotal
            vals[obj.id] = is_paid
        return vals

    def void(self, ids, context={}):
        obj = self.browse(ids)[0]
        for pick in obj.pickings:
            if pick.state != "voided":
                raise Exception(
                    "There are still goods receipts for this purchase order")
        for inv in obj.invoices:
            if inv.state != "voided":
                raise Exception(
                    "There are still invoices for this purchase order")
        obj.write({"state": "voided"})

    def copy(self, ids, context):
        obj = self.browse(ids)[0]
        vals = {
            "contact_id": obj.contact_id.id,
            "date": obj.date,
            "ref": obj.ref,
            "currency_id": obj.currency_id.id,
            "tax_type": obj.tax_type,
            "lines": [],
        }
        for line in obj.lines:
            line_vals = {
                "product_id": line.product_id.id,
                "description": line.description,
                "qty": line.qty,
                "uom_id": line.uom_id.id,
                "unit_price": line.unit_price,
                "tax_id": line.tax_id.id,
            }
            vals["lines"].append(("create", line_vals))
        new_id = self.create(vals)
        new_obj = self.browse(new_id)
        return {
            "next": {
                "name": "purchase",
                "mode": "form",
                "active_id": new_id,
            },
            "flash":
            "Purchase order %s copied to %s" % (obj.number, new_obj.number),
        }

    def get_invoices(self, ids, context={}):
        vals = {}
        for obj in self.browse(ids):
            inv_ids = []
            for inv_line in obj.invoice_lines:
                inv_id = inv_line.invoice_id.id
                if inv_id not in inv_ids:
                    inv_ids.append(inv_id)
            vals[obj.id] = inv_ids
        return vals

    def get_pickings(self, ids, context={}):
        vals = {}
        for obj in self.browse(ids):
            pick_ids = []
            for move in obj.stock_moves:
                pick_id = move.picking_id.id
                if pick_id not in pick_ids:
                    pick_ids.append(pick_id)
            vals[obj.id] = pick_ids
        return vals

    def onchange_contact(self, context):
        data = context["data"]
        contact_id = data.get("contact_id")
        if not contact_id:
            return {}
        contact = get_model("contact").browse(contact_id)
        data["payment_terms"] = contact.payment_terms
        data["price_list_id"] = contact.purchase_price_list_id.id
        if contact.currency_id:
            data["currency_id"] = contact.currency_id.id
        else:
            settings = get_model("settings").browse(1)
            data["currency_id"] = settings.currency_id.id
        return data

    def check_received_qtys(self, ids, context={}):
        obj = self.browse(ids)[0]
        for line in obj.lines:
            if line.qty_received > (line.qty_stock or line.qty):
                raise Exception(
                    "Can not receive excess quantity for purchase order %s and product %s (order qty: %s, received qty: %s)"
                    % (obj.number, line.product_id.code, line.qty_stock
                       or line.qty, line.qty_received))

    def get_purchase_form_template(self, ids, context={}):
        obj = self.browse(ids)[0]
        if obj.state == "draft":
            return "rfq_form"
        else:
            return "purchase_form"

    def get_amount_total_words(self, ids, context={}):
        vals = {}
        for obj in self.browse(ids):
            amount_total_words = utils.num2word(obj.amount_total)
            vals[obj.id] = amount_total_words
            return vals

    def onchange_sequence(self, context={}):
        data = context["data"]
        seq_id = data["sequence_id"]
        if not seq_id:
            return None
        while 1:
            num = get_model("sequence").get_next_number(seq_id,
                                                        context=context)
            res = self.search([["number", "=", num]])
            if not res:
                break
            get_model("sequence").increment_number(seq_id, context=context)
        data["number"] = num
        return data

    def delete(self, ids, **kw):
        for obj in self.browse(ids):
            if obj.state in ("confirmed", "done"):
                raise Exception("Can not delete purchase order in this status")
        super().delete(ids, **kw)
Example #30
0
class ReconcileAdjust(Model):
    _name = "reconcile.adjust"
    _transient = True
    _fields = {
        "line_id":
        fields.Many2One("account.statement.line",
                        "Statement Line",
                        required=True,
                        on_delete="cascade"),
        "amount":
        fields.Decimal("Adjustment Amount", required=True, readonly=True),
        "account_id":
        fields.Many2One("account.account",
                        "Adjustment Account",
                        required=True,
                        on_delete="cascade"),
        "date":
        fields.Date("Adjustment Date", required=True),
        "warning":
        fields.Boolean("Warning", readonly=True),
    }

    def default_get(self, field_names=None, context={}, **kw):
        if not field_names:
            return {}
        line_id = context.get("line_id")
        if not line_id:
            return {}
        line_id = int(line_id)
        st_line_ids, acc_line_ids = get_model(
            "account.statement.line").get_reconcile_lines([line_id])
        total_st = 0
        for st_line in get_model("account.statement.line").browse(st_line_ids):
            total_st += st_line.received - st_line.spent
        total_acc = 0
        for acc_line in get_model("account.move.line").browse(acc_line_ids):
            total_acc += acc_line.debit - acc_line.credit
        amt = total_st - total_acc
        vals = {
            "line_id": line_id,
            "amount": amt,
            "warning": abs(amt) > 1,
            "date": time.strftime("%Y-%m-%d"),
        }
        return vals

    def do_adjust(self, ids, context={}):
        obj = self.browse(ids)[0]
        st_line = obj.line_id
        account_id = st_line.statement_id.account_id.id
        amt = obj.amount
        vals = {
            "type":
            amt > 0 and "in" or "out",
            "pay_type":
            "adjust",
            "date":
            obj.date,
            "account_id":
            account_id,
            "lines": [("create", {
                "type": "adjust",
                "description": "Adjustment",
                "amount": abs(amt),
                "account_id": obj.account_id.id,
            })],
        }
        pmt_id = get_model("account.payment").create(vals,
                                                     context={
                                                         "type": vals["type"],
                                                         "date": obj.date
                                                     })
        get_model("account.payment").post([pmt_id])
        pmt = get_model("account.payment").browse(pmt_id)
        acc_line = pmt.move_id.lines[0]
        acc_line.write({"statement_lines": [("set", [st_line.id])]})
        st_line_ids, acc_line_ids = get_model(
            "account.statement.line").get_reconcile_lines([st_line.id])
        total_st = 0
        for st_line in get_model("account.statement.line").browse(st_line_ids):
            total_st += st_line.received - st_line.spent
        total_acc = 0
        for acc_line in get_model("account.move.line").browse(acc_line_ids):
            total_acc += acc_line.debit - acc_line.credit
        if total_st != total_acc:
            raise Exception("Reconciliation error")
        get_model("account.statement.line").write(st_line_ids,
                                                  {"state": "reconciled"})
        get_model("account.move.line").write(acc_line_ids,
                                             {"state": "reconciled"})
        return {
            "next": {
                "name": "bank",
                "mode": "page",
                "active_id": account_id,
                "related_tab": 0,
            }
        }