From 28397facb6b5567b836d7b0b9c0c764f69ac335b Mon Sep 17 00:00:00 2001 From: aserrador Date: Mon, 20 Oct 2025 15:18:28 -0500 Subject: [PATCH] feat: Add Populate Scripts --- demo/populate_scripts/__main__.py | 109 +++++++++++++++++++++++++++ demo/populate_scripts/account.py | 119 ++++++++++++++++++++++++++++++ demo/populate_scripts/company.py | 98 ++++++++++++++++++++++++ demo/populate_scripts/country.py | 24 ++++++ demo/populate_scripts/currency.py | 16 ++++ demo/populate_scripts/parties.py | 39 ++++++++++ demo/populate_scripts/tools.py | 10 +++ 7 files changed, 415 insertions(+) create mode 100644 demo/populate_scripts/__main__.py create mode 100644 demo/populate_scripts/account.py create mode 100644 demo/populate_scripts/company.py create mode 100644 demo/populate_scripts/country.py create mode 100644 demo/populate_scripts/currency.py create mode 100644 demo/populate_scripts/parties.py create mode 100644 demo/populate_scripts/tools.py diff --git a/demo/populate_scripts/__main__.py b/demo/populate_scripts/__main__.py new file mode 100644 index 0000000..189b7ad --- /dev/null +++ b/demo/populate_scripts/__main__.py @@ -0,0 +1,109 @@ +from argparse import ArgumentDefaultsHelpFormatter, ArgumentParser + +from proteus import Model, Wizard +from proteus import config as pconfig + + +import currency +import company as company_ +import parties +from account import setup as setup_account + +import json + + +def set_config(database, config_file): + return pconfig.set_trytond(database, config_file=config_file) + + +def activate_modules(config, modules): + Module = Model.get('ir.module') + modules = Module.find([ + ('name', 'in', modules), + ]) + + for module in modules: + if module.state == 'activate': + module.click('upgrade') + else: + module.click('activate') + + modules = [m.name for m in Module.find([('state', '=', 'to_activate')])] + Wizard('ir.module.activate_upgrade').execute('upgrade') + + ConfigWizardItem = Model.get('ir.module.config_wizard.item') + for item in ConfigWizardItem.find([('state', '!=', 'done')]): + item.state = 'done' + item.save() + + activated_modules = [ + m.name for m in Module.find([('state', '=', 'activated')]) + ] + + return modules, activated_modules + + +def setup_languages(config, demo_password, company_config, company=None): + User = Model.get('res.user') + Lang = Model.get('ir.lang') + Action = Model.get('ir.action') + + langs = Lang.find([("code", "in", company_config["languages"]["codes"])]) + Lang.click(langs, 'load_translations') + + company_lang, = Lang.find([ + ("code", "=", company_config["company_language"]) + ]) + + admin, = User.find( + [("id", "=", 1)] + ) + # Use root to skip password validation + + menu, = Action.find([('usage', '=', 'menu')]) + + admin.menu = menu + admin.language = company_lang + admin.save() + + +def main(database, modules, demo_password, company_config, config_file=None): + config = set_config(database, config_file) + to_activate, activated = activate_modules(config, modules) + + if 'currency' in to_activate or 'currency' in activated: + currency.do_import() + + if 'company' in to_activate: + company = company_.setup(config, activated, company_config) + elif 'company' in activated: + if not (company := company_.get(company_config)): + company = company_.setup(config, activated, company_config) + else: + company = company + else: + company = None + + setup_languages(config, demo_password, company_config, company=company) + setup_account(config, activated, company) + # parties.setup_parties(database=database, config_file=config_file) + + +if __name__ == '__main__': + with open('demo/data/config.json') as file: + config_tryton = json.load(file) + + parser = ArgumentParser(formatter_class=ArgumentDefaultsHelpFormatter) + parser.add_argument('-c', '--config', dest='config_file') + parser.add_argument('-m', '--module', dest='modules', nargs='+', + help='module to activate', default=config_tryton.get( + "modules")) + + parser.add_argument('--demo_password', dest='demo_password', + default='demo', help='demo password') + parser.add_argument('-d', '--database', dest='database', + default='demo', help="database name") + options = parser.parse_args() + + main(options.database, options.modules, options.demo_password, + config_file=options.config_file, company_config=config_tryton) diff --git a/demo/populate_scripts/account.py b/demo/populate_scripts/account.py new file mode 100644 index 0000000..dd0e280 --- /dev/null +++ b/demo/populate_scripts/account.py @@ -0,0 +1,119 @@ +# This file is part of Tryton. The COPYRIGHT file at the top level of +# this repository contains the full copyright notices and license terms. +import datetime as dt + +from dateutil.relativedelta import relativedelta + +from proteus import Model, Wizard +from proteus import config as pconfig + + +import company + +import json + + +def set_config(database, config_file): + return pconfig.set_trytond(database, config_file=config_file) + + +def setup(config, modules, company): + AccountTemplate = Model.get('account.account.template') + Account = Model.get('account.account') + FiscalYear = Model.get('account.fiscalyear') + SequenceStrict = Model.get('ir.sequence.strict') + SequenceType = Model.get('ir.sequence.type') + Party = Model.get('party.party') + WriteOff = Model.get('account.move.reconcile.write_off') + Journal = Model.get('account.journal') + + root_template, = AccountTemplate.find([ + ('parent', '=', None), + ('name', '=', 'Universal Chart of Accounts'), + ]) + create_chart_account = Wizard('account.create_chart') + create_chart_account.execute('account') + create_chart_account.form.account_template = root_template + create_chart_account.form.company = company + create_chart_account.execute('create_account') + + receivable, = Account.find([ + ('company', '=', company.id), + ('code', '=', '1.2.1'), + ]) + payable, = Account.find([ + ('company', '=', company.id), + ('code', '=', '2.1.1'), + ]) + + create_chart_account.form.account_receivable = receivable + create_chart_account.form.account_payable = payable + create_chart_account.execute('create_properties') + + # Set account for parties created without company + parties = Party.find([]) + for party in parties: + party.account_receivable = receivable + party.account_payable = payable + Party.save(parties) + + move_sequence_type, = SequenceType.find( + [('name', '=', "Account Move")], limit=1) + invoice_sequence_type, = SequenceType.find([ + ('name', '=', "Invoice"), + ], limit=1) + today = dt.date.today() + for start_date in ( + today + relativedelta(month=1, day=1, years=-1), + today + relativedelta(month=1, day=1), + today + relativedelta(month=1, day=1, years=1)): + fiscalyear = FiscalYear(name='%s' % start_date.year) + fiscalyear.start_date = start_date + fiscalyear.end_date = start_date + relativedelta(month=12, day=31) + fiscalyear.company = company + move_sequence = SequenceStrict( + name='%s' % start_date.year, + sequence_type=move_sequence_type, + company=company) + move_sequence.save() + fiscalyear.move_sequence = move_sequence + invoice_sequence, = fiscalyear.invoice_sequences + if 'account_invoice' in modules: + for attr, name in (('out_invoice_sequence', 'Invoice'), + ('in_invoice_sequence', 'Supplier Invoice'), + ('out_credit_note_sequence', 'Credit Note'), + ('in_credit_note_sequence', 'Supplier Credit Note')): + sequence = SequenceStrict( + name='%s %s' % (name, start_date.year), + sequence_type=invoice_sequence_type, + company=company) + sequence.save() + setattr(invoice_sequence, attr, sequence) + fiscalyear.save() + fiscalyear.click('create_period') + + expense, = Account.find([ + ('company', '=', company.id), + ('code', '=', '6.2.1'), + ], limit=1) + write_off = WriteOff() + write_off.name = "Currency Exchange" + write_off.journal, = Journal.find( + [('code', '=', 'EXC'), ('type', '=', 'write-off')], limit=1) + write_off.credit_account = expense + write_off.debit_account = expense + write_off.save() + + +def run(config, modules, company_): + setup(config, modules, company_) + + +if __name__ == '__main__': + with open('../data/config.json') as file: + config_tryton = json.load(file) + config = set_config("tryton", "/root/app/.dev/trytond.cfg") + + company_, = company.get(config_tryton) + + run(config, ["account_invoice"], company_) diff --git a/demo/populate_scripts/company.py b/demo/populate_scripts/company.py new file mode 100644 index 0000000..c4d351c --- /dev/null +++ b/demo/populate_scripts/company.py @@ -0,0 +1,98 @@ +import datetime as dt +from decimal import Decimal + +from proteus import Model, Wizard + +from tools import open_file + +dir = "./demo/data" +file_ = "party_company.csv" +path = "".join([dir, "/", file_]) + + +def setup(config, modules, company_config): + + Identifier = Model.get('party.identifier') + Company = Model.get('company.company') + Country = Model.get('country.country') + Currency = Model.get('currency.currency') + Party = Model.get('party.party') + Subdivision = Model.get('country.subdivision') + + company_currency, = Currency.find([ + ('code', '=', company_config["company_currency"]) + ]) + + rate = company_currency.rates.new() + rate.date = dt.date(dt.date.today().year, 1, 1) + rate.rate = Decimal('1') + + company_currency.save() + + try: + company_country, = Country.find( + [('code', '=', company_config["company_country"])]) + except ValueError: + company_country = None + + try: + company_subdivision, = Subdivision.find( + [('code', '=', company_config["company_subdivision"])] + ) + except ValueError: + company_subdivision = None + + try: + company_municipality, = Subdivision.find( + [('code', '=', company_config["company_municipality"])] + ) + except ValueError: + company_municipality = None + + CompanyWizard = Wizard('company.company.config') + CompanyWizard.execute('company') + + company = CompanyWizard.form + party = Party( + name=company_config["company_name"], + type_person=company_config["type_person"] + ) + + identifier = Identifier() + identifier.type = company_config["company_identifier_type"] + identifier.code = company_config["company_identifier"] + + party.identifiers.append(identifier) + + address = party.addresses[0] + address.street = company_config["company_street"] + address.country = company_country + address.subdivision = company_subdivision + address.subdivision_municipality = company_municipality + + party.addresses.append(address) + party.save() + + company.party = party + company.currency = company_currency + company.timezone = company_config["company_timezone"] + CompanyWizard.execute('add') + + # Reload context + User = Model.get('res.user') + config._context = User.get_preferences(True, {}) + + company, = Company.find() + + return company + + +def get(company_config): + + Company = Model.get('company.company') + + if company := Company.find([ + ('party.name', '=', company_config["company_name"]) + ]): + return company + return diff --git a/demo/populate_scripts/country.py b/demo/populate_scripts/country.py new file mode 100644 index 0000000..bae451c --- /dev/null +++ b/demo/populate_scripts/country.py @@ -0,0 +1,24 @@ +# This file is part of Tryton. The COPYRIGHT file at the top level of +# this repository contains the full copyright notices and license terms. + +import subprocess +import sys + +try: + import pycountry +except ImportError: + subprocess.check_call([sys.executable, '-m', 'pip', 'install', 'pycountry']) + import pycountry + + +try: + from trytond.modules.country.scripts.import_countries import do_import +except ImportError: + def do_import(*args, **kwargs): + pass + +__all__ = [do_import] + + +if __name__ == '__main__': + do_import() diff --git a/demo/populate_scripts/currency.py b/demo/populate_scripts/currency.py new file mode 100644 index 0000000..d2d83a9 --- /dev/null +++ b/demo/populate_scripts/currency.py @@ -0,0 +1,16 @@ +# This file is part of Tryton. The COPYRIGHT file at the top level of +# this repository contains the full copyright notices and license terms. +from proteus import Model + +try: + from trytond.modules.currency.scripts.import_currencies import do_import +except ImportError: + def do_import(*args, **kwargs): + Currency = Model.get('currency.currency') + usd = Currency(name="USD", code='USD') + usd.save() + +__all__ = [do_import] + +if __name__ == '__main__': + do_import() diff --git a/demo/populate_scripts/parties.py b/demo/populate_scripts/parties.py new file mode 100644 index 0000000..6d2501f --- /dev/null +++ b/demo/populate_scripts/parties.py @@ -0,0 +1,39 @@ +from argparse import ArgumentDefaultsHelpFormatter, ArgumentParser +from proteus import Model +from proteus import config as pconfig + +from tools import open_file + +import json + +dir = "./demo/data" +file_ = "parties.csv" +path = "".join([dir, "/", file_]) + + +def set_config(database, config_file): + return pconfig.set_trytond(database, config_file=config_file) + + +def setup_parties(database, config_file): + set_config(database, config_file) + data = open_file(path) + + Party = Model.get("party.party") + parties = [ + Party(name=r.get("Name")) for r in data] + + return Party.save(parties) + + +if __name__ == '__main__': + with open('demo/data/config.json') as file: + config_tryton = json.load(file) + + parser = ArgumentParser(formatter_class=ArgumentDefaultsHelpFormatter) + parser.add_argument('-c', '--config', dest='config_file') + parser.add_argument('-d', '--database', dest='database', + default='demo', help="database name") + options = parser.parse_args() + + setup_parties(options.database, options.config_file) diff --git a/demo/populate_scripts/tools.py b/demo/populate_scripts/tools.py new file mode 100644 index 0000000..8adb5e8 --- /dev/null +++ b/demo/populate_scripts/tools.py @@ -0,0 +1,10 @@ +import csv + +def open_file(file_path): + content = [] + with open(file_path, mode='r', encoding='utf-8') as csvfile: + data = csv.DictReader(csvfile) + for row in data: + content.append(row) + + return content