[IMP] repository cleanup
- move packaging stuff to setup - remove historical stuff - remove oe, odoo-cmd-fme will be merged with the convered commands - add an odoo.py script to run odo and boostrap it - simplify README - prepare to move documentation to the github wiki
81
README.md
|
@ -3,71 +3,46 @@ About Odoo
|
|||
|
||||
Odoo is a suite of open source Business apps. More info at http://www.odoo.com
|
||||
|
||||
Installation
|
||||
============
|
||||
Evaluating Odoo
|
||||
---------------
|
||||
|
||||
[Setup/migration guide for employees](https://github.com/odoo/odoo/blob/master/doc/git.rst)
|
||||
The easiest way to test Odoo is the free trial, NO email registration is
|
||||
required, select "skip this step" to skip it.
|
||||
|
||||
https://www.odoo.com/page/start
|
||||
|
||||
|
||||
Migration from bazaar
|
||||
=====================
|
||||
Getting starting with Odoo developement
|
||||
---------------------------------------
|
||||
|
||||
If you have existing bazaar branches and want to move them to a git repository,
|
||||
there are several options:
|
||||
If you are a developer type the following command at your terminal:
|
||||
|
||||
* download http://nightly.openerp.com/move-branch.zip and run it with
|
||||
`python move-branch.zip -h` (for the help). It should be able to convert
|
||||
simple-enough branches for you (even if they have merge commits &al)
|
||||
* Extract the branch contents as patches and use `git apply` or `git am` to
|
||||
rebuild a branch from them
|
||||
* Replay the branch by hand
|
||||
wget -O- https://raw.githubusercontent.com/odoo/odoo/master/odoo.py | python
|
||||
|
||||
Then follow the tutorial here:
|
||||
|
||||
https://doc.openerp.com/trunk/server/howto/howto_website/
|
||||
|
||||
If you are an Odoo employee type the following to add the odoo-dev remote
|
||||
|
||||
$ cd odoo; ./odoo.py setup_git_dev
|
||||
|
||||
|
||||
System Requirements
|
||||
-------------------
|
||||
Packages, tarballs and installers
|
||||
---------------------------------
|
||||
|
||||
The dependencies are listed in setup.py
|
||||
* Debian packages
|
||||
|
||||
Add this apt repository to your /etc/apt/sources.list file
|
||||
|
||||
Debian/Ubuntu
|
||||
-------------
|
||||
deb http://nightly.openerp.com/8.0/deb/ ./
|
||||
|
||||
Add the apt repository
|
||||
|
||||
deb http://nightly.openerp.com/7.0/deb/ ./
|
||||
|
||||
in your source.list and type:
|
||||
Then type:
|
||||
|
||||
$ sudo apt-get update
|
||||
$ sudo apt-get install openerp
|
||||
$ sudo apt-get install odoo
|
||||
|
||||
Or download the deb file and type:
|
||||
|
||||
$ sudo dpkg -i <openerp-deb-filename>
|
||||
$ sudo apt-get install -f
|
||||
|
||||
RedHat, Fedora, CentOS
|
||||
----------------------
|
||||
|
||||
Install the required dependencies:
|
||||
|
||||
$ yum install python
|
||||
$ easy_install pip
|
||||
$ pip install .....
|
||||
|
||||
Install the openerp rpm
|
||||
|
||||
$ rpm -i openerp-VERSION.rpm
|
||||
|
||||
Windows
|
||||
-------
|
||||
|
||||
Check the notes in setup.py
|
||||
|
||||
|
||||
Setting up your database
|
||||
------------------------
|
||||
|
||||
Point your browser to http://localhost:8069/ and click "Manage Databases", the
|
||||
default master password is "admin".
|
||||
* Source tarballs http://nightly.openerp.com/
|
||||
* Windows installer http://nightly.openerp.com/
|
||||
* RPM package http://nightly.openerp.com/
|
||||
|
||||
|
|
95
checkout.sh
|
@ -1,95 +0,0 @@
|
|||
#/bin/sh
|
||||
set -e
|
||||
|
||||
ODOO=https://github.com/odoo/odoo.git
|
||||
DEV=https://github.com/odoo-dev/odoo.git
|
||||
|
||||
usage () {
|
||||
cat <<EOF
|
||||
Usage: $0 [-m] [COPYNAME]
|
||||
|
||||
Checks out and sets up the Odoo git repository for "internal" development.
|
||||
|
||||
* Checks out the "production" repository (production branches) as the "odoo"
|
||||
remote
|
||||
* Checks out the "development" repository (for employee development branches)
|
||||
as the "dev" repository
|
||||
|
||||
By default, the working copy is "odoo"
|
||||
|
||||
Options:
|
||||
-h displays this help text
|
||||
-m includes github's merge refs. These are the pull requests to "odoo"
|
||||
which merge cleanly into the main repository, after having applied
|
||||
them to said repository
|
||||
EOF
|
||||
}
|
||||
|
||||
while getopts :hm opt
|
||||
do
|
||||
case $opt in
|
||||
h)
|
||||
usage
|
||||
exit 0
|
||||
;;
|
||||
m)
|
||||
include_merge=yes
|
||||
;;
|
||||
*)
|
||||
usage
|
||||
exit 1
|
||||
;;
|
||||
esac
|
||||
done
|
||||
|
||||
shift $((OPTIND-1))
|
||||
copyname=${1:-"odoo"}
|
||||
|
||||
# Collect basic configuration data, ensures correct configuration of that repo
|
||||
printf "Enter your full name: "
|
||||
read name
|
||||
printf "Enter your (work) email: "
|
||||
read email
|
||||
|
||||
# create & set up repo
|
||||
git init $copyname
|
||||
cd $copyname
|
||||
|
||||
git config user.name "$name"
|
||||
git config user.email "$email"
|
||||
|
||||
# pre-push script preventing push to odoo repo by default. Git just execs
|
||||
# them, so they need a correct shebang and exec bit
|
||||
# if things get more extensive, should probably use git init templates
|
||||
cat <<EOF > .git/hooks/pre-push
|
||||
#!/bin/sh
|
||||
remote="\$1"
|
||||
url="\$2"
|
||||
|
||||
if [ "\$url" != "$ODOO" ]
|
||||
then
|
||||
exit 0
|
||||
fi
|
||||
|
||||
echo "Pushing to the odoo remote ($ODOO) is forbidden, push to the dev remote"
|
||||
echo
|
||||
echo "See git help push if you really want to push to odoo"
|
||||
exit 1
|
||||
|
||||
EOF
|
||||
chmod +x .git/hooks/pre-push
|
||||
|
||||
# add basic repos as remotes
|
||||
git remote add odoo $ODOO
|
||||
git remote add dev $DEV
|
||||
|
||||
if [ $include_merge ]
|
||||
then
|
||||
git remote add merge $ODOO
|
||||
git config remote.merge.fetch '+refs/pull/*/merge:refs/remotes/merge/*'
|
||||
fi
|
||||
|
||||
echo
|
||||
git remote update
|
||||
|
||||
exit 0
|
|
@ -1,34 +0,0 @@
|
|||
.. _adding-command:
|
||||
|
||||
Adding a new command
|
||||
====================
|
||||
|
||||
``oe`` uses the argparse_ library to implement commands. Each
|
||||
command lives in its own ``openerpcommand/<command>.py`` file.
|
||||
|
||||
.. _argparse: http://docs.python.org/2.7/library/argparse.html
|
||||
|
||||
To create a new command, probably the most simple way to get started is to
|
||||
copy/paste an existing command, say ``openerpcommand/initialize.py`` to
|
||||
``openerpcommand/foo.py``. In the newly created file, the important bits
|
||||
are the ``run(args)`` and ``add_parser(subparsers)`` functions.
|
||||
|
||||
``add_parser``'s responsability is to create a (sub-)parser for the command,
|
||||
i.e. describe the different options and flags. The last thing it does is to set
|
||||
``run`` as the function to call when the command is invoked.
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
> def add_parser(subparsers):
|
||||
> parser = subparsers.add_parser('<command-name>',
|
||||
> description='...')
|
||||
> parser.add_argument(...)
|
||||
> ...
|
||||
> parser.set_defaults(run=run)
|
||||
|
||||
``run(args)`` actually implements the command. It should be kept as simple as
|
||||
possible and delegate most of its work to small functions (probably placed at
|
||||
the top of the new file). In other words, its responsability is mainly to
|
||||
deal with the presence/absence/pre-processing of ``argparse``'s arguments.
|
||||
|
||||
Finally, the module must be added to ``openerpcommand/__init__.py``.
|
|
@ -1,40 +0,0 @@
|
|||
.. _commands:
|
||||
|
||||
Available commands
|
||||
==================
|
||||
|
||||
This page explain some of the available ``oe`` commands. For an overview about
|
||||
``oe``, see :doc:`openerp-command`.
|
||||
|
||||
Keep in mind that ``oe --help`` and ``oe <command> --help`` already give a lot
|
||||
of information about the commands and their options and flags.
|
||||
|
||||
``web``
|
||||
-------
|
||||
|
||||
The ``web`` command is used to create a single OpenERP server process to handle
|
||||
regular HTTP requests and XML-RPC requests. It is possible to execute such
|
||||
process multiple times, possibly on different machines.
|
||||
|
||||
It is possible to chose the ``--threaded`` or ``--gevent`` flags. It is
|
||||
recommanded to use ``--threaded`` only when running a single process.
|
||||
``--gevent`` is experimental; it is planned to use it for the embedded chat
|
||||
feature.
|
||||
|
||||
Example invocation::
|
||||
|
||||
> oe web --addons ../../addons/trunk:../../web/trunk/addons --threaded
|
||||
|
||||
``cron``
|
||||
--------
|
||||
|
||||
The ``cron`` command is used to create a single OpenERP process to execute
|
||||
so-called cron jobs, also called scheduled tasks in the OpenERP interface. As
|
||||
for the ``web`` command, multiple cron processes can be run side by side.
|
||||
|
||||
It is necessary to specify on the command-line which database need to be
|
||||
watched by the cron process with the ``--database`` option.
|
||||
|
||||
Example invocation::
|
||||
|
||||
> oe cron --addons ../../addons/trunk:../../web/trunk/addons --database production
|
|
@ -30,16 +30,6 @@ OpenERP Server
|
|||
form-view-guidelines
|
||||
ir_actions
|
||||
|
||||
OpenERP Command
|
||||
'''''''''''''''
|
||||
|
||||
.. toctree::
|
||||
:maxdepth: 1
|
||||
|
||||
openerp-command.rst
|
||||
commands.rst
|
||||
adding-command.rst
|
||||
|
||||
OpenERP Server API
|
||||
''''''''''''''''''
|
||||
|
||||
|
|
|
@ -1,66 +0,0 @@
|
|||
.. _openerp-command:
|
||||
|
||||
The ``oe`` script
|
||||
=================
|
||||
|
||||
The ``oe`` script provides a set of command-line tools around the OpenERP
|
||||
framework. It is meant to replace the older ``openerp-server`` script (which
|
||||
is still available).
|
||||
|
||||
Using ``oe``
|
||||
------------
|
||||
|
||||
In contrast to the previous ``openerp-server`` script, ``oe`` defines a few
|
||||
commands, each with its own set of flags and options. You can get some
|
||||
information for any of them with
|
||||
|
||||
::
|
||||
|
||||
> oe <command> --help
|
||||
|
||||
For instance::
|
||||
|
||||
> oe run-tests --help
|
||||
|
||||
Some ``oe`` options can be provided via environment variables. For instance::
|
||||
|
||||
> export OPENERP_DATABASE=trunk
|
||||
> export OPENERP_HOST=127.0.0.1
|
||||
> export OPENERP_PORT=8069
|
||||
|
||||
Depending on your needs, you can group all of the above in one single script;
|
||||
for instance here is a, say, ``test-trunk-view-validation.sh`` file::
|
||||
|
||||
COMMAND_REPO=/home/thu/repos/command/trunk/
|
||||
SERVER_REPO=/home/thu/repos/server/trunk
|
||||
|
||||
export PYTHONPATH=$SERVER_REPO:$COMMAND_REPO
|
||||
export PATH=$SERVER_REPO:$COMMAND_REPO:$PATH
|
||||
export OPENERP_DATABASE=trunk
|
||||
export OPENERP_HOST=127.0.0.1
|
||||
export OPENERP_PORT=8069
|
||||
|
||||
# The -d ignored is actually needed by `oe` even though `test_view_validation`
|
||||
# itself does not need it.
|
||||
oe run-tests -d ignored -m openerp.test_view_validation
|
||||
|
||||
Available commands
|
||||
-------------------
|
||||
|
||||
See the :doc:`commands` page.
|
||||
|
||||
Adding new commands
|
||||
-------------------
|
||||
|
||||
See the :doc:`adding-command` page.
|
||||
|
||||
Bash completion
|
||||
---------------
|
||||
|
||||
A preliminary ``oe-bash-completion`` file is provided. After sourcing it,
|
||||
|
||||
::
|
||||
|
||||
> . oe-bash-completion
|
||||
|
||||
completion (using the TAB character) in Bash should work.
|
1822
history/Changelog
|
@ -1,951 +0,0 @@
|
|||
4.2.1
|
||||
Bugfixes
|
||||
Fix context for source_count function
|
||||
Create stock move on production for products without BOM lines
|
||||
Add IBAN fields in bank view
|
||||
Fix uninitialize variable in import data
|
||||
Update due date on invoice when payment term change
|
||||
Fix store on field function that have type many2one or one2one
|
||||
Request summary must be truncate
|
||||
Partner event name must be truncate
|
||||
Remove parent field on partner contact view
|
||||
Fix icon type on journal period
|
||||
Remove exception on the size of char field
|
||||
Fix reference on move line that comes from invoice (Customer != Supplier)
|
||||
Add function search on sheet_id of timesheet_sheet
|
||||
Don't return 0 for balance account if there is no fiscal year
|
||||
Fix set to draft for expense, now really set to draft
|
||||
Add product and partner in the recursive call of tax compute
|
||||
Don't compute balance account for inactive account
|
||||
Fix bad encoding in log message on report_sxw
|
||||
Fix overdue report for refund lines
|
||||
Don't start server in non secure mode if secure mode have been set
|
||||
Fix default value of move line if move_id is not find
|
||||
Fix _product_partner_ref for cannot concatenate 'str' and 'bool' objects
|
||||
Add partner_id in the context of SO for browsing the product
|
||||
Fix multi-tax code on invoice
|
||||
Fix tax definition for Belgium chart
|
||||
Remove compute debit/credit on inactive account
|
||||
Fix the way the tax are rounded for invoice with tax included prices
|
||||
Fix SO to use the right uom and price to create invoice
|
||||
Fix on_chnage uos on SO to return the id not the browse record
|
||||
Add condition on the button "Sending goods>Packing to be invoiced" to show
|
||||
only customer packings
|
||||
Fix zero division error when the quantity is zero on an invoice line
|
||||
Fix duplicate timesheet line that have been invoiced
|
||||
Fix invoice report for bad removeParentNode tag
|
||||
Fix priority for product view
|
||||
Fix tax line computation when encoding account lines manually
|
||||
Fix refund supplier invoice to have the same journal
|
||||
New chinese translation
|
||||
Pass context to action_done on stock move
|
||||
Add product_uom change on sale order line
|
||||
Fix demo data for working time UOM
|
||||
Fix _sheet function in timesheet_sheet when called with a list of non
|
||||
unique id
|
||||
Remove commit inside function validate on account move
|
||||
Use one function to post account move
|
||||
Fix computation of sale/purchase amount in segmentation module
|
||||
Use standar uom converion in analytic lines
|
||||
Add journal_id in context for account move line search in payment module
|
||||
Fix wrong id used by pricelist based on partner form
|
||||
Use partner reference from SO/PO for invoice name if there is one
|
||||
Make analysis analytic module include child accounts
|
||||
|
||||
4.2.0
|
||||
Summary:
|
||||
Add new view graph
|
||||
REPORT_INTRASTAT: new module
|
||||
KERNEL: add netrpc (speed improvement)
|
||||
REPORT_STOCK: add report on stock by stock location and production lots
|
||||
HR_TIMESHEET_INVOICE: add final invoice
|
||||
MULTI_COMPANY_ACCOUNT: new module
|
||||
ADD modules publication tools
|
||||
KERNEL: add timezone
|
||||
KERNEL: add concurnecy check
|
||||
BASE: allow to specify many view_id in act_window
|
||||
BASE: add ir.rules (acces base on record fields)
|
||||
KERNEL: add search_count on objects
|
||||
KERNEL: add assert tools (unit test)
|
||||
KERNEL: improve workflow speed
|
||||
KERNEL: move some modules to extra_addons
|
||||
Bugfixes:
|
||||
Fix pooler for multi-db
|
||||
REPORT_ANALYTIC: new reports
|
||||
BOARD_ACCOUNT: new dashboard for accountants
|
||||
PURCHASE: allow multiple pickings for the same purchase order
|
||||
STOCK: When refunding picking: confirm & Assign the newly generated picking
|
||||
PRODUCT: add average price
|
||||
STOCK: Fix workflow for stock
|
||||
TOOLS: Fix export translate for wizard
|
||||
KERNEL: add id in import_data
|
||||
BASE: add history rate to currency
|
||||
ACCOUNT: partner_id is now required for an invoice
|
||||
HR_TIMESHEET: add exception if employee haven't product
|
||||
I18N: new fr_CH file
|
||||
HR_EXPENSE: fix domain
|
||||
ACCOUNT: Fix invoice with currency and payment term
|
||||
ACCOUNT: Fix currency
|
||||
KERNEL: add pidfile
|
||||
ACCOUNT,PURCHASE,SALE: use partner lang for description
|
||||
Model Acces: Unlink permission (delete) is now available
|
||||
KERNEL: Remove set for python2.3
|
||||
HR: add id to Attendance menu
|
||||
PRODUCT: add dimension to packaging
|
||||
ACCOUNT: new cash_discount on payment term
|
||||
KERNEL: Add price accuracy
|
||||
BASE: Function to remove installed modules
|
||||
REPORT_SALE: fix for sale without line
|
||||
PURCHASE: remove use of currency
|
||||
KERNEL: fix set without values
|
||||
PURCHASE: fix domain pricelist
|
||||
INVOICE: use date for currency rate
|
||||
KERNEL: Fix import many2many by id
|
||||
KERNEL: run the cron
|
||||
ACCOUNT: bank statment line now have a ref t othe corresponding invoice
|
||||
ACCOUNT: Add possibilitty to include tax amount in base amount for the computation of the next taxes
|
||||
ACCOUNT: Add product in tax compute python code
|
||||
KERNEL: use reportlab 2.0
|
||||
BASE: fix import the same lang
|
||||
ACCOUNT: fix tax code
|
||||
ACCOUNT: define tax account for invoice and refund
|
||||
ACCOUNT: add supplier tax to product
|
||||
ACCOUNT: don't overwrite tax_code on the creation for account line
|
||||
PURCHASE: use partner code for report order
|
||||
KERNEL: fix pooler netsvc for multi-db
|
||||
TOOLS: add ref to function tag
|
||||
PRODUCT: fix digits on volume and weight, add weight_net
|
||||
ACCOUNT: split to new module account_cash_discount
|
||||
ORM : error message on python constraints are now displayed correctly
|
||||
ACCOUNT: add partner to tax compute context
|
||||
KERNEL: improve logger
|
||||
PROJECT: add check_recursion for project
|
||||
HR_TIMESHEET_INVOICE: improve create invoice
|
||||
ACCOUNT: add product_id to analytic line create by invoice
|
||||
KERNEL: fix the inheritance mechanism
|
||||
KERNEL: Fix use always basename for cvs file
|
||||
BASE: fix IBAN len to 27
|
||||
INVOICE: fix invoice number for analytic
|
||||
REPORT: add replace tag for custom header
|
||||
ACCOUNT: add ref to analytic line
|
||||
BASE: prevent exception in ir_cron
|
||||
SALE: fix uos for tax_amount
|
||||
MRP: fix dbname in _procure_confirm
|
||||
HR_EXPENSE: add domain to analytic_account
|
||||
KERNEL: use 0 instead of False for fix on _fnct_read
|
||||
SUBSCRIPTION: add required to model
|
||||
HR_TIMESHEET: add rounding on report
|
||||
SALE: Fix cancel invoice and recreate invoice, now cancel also the order lines
|
||||
STOCK-DELIVERY: add wizard invoice_onshipping from delivery to stock
|
||||
STOCK: use tax from sale for invoice
|
||||
BASE: improve copy of res.partner
|
||||
ACCOUNT: pay only invoice if not in state draft
|
||||
REPORT: fix rml translation, translate before eval
|
||||
PRODUCT_EXTENDED: don't use seller price for bom price
|
||||
ACCOUNT_TAX_INCLUDE: fix right amount in account move generate with tax_include
|
||||
BASE: improve workflow print
|
||||
SALE: fix workflow error when create invoice from wizard
|
||||
MRP: Use company currency for Product Cost Structure
|
||||
BASE: prevent recursion in company
|
||||
KERNEL: Fix deleted property and many2one
|
||||
KERNEL: allow directory for import csv
|
||||
KERNEL: add store option to fields function
|
||||
ACCOUNT: use property_account_tax on on_change_product
|
||||
KERNEL: add right-click for translate label
|
||||
KERNEL: fix log of backtrace
|
||||
KERNEL: fix search on xxx2many
|
||||
BASE: use tool to call popen.pipe2
|
||||
KERNEL: fix print workflow on win32
|
||||
BASE: fix US states
|
||||
KERNEL: use python 2.3 format_exception
|
||||
ACCOUNT: add multi-company into base accounting
|
||||
KERNEL: check return code for exec_pg_command_pipe
|
||||
KERNEL: fix search with active args
|
||||
KERNEL: improve _sql_contsraints, now insert if doesn't exist
|
||||
KERNEL: remove old inheritor and add _constraints and _sql_constraints to the fields inherited
|
||||
CRM: bugfix mailgate
|
||||
PURCHASE: fix the UOM for purchase line and improve update price unit
|
||||
ACCOUNT: new invoice view
|
||||
KERNEL,BASE: allow to create zip modules
|
||||
BASE: add right-to-left
|
||||
KERNEL: copy now ignore technical values ('create_date', 'create_uid', 'write_date' and 'write_uid')
|
||||
ACCOUNT_TAX_INCLUDE: Now the module manage correctly the case when the taxes defined on the product differ from the taxes defined on the invoice line
|
||||
ALL: fix colspan 3 -> 4
|
||||
KERNEL: use context for search
|
||||
ACCOUNT: improve speed of analytic account
|
||||
ACCOUNT: fix search debit/credit on partner
|
||||
ACCOUNT: fix refund invoice if no product_id nor uos_id on lines
|
||||
MRP: fix scheduler location of product to produce and method, date of automatic orderpoint
|
||||
KERNEL: many2many : fix unlink and link action
|
||||
MRP: add default product_uom from context and add link from product to bom
|
||||
PROJECT: improve speed for function fields
|
||||
ALL: remove bad act_window name
|
||||
KERNEL: modification for compatibility with postgres 7.4
|
||||
KERNEL: fix size for selection field
|
||||
KERNEL: fix compatibility for python2.5
|
||||
KERNEL: add new win32 build script
|
||||
KERNEL: add test for duplicate report and wizard
|
||||
ACCOUNT: force round amount fixed in payment term
|
||||
KERNEL: fix print screen
|
||||
CRM: Better ergonomy
|
||||
SERVER: add sum tag on tree view that display sum of the selected lines
|
||||
KERNEL: allow subfield query on one2many
|
||||
KERNEL: fix create_date and write_date as there are timestamp now
|
||||
SERVER: improve language
|
||||
KERNEL: fix search on fields function of type one2many, many2many
|
||||
ACCOUNT: fix pay invoice to use period
|
||||
ACCOUNT: add check recursion in account.tax.code
|
||||
MRP: fix compute cycle for workcenter
|
||||
BASE: add constraint uniq module name
|
||||
BASE: improve update module list
|
||||
ACCOUNT: add round to last payment term
|
||||
KERNEL: don't modify the args of the call
|
||||
KERNEL: don't use mutable as default value in function defintion
|
||||
KERNEL: fix orm for sql query with reserved words
|
||||
|
||||
16/03/2007
|
||||
4.0.3
|
||||
Summary:
|
||||
Improve the migration scripts
|
||||
Some bugfixes
|
||||
Print workflow on win32 (with ghostscript)
|
||||
|
||||
Bugfixes:
|
||||
BASE: Fix "set default value"
|
||||
HR_TIMESHEET_INVOICE: Improve invoice on timesheet
|
||||
ACCOUNT: Fix tax amount
|
||||
KERNEL: correct the delete for property
|
||||
PURCHASE: fix the journal for invoice created by PO
|
||||
KERNEL: fix the migration for id removed
|
||||
Add id to some menuitem
|
||||
BASE: prevent exception in ir_cron when the DB is dropped
|
||||
HR: Fix sign-in/sign-out, the user is now allowed to provide a date in
|
||||
the future
|
||||
SALE: fix uos for the tax amount
|
||||
MRP: fix wrong dbname in _procure_confirm
|
||||
HR_EXPENSE: add domain to analytic_account
|
||||
ACCOUNT: fix debit_get
|
||||
SUBSCRIPTION: model is required now
|
||||
HR_TIMESHEET: add rounding value to report
|
||||
SALE: Fix cancel and recreate invoice, now cancel also the order lines
|
||||
STOCK: use the tax define in sale for the invoice
|
||||
ACCOUNT: add test to pay only if invoice not in state draft
|
||||
KERNEL: root have access to all records
|
||||
REPORT: fix rml translation to translate before the eval
|
||||
ACCOUNT_TAX_INCLUDE: Use the right amount in account mmove generate
|
||||
with tax_include
|
||||
BASE: Improve the workflow print
|
||||
SALE: Fix workflow error when creating invoice from the wizard
|
||||
PRODUCT_EXTENDED: don't use pricelist to compute standard price
|
||||
MRP: Use company currency for product cost structure
|
||||
KERNEL: fix where clause when deleting false items
|
||||
ACCOUNT: product source account depend on the invoice type now
|
||||
ACCOUNT: use the property account tax for the on_change_product
|
||||
ACCOUNT: use the invoice date for the date of analytic line
|
||||
ACCOUNT: Fix the pay invoice when multi-currency
|
||||
HR_TIMESHEET_PROJECT: use the right product
|
||||
STOCK: Fix to assign picking with product consumable and call the
|
||||
workflow
|
||||
STOCK: Fix the split lot production
|
||||
PURCHASE: fix workflow for purchase with manual invoice to not set
|
||||
invoice and paid
|
||||
DELIVERY: can use any type of journal for invoice
|
||||
KERNEL: fix search on xxx2many
|
||||
ACCOUNT: add id to sequence record
|
||||
KERNEL: set properly the demo flag for module installed
|
||||
KERNEL: Fix print workflow on win32
|
||||
LETTER: fix print letter
|
||||
|
||||
Migration:
|
||||
Fix migration for postreSQL 7.4
|
||||
Fix the default value of demo in module
|
||||
Fix migration of account_uos to product_uos
|
||||
|
||||
Wed Jan 17 15:06:07 CET 2007
|
||||
4.0.2
|
||||
Summary:
|
||||
Improve the migration
|
||||
Some bugfixes
|
||||
Improve tax
|
||||
|
||||
Bugfixes:
|
||||
Fix tax for invoice, refund, etc
|
||||
SALE: fix view priority
|
||||
PURCHASE: wizard may crash on some data
|
||||
BASE: Fix import the same lang
|
||||
BASE: start the cron
|
||||
PURCHASE: fix domain for pricelist
|
||||
KERNEL: fix object set without values
|
||||
REPORT_SALE: fix for sale without line
|
||||
KERNEL: add pidfile
|
||||
BASE: remove 'set' for python2.3 compliant
|
||||
Migration:
|
||||
Migrate hr_timesheet user_id
|
||||
|
||||
Fri Dec 22 12:01:26 CET 2006
|
||||
4.0.1
|
||||
Summary:
|
||||
Improve the migration
|
||||
Some bugfixes
|
||||
|
||||
Bugfixes:
|
||||
HR_EXPENSE: Fix domain
|
||||
HR_TIMESHEET: Fix employee without product
|
||||
TOOLS: Fix export translate
|
||||
BASE: fix for concurrency of sequence number
|
||||
MRP: fix report
|
||||
CRM: fix graph report
|
||||
KERNEL: fix instance of osv_pool
|
||||
KERNEL: fix setup.py
|
||||
|
||||
|
||||
Mon Dec 4 18:01:55 CET 2006
|
||||
4.0.0
|
||||
Summary:
|
||||
Some bugfixes
|
||||
|
||||
Tue Nov 28 14:44:20 CET 2006
|
||||
4.0.0-rc1
|
||||
Summary:
|
||||
This is a stable version (RC1) with lots of new features. Main
|
||||
Improvements were:
|
||||
Accounting: more functions, new modules, more stable
|
||||
Much more better ergonomy
|
||||
Lots of simplification to allows non IT people to use and
|
||||
configure Tiny ERP: manage database, step by step configuration
|
||||
menu, auto-installers, better help, ...
|
||||
|
||||
New:
|
||||
Skill management module
|
||||
ACCOUNT:
|
||||
New and simpler bank statement form
|
||||
New reports:
|
||||
on Timesheets (analytic accounting)
|
||||
Theorical revenue based on time spent
|
||||
Global timesheet report by month
|
||||
Chart of accounts
|
||||
Different taxes methods supported
|
||||
Gross (brut)
|
||||
Net
|
||||
Fixed amount
|
||||
INVOICE:
|
||||
invoice on shipping (manufacturing industry)
|
||||
invoice on timesheet (services)
|
||||
PURCHASE:
|
||||
different invoicing control method (on order, on shipping,
|
||||
manual)
|
||||
Support of prices tax included /excluded in sales orders
|
||||
New modules:
|
||||
Sale_journal, stock_journal for bigger industries:
|
||||
Divide works in different journals
|
||||
New invoicing method from partner, to so, to picking
|
||||
Daily, Monthly (grouped by partner or not)
|
||||
New modules for prices with taxes included / excluded
|
||||
New chart of accounts supported:
|
||||
l10n_be/ l10n_chart_be_frnl/
|
||||
l10n_chart_id/ l10n_chart_uk/
|
||||
l10n_ca-qc/ l10n_chart_br/
|
||||
l10n_chart_it/ l10n_chart_us_general/
|
||||
l10n_ch/ l10n_chart_ca_en/
|
||||
l10n_chart_it_cc2424/ l10n_chart_us_manufacturing/
|
||||
l10n_ch_pcpbl_association/ l10n_chart_ca_fr/
|
||||
l10n_chart_la/ l10n_chart_us_service/
|
||||
l10n_ch_pcpbl_independant/ l10n_chart_ch_german/
|
||||
l10n_chart_nl/ l10n_chart_us_ucoa/
|
||||
l10n_ch_pcpbl_menage/ l10n_chart_cn/
|
||||
l10n_chart_nl_standard/ l10n_chart_us_ucoa_ez/
|
||||
l10n_ch_pcpbl_plangen/ l10n_chart_cn_traditional/
|
||||
l10n_chart_no/ l10n_chart_ve/
|
||||
l10n_ch_pcpbl_plangensimpl/ l10n_chart_co/
|
||||
l10n_chart_pa/ l10n_fr/
|
||||
l10n_ch_vat_brut/ l10n_chart_cz/
|
||||
l10n_chart_pl/ l10n_se/
|
||||
l10n_ch_vat_forfait/ l10n_chart_da/
|
||||
l10n_chart_sp/ l10n_simple/
|
||||
l10n_ch_vat_net/ l10n_chart_de_datev_skr03/
|
||||
l10n_chart_sw/
|
||||
l10n_chart_at/ l10n_chart_de_skr03/
|
||||
l10n_chart_sw_church/
|
||||
l10n_chart_au/ l10n_chart_hu/
|
||||
l10n_chart_sw_food/
|
||||
Step by step configuration menu
|
||||
Setup wizard on first connection
|
||||
Select a company profile, auto-install language, demo data, ...
|
||||
|
||||
Imrovements:
|
||||
KERNEL: Demo data improved
|
||||
Better import / export system
|
||||
KERNEL: Multi-database management system
|
||||
Backup, Restore, Create, Drop from the client
|
||||
PRODUCT/PRODUCT_EXTD: Eavily change the product form, use the new
|
||||
object to compute the pricelist
|
||||
REPORTS:
|
||||
Better Sale order, purchase order, invocies and customers reports
|
||||
ACCOUNT: Support of taxes in accounts
|
||||
management of the VAT taxes for most european countries:
|
||||
Support of VAT codes in invoices
|
||||
Better computation of default values in accounting entries
|
||||
Preferences in partners, override products
|
||||
Bugfix when closing a fiscal year
|
||||
Better ergonomy when writting entries
|
||||
New Module Management System:
|
||||
Install / Upgrade new modules directly from the client
|
||||
Install new languages
|
||||
KERNEL:
|
||||
Ability to add select=True at the object level for postgresql indexes
|
||||
Bugfix in search in some inherited objects
|
||||
Added the ability to call methods from a browse object
|
||||
KERNEL+BASE: changed the way the migration system works for menuitems:
|
||||
now you can change a menuitem defined elsewhere. And this will work
|
||||
whether that menuitem has an id or not (it use the name of the
|
||||
menuitem to find it)
|
||||
KERNEL:
|
||||
Installing a module from the client
|
||||
Better Windows Auto-Installer
|
||||
DELIVERY:
|
||||
Delivery and invoicing on picking list
|
||||
KERNEL:
|
||||
Distinction between active (by default) and installable
|
||||
ACCOUNT/PROJECT: Added support for the type of invoicing
|
||||
CRM:
|
||||
eMAil gateway
|
||||
Management of different departments and sections
|
||||
Rule system
|
||||
About 20 new statistics reporting
|
||||
eCommerce interface:
|
||||
Better Joomla (virtuemart, OSCommerce) support
|
||||
Joomla is now fully functionnal
|
||||
|
||||
Bugfixes:
|
||||
ACCOUNT: tree view on reporting analytic account
|
||||
KERNEL: Fix the bug that happened when mixing active and child_of
|
||||
search
|
||||
KERNEL: Check for the existance of active when computing child_of
|
||||
PRODUCT: production computation with different UoM
|
||||
|
||||
------------------------------------------------------------------------
|
||||
|
||||
Fri Oct 6 14:44:05 CEST 2006
|
||||
Server 3.4.2
|
||||
Improvements:
|
||||
BASE: changed workflow print system so that it handles inexisting
|
||||
workflows more gracefully (patch from Geoff Gardiner)
|
||||
MRP: new view to take into account the orderpoint exceptions
|
||||
MRP: made menu title more explicit
|
||||
|
||||
Bugfixes:
|
||||
ACCOUNT: fixed typo in invoice + changed sxw file so that it is in
|
||||
sync with the rml file
|
||||
DELIVERY: fixed taxes on delivery line (patch from Brice Vissière)
|
||||
PROJECT: skip tasks without user in Gantt charts (it crashed the report)
|
||||
PRODUCT: fixed bug when no active pricelist version was found
|
||||
PRODUCT_EXTENDED: correct recursive computation of the price
|
||||
SALE: get product price from price list even when quantity is set after
|
||||
the product is set
|
||||
STOCK: fixed partial picking
|
||||
|
||||
Packaging:
|
||||
Changed migration script so that it works on PostgreSQL 7.4
|
||||
|
||||
------------------------------------------------------------------------
|
||||
|
||||
Tue Sep 12 15:10:31 CEST 2006
|
||||
Server 3.4.1
|
||||
Bugfixes:
|
||||
ACCOUNT: fixed a bug which prevented to reconcile posted moves.
|
||||
|
||||
------------------------------------------------------------------------
|
||||
|
||||
Mon Sep 11 16:12:10 CEST 2006
|
||||
Server 3.4.0 (changes since 3.3.0)
|
||||
New modules:
|
||||
ESALE_JOOMLA: integration with Joomla CMS
|
||||
HR_TIMESHEET_ICAL: import iCal to automatically complete timesheet
|
||||
based on outlook meetings
|
||||
PARTNER_LDAP: adds partner synchronization with an LDAP server
|
||||
SALE_REBATE: adds rebates to sale orders
|
||||
|
||||
4 new modules for reporting using postgresql views:
|
||||
REPORT_CRM: reporting on CRM cases: by month, user, ...
|
||||
REPORT_PROJECT: reporting on projects: tasks closed by project, user,
|
||||
month, ...
|
||||
REPORT_PURCHASE: reporting on purchases
|
||||
REPORT_SALE: reporting on sales by periods and by product, category of
|
||||
product, ...
|
||||
|
||||
New features:
|
||||
KERNEL: Tiny ERP server and client may now communicate through HTTPS.
|
||||
To launch the server with HTTPS, use the -S or --secure option
|
||||
Note that if the server runs on HTTPS, the clients MUST connect
|
||||
with the "secure" option checked.
|
||||
KERNEL: the server can now run as a service on Windows
|
||||
Printscreen function (Tree view print)
|
||||
KERNEL: added a new --stop-after-init option which stops the server
|
||||
just before it starts listening
|
||||
KERNEL: added support for a new forcecreate attribute on XML record
|
||||
fields: it is useful for records are in a data node marked as
|
||||
"noupdate" but the record still needs to be added if it doesn't
|
||||
exit yet. The typical use for that is when you add a new record
|
||||
to a noupdate file/node.
|
||||
KERNEL: manage SQL constraints with human-readable error message on the
|
||||
client side, eg: Unique constraints
|
||||
KERNEL: added a new system to be able to specify the tooltip for each
|
||||
field in the definition of the field (by using the new help=""
|
||||
attribute)
|
||||
ACCOUNT: new report: aged trial balance system
|
||||
ACCOUNT: added a wizard to pay an invoice from the invoice form
|
||||
BASE: print on a module to print the reference guide using introspection
|
||||
HR: added report on attendance errors
|
||||
PRODUCT: products now support multi-Level variants
|
||||
|
||||
Improvements:
|
||||
KERNEL: speed improvement in many parts of the system thanks to some
|
||||
optimizations and a new caching system
|
||||
KERNEL: New property system which replace the, now deprecated, ir_set
|
||||
system. This leads to better migration of properties, more
|
||||
practical use of them (they can be used like normal fields),
|
||||
they can be translated, they are "multi-company aware", and
|
||||
you can specify access rights for them on a per field basis.
|
||||
KERNEL: Under windows, the server looks for its configuration file in
|
||||
the "etc" sub directory (relative to the installation path).
|
||||
This was needed so that the server can be run as a windows
|
||||
service (using the SYSTEM profile).
|
||||
KERNEL: added ability to import CSV files from the __terp__.py file
|
||||
KERNEL: force freeing cursor when closing them, so that they are
|
||||
available again immediately and not when garbage collected.
|
||||
KERNEL: automatically drop not null/required constraints from removed
|
||||
fields (ie which are in the database but not in the object)
|
||||
KERNEL: added a command-line option to specify which smtp server to use
|
||||
to send emails.
|
||||
KERNEL: made browse_record hashable
|
||||
ALL: removed shortcuts for the demo user.
|
||||
ACCOUNT: better invoice report
|
||||
ACCOUNT: Modifs for account chart, removed old stock_income account type
|
||||
ACCOUNT: made the test_paid method on invoices more tolerant to buggy
|
||||
data (open invoices without move/movelines)
|
||||
ACCOUNT: better bank statement reconciliation system
|
||||
ACCOUNT: accounting entries encoding improved a lot (using journal)
|
||||
ACCOUNT: Adding a date and max Qty field in analytic accounts for
|
||||
support contract
|
||||
ACCOUNT: Adding the View type to analytic account / cost account
|
||||
ACCOUNT: changed test_paid so that the workflow works even if there is
|
||||
no move line
|
||||
ACCOUNT: Cleanup credit/debit and balance computation methods. Should
|
||||
be faster too.
|
||||
ACCOUNT: use the normal sequence (from the journal) for the name of
|
||||
moves generated from invoices instead of the longer name.
|
||||
ACCOUNT: print Payment delay in invoices
|
||||
ACCOUNT: account chart show subtotals
|
||||
ACCOUNT: Subtotal in view accounts
|
||||
ACCOUNT: Replaced some Typo: moves-> entries, Transaction -> entry
|
||||
ACCOUNT: added quantities in analytic accounts view, and modified
|
||||
cost ledger report for partners/customers
|
||||
ACCOUNT: added default value for the currency field in invoices
|
||||
ACCOUNT: added the comment/notes field on the invoice report
|
||||
BASE: added menuitem (and action) to access partner functions (in the
|
||||
definitions menu)
|
||||
BASE: better demo data
|
||||
BASE: duplicating a menu item now duplicates its action and submenus
|
||||
BASE: Bank Details on Partners
|
||||
CRM: View on all actions made on cases (used by our ISO9002 customer
|
||||
to manage corrections to actions)
|
||||
CRM: fixed wizard to create a sale order from a case
|
||||
CRM: search on non active case, not desactivated by default
|
||||
CRM: Case ID in fields with search
|
||||
HR_TIMESHEET: new "sign_in, sign_out" using projects. It fills
|
||||
timesheets and attendance at the same time.
|
||||
HR_TIMESHEET: added cost unit to employee demo data
|
||||
MRP: improvement in the scheduler
|
||||
MRP: purchase order lines' description generated from a procurement
|
||||
defaults to the product name instead of procurement name
|
||||
MRP: Better traceability
|
||||
MRP: Better view for procurement in exception
|
||||
MRP: Added production delay in product forms. Use this delay for
|
||||
average production delay for one product
|
||||
MRP: dates scheduler, better computation
|
||||
MRP: added constraint for non 0 BoM lines
|
||||
PRODUCT: Better pricelist system (on template or variant of product)
|
||||
PRODUCT_EXTENDED: Compute the price only if there is a supplier
|
||||
PROJECT: when a task is closed, use the task's customer to warn the
|
||||
customer if it is set, otherwise use the project contact.
|
||||
PROJECT: better system to automatically send an email to the customer
|
||||
when a task is closed or reopened.
|
||||
PURCHASE: date_planned <= current_time line in red
|
||||
PURCHASE: better purchase order report
|
||||
PURCHASE: better purchase order duplication: you can now duplicate non
|
||||
draft purchase orders and the new one will become draft.
|
||||
SALE: better sale order report
|
||||
SALE: better demo data for sale orders
|
||||
SALE: better view for buttons in sale.order
|
||||
SALE: select product => description = product name instead of code
|
||||
SALE: warehouse field in shop is now required
|
||||
SCRUM: lots of improvements for better useability
|
||||
STOCK: allows to confirm empty picking lists.
|
||||
STOCK: speed up stock computation methods
|
||||
|
||||
Bugfixes:
|
||||
KERNEL: fix a huge bug in the search method for objects involving
|
||||
"old-style" inheritance (inherits) which prevented some records
|
||||
to be accessible in some cases. Most notable example was some
|
||||
products were not accessible in the sale order lines if you had
|
||||
more products in your database than the limit of your search
|
||||
(80 by default).
|
||||
KERNEL: fixed bug which caused OO (sxw) reports to behave badly (crash
|
||||
on Windows and not print correctly on Linux) when data
|
||||
contained XML entities (&, <, >)
|
||||
KERNEL: reports are now fully concurrency compliant
|
||||
KERNEL: fixed bug which caused menuitems without id to cause havoc on
|
||||
update. The menuitems themselves were not created (which is
|
||||
correct) but they created a bad "default" action for all
|
||||
menuitems without action (such as all "menu folders").
|
||||
KERNEL: fix a small security issue: we should check the password of the
|
||||
user when a user asks for the result of a report (in addition
|
||||
to the user id and id of that report)
|
||||
KERNEL: bugfix in view inheritancy
|
||||
KERNEL: fixed duplicating resource with a state field whose selection
|
||||
doesn't contain a 'draft' value (for example project tasks). It
|
||||
now uses the default value of the resource for that field.
|
||||
KERNEL: fixed updating many2many fields using the (4, id) syntax
|
||||
KERNEL: load/save the --logfile option correctly in the config file
|
||||
KERNEL: fixed duplicating a resource with many2many fields
|
||||
ALL: all properties should be inside a data tag with "noupdate" and
|
||||
should have a forcecreate attribute.
|
||||
ACCOUNT: fixed rounding bug in tax computation method
|
||||
ACCOUNT: bugfix in balance and aged balance reports
|
||||
ACCOUNT: fixing precision in function fields methods
|
||||
ACCOUNT: fixed creation of account move lines without using the client
|
||||
interface
|
||||
ACCOUNT: fixed duplicating invoices
|
||||
ACCOUNT: fixed opening an invoices whose description contained non
|
||||
ASCII chars at specific position
|
||||
ACCOUNT: small bugfixes in all accounting reports
|
||||
ACCOUNT: fixed crash when --without-demo due to missing payment.term
|
||||
ACCOUNT: fixed bug in automatic reconciliation
|
||||
ACCOUNT: pass the address to the tax computation method so that it is
|
||||
available in the tax "python applicable code"
|
||||
BASE: allows to delete a request which has a history (it now deletes the
|
||||
history as well as the request)
|
||||
BASE: override copy method for users so that we can duplicate them
|
||||
BASE: fixed bug when the user search for a partner by hitting on an
|
||||
empty many2one field (it searched for a partner with ref=='')
|
||||
BASE: making ir.sequence call thread-safe.
|
||||
CRM: fixed a bug which introduced an invalid case state when closing a
|
||||
case (Thanks to Leigh Willard)
|
||||
HR: added domain to category tree view so that they are not displayed
|
||||
twice
|
||||
HR_TIMESHEET: fixed print graph
|
||||
HR_TIMESHEET: fixed printing timesheet report
|
||||
HR_TIMESHEET: Remove a timesheet entry removes the analytic line
|
||||
MRP: bugfix on "force reservation"
|
||||
MRP: fixed bugs in some reports and MRP scheduler when a partner has
|
||||
no address
|
||||
MRP: fix Force production button if no product available
|
||||
MRP: when computing lots of procurements, the scheduler could raise
|
||||
locking error at the database level. Fixed.
|
||||
PRODUCT: added missing context to compute product list price
|
||||
PRODUCT: fixed field type of qty_available and virtual_available
|
||||
(integer->float). This prevented these fields to be displayed
|
||||
in forms.
|
||||
PROJECT: fixed the view of unassigned task (form and list) instead of
|
||||
form only.
|
||||
PURCHASE: fixed merging orders that made inventory errors when coming
|
||||
from a procurement (orderpoint).
|
||||
PURCHASE: fix bug which prevented to make a purchase order with
|
||||
"manual" lines (ie without product)
|
||||
PURCHASE: fix wizard to group purchase orders in several ways:
|
||||
- only group orders if they are to the same location
|
||||
- only group lines if they are the same except for qty and unit
|
||||
- fix the workflow redirect method so that procurement are not
|
||||
canceled when we merge orders
|
||||
SALE: fixed duplicating a confirmed sale order
|
||||
SALE: fixed making sale orders with "manual" lines (without product)
|
||||
STOCK: future stock prevision bugfix (for move when date_planned < now)
|
||||
STOCK: better view for stock.move
|
||||
STOCK: fixed partial pickings (waiting for a production)
|
||||
Miscellaneous minor bugfixes
|
||||
|
||||
Packaging:
|
||||
Fixed bug in setup.py which didn't copy csv files nor some sub-
|
||||
directories
|
||||
Added a script to migrate a 3.3.0 server to 3.4.0 (you should read the
|
||||
README file in doc/migrate/3.3.0-3.4.0)
|
||||
Removed OsCommerce module
|
||||
|
||||
------------------------------------------------------------------------
|
||||
|
||||
Fri May 19 10:16:18 CEST 2006
|
||||
Server 3.3.0
|
||||
New features:
|
||||
NEW MODULE: hr_timesheet_project
|
||||
Automatically maps projects and tasks to analytic account
|
||||
So that hours spent closing tasks are automatically encoded
|
||||
KERNEL: Added a logfile and a pidfile option (patch from Dan Horak)
|
||||
STOCK: Added support for revisions of tracking numbers
|
||||
STOCK: Added support for revision of production lots
|
||||
STOCK: Added a "splitting and tracking lines" wizard
|
||||
PRODUCT_EXTENDED: Added a method to compute the cost of a product
|
||||
automatically from the cost of its parts
|
||||
|
||||
Improvements:
|
||||
ALL: Small improvements in wizards (order of buttons)
|
||||
PRODUCT: Remove packaging info from supplierinfo
|
||||
PROJECT: Better task view (moved unused fields to other tab)
|
||||
SALE: Keep formating for sale order lines' notes in the sale order report
|
||||
|
||||
Bugfixes:
|
||||
KERNEL: Fixed bug which caused field names with non ascii chars didn't work
|
||||
in list mode on Windows
|
||||
KERNEL: Fix concurrency issue with UpdatableStr with the use of
|
||||
threading.local
|
||||
KERNEL: Removed browse_record __unicode__ method... It made the sale order
|
||||
report crash when using product names with non ASCII characters
|
||||
KERNEL: Fixed bug which caused the translation export to fail when the server
|
||||
was not launched from the directory its source is.
|
||||
BASE: Updating a menuitem now takes care its parent menus
|
||||
BASE: Fixed a cursor locking issue with updates
|
||||
BASE: Fixed viewing sequence types as a tree/list
|
||||
HR: Month field needs to be required in the "hours spent" report
|
||||
PURCHASE: fixed group purchase order wizard:
|
||||
- if there were orders from several different suppliers, it created a purchase
|
||||
order for only the first supplier but canceled other orders, even those which
|
||||
weren't merged in the created order (closes bugzilla #236)
|
||||
- doesn't trash "manual" lines (ie lines with no product)
|
||||
- pay attentions to unit factors when adding several lines together
|
||||
MRP: fixed workcenter load report (prints only the selected workcenters) and
|
||||
does't crash if the user didn't select all workcenters
|
||||
|
||||
Miscellaneous:
|
||||
Removed pydot from required dependencies
|
||||
|
||||
------------------------------------------------------------------------
|
||||
|
||||
Server 3.3.0-rc1
|
||||
================
|
||||
|
||||
Changelog for Users
|
||||
-------------------
|
||||
|
||||
New module: OS Commerce
|
||||
Integration with Tiny ERP and OS Commerce
|
||||
Synchronisation 100% automated with eSale;
|
||||
Import of categories of products
|
||||
Export of products (with photos support)
|
||||
Import of Orders (with the eslae module)
|
||||
Export of stock level
|
||||
Import of OSCommerce Taxes
|
||||
Multiple shop allowed with different rules/products
|
||||
Simple Installation
|
||||
|
||||
New Module: HR_TIMESHEET
|
||||
Management by affair, timesheets creates analytic entries in the
|
||||
accounting to get costs and revenue of each affairs. Affairs are
|
||||
structured in trees.
|
||||
|
||||
New Module: Account Follow Up
|
||||
Multi-Level and configurable Follows ups for the accounting module
|
||||
|
||||
New module; Productivity Analysis of users
|
||||
A module to compare productivity of users of Tiny ERP
|
||||
Generic module, you can compare everything (sales, products, partners,
|
||||
...)
|
||||
|
||||
New Modules for localisations:
|
||||
Accounting localisations for be, ca, fr, de, ch, sw
|
||||
Fix: corrected encoding (latin1 to utf8) of Swedish account tree XML file
|
||||
|
||||
New Module - Sandwich
|
||||
Allows employees to order the lunch
|
||||
Keeps employees preferences
|
||||
|
||||
New Module TOOLS:
|
||||
Email automatic importation/integration in the ERP
|
||||
|
||||
New Module EDI:
|
||||
Import of EDI sale orders
|
||||
Export of shippings
|
||||
|
||||
Multi-Company:
|
||||
Tiny ERP is now fully multi-company !
|
||||
New Company and configuration can be made in the client side.
|
||||
|
||||
ACCOUNTING:
|
||||
Better Entries > Standard Entries (Editable Tree, like in Excel)
|
||||
Automatic creation of lines
|
||||
Journal centralised or not
|
||||
Counterpart of lines in one line or one counterpart per entry
|
||||
Analytic accounting recoded from scratch
|
||||
5 new reports
|
||||
Completly integrated with:
|
||||
production,
|
||||
hr_timesheet > Management by affairs
|
||||
sales & purchases,
|
||||
Tasks.
|
||||
Added unreconciliation functionnalities
|
||||
Added account tree fast rendering
|
||||
Better tax computation system supporting worldwide specific countries
|
||||
Better subscription system
|
||||
Wizard to close a period
|
||||
Wizard to clase a fiscal year
|
||||
Very powerfull, simple and complete multi-currency system
|
||||
in pricelists, sale order, purchases, ...
|
||||
Added required fields in currencies (currency code)
|
||||
Added decimal support
|
||||
Better search on accounts (on code, shortcut or name)
|
||||
Added constraint;
|
||||
on users
|
||||
on group
|
||||
on accounts in a journal
|
||||
added menuitem for automatic reconciliation; Multi-Levels
|
||||
added factor to analytic units
|
||||
added form view for budget items dotations
|
||||
made number of digits in quantity field of the budget spread wizard coherent with the object field
|
||||
fixed journal on purchase invoices/refunds (SugarCRM #6)
|
||||
Better bank statement reconciliation
|
||||
Fixed some reports
|
||||
|
||||
STOCK:
|
||||
Better view for location (using localisation of locations; posx, posy, posz)
|
||||
|
||||
MARKETING:
|
||||
fixed small bug when a partner has no adress
|
||||
state field of marketing partner set as readonly
|
||||
fixed marketing steps form view
|
||||
better history view
|
||||
disabled completely send sms wizard
|
||||
fixed send email wizard
|
||||
good priority -> high priority
|
||||
fixed 'call again later' button
|
||||
|
||||
NETWORK:
|
||||
added tree view for login/password
|
||||
|
||||
HR:
|
||||
added holiday_status (=type of ...) to expense claim form view
|
||||
|
||||
BASE (partner):
|
||||
fixed email_send and _email_send methods
|
||||
removed partner without addresses from demo data
|
||||
Added a date field in the partner form
|
||||
|
||||
MRP:
|
||||
New report: workcenter futur loads
|
||||
Analytic entries when production done.
|
||||
SCHEDULER: better error msg in the generated request
|
||||
Allows services in BoMs (for eg, subcontracting)
|
||||
|
||||
Project/Service Management:
|
||||
create orders from tasks; bugfixes
|
||||
Completly integrated with the rest of the ERP
|
||||
Services can now be MTO/MTS, Buy (subcontracting), produce (task), ...
|
||||
Services can be used anywhere (sale.order, bom, ...)
|
||||
See this graph;
|
||||
http://tiny.be/download/flux/flux_procurement.png
|
||||
tasks sorted by ... AND id, so that the order is not random
|
||||
within a priority
|
||||
|
||||
Automatic translations of all wizards
|
||||
|
||||
Scrum Project Management
|
||||
Better Ergonomy; click on a sprint to view tasks
|
||||
Planned, Effetive hours and progress in backlog, project and sprint
|
||||
Better Burndown Chart computation
|
||||
Better (simpler) view of tasks
|
||||
|
||||
Better demo Data
|
||||
In All modules, eth converted to english
|
||||
|
||||
PRODUCT:
|
||||
computing the weight of the packaging
|
||||
Added last order date
|
||||
Alternative suppliers (with delay, prefs, ...) for one product
|
||||
|
||||
PRICELISTS:
|
||||
much more powerfull system
|
||||
views simplified
|
||||
one pricelist per usage: sale, order, pvc
|
||||
price_type on product_view
|
||||
Multi-Currency pricelist (EUR pricelist can depend on a $ one)
|
||||
|
||||
HR-TIMESHEET: fixed bugs in hours report:
|
||||
sum all lines for the same day instead of displaying only the first one
|
||||
it now uses the analytic unit factor, so that mixing hours and days has some sense
|
||||
close cursor
|
||||
|
||||
SALE:
|
||||
invoices generated from a sale order are pre-computed (taxes are computed)
|
||||
|
||||
new invoicing functionnality;
|
||||
invoice on order quantities or,
|
||||
invoice on shipped quantities
|
||||
|
||||
Invoice on a sale.order or a sale.order.line
|
||||
|
||||
added default value for uos_qty in sale order lines (default to 1)
|
||||
|
||||
|
||||
Changelog for Developers
|
||||
------------------------
|
||||
|
||||
New option --debug, that opens a python interpreter when an exception
|
||||
occurs on the server side.
|
||||
|
||||
Better wizard system. Arguements self, cr, uid, context are passed in all
|
||||
functions of the wizard like normal objects. All wizards converted.
|
||||
|
||||
Speed improvements in many views; partners, sale.order, ...
|
||||
less requests from client to server when opening a form
|
||||
|
||||
Better translation system, wizard terms are exported.
|
||||
|
||||
Script to render module dependency graph
|
||||
|
||||
KERNEL+ALL: pass context to methods computing a selection.
|
||||
|
||||
Modification for actions and view definitions:
|
||||
Actions Window:
|
||||
New field: view_mode = 'tree,form' or 'form,tree' -> default='form,tree'
|
||||
New role of view_type: tree (with shortcuts), form (others with switch button)
|
||||
If you need a form that opens in list mode:
|
||||
view_mode = 'tree,form' or 'tree'
|
||||
view_type = form
|
||||
You can define a view in a view (for example sale.order.line in
|
||||
sale.order)
|
||||
less requests on the client side, no need to define 2 views
|
||||
|
||||
Better command-line option message
|
||||
|
||||
Fixed bug which prevented to search for names using non ASCII
|
||||
chars in many2one or many2many fields
|
||||
|
||||
Report Engine: bugfix for concurrency
|
||||
|
||||
Support of SQL constraints
|
||||
Uniq, check, ...
|
||||
Good error message in the client side (check an account entry with
|
||||
credit and debit >0)
|
||||
|
||||
Fixed: when an exception was raised, the cursor wasn't closed and this
|
||||
could cause a freeze in some cases
|
||||
|
||||
Sequence can contains code: %(year)s, ... for prefix, suffix
|
||||
EX: ORDER %(year)/0005
|
||||
|
||||
Bugfixes for automatic migration system
|
||||
|
||||
bugfix on default value with creation of inherits
|
||||
|
||||
Improvement in report_sxw; you can redefine preprocess to do some
|
||||
preprocessing before printing
|
||||
|
||||
Barcode support enabled by default
|
||||
|
||||
Fixed OpenOffice reports when the server is not launched from the
|
||||
directory the code reside
|
||||
|
||||
Print workflow use a pipe instead of using a temporary file (now workflows
|
||||
works on Windows Servers)
|
||||
|
||||
Inheritancy improved (multiple arguments: replace, inside, after, before)
|
||||
|
||||
Lots of small bugfixes
|
||||
|
|
@ -1,10 +0,0 @@
|
|||
2010-07-XX: 6.0.0
|
||||
=================
|
||||
|
||||
Improvements (server)
|
||||
---------------------
|
||||
|
||||
* support of 'ref' attribute for importing 'reference' field values, as for many2one fields.
|
||||
* experimental xml2yml script in /tools for conversion of XML data/test files to the new YAML format
|
||||
|
||||
|
|
@ -1,55 +0,0 @@
|
|||
OpenERP Quick Installation Guide
|
||||
---------------------------------
|
||||
|
||||
This file contains a quick guide to configure and install the OpenERP server.
|
||||
|
||||
Required dependencies:
|
||||
---------------------
|
||||
|
||||
You need the following software installed:
|
||||
|
||||
* Python 2.5 or 2.6
|
||||
* Postgresql 8.2 or above
|
||||
* Psycopg2 python module
|
||||
* Reportlab pdf generation library for python
|
||||
* lxml python module
|
||||
* pytz python module
|
||||
* PyYaml python module (install with: easy_install PyYaml)
|
||||
|
||||
Some dependencies are only required for specific purposes:
|
||||
|
||||
for rendering workflows graphs, you need:
|
||||
* graphviz
|
||||
* pyparsing
|
||||
|
||||
For Luxembourg localization, you also need:
|
||||
* pdftk (http://www.pdflabs.com/tools/pdftk-the-pdf-toolkit/)
|
||||
|
||||
for generating reports using non .jpg images, you need:
|
||||
* Python Imaging Library for python
|
||||
|
||||
For Debian-based distributions, the required packages can be installed with the
|
||||
following command:
|
||||
|
||||
#> apt-get install -y postgresql graphviz python-psycopg2 python-lxml python-tz python-imaging
|
||||
|
||||
For Fedora
|
||||
if they are not installed, install:
|
||||
python and postgresql
|
||||
|
||||
uses yum or you can recover required packages on fedora web site in "core" or "extra" repository :
|
||||
postgresql-python
|
||||
python-lxml
|
||||
python-imaging
|
||||
python-psycopg2
|
||||
python-reportlab
|
||||
graphviz
|
||||
You can find pyparsing at http://pyparsing.sourceforge.net/
|
||||
|
||||
1. Check that all the required dependencies are installed.
|
||||
|
||||
2. Launch the program "python ./bin/openerp-server.py -r db_user -w db_password --db_host 127.0.0.1".
|
||||
See the man page for more information about options.
|
||||
|
||||
3. Connect to the server using the GUI client. And follow the instructions to create a new database.
|
||||
|
|
@ -1,31 +0,0 @@
|
|||
Important note for OpenERP build >= 5.0.1-11xrg:
|
||||
|
||||
THE USERNAME HAS CHANGED!!
|
||||
|
||||
Former user "tinyerp" is now called "openerp".
|
||||
|
||||
|
||||
For that, you will have to make sure the following files are chowned
|
||||
to the new user:
|
||||
|
||||
/var/log/openerp
|
||||
/var/spool/openerp
|
||||
/var/run/openerp
|
||||
/etc/openerp
|
||||
/etc/openerp/cert.cfg
|
||||
/etc/openerp-server.conf
|
||||
/etc/logrotate.d/openerp-server
|
||||
|
||||
Then, rename the user in the postgres database:
|
||||
|
||||
psql -U postgres postgres
|
||||
|
||||
ALTER ROLE tinyerp RENAME TO openerp;
|
||||
|
||||
Then, edit your openerp-server.conf to depict the change:
|
||||
- db_user = tinyerp
|
||||
+ db_user = openerp
|
||||
|
||||
Good luck!
|
||||
|
||||
|
|
@ -1,24 +0,0 @@
|
|||
#!/bin/bash
|
||||
|
||||
ADMIN_PASSWD='admin'
|
||||
method_1() {
|
||||
cat '-' << EOF
|
||||
<xml>
|
||||
<methodCall>
|
||||
<methodName>set_loglevel</methodName>
|
||||
<params>
|
||||
<param><value><string>$ADMIN_PASSWD</string></value>
|
||||
</param>
|
||||
<param>
|
||||
<value><string>$1</string></value>
|
||||
</param>
|
||||
</params>
|
||||
</methodCall>
|
||||
EOF
|
||||
}
|
||||
LEVEL=10
|
||||
|
||||
if [ -n "$1" ] ; then LEVEL=$1 ; fi
|
||||
|
||||
method_1 $LEVEL | POST -c 'text/xml' http://localhost:8069/xmlrpc/common
|
||||
#eof
|
|
@ -1,121 +0,0 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
##############################################################################
|
||||
#
|
||||
# OpenERP, Open Source Management Solution
|
||||
# Copyright (C) 2004-2009 Tiny SPRL (<http://tiny.be>).
|
||||
#
|
||||
# This program is free software: you can redistribute it and/or modify
|
||||
# it under the terms of the GNU Affero General Public License as
|
||||
# published by the Free Software Foundation, either version 3 of the
|
||||
# License, or (at your option) any later version.
|
||||
#
|
||||
# This program is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU Affero General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU Affero General Public License
|
||||
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
#
|
||||
##############################################################################
|
||||
|
||||
#
|
||||
# This script will automatically test all profiles, localisations and language
|
||||
# packs You must start the OpenERP server and not have a test database. You
|
||||
# may also have to change some data in the top of this file.
|
||||
#
|
||||
|
||||
import xmlrpclib
|
||||
import time
|
||||
import base64
|
||||
|
||||
url = 'http://localhost:8069/xmlrpc'
|
||||
profiles = [
|
||||
'profile_accounting',
|
||||
'profile_service',
|
||||
'profile_manufacturing'
|
||||
]
|
||||
l10n_charts = [
|
||||
'l10n_uk',
|
||||
False,
|
||||
'l10n_be',
|
||||
'l10n_fr'
|
||||
]
|
||||
dbname = 'test'
|
||||
admin_passwd = 'admin'
|
||||
lang = False # List of langs of False for all
|
||||
|
||||
|
||||
sock = xmlrpclib.ServerProxy(url+'/object')
|
||||
sock2 = xmlrpclib.ServerProxy(url+'/db')
|
||||
sock3 = xmlrpclib.ServerProxy(url+'/common')
|
||||
sock4 = xmlrpclib.ServerProxy(url+'/wizard')
|
||||
demos = [True]
|
||||
|
||||
langs = lang or (map(lambda x: x[0], sock2.list_lang()) + ['en_US'])
|
||||
|
||||
def wait(id):
|
||||
progress=0.0
|
||||
while not progress==1.0:
|
||||
time.sleep(3)
|
||||
progress,users = sock2.get_progress(admin_passwd, id)
|
||||
time.sleep(3)
|
||||
return True
|
||||
|
||||
def wizard_run(wizname, fieldvalues=None, endstate='end'):
|
||||
if fieldvalues is None:
|
||||
fieldvalues = {}
|
||||
wiz_id = sock4.create(dbname, uid, 'admin', wizname)
|
||||
state = 'init'
|
||||
datas = {'form':{}}
|
||||
while state!=endstate:
|
||||
res = sock4.execute(dbname, uid, 'admin', wiz_id, datas, state, {})
|
||||
if 'datas' in res:
|
||||
datas['form'].update( res['datas'] )
|
||||
if res['type']=='form':
|
||||
for field in res['fields'].keys():
|
||||
datas['form'][field] = res['fields'][field].get('value', False)
|
||||
state = res['state'][-1][0]
|
||||
datas['form'].update(fieldvalues)
|
||||
elif res['type']=='state':
|
||||
state = res['state']
|
||||
return True
|
||||
|
||||
for demo in demos:
|
||||
for l10n in l10n_charts:
|
||||
print 'Testing localisation', l10n, '...'
|
||||
for prof in profiles:
|
||||
print '\tTesting profile', prof, '...'
|
||||
id = sock2.create(admin_passwd, dbname, demo, lang)
|
||||
wait(id)
|
||||
uid = sock3.login(dbname, 'admin','admin')
|
||||
|
||||
idprof = sock.execute(dbname, uid, 'admin', 'ir.module.module', 'search', [('name','=',prof)])
|
||||
if l10n:
|
||||
idl10n = sock.execute(dbname, uid, 'admin', 'ir.module.module', 'search', [('name','=',l10n)])
|
||||
else:
|
||||
idl10n = [-1]
|
||||
wizard_run('base_setup.base_setup', {
|
||||
'profile': idprof[0],
|
||||
'charts': idl10n[0],
|
||||
}, 'menu')
|
||||
for lang in langs:
|
||||
print '\t\tTesting Language', lang, '...'
|
||||
wizard_run('module.lang.install', {'lang': lang})
|
||||
|
||||
ok = False
|
||||
range = 4
|
||||
while (not ok) and range:
|
||||
try:
|
||||
time.sleep(2)
|
||||
id = sock2.drop(admin_passwd, dbname)
|
||||
ok = True
|
||||
except:
|
||||
range -= 1
|
||||
time.sleep(2)
|
||||
|
||||
|
||||
|
||||
|
||||
# vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4:
|
||||
|
|
@ -1,24 +0,0 @@
|
|||
|
||||
SELECT model, res_id, module FROM ir_model_data
|
||||
WHERE model = 'ir.actions.act_window'
|
||||
AND NOT EXISTS (SELECT 1 FROM ir_act_window WHERE id = ir_model_data.res_id);
|
||||
|
||||
|
||||
SELECT model, res_id, module FROM ir_model_data
|
||||
WHERE model = 'ir.ui.menu'
|
||||
AND NOT EXISTS (SELECT 1 FROM ir_ui_menu WHERE id = ir_model_data.res_id);
|
||||
|
||||
SELECT model, res_id, module FROM ir_model_data
|
||||
WHERE model = 'ir.ui.view'
|
||||
AND NOT EXISTS (SELECT 1 FROM ir_ui_view WHERE id = ir_model_data.res_id);
|
||||
|
||||
DONT DELETE FROM ir_model_data
|
||||
WHERE model = 'ir.actions.act_window'
|
||||
AND NOT EXISTS (SELECT 1 FROM ir_act_window WHERE id = ir_model_data.res_id);
|
||||
|
||||
DONT DELETE FROM ir_model_data
|
||||
WHERE model = 'ir.ui.menu'
|
||||
AND NOT EXISTS (SELECT 1 FROM ir_ui_menu WHERE id = ir_model_data.res_id);
|
||||
-- Other cleanups:
|
||||
-- DELETE from ir_model_data where module = 'audittrail' AND model = 'ir.ui.view' AND NOT EXISTS( SELECT 1 FROM ir_ui_view WHERE ir_ui_view.id = ir_model_data.res_id);
|
||||
-- DELETE from ir_model_data where module = 'audittrail' AND model = 'ir.ui.menu' AND NOT EXISTS( SELECT 1 FROM ir_ui_menu WHERE id = ir_model_data.res_id);
|
|
@ -1,32 +0,0 @@
|
|||
#!/bin/bash
|
||||
##############################################################################
|
||||
#
|
||||
# Copyright (c) 2004-2009 TINY SPRL. (http://tiny.be)
|
||||
#
|
||||
# $Id$
|
||||
#
|
||||
# WARNING: This program as such is intended to be used by professional
|
||||
# programmers who take the whole responsability of assessing all potential
|
||||
# consequences resulting from its eventual inadequacies and bugs
|
||||
# End users who are looking for a ready-to-use solution with commercial
|
||||
# garantees and support are strongly adviced to contact a Free Software
|
||||
# Service Company
|
||||
#
|
||||
# This program is Free Software; you can redistribute it and/or
|
||||
# modify it under the terms of the GNU General Public License
|
||||
# as published by the Free Software Foundation; either version 2
|
||||
# of the License, or (at your option) any later version.
|
||||
#
|
||||
# This program is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with this program; if not, write to the Free Software
|
||||
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
#
|
||||
##############################################################################
|
||||
|
||||
`dirname $0`/module_graph.py $@ | dot -Tpng -o > module_graph.png
|
||||
|
|
@ -1,19 +0,0 @@
|
|||
#!/bin/bash
|
||||
|
||||
# ADMIN_PASSWD='admin'
|
||||
method_1() {
|
||||
cat '-' << EOF
|
||||
<xml>
|
||||
<methodCall>
|
||||
<methodName>get_stats</methodName>
|
||||
<params>
|
||||
</params>
|
||||
</methodCall>
|
||||
EOF
|
||||
}
|
||||
LEVEL=10
|
||||
|
||||
if [ -n "$1" ] ; then LEVEL=$1 ; fi
|
||||
|
||||
method_1 $LEVEL | POST -c 'text/xml' http://localhost:8069/xmlrpc/common
|
||||
#eof
|
|
@ -1,16 +0,0 @@
|
|||
#!/bin/bash
|
||||
|
||||
# ADMIN_PASSWD='admin'
|
||||
method_1() {
|
||||
cat '-' << EOF
|
||||
<xml>
|
||||
<methodCall>
|
||||
<methodName>list_http_services</methodName>
|
||||
<params>
|
||||
</params>
|
||||
</methodCall>
|
||||
EOF
|
||||
}
|
||||
|
||||
method_1 | POST -c 'text/xml' http://localhost:8069/xmlrpc/common
|
||||
#eof
|
|
@ -1,35 +0,0 @@
|
|||
This document describes the steps to follow to migrate from a version 3.3.0 of Tiny ERP server to a version 3.4.0
|
||||
|
||||
Warning: the migration scripts involved in this migration are only meant for
|
||||
a standard Tiny ERP installation. It might not work or even break some data
|
||||
if you added or modified some code to the default Tiny ERP distribution.
|
||||
|
||||
To migrate a 3.3.0 server to version 3.4.0 you should:
|
||||
|
||||
- stop Tiny ERP server 3.3.0
|
||||
|
||||
- backup your database
|
||||
For example: pg_dump terp330 > backup330.sql
|
||||
|
||||
- run the pre.py script (located in this directory)
|
||||
You might need to pass it some optional arguments so that it can connect
|
||||
to the database.
|
||||
|
||||
For example: python pre.py -d terp330
|
||||
|
||||
- run TinyERP server 3.4.0 with "-u all" in the parameters
|
||||
For example: ./tinyerp-server.py -d terp330 -u all
|
||||
|
||||
- stop TinyERP server 3.4.0
|
||||
|
||||
- run the post.py script (located in this directory)
|
||||
|
||||
You might need to pass it some optional arguments so that it can connect
|
||||
to the database.
|
||||
|
||||
For example: python post.py -d terp330
|
||||
|
||||
- run TinyERP server 3.4.0 again with "-u all" in the parameters
|
||||
For example: ./tinyerp-server.py -d terp330 -u all
|
||||
|
||||
- you are ready to work with the new version.
|
|
@ -1,146 +0,0 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
##############################################################################
|
||||
#
|
||||
# OpenERP, Open Source Management Solution
|
||||
# Copyright (C) 2004-2009 Tiny SPRL (<http://tiny.be>).
|
||||
#
|
||||
# This program is free software: you can redistribute it and/or modify
|
||||
# it under the terms of the GNU Affero General Public License as
|
||||
# published by the Free Software Foundation, either version 3 of the
|
||||
# License, or (at your option) any later version.
|
||||
#
|
||||
# This program is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU Affero General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU Affero General Public License
|
||||
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
#
|
||||
##############################################################################
|
||||
|
||||
__author__ = 'Gaetan de Menten, <ged@tiny.be>'
|
||||
__version__ = '0.1.0'
|
||||
|
||||
import psycopg
|
||||
import optparse
|
||||
import ConfigParser
|
||||
|
||||
# -----
|
||||
|
||||
parser = optparse.OptionParser(version="Tiny ERP server migration script " + __version__)
|
||||
|
||||
parser.add_option("-c", "--config", dest="config", help="specify path to Tiny ERP config file")
|
||||
|
||||
group = optparse.OptionGroup(parser, "Database related options")
|
||||
group.add_option("--db_host", dest="db_host", help="specify the database host")
|
||||
group.add_option("--db_port", dest="db_port", help="specify the database port")
|
||||
group.add_option("-d", "--database", dest="db_name", help="specify the database name")
|
||||
group.add_option("-r", "--db_user", dest="db_user", help="specify the database user name")
|
||||
group.add_option("-w", "--db_password", dest="db_password", help="specify the database password")
|
||||
parser.add_option_group(group)
|
||||
|
||||
options = optparse.Values()
|
||||
options.db_name = 'terp' # default value
|
||||
parser.parse_args(values=options)
|
||||
|
||||
if hasattr(options, 'config'):
|
||||
configparser = ConfigParser.ConfigParser()
|
||||
configparser.read([options.config])
|
||||
for name, value in configparser.items('options'):
|
||||
if not (hasattr(options, name) and getattr(options, name)):
|
||||
if value in ('true', 'True'):
|
||||
value = True
|
||||
if value in ('false', 'False'):
|
||||
value = False
|
||||
setattr(options, name, value)
|
||||
|
||||
# -----
|
||||
|
||||
host = hasattr(options, 'db_host') and "host=%s" % options.db_host or ''
|
||||
port = hasattr(options, 'db_port') and "port=%s" % options.db_port or ''
|
||||
name = "dbname=%s" % options.db_name
|
||||
user = hasattr(options, 'db_user') and "user=%s" % options.db_user or ''
|
||||
password = hasattr(options, 'db_password') and "password=%s" % options.db_password or ''
|
||||
|
||||
db = psycopg.connect('%s %s %s %s %s' % (host, port, name, user, password), serialize=0)
|
||||
cr = db.cursor()
|
||||
|
||||
# ------------------------------------------- #
|
||||
# convert partner payment terms to properties #
|
||||
# ------------------------------------------- #
|
||||
|
||||
# setup
|
||||
|
||||
cr.execute("select id from ir_model_fields where name='property_payment_term' and model='res.partner'")
|
||||
fields_id = cr.fetchone()[0]
|
||||
|
||||
cr.execute("select company_id from res_users where company_id is not null limit 1")
|
||||
company_id = cr.fetchone()[0]
|
||||
|
||||
# get partners
|
||||
cr.execute("SELECT c.relname FROM pg_class c, pg_attribute a WHERE c.relname='res_partner' AND a.attname='payment_term' AND c.oid=a.attrelid")
|
||||
partners=[]
|
||||
drop_payment_term=False
|
||||
if cr.rowcount:
|
||||
drop_payment_term=True
|
||||
cr.execute("select id, payment_term from res_partner where payment_term is not null")
|
||||
partners = cr.dictfetchall()
|
||||
|
||||
# loop over them
|
||||
|
||||
for partner in partners:
|
||||
value = 'account.payment.term,%d' % partner['payment_term']
|
||||
res_id = 'res.partner,%d' % partner['id']
|
||||
cr.execute(
|
||||
"insert into ir_property(name, value, res_id, company_id, fields_id) "\
|
||||
"values(%s, %s, %s, %d, %d)",
|
||||
('property_payment_term', value, res_id, company_id, fields_id))
|
||||
|
||||
# remove the field
|
||||
if drop_payment_term:
|
||||
cr.execute("alter table res_partner drop column payment_term")
|
||||
cr.execute("delete from ir_model_fields where model = 'res.partner' and name = 'payment_term'")
|
||||
|
||||
cr.commit()
|
||||
|
||||
# ------------------------ #
|
||||
# remove duplicate reports #
|
||||
# ------------------------ #
|
||||
|
||||
cr.execute("select model, report_name from ir_act_report_xml group by model, report_name having count(*)>1")
|
||||
reports_wh_duplicates = cr.dictfetchall()
|
||||
|
||||
cr.execute("select res_id from ir_model_data where model='ir.actions.report.xml'")
|
||||
registered_reports = cr.fetchall()
|
||||
reg_reports_ids = ','.join([str(id) for (id,) in registered_reports])
|
||||
|
||||
for report in reports_wh_duplicates:
|
||||
cr.execute("select id from ir_act_report_xml where model=%s and report_name=%s and id not in ("+reg_reports_ids+")", (report['model'], report['report_name']))
|
||||
(id,) = cr.fetchone()
|
||||
cr.execute("delete from ir_act_report_xml where id=%d", (id,))
|
||||
cr.execute("delete from ir_values where value='ir.actions.report.xml,%d'", (id,))
|
||||
|
||||
cr.commit()
|
||||
|
||||
# ------------------------------------- #
|
||||
# remove duplicate workflow transitions #
|
||||
# ------------------------------------- #
|
||||
|
||||
# this removes all transitions which are not registered in ir_model_data
|
||||
|
||||
cr.execute("delete from wkf_transition where id not in (select res_id from ir_model_data where model='workflow.transition')")
|
||||
cr.commit()
|
||||
|
||||
# -------------------------------- #
|
||||
# remove bad "default" menu action #
|
||||
# -------------------------------- #
|
||||
|
||||
cr.execute("delete from ir_values where key='action' and model='ir.ui.menu' and res_id is null")
|
||||
cr.commit()
|
||||
|
||||
cr.close()
|
||||
|
||||
|
||||
# vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4:
|
||||
|
|
@ -1,112 +0,0 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
##############################################################################
|
||||
#
|
||||
# OpenERP, Open Source Management Solution
|
||||
# Copyright (C) 2004-2009 Tiny SPRL (<http://tiny.be>).
|
||||
#
|
||||
# This program is free software: you can redistribute it and/or modify
|
||||
# it under the terms of the GNU Affero General Public License as
|
||||
# published by the Free Software Foundation, either version 3 of the
|
||||
# License, or (at your option) any later version.
|
||||
#
|
||||
# This program is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU Affero General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU Affero General Public License
|
||||
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
#
|
||||
##############################################################################
|
||||
|
||||
__author__ = 'Gaetan de Menten, <ged@tiny.be>'
|
||||
__version__ = '0.1.0'
|
||||
|
||||
import psycopg
|
||||
import optparse
|
||||
import ConfigParser
|
||||
|
||||
# -----
|
||||
|
||||
parser = optparse.OptionParser(version="Tiny ERP server migration script " + __version__)
|
||||
|
||||
parser.add_option("-c", "--config", dest="config", help="specify path to Tiny ERP config file")
|
||||
|
||||
group = optparse.OptionGroup(parser, "Database related options")
|
||||
group.add_option("--db_host", dest="db_host", help="specify the database host")
|
||||
group.add_option("--db_port", dest="db_port", help="specify the database port")
|
||||
group.add_option("-d", "--database", dest="db_name", help="specify the database name")
|
||||
group.add_option("-r", "--db_user", dest="db_user", help="specify the database user name")
|
||||
group.add_option("-w", "--db_password", dest="db_password", help="specify the database password")
|
||||
parser.add_option_group(group)
|
||||
|
||||
options = optparse.Values()
|
||||
options.db_name = 'terp' # default value
|
||||
parser.parse_args(values=options)
|
||||
|
||||
if hasattr(options, 'config'):
|
||||
configparser = ConfigParser.ConfigParser()
|
||||
configparser.read([options.config])
|
||||
for name, value in configparser.items('options'):
|
||||
if not (hasattr(options, name) and getattr(options, name)):
|
||||
if value in ('true', 'True'):
|
||||
value = True
|
||||
if value in ('false', 'False'):
|
||||
value = False
|
||||
setattr(options, name, value)
|
||||
|
||||
# -----
|
||||
|
||||
host = hasattr(options, 'db_host') and "host=%s" % options.db_host or ''
|
||||
port = hasattr(options, 'db_port') and "port=%s" % options.db_port or ''
|
||||
name = "dbname=%s" % options.db_name
|
||||
user = hasattr(options, 'db_user') and "user=%s" % options.db_user or ''
|
||||
password = hasattr(options, 'db_password') and "password=%s" % options.db_password or ''
|
||||
|
||||
db = psycopg.connect('%s %s %s %s %s' % (host, port, name, user, password), serialize=0)
|
||||
cr = db.cursor()
|
||||
|
||||
# ------------------------- #
|
||||
# change some columns types #
|
||||
# ------------------------- #
|
||||
|
||||
def change_column(cr, table, column, new_type, copy):
|
||||
commands = [
|
||||
"ALTER TABLE %s RENAME COLUMN %s TO temp_column" % (table, column),
|
||||
"ALTER TABLE %s ADD COLUMN %s %s" % (table, column, new_type),
|
||||
"ALTER TABLE %s DROP COLUMN temp_column" % table
|
||||
]
|
||||
if copy:
|
||||
commands.insert(
|
||||
2,
|
||||
"UPDATE %s SET %s=temp_column::%s" % (table, column, new_type))
|
||||
|
||||
for command in commands:
|
||||
cr.execute(command)
|
||||
|
||||
change_column(cr, 'account_account_type', 'code_from', 'varchar(10)', False)
|
||||
change_column(cr, 'account_account_type', 'code_to', 'varchar(10)', False)
|
||||
cr.commit()
|
||||
|
||||
# ----------------------------------------------------- #
|
||||
# add some fields (which cannot be added automatically) #
|
||||
# ----------------------------------------------------- #
|
||||
|
||||
for line in (
|
||||
"alter table ir_model_fields add group_name varchar(64)",
|
||||
"alter table ir_model_fields add view_load boolean",
|
||||
"alter table ir_model_fields alter group_name set default ''",
|
||||
"alter table ir_model_fields alter view_load set default False",
|
||||
"delete from ir_values where value like '%,False'",
|
||||
):
|
||||
try:
|
||||
cr.execute(line)
|
||||
except psycopg.ProgrammingError, e:
|
||||
cr.commit()
|
||||
print e
|
||||
|
||||
cr.commit()
|
||||
cr.close()
|
||||
|
||||
# vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4:
|
||||
|
|
@ -1,29 +0,0 @@
|
|||
This document describes the steps to follow to migrate from a version 3.4.0 of Tiny ERP server to a version 4.0.0
|
||||
|
||||
Warning: the migration scripts involved in this migration are only meant for
|
||||
a standard Tiny ERP installation. It might not work or even break some data
|
||||
if you added or modified some code to the default Tiny ERP distribution.
|
||||
|
||||
To migrate a 3.4.0 server to version 4.0.0 you should:
|
||||
|
||||
- stop Tiny ERP server 3.4.0
|
||||
|
||||
- backup your database
|
||||
For example: pg_dump terp340 > backup340.sql
|
||||
|
||||
- run the pre.py script (located in this directory)
|
||||
You might need to pass it some optional arguments so that it can connect
|
||||
to the database.
|
||||
|
||||
For example: python pre.py -d terp340
|
||||
|
||||
- run TinyERP server 4.0.0 with "-d terp340 -u all" in the parameters
|
||||
For example: ./tinyerp-server.py -d terp340 -u all
|
||||
|
||||
- stop TinyERP server 4.0.0
|
||||
|
||||
- run the post.py script (located in this directory)
|
||||
You might need to pass it some optional arguments so that it can connect
|
||||
to the database.
|
||||
|
||||
- you are ready to work with the new version.
|
|
@ -1,87 +0,0 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
##############################################################################
|
||||
#
|
||||
# OpenERP, Open Source Management Solution
|
||||
# Copyright (C) 2004-2009 Tiny SPRL (<http://tiny.be>).
|
||||
#
|
||||
# This program is free software: you can redistribute it and/or modify
|
||||
# it under the terms of the GNU Affero General Public License as
|
||||
# published by the Free Software Foundation, either version 3 of the
|
||||
# License, or (at your option) any later version.
|
||||
#
|
||||
# This program is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU Affero General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU Affero General Public License
|
||||
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
#
|
||||
##############################################################################
|
||||
|
||||
__author__ = 'Gaetan de Menten, <ged@tiny.be>'
|
||||
__version__ = '0.1.0'
|
||||
|
||||
import psycopg
|
||||
import optparse
|
||||
import ConfigParser
|
||||
|
||||
# -----
|
||||
|
||||
parser = optparse.OptionParser(version="Tiny ERP server migration script " + __version__)
|
||||
|
||||
parser.add_option("-c", "--config", dest="config", help="specify path to Tiny ERP config file")
|
||||
|
||||
group = optparse.OptionGroup(parser, "Database related options")
|
||||
group.add_option("--db_host", dest="db_host", help="specify the database host")
|
||||
group.add_option("--db_port", dest="db_port", help="specify the database port")
|
||||
group.add_option("-d", "--database", dest="db_name", help="specify the database name")
|
||||
group.add_option("-r", "--db_user", dest="db_user", help="specify the database user name")
|
||||
group.add_option("-w", "--db_password", dest="db_password", help="specify the database password")
|
||||
parser.add_option_group(group)
|
||||
|
||||
options = optparse.Values()
|
||||
options.db_name = 'terp' # default value
|
||||
parser.parse_args(values=options)
|
||||
|
||||
if hasattr(options, 'config'):
|
||||
configparser = ConfigParser.ConfigParser()
|
||||
configparser.read([options.config])
|
||||
for name, value in configparser.items('options'):
|
||||
if not (hasattr(options, name) and getattr(options, name)):
|
||||
if value in ('true', 'True'):
|
||||
value = True
|
||||
if value in ('false', 'False'):
|
||||
value = False
|
||||
setattr(options, name, value)
|
||||
|
||||
# -----
|
||||
|
||||
host = hasattr(options, 'db_host') and "host=%s" % options.db_host or ''
|
||||
port = hasattr(options, 'db_port') and "port=%s" % options.db_port or ''
|
||||
name = "dbname=%s" % options.db_name
|
||||
user = hasattr(options, 'db_user') and "user=%s" % options.db_user or ''
|
||||
password = hasattr(options, 'db_password') and "password=%s" % options.db_password or ''
|
||||
|
||||
db = psycopg.connect('%s %s %s %s %s' % (host, port, name, user, password), serialize=0)
|
||||
cr = db.cursor()
|
||||
|
||||
# --------------- #
|
||||
# remove old menu #
|
||||
# --------------- #
|
||||
|
||||
cr.execute("delete from ir_ui_menu where (id not in (select parent_id from ir_ui_menu where parent_id is not null)) and (id not in (select res_id from ir_values where model='ir.ui.menu'))")
|
||||
cr.commit()
|
||||
|
||||
# --------------- #
|
||||
# remove ir_value #
|
||||
# --------------- #
|
||||
|
||||
cr.execute("delete from ir_values where model = 'ir.ui.menu' and res_id is null")
|
||||
cr.commit()
|
||||
|
||||
cr.close()
|
||||
|
||||
|
||||
# vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4:
|
||||
|
|
@ -1,116 +0,0 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
##############################################################################
|
||||
#
|
||||
# OpenERP, Open Source Management Solution
|
||||
# Copyright (C) 2004-2009 Tiny SPRL (<http://tiny.be>).
|
||||
#
|
||||
# This program is free software: you can redistribute it and/or modify
|
||||
# it under the terms of the GNU Affero General Public License as
|
||||
# published by the Free Software Foundation, either version 3 of the
|
||||
# License, or (at your option) any later version.
|
||||
#
|
||||
# This program is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU Affero General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU Affero General Public License
|
||||
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
#
|
||||
##############################################################################
|
||||
|
||||
__author__ = 'Gaetan de Menten, <ged@tiny.be>'
|
||||
__version__ = '0.1.0'
|
||||
|
||||
import psycopg
|
||||
import optparse
|
||||
import ConfigParser
|
||||
|
||||
# -----
|
||||
|
||||
parser = optparse.OptionParser(version="Tiny ERP server migration script " + __version__)
|
||||
|
||||
parser.add_option("-c", "--config", dest="config", help="specify path to Tiny ERP config file")
|
||||
|
||||
group = optparse.OptionGroup(parser, "Database related options")
|
||||
group.add_option("--db_host", dest="db_host", help="specify the database host")
|
||||
group.add_option("--db_port", dest="db_port", help="specify the database port")
|
||||
group.add_option("-d", "--database", dest="db_name", help="specify the database name")
|
||||
group.add_option("-r", "--db_user", dest="db_user", help="specify the database user name")
|
||||
group.add_option("-w", "--db_password", dest="db_password", help="specify the database password")
|
||||
parser.add_option_group(group)
|
||||
|
||||
options = optparse.Values()
|
||||
options.db_name = 'terp' # default value
|
||||
parser.parse_args(values=options)
|
||||
|
||||
if hasattr(options, 'config'):
|
||||
configparser = ConfigParser.ConfigParser()
|
||||
configparser.read([options.config])
|
||||
for name, value in configparser.items('options'):
|
||||
if not (hasattr(options, name) and getattr(options, name)):
|
||||
if value in ('true', 'True'):
|
||||
value = True
|
||||
if value in ('false', 'False'):
|
||||
value = False
|
||||
setattr(options, name, value)
|
||||
|
||||
# -----
|
||||
|
||||
host = hasattr(options, 'db_host') and "host=%s" % options.db_host or ''
|
||||
port = hasattr(options, 'db_port') and "port=%s" % options.db_port or ''
|
||||
name = "dbname=%s" % options.db_name
|
||||
user = hasattr(options, 'db_user') and "user=%s" % options.db_user or ''
|
||||
password = hasattr(options, 'db_password') and "password=%s" % options.db_password or ''
|
||||
|
||||
db = psycopg.connect('%s %s %s %s %s' % (host, port, name, user, password), serialize=0)
|
||||
cr = db.cursor()
|
||||
|
||||
# ---------------------------------------------------------------- #
|
||||
# move user id from hr_analytic_timesheet to account_analytic_line #
|
||||
# ---------------------------------------------------------------- #
|
||||
|
||||
cr.execute("UPDATE account_analytic_line SET user_id = hr_analytic_timesheet.user_id FROM hr_analytic_timesheet WHERE hr_analytic_timesheet.line_id = account_analytic_line.id")
|
||||
cr.commit()
|
||||
|
||||
# --------------- #
|
||||
# remove old menu #
|
||||
# --------------- #
|
||||
|
||||
while True:
|
||||
cr.execute("select id from ir_ui_menu where (id not in (select parent_id from ir_ui_menu where parent_id is not null)) and (id not in (select res_id from ir_values where model='ir.ui.menu'))")
|
||||
if not cr.rowcount:
|
||||
break
|
||||
cr.execute("delete from ir_ui_menu where (id not in (select parent_id from ir_ui_menu where parent_id is not null)) and (id not in (select res_id from ir_values where model='ir.ui.menu'))")
|
||||
cr.commit()
|
||||
|
||||
# ----------------------------------------- #
|
||||
# add default value for discount in invoice #
|
||||
# ----------------------------------------- #
|
||||
|
||||
cr.execute("update account_invoice_line set discount=0.0 where discount is null;")
|
||||
cr.commit()
|
||||
|
||||
|
||||
# -------------------------------------------------------------------------- #
|
||||
# update constraint account_invoice_line_uos_id_fkey on account_invoice_line #
|
||||
# -------------------------------------------------------------------------- #
|
||||
|
||||
cr.execute("ALTER TABLE account_invoice_line DROP CONSTRAINT account_invoice_line_uos_id_fkey")
|
||||
cr.execute("ALTER TABLE account_invoice_line ADD FOREIGN KEY (uos_id) REFERENCES product_uom(id) ON DELETE SET NULL")
|
||||
cr.commit()
|
||||
|
||||
print """
|
||||
WARNING: account_uos has been replaced by product_uom.
|
||||
It is not possible to migrate the data automatically so you need to create the old account_uos in the new product_uom.
|
||||
And then update the field uos_id of the table account_invoice to match the new id of product_uom.
|
||||
|
||||
EXAMPLE:
|
||||
UPDATE account_invoice SET uos_id = new_id WHERE uos_id = old_id;
|
||||
"""
|
||||
|
||||
cr.close()
|
||||
|
||||
|
||||
# vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4:
|
||||
|
|
@ -1,145 +0,0 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
##############################################################################
|
||||
#
|
||||
# OpenERP, Open Source Management Solution
|
||||
# Copyright (C) 2004-2009 Tiny SPRL (<http://tiny.be>).
|
||||
#
|
||||
# This program is free software: you can redistribute it and/or modify
|
||||
# it under the terms of the GNU Affero General Public License as
|
||||
# published by the Free Software Foundation, either version 3 of the
|
||||
# License, or (at your option) any later version.
|
||||
#
|
||||
# This program is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU Affero General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU Affero General Public License
|
||||
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
#
|
||||
##############################################################################
|
||||
|
||||
__author__ = 'Gaetan de Menten, <ged@tiny.be>'
|
||||
__version__ = '0.1.0'
|
||||
|
||||
import psycopg
|
||||
import optparse
|
||||
import ConfigParser
|
||||
|
||||
# -----
|
||||
|
||||
parser = optparse.OptionParser(version="Tiny ERP server migration script " + __version__)
|
||||
|
||||
parser.add_option("-c", "--config", dest="config", help="specify path to Tiny ERP config file")
|
||||
|
||||
group = optparse.OptionGroup(parser, "Database related options")
|
||||
group.add_option("--db_host", dest="db_host", help="specify the database host")
|
||||
group.add_option("--db_port", dest="db_port", help="specify the database port")
|
||||
group.add_option("-d", "--database", dest="db_name", help="specify the database name")
|
||||
group.add_option("-r", "--db_user", dest="db_user", help="specify the database user name")
|
||||
group.add_option("-w", "--db_password", dest="db_password", help="specify the database password")
|
||||
parser.add_option_group(group)
|
||||
|
||||
options = optparse.Values()
|
||||
options.db_name = 'terp' # default value
|
||||
parser.parse_args(values=options)
|
||||
|
||||
if hasattr(options, 'config'):
|
||||
configparser = ConfigParser.ConfigParser()
|
||||
configparser.read([options.config])
|
||||
for name, value in configparser.items('options'):
|
||||
if not (hasattr(options, name) and getattr(options, name)):
|
||||
if value in ('true', 'True'):
|
||||
value = True
|
||||
if value in ('false', 'False'):
|
||||
value = False
|
||||
setattr(options, name, value)
|
||||
|
||||
# -----
|
||||
|
||||
host = hasattr(options, 'db_host') and "host=%s" % options.db_host or ''
|
||||
port = hasattr(options, 'db_port') and "port=%s" % options.db_port or ''
|
||||
name = "dbname=%s" % options.db_name
|
||||
user = hasattr(options, 'db_user') and "user=%s" % options.db_user or ''
|
||||
password = hasattr(options, 'db_password') and "password=%s" % options.db_password or ''
|
||||
|
||||
db = psycopg.connect('%s %s %s %s %s' % (host, port, name, user, password), serialize=0)
|
||||
cr = db.cursor()
|
||||
|
||||
# ------------------------- #
|
||||
# change some columns types #
|
||||
# ------------------------- #
|
||||
|
||||
def change_column(cr, table, column, new_type, copy):
|
||||
commands = [
|
||||
"ALTER TABLE %s RENAME COLUMN %s TO temp_column" % (table, column),
|
||||
"ALTER TABLE %s ADD COLUMN %s %s" % (table, column, new_type),
|
||||
"ALTER TABLE %s DROP COLUMN temp_column" % table
|
||||
]
|
||||
if copy:
|
||||
commands.insert(
|
||||
2,
|
||||
"UPDATE %s SET %s=temp_column::%s" % (table, column, new_type))
|
||||
|
||||
for command in commands:
|
||||
cr.execute(command)
|
||||
|
||||
#change_column(cr, 'crm_case', 'date_closed', 'timestamp', True)
|
||||
cr.commit()
|
||||
|
||||
# -------------------- #
|
||||
# add module if needed #
|
||||
# -------------------- #
|
||||
|
||||
cr.execute("SELECT name FROM ir_module_module")
|
||||
if not cr.rowcount:
|
||||
for module in set(['base', 'marketing', 'subscription', 'account', 'base_partner_relation', 'audittrail', 'account_followup', 'product', 'hr', 'l10n_simple', 'crm', 'stock', 'hr_timesheet', 'purchase', 'report_purchase', 'mrp', 'sale', 'report_sale', 'delivery', 'project', 'sale_crm', 'hr_timesheet_project', 'scrum', 'report_project',
|
||||
'account_followup',
|
||||
'account',
|
||||
'audittrail',
|
||||
'base_partner_relation',
|
||||
'crm',
|
||||
'delivery',
|
||||
'edi',
|
||||
'hr_evaluation',
|
||||
'hr_expense',
|
||||
'hr',
|
||||
'hr_timesheet_invoice',
|
||||
'hr_timesheet_project',
|
||||
'hr_timesheet',
|
||||
'l10n_simple',
|
||||
'marketing',
|
||||
'mrp',
|
||||
'network',
|
||||
'product',
|
||||
'project',
|
||||
'purchase',
|
||||
'report_crm',
|
||||
'report_project',
|
||||
'report_purchase',
|
||||
'report_sale',
|
||||
'sale_crm',
|
||||
'sale',
|
||||
'sandwich',
|
||||
'scrum',
|
||||
'stock']):
|
||||
cr.execute("INSERT INTO ir_module_module (name, state) VALUES ('%s', 'installed')" % module)
|
||||
cr.commit()
|
||||
|
||||
|
||||
# ----------------------------------------------------- #
|
||||
# add some fields (which cannot be added automatically) #
|
||||
# ----------------------------------------------------- #
|
||||
|
||||
for line in (
|
||||
"ALTER TABLE ir_module_module ADD demo BOOLEAN DEFAULT False",
|
||||
"delete from ir_values where value like '%,False'",
|
||||
"""UPDATE ir_ui_view set arch='<?xml version="1.0"?><tree string="Menu" toolbar="1"><field icon="icon" name="name"/></tree>' where name='ir.ui.menu.tree' and type='tree' and field_parent='child_id'""",
|
||||
):
|
||||
cr.execute(line)
|
||||
|
||||
cr.commit()
|
||||
cr.close()
|
||||
|
||||
# vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4:
|
||||
|
|
@ -1,127 +0,0 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
##############################################################################
|
||||
#
|
||||
# OpenERP, Open Source Management Solution
|
||||
# Copyright (C) 2004-2009 Tiny SPRL (<http://tiny.be>).
|
||||
#
|
||||
# This program is free software: you can redistribute it and/or modify
|
||||
# it under the terms of the GNU Affero General Public License as
|
||||
# published by the Free Software Foundation, either version 3 of the
|
||||
# License, or (at your option) any later version.
|
||||
#
|
||||
# This program is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU Affero General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU Affero General Public License
|
||||
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
#
|
||||
##############################################################################
|
||||
|
||||
__author__ = 'Gaetan de Menten, <ged@tiny.be>'
|
||||
__version__ = '0.1.0'
|
||||
|
||||
import psycopg
|
||||
import optparse
|
||||
import ConfigParser
|
||||
|
||||
# -----
|
||||
|
||||
parser = optparse.OptionParser(version="Tiny ERP server migration script " + __version__)
|
||||
|
||||
parser.add_option("-c", "--config", dest="config", help="specify path to Tiny ERP config file")
|
||||
|
||||
group = optparse.OptionGroup(parser, "Database related options")
|
||||
group.add_option("--db_host", dest="db_host", help="specify the database host")
|
||||
group.add_option("--db_port", dest="db_port", help="specify the database port")
|
||||
group.add_option("-d", "--database", dest="db_name", help="specify the database name")
|
||||
group.add_option("-r", "--db_user", dest="db_user", help="specify the database user name")
|
||||
group.add_option("-w", "--db_password", dest="db_password", help="specify the database password")
|
||||
parser.add_option_group(group)
|
||||
|
||||
options = optparse.Values()
|
||||
options.db_name = 'terp' # default value
|
||||
parser.parse_args(values=options)
|
||||
|
||||
if hasattr(options, 'config'):
|
||||
configparser = ConfigParser.ConfigParser()
|
||||
configparser.read([options.config])
|
||||
for name, value in configparser.items('options'):
|
||||
if not (hasattr(options, name) and getattr(options, name)):
|
||||
if value in ('true', 'True'):
|
||||
value = True
|
||||
if value in ('false', 'False'):
|
||||
value = False
|
||||
setattr(options, name, value)
|
||||
|
||||
# -----
|
||||
|
||||
host = hasattr(options, 'db_host') and "host=%s" % options.db_host or ''
|
||||
port = hasattr(options, 'db_port') and "port=%s" % options.db_port or ''
|
||||
name = "dbname=%s" % options.db_name
|
||||
user = hasattr(options, 'db_user') and "user=%s" % options.db_user or ''
|
||||
password = hasattr(options, 'db_password') and "password=%s" % options.db_password or ''
|
||||
|
||||
db = psycopg.connect('%s %s %s %s %s' % (host, port, name, user, password), serialize=0)
|
||||
cr = db.cursor()
|
||||
|
||||
# ------------------------- #
|
||||
# change some columns types #
|
||||
# ------------------------- #
|
||||
|
||||
def change_column(cr, table, column, new_type, copy):
|
||||
commands = [
|
||||
"ALTER TABLE %s RENAME COLUMN %s TO temp_column" % (table, column),
|
||||
"ALTER TABLE %s ADD COLUMN %s %s" % (table, column, new_type),
|
||||
"ALTER TABLE %s DROP COLUMN temp_column" % table
|
||||
]
|
||||
if copy:
|
||||
commands.insert(
|
||||
2,
|
||||
"UPDATE %s SET %s=temp_column::%s" % (table, column, new_type))
|
||||
|
||||
for command in commands:
|
||||
cr.execute(command)
|
||||
|
||||
change_column(cr, 'crm_case', 'date_closed', 'timestamp', True)
|
||||
cr.commit()
|
||||
|
||||
# -------------------- #
|
||||
# add module if needed #
|
||||
# -------------------- #
|
||||
|
||||
cr.execute("SELECT name FROM ir_module_module")
|
||||
if not cr.rowcount:
|
||||
for module in ('base', 'marketing', 'subscription', 'account', 'base_partner_relation', 'audittrail', 'account_followup', 'product', 'hr', 'l10n_simple', 'crm', 'stock', 'hr_timesheet', 'purchase', 'report_purchase', 'mrp', 'sale', 'report_sale', 'delivery', 'project', 'sale_crm', 'hr_timesheet_project', 'scrum', 'report_project'):
|
||||
cr.execute("INSERT INTO ir_module_module (name, state) VALUES ('%s', 'installed')" % module)
|
||||
cr.commit()
|
||||
|
||||
# --------------- #
|
||||
# remove old menu #
|
||||
# --------------- #
|
||||
|
||||
while True:
|
||||
cr.execute("select id from ir_ui_menu where id not in (select parent_id from ir_ui_menu where parent_id is not null) and id not in (select res_id from ir_model_data where model='ir.ui.menu')")
|
||||
if not cr.rowcount:
|
||||
break
|
||||
cr.execute("delete from ir_ui_menu where id not in (select parent_id from ir_ui_menu where parent_id is not null) and id not in (select res_id from ir_model_data where model='ir.ui.menu')")
|
||||
cr.commit()
|
||||
|
||||
# ----------------------------------------------------- #
|
||||
# add some fields (which cannot be added automatically) #
|
||||
# ----------------------------------------------------- #
|
||||
|
||||
for line in (
|
||||
"ALTER TABLE ir_module_module ADD demo BOOLEAN",
|
||||
"ALTER TABLE ir_module_module SET demo DEFAULT False",
|
||||
"DELETE FROM ir_values WHERE VALUE LIKE '%,False'",
|
||||
"""UPDATE ir_ui_view set arch='<?xml version="1.0"?><tree string="Menu" toolbar="1"><field icon="icon" name="name"/></tree>' where name='ir.ui.menu.tree' and type='tree' and field_parent='child_id'""",
|
||||
):
|
||||
cr.execute(line)
|
||||
|
||||
cr.commit()
|
||||
cr.close()
|
||||
|
||||
# vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4:
|
||||
|
|
@ -1,247 +0,0 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
##############################################################################
|
||||
#
|
||||
# OpenERP, Open Source Management Solution
|
||||
# Copyright (C) 2004-2009 Tiny SPRL (<http://tiny.be>).
|
||||
#
|
||||
# This program is free software: you can redistribute it and/or modify
|
||||
# it under the terms of the GNU Affero General Public License as
|
||||
# published by the Free Software Foundation, either version 3 of the
|
||||
# License, or (at your option) any later version.
|
||||
#
|
||||
# This program is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU Affero General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU Affero General Public License
|
||||
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
#
|
||||
##############################################################################
|
||||
|
||||
__author__ = 'Cédric Krier, <ced@tinyerp.com>'
|
||||
__version__ = '0.1.0'
|
||||
|
||||
import psycopg
|
||||
import optparse
|
||||
import ConfigParser
|
||||
|
||||
# -----
|
||||
|
||||
parser = optparse.OptionParser(version="Tiny ERP server migration script " + __version__)
|
||||
|
||||
parser.add_option("-c", "--config", dest="config", help="specify path to Tiny ERP config file")
|
||||
|
||||
group = optparse.OptionGroup(parser, "Database related options")
|
||||
group.add_option("--db_host", dest="db_host", help="specify the database host")
|
||||
group.add_option("--db_port", dest="db_port", help="specify the database port")
|
||||
group.add_option("-d", "--database", dest="db_name", help="specify the database name")
|
||||
group.add_option("-r", "--db_user", dest="db_user", help="specify the database user name")
|
||||
group.add_option("-w", "--db_password", dest="db_password", help="specify the database password")
|
||||
parser.add_option_group(group)
|
||||
|
||||
options = optparse.Values()
|
||||
options.db_name = 'terp' # default value
|
||||
parser.parse_args(values=options)
|
||||
|
||||
if hasattr(options, 'config'):
|
||||
configparser = ConfigParser.ConfigParser()
|
||||
configparser.read([options.config])
|
||||
for name, value in configparser.items('options'):
|
||||
if not (hasattr(options, name) and getattr(options, name)):
|
||||
if value in ('true', 'True'):
|
||||
value = True
|
||||
if value in ('false', 'False'):
|
||||
value = False
|
||||
setattr(options, name, value)
|
||||
|
||||
# -----
|
||||
|
||||
host = hasattr(options, 'db_host') and "host=%s" % options.db_host or ''
|
||||
port = hasattr(options, 'db_port') and "port=%s" % options.db_port or ''
|
||||
name = "dbname=%s" % options.db_name
|
||||
user = hasattr(options, 'db_user') and "user=%s" % options.db_user or ''
|
||||
password = hasattr(options, 'db_password') and "password=%s" % options.db_password or ''
|
||||
|
||||
db = psycopg.connect('%s %s %s %s %s' % (host, port, name, user, password), serialize=0)
|
||||
cr = db.cursor()
|
||||
|
||||
# ------------------------ #
|
||||
# change currency rounding #
|
||||
# ------------------------ #
|
||||
|
||||
cr.execute("""SELECT c.relname,a.attname,a.attlen,a.atttypmod,a.attnotnull,a.atthasdef,t.typname,CASE WHEN a.attlen=-1 THEN a.atttypmod-4 ELSE a.attlen END as size FROM pg_class c,pg_attribute a,pg_type t WHERE c.relname='res_currency' AND a.attname='rounding' AND c.oid=a.attrelid AND a.atttypid=t.oid""")
|
||||
res = cr.dictfetchall()
|
||||
if res[0]['typname'] != 'numeric':
|
||||
for line in (
|
||||
"ALTER TABLE res_currency RENAME rounding TO rounding_bak",
|
||||
"ALTER TABLE res_currency ADD rounding NUMERIC(12,6)",
|
||||
"UPDATE res_currency SET rounding = power(10, - rounding_bak)",
|
||||
"ALTER TABLE res_currency DROP rounding_bak",
|
||||
):
|
||||
cr.execute(line)
|
||||
cr.commit()
|
||||
|
||||
# ----------------------------- #
|
||||
# drop constraint on ir_ui_view #
|
||||
# ----------------------------- #
|
||||
|
||||
cr.execute('SELECT conname FROM pg_constraint where conname = \'ir_ui_view_type\'')
|
||||
if cr.fetchall():
|
||||
cr.execute('ALTER TABLE ir_ui_view DROP CONSTRAINT ir_ui_view_type')
|
||||
cr.commit()
|
||||
|
||||
# ------------------------ #
|
||||
# update res.partner.bank #
|
||||
# ------------------------ #
|
||||
|
||||
cr.execute('SELECT a.attname FROM pg_class c, pg_attribute a WHERE c.relname = \'res_partner_bank\' AND a.attname = \'iban\' AND c.oid = a.attrelid')
|
||||
if cr.fetchall():
|
||||
cr.execute('ALTER TABLE res_partner_bank RENAME iban TO acc_number')
|
||||
cr.commit()
|
||||
|
||||
# ------------------------------------------- #
|
||||
# Add perm_id to ir_model and ir_model_fields #
|
||||
# ------------------------------------------- #
|
||||
|
||||
cr.execute('SELECT a.attname FROM pg_class c, pg_attribute a WHERE c.relname = \'ir_model\' AND a.attname = \'perm_id\' AND c.oid = a.attrelid')
|
||||
if not cr.fetchall():
|
||||
cr.execute("ALTER TABLE ir_model ADD perm_id int references perm on delete set null")
|
||||
cr.commit()
|
||||
|
||||
cr.execute('SELECT a.attname FROM pg_class c, pg_attribute a WHERE c.relname = \'ir_model_fields\' AND a.attname = \'perm_id\' AND c.oid = a.attrelid')
|
||||
if not cr.fetchall():
|
||||
cr.execute("ALTER TABLE ir_model_fields ADD perm_id int references perm on delete set null")
|
||||
cr.commit()
|
||||
|
||||
|
||||
# --------------------------------- #
|
||||
# remove name for all ir_act_window #
|
||||
# --------------------------------- #
|
||||
|
||||
cr.execute("UPDATE ir_act_window SET name = ''")
|
||||
cr.commit()
|
||||
|
||||
# ------------------------------------------------------------------------ #
|
||||
# Create a "allow none" default access to keep the behaviour of the system #
|
||||
# ------------------------------------------------------------------------ #
|
||||
|
||||
cr.execute('SELECT model_id FROM ir_model_access')
|
||||
res= cr.fetchall()
|
||||
for r in res:
|
||||
cr.execute('SELECT id FROM ir_model_access WHERE model_id = %d AND group_id IS NULL', (r[0],))
|
||||
if not cr.fetchall():
|
||||
cr.execute("INSERT into ir_model_access (name,model_id,group_id) VALUES ('Auto-generated access by migration',%d,NULL)",(r[0],))
|
||||
cr.commit()
|
||||
|
||||
# ------------------------------------------------- #
|
||||
# Drop view report_account_analytic_line_to_invoice #
|
||||
# ------------------------------------------------- #
|
||||
|
||||
cr.execute('SELECT viewname FROM pg_views WHERE viewname = \'report_account_analytic_line_to_invoice\'')
|
||||
if cr.fetchall():
|
||||
cr.execute('DROP VIEW report_account_analytic_line_to_invoice')
|
||||
cr.commit()
|
||||
|
||||
# --------------------------- #
|
||||
# Drop state from hr_employee #
|
||||
# --------------------------- #
|
||||
|
||||
cr.execute('SELECT * FROM pg_class c, pg_attribute a WHERE c.relname=\'hr_employee\' AND a.attname=\'state\' AND c.oid=a.attrelid')
|
||||
if cr.fetchall():
|
||||
cr.execute('ALTER TABLE hr_employee DROP state')
|
||||
cr.commit()
|
||||
|
||||
# ------------ #
|
||||
# Add timezone #
|
||||
# ------------ #
|
||||
|
||||
cr.execute('SELECT id FROM ir_values where model=\'res.users\' AND key=\'meta\' AND name=\'tz\'')
|
||||
if not cr.fetchall():
|
||||
import pytz, pickle
|
||||
meta = pickle.dumps({'type':'selection', 'string':'Timezone', 'selection': [(x, x) for x in pytz.all_timezones]})
|
||||
value = pickle.dumps(False)
|
||||
cr.execute('INSERT INTO ir_values (name, key, model, meta, key2, object, value) VALUES (\'tz\', \'meta\', \'res.users\', %s, \'tz\', %s, %s)', (meta,False, value))
|
||||
cr.commit()
|
||||
|
||||
# ------------------------- #
|
||||
# change product_uom factor #
|
||||
# ------------------------- #
|
||||
|
||||
cr.execute('SELECT a.attname FROM pg_class c, pg_attribute a, pg_type t WHERE c.relname = \'product_uom\' AND a.attname = \'factor\' AND c.oid = a.attrelid AND a.atttypid = t.oid AND t.typname = \'float8\'')
|
||||
if cr.fetchall():
|
||||
cr.execute('SELECT viewname FROM pg_views WHERE viewname = \'report_account_analytic_planning_stat_account\'')
|
||||
if cr.fetchall():
|
||||
cr.execute('DROP VIEW report_account_analytic_planning_stat_account')
|
||||
cr.execute('SELECT viewname FROM pg_views WHERE viewname = \'report_account_analytic_planning_stat\'')
|
||||
if cr.fetchall():
|
||||
cr.execute('DROP VIEW report_account_analytic_planning_stat')
|
||||
cr.execute('SELECT viewname FROM pg_views WHERE viewname = \'report_account_analytic_planning_stat_user\'')
|
||||
if cr.fetchall():
|
||||
cr.execute('DROP VIEW report_account_analytic_planning_stat_user')
|
||||
cr.execute('SELECT viewname FROM pg_views WHERE viewname = \'report_purchase_order_product\'')
|
||||
if cr.fetchall():
|
||||
cr.execute('DROP VIEW report_purchase_order_product')
|
||||
cr.execute('SELECT viewname FROM pg_views WHERE viewname = \'report_purchase_order_category\'')
|
||||
if cr.fetchall():
|
||||
cr.execute('DROP VIEW report_purchase_order_category')
|
||||
cr.execute('SELECT viewname FROM pg_views WHERE viewname = \'report_sale_order_product\'')
|
||||
if cr.fetchall():
|
||||
cr.execute('DROP VIEW report_sale_order_product')
|
||||
cr.execute('SELECT viewname FROM pg_views WHERE viewname = \'report_sale_order_category\'')
|
||||
if cr.fetchall():
|
||||
cr.execute('DROP VIEW report_sale_order_category')
|
||||
cr.execute('SELECT viewname FROM pg_views WHERE viewname = \'report_hr_timesheet_invoice_journal\'')
|
||||
if cr.fetchall():
|
||||
cr.execute('DROP VIEW report_hr_timesheet_invoice_journal')
|
||||
|
||||
cr.execute('ALTER TABLE product_uom RENAME COLUMN factor to temp_column')
|
||||
cr.execute('ALTER TABLE product_uom ADD COLUMN factor NUMERIC(12,6)')
|
||||
cr.execute('UPDATE product_uom SET factor = temp_column')
|
||||
cr.execute('ALTER TABLE product_uom ALTER factor SET NOT NULL')
|
||||
cr.execute('ALTER TABLE product_uom DROP COLUMN temp_column')
|
||||
cr.commit()
|
||||
|
||||
|
||||
# ------------------------------------------------- #
|
||||
# Drop name_uniq constraint on stock_production_lot #
|
||||
# ------------------------------------------------- #
|
||||
|
||||
cr.execute('SELECT conname FROM pg_constraint where conname = \'stock_production_lot_name_uniq\'')
|
||||
if cr.fetchall():
|
||||
cr.execute('ALTER TABLE stock_production_lot DROP CONSTRAINT stock_production_lot_name_uniq')
|
||||
cr.commit()
|
||||
|
||||
# ------------------------------------ #
|
||||
# Put country/state code in upper case #
|
||||
# ------------------------------------ #
|
||||
|
||||
cr.execute('UPDATE res_country SET code = UPPER(code)')
|
||||
cr.execute('UPDATE res_country_state SET code = UPPER(code)')
|
||||
cr.commit()
|
||||
|
||||
# --------------------------------------------- #
|
||||
# Add primary key on tables inherits ir_actions #
|
||||
# --------------------------------------------- #
|
||||
|
||||
cr.execute('SELECT indexname FROm pg_indexes WHERE indexname = \'ir_act_report_xml_pkey\' and tablename = \'ir_act_report_xml\'')
|
||||
if not cr.fetchall():
|
||||
cr.execute('ALTER TABLE ir_act_report_xml ADD PRIMARY KEY (id)')
|
||||
cr.execute('SELECT indexname FROm pg_indexes WHERE indexname = \'ir_act_report_custom_pkey\' and tablename = \'ir_act_report_custom\'')
|
||||
if not cr.fetchall():
|
||||
cr.execute('ALTER TABLE ir_act_report_custom ADD PRIMARY KEY (id)')
|
||||
cr.execute('SELECT indexname FROm pg_indexes WHERE indexname = \'ir_act_group_pkey\' and tablename = \'ir_act_group\'')
|
||||
if not cr.fetchall():
|
||||
cr.execute('ALTER TABLE ir_act_group ADD PRIMARY KEY (id)')
|
||||
cr.execute('SELECT indexname FROm pg_indexes WHERE indexname = \'ir_act_execute_pkey\' and tablename = \'ir_act_execute\'')
|
||||
if not cr.fetchall():
|
||||
cr.execute('ALTER TABLE ir_act_execute ADD PRIMARY KEY (id)')
|
||||
cr.execute('SELECT indexname FROm pg_indexes WHERE indexname = \'ir_act_wizard_pkey\' and tablename = \'ir_act_wizard\'')
|
||||
if not cr.fetchall():
|
||||
cr.execute('ALTER TABLE ir_act_wizard ADD PRIMARY KEY (id)')
|
||||
cr.commit()
|
||||
|
||||
cr.close
|
||||
|
||||
# vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4:
|
||||
|
|
@ -1 +0,0 @@
|
|||
Those scripts are provide as example of customization of migration scripts
|
|
@ -1,188 +0,0 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
##############################################################################
|
||||
#
|
||||
# OpenERP, Open Source Management Solution
|
||||
# Copyright (C) 2004-2009 Tiny SPRL (<http://tiny.be>).
|
||||
#
|
||||
# This program is free software: you can redistribute it and/or modify
|
||||
# it under the terms of the GNU Affero General Public License as
|
||||
# published by the Free Software Foundation, either version 3 of the
|
||||
# License, or (at your option) any later version.
|
||||
#
|
||||
# This program is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU Affero General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU Affero General Public License
|
||||
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
#
|
||||
##############################################################################
|
||||
|
||||
__author__ = 'Cédric Krier, <ced@tinyerp.com>'
|
||||
__version__ = '0.1.0'
|
||||
|
||||
import psycopg
|
||||
import optparse
|
||||
import ConfigParser
|
||||
|
||||
# -----
|
||||
|
||||
parser = optparse.OptionParser(version="Tiny ERP server migration script " + __version__)
|
||||
|
||||
parser.add_option("-c", "--config", dest="config", help="specify path to Tiny ERP config file")
|
||||
|
||||
group = optparse.OptionGroup(parser, "Database related options")
|
||||
group.add_option("--db_host", dest="db_host", help="specify the database host")
|
||||
group.add_option("--db_port", dest="db_port", help="specify the database port")
|
||||
group.add_option("-d", "--database", dest="db_name", help="specify the database name")
|
||||
group.add_option("-r", "--db_user", dest="db_user", help="specify the database user name")
|
||||
group.add_option("-w", "--db_password", dest="db_password", help="specify the database password")
|
||||
parser.add_option_group(group)
|
||||
|
||||
options = optparse.Values()
|
||||
options.db_name = 'terp' # default value
|
||||
parser.parse_args(values=options)
|
||||
|
||||
if hasattr(options, 'config'):
|
||||
configparser = ConfigParser.ConfigParser()
|
||||
configparser.read([options.config])
|
||||
for name, value in configparser.items('options'):
|
||||
if not (hasattr(options, name) and getattr(options, name)):
|
||||
if value in ('true', 'True'):
|
||||
value = True
|
||||
if value in ('false', 'False'):
|
||||
value = False
|
||||
setattr(options, name, value)
|
||||
|
||||
raise Exception('This script is provided as an example, you must custom it before')
|
||||
|
||||
# -----
|
||||
|
||||
host = hasattr(options, 'db_host') and "host=%s" % options.db_host or ''
|
||||
port = hasattr(options, 'db_port') and "port=%s" % options.db_port or ''
|
||||
name = "dbname=%s" % options.db_name
|
||||
user = hasattr(options, 'db_user') and "user=%s" % options.db_user or ''
|
||||
password = hasattr(options, 'db_password') and "password=%s" % options.db_password or ''
|
||||
|
||||
db = psycopg.connect('%s %s %s %s %s' % (host, port, name, user, password), serialize=0)
|
||||
cr = db.cursor()
|
||||
|
||||
# fix country
|
||||
|
||||
|
||||
cr.execute('SELECT code from res_country where code is not null group by code')
|
||||
res = cr.fetchall()
|
||||
|
||||
for c in res:
|
||||
cr.execute('SELECT max(id) from res_country where code = %s group by code', (c[0],))
|
||||
res2 = cr.fetchone()
|
||||
cr.execute('SELECT id from res_country where code = %s', (c[0],))
|
||||
ids = ','.join(map(lambda x: str(x[0]), cr.fetchall()))
|
||||
cr.execute('UPDATE res_partner_address set country_id = %d where country_id in ('+ids+')', (res2[0],))
|
||||
cr.execute('DELETE FROM res_country WHERE code = %s and id <> %d', (c[0], res2[0],))
|
||||
cr.commit()
|
||||
|
||||
|
||||
cr.execute('SELECT viewname FROM pg_views WHERE viewname = \'report_account_analytic_planning_stat\'')
|
||||
if cr.fetchall():
|
||||
cr.execute('DROP VIEW report_account_analytic_planning_stat')
|
||||
cr.commit()
|
||||
|
||||
|
||||
cr.execute('SELECT name from ( SELECT name, count(name) AS n FROM res_partner GROUP BY name ) AS foo WHERE n > 1')
|
||||
res = cr.fetchall()
|
||||
|
||||
|
||||
for p in res:
|
||||
cr.execute('SELECT max(id) FROM res_partner WHERE name = %s GROUP BY name', (p[0],))
|
||||
res2 = cr.fetchone()
|
||||
cr.execute('UPDATE res_partner set active = False WHERE name = %s and id <> %d', (p[0], res2[0],))
|
||||
cr.execute('SELECT id FROM res_partner WHERE name = %s AND id <> %d', (p[0], res2[0],))
|
||||
res3 = cr.fetchall()
|
||||
i = 0
|
||||
for id in res3:
|
||||
name = p[0]+' old'
|
||||
if i:
|
||||
name = name + ' ' + str(i)
|
||||
cr.execute('UPDATE res_partner set name = %s WHERE id = %d', (name, id[0]))
|
||||
i += 1
|
||||
cr.commit()
|
||||
|
||||
cr.execute('SELECT viewname FROM pg_views WHERE viewname = \'report_account_analytic_line_to_invoice\'')
|
||||
if cr.fetchall():
|
||||
cr.execute('DROP VIEW report_account_analytic_line_to_invoice')
|
||||
cr.execute('SELECT viewname FROM pg_views WHERE viewname = \'report_timesheet_invoice\'')
|
||||
if cr.fetchall():
|
||||
cr.execute('drop VIEW report_timesheet_invoice')
|
||||
cr.execute('SELECT viewname FROM pg_views WHERE viewname = \'report_purchase_order_category\'')
|
||||
if cr.fetchall():
|
||||
cr.execute('drop VIEW report_purchase_order_category')
|
||||
cr.execute('SELECT viewname FROM pg_views WHERE viewname = \'report_purchase_order_product\'')
|
||||
if cr.fetchall():
|
||||
cr.execute('drop VIEW report_purchase_order_product')
|
||||
cr.execute('SELECT viewname FROM pg_views WHERE viewname = \'report_sale_order_category\'')
|
||||
if cr.fetchall():
|
||||
cr.execute('drop VIEW report_sale_order_category')
|
||||
cr.execute('SELECT viewname FROM pg_views WHERE viewname = \'report_sale_order_product\'')
|
||||
if cr.fetchall():
|
||||
cr.execute('drop VIEW report_sale_order_product')
|
||||
cr.execute('SELECT viewname FROM pg_views WHERE viewname = \'report_timesheet_user\'')
|
||||
if cr.fetchall():
|
||||
cr.execute('drop VIEW report_timesheet_user')
|
||||
cr.execute('SELECT viewname FROM pg_views WHERE viewname = \'report_task_user_pipeline_open\'')
|
||||
if cr.fetchall():
|
||||
cr.execute('drop VIEW report_task_user_pipeline_open')
|
||||
cr.execute('SELECT viewname FROM pg_views WHERE viewname = \'hr_timesheet_sheet_sheet_day\'')
|
||||
if cr.fetchall():
|
||||
cr.execute('drop VIEW hr_timesheet_sheet_sheet_day')
|
||||
cr.execute('SELECT viewname FROM pg_views WHERE viewname = \'hr_timesheet_sheet_sheet_account\'')
|
||||
if cr.fetchall():
|
||||
cr.execute('drop VIEW hr_timesheet_sheet_sheet_account')
|
||||
cr.execute('SELECT viewname from pg_views where viewname = \'sale_journal_sale_stats\'')
|
||||
if cr.fetchall():
|
||||
cr.execute('drop VIEW sale_journal_sale_stats')
|
||||
cr.execute('SELECT viewname from pg_views where viewname = \'sale_journal_picking_stats\'')
|
||||
if cr.fetchall():
|
||||
cr.execute('drop VIEW sale_journal_picking_stats')
|
||||
cr.execute('SELECT viewname from pg_views where viewname = \'sale_journal_invoice_type_stats\'')
|
||||
if cr.fetchall():
|
||||
cr.execute('drop VIEW sale_journal_invoice_type_stats')
|
||||
|
||||
cr.execute('ALTER TABLE product_template ALTER list_price TYPE numeric(16,2)')
|
||||
cr.execute('ALTER TABLE product_template ALTER standard_price TYPE numeric(16,2)')
|
||||
cr.execute('ALTER TABLE product_product ALTER price_extra TYPE numeric(16,2)')
|
||||
cr.execute('ALTER TABLE product_product ALTER price_margin TYPE numeric(16,2)')
|
||||
cr.execute('ALTER TABLE pricelist_partnerinfo ALTER price TYPE numeric(16,2)')
|
||||
cr.execute('ALTER TABLE account_invoice_line ALTER price_unit TYPE numeric(16,2)')
|
||||
cr.execute('ALTER TABLE purchase_order_line ALTER price_unit TYPE numeric(16,2)')
|
||||
cr.execute('ALTER TABLE sale_order_line ALTER price_unit TYPE numeric(16,2)')
|
||||
cr.commit()
|
||||
|
||||
|
||||
cr.execute('SELECT tablename FROM pg_tables WHERE tablename = \'subscription_document_fields\'')
|
||||
if cr.fetchall():
|
||||
cr.execute('DROP TABLE subscription_document_fields')
|
||||
cr.execute('SELECT tablename FROM pg_tables WHERE tablename = \'subscription_document\'')
|
||||
if cr.fetchall():
|
||||
cr.execute('DROP TABLE subscription_document')
|
||||
cr.execute('SELECT tablename FROM pg_tables WHERE tablename = \'subscription_subscription_history\'')
|
||||
if cr.fetchall():
|
||||
cr.execute('DROP TABLE subscription_subscription_history')
|
||||
cr.commit()
|
||||
|
||||
# -------------------- #
|
||||
# Change currency rate #
|
||||
# -------------------- #
|
||||
|
||||
cr.execute('SELECT a.attname FROM pg_class c, pg_attribute a WHERE c.relname = \'res_currency_rate\' AND a.attname = \'rate_old\' AND c.oid = a.attrelid')
|
||||
if not cr.fetchall():
|
||||
cr.execute('ALTER TABLE res_currency_rate ADD rate_old NUMERIC(12,6)')
|
||||
cr.execute('UPDATE res_currency_rate SET rate_old = rate')
|
||||
cr.execute('UPDATE res_currency_rate SET rate = (1 / rate_old)')
|
||||
cr.commit()
|
||||
|
||||
cr.close
|
||||
|
||||
# vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4:
|
||||
|
|
@ -1,110 +0,0 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
##############################################################################
|
||||
#
|
||||
# OpenERP, Open Source Management Solution
|
||||
# Copyright (C) 2004-2009 Tiny SPRL (<http://tiny.be>).
|
||||
#
|
||||
# This program is free software: you can redistribute it and/or modify
|
||||
# it under the terms of the GNU Affero General Public License as
|
||||
# published by the Free Software Foundation, either version 3 of the
|
||||
# License, or (at your option) any later version.
|
||||
#
|
||||
# This program is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU Affero General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU Affero General Public License
|
||||
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
#
|
||||
##############################################################################
|
||||
|
||||
__version__ = '0.1.0'
|
||||
|
||||
import psycopg
|
||||
import optparse
|
||||
import ConfigParser
|
||||
|
||||
# -----
|
||||
|
||||
parser = optparse.OptionParser(version="Tiny ERP server migration script " + __version__)
|
||||
|
||||
parser.add_option("-c", "--config", dest="config", help="specify path to Tiny ERP config file")
|
||||
|
||||
group = optparse.OptionGroup(parser, "Database related options")
|
||||
group.add_option("--db_host", dest="db_host", help="specify the database host")
|
||||
group.add_option("--db_port", dest="db_port", help="specify the database port")
|
||||
group.add_option("-d", "--database", dest="db_name", help="specify the database name")
|
||||
group.add_option("-r", "--db_user", dest="db_user", help="specify the database user name")
|
||||
group.add_option("-w", "--db_password", dest="db_password", help="specify the database password")
|
||||
parser.add_option_group(group)
|
||||
|
||||
options = optparse.Values()
|
||||
options.db_name = 'terp' # default value
|
||||
parser.parse_args(values=options)
|
||||
|
||||
if hasattr(options, 'config'):
|
||||
configparser = ConfigParser.ConfigParser()
|
||||
configparser.read([options.config])
|
||||
for name, value in configparser.items('options'):
|
||||
if not (hasattr(options, name) and getattr(options, name)):
|
||||
if value in ('true', 'True'):
|
||||
value = True
|
||||
if value in ('false', 'False'):
|
||||
value = False
|
||||
setattr(options, name, value)
|
||||
|
||||
# -----
|
||||
|
||||
host = hasattr(options, 'db_host') and "host=%s" % options.db_host or ''
|
||||
port = hasattr(options, 'db_port') and "port=%s" % options.db_port or ''
|
||||
name = "dbname=%s" % options.db_name
|
||||
user = hasattr(options, 'db_user') and "user=%s" % options.db_user or ''
|
||||
password = hasattr(options, 'db_password') and "password=%s" % options.db_password or ''
|
||||
|
||||
db = psycopg.connect('%s %s %s %s %s' % (host, port, name, user, password), serialize=0)
|
||||
cr = db.cursor()
|
||||
|
||||
# ------------------------------ #
|
||||
# drop not null on ir_attachment #
|
||||
# ------------------------------ #
|
||||
|
||||
cr.execute('ALTER TABLE ir_attachment \
|
||||
ALTER COLUMN res_model DROP NOT NULL, \
|
||||
ALTER COLUMN res_id DROP NOT NULL')
|
||||
cr.commit()
|
||||
|
||||
# ---------------------------------- #
|
||||
# change case date_deadline rounding #
|
||||
# ---------------------------------- #
|
||||
|
||||
cr.execute("""SELECT
|
||||
c.relname,a.attname,a.attlen,a.atttypmod,a.attnotnull,a.atthasdef,t.typname,CASE
|
||||
WHEN a.attlen=-1 THEN a.atttypmod-4 ELSE a.attlen END as size FROM pg_class
|
||||
c,pg_attribute a,pg_type t WHERE c.relname='crm_case' AND
|
||||
a.attname='date_deadline' AND c.oid=a.attrelid AND a.atttypid=t.oid""")
|
||||
|
||||
res = cr.dictfetchall()
|
||||
if res[0]['typname'] != 'timestamp':
|
||||
for line in (
|
||||
"ALTER TABLE crm_case RENAME date_deadline TO date_deadline_bak",
|
||||
"ALTER TABLE crm_case ADD date_deadline timestamp",
|
||||
"UPDATE crm_case SET date_deadline = date_deadline_bak",
|
||||
"ALTER TABLE crm_case DROP date_deadline_bak",
|
||||
):
|
||||
cr.execute(line)
|
||||
cr.commit()
|
||||
|
||||
cr.execute('drop view report_task_user_pipeline_open');
|
||||
cr.commit()
|
||||
|
||||
cr.execute('alter table ir_model_fields add state varchar(26)')
|
||||
cr.execute('alter table ir_model_fields add select_level varchar(3)')
|
||||
cr.execute('alter table ir_act_wizard add primary key(id)')
|
||||
cr.commit()
|
||||
|
||||
|
||||
cr.close()
|
||||
|
||||
# vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4:
|
||||
|
|
@ -1,87 +0,0 @@
|
|||
#!/usr/bin/env python
|
||||
# -*- coding: utf-8 -*-
|
||||
##############################################################################
|
||||
#
|
||||
# OpenERP, Open Source Management Solution
|
||||
# Copyright (C) 2004-2009 Tiny SPRL (<http://tiny.be>).
|
||||
#
|
||||
# This program is free software: you can redistribute it and/or modify
|
||||
# it under the terms of the GNU Affero General Public License as
|
||||
# published by the Free Software Foundation, either version 3 of the
|
||||
# License, or (at your option) any later version.
|
||||
#
|
||||
# This program is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU Affero General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU Affero General Public License
|
||||
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
#
|
||||
##############################################################################
|
||||
|
||||
# TODO handle the case of zip modules
|
||||
|
||||
import os
|
||||
import optparse
|
||||
import sys
|
||||
import glob
|
||||
|
||||
# TODO use the same function provided in openerp.modules
|
||||
def load_information_from_description_file(module):
|
||||
"""
|
||||
:param module: The name of the module (sale, purchase, ...)
|
||||
"""
|
||||
for filename in ['__openerp__.py', '__terp__.py']:
|
||||
description_file = os.path.join(module, filename)
|
||||
if os.path.isfile(description_file):
|
||||
return eval(file(description_file).read())
|
||||
|
||||
return {}
|
||||
|
||||
def get_valid_path(paths, module):
|
||||
for path in paths:
|
||||
full = os.path.join(path, module)
|
||||
if os.path.exists(full):
|
||||
return full
|
||||
return None
|
||||
|
||||
parser = optparse.OptionParser(usage="%prog [options] [module1 [module2 ...]]")
|
||||
parser.add_option("-p", "--addons-path", dest="path", help="addons directory", action="append")
|
||||
(opt, args) = parser.parse_args()
|
||||
|
||||
modules = []
|
||||
if not opt.path:
|
||||
opt.path = ["."]
|
||||
|
||||
if not args:
|
||||
for path in opt.path:
|
||||
modules += map(os.path.dirname, glob.glob(os.path.join(path, '*', '__openerp__.py')))
|
||||
modules += map(os.path.dirname, glob.glob(os.path.join(path, '*', '__terp__.py')))
|
||||
else:
|
||||
for module in args:
|
||||
valid_path = get_valid_path(opt.path, module)
|
||||
if valid_path:
|
||||
modules.append(valid_path)
|
||||
|
||||
all_modules = set(map(os.path.basename, modules))
|
||||
print 'digraph G {'
|
||||
while len(modules):
|
||||
f = modules.pop(0)
|
||||
module_name = os.path.basename(f)
|
||||
all_modules.add(module_name)
|
||||
info = load_information_from_description_file(f)
|
||||
if info.get('installable', True):
|
||||
for name in info.get('depends',[]):
|
||||
valid_path = get_valid_path(opt.path, name)
|
||||
if name not in all_modules:
|
||||
if valid_path:
|
||||
modules.append(valid_path)
|
||||
else:
|
||||
all_modules.add(name)
|
||||
print '\t%s [color=red]' % (name,)
|
||||
print '\t%s -> %s;' % (module_name, name)
|
||||
print '}'
|
||||
|
||||
# vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4:
|
||||
|
|
@ -1,347 +0,0 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
##############################################################################
|
||||
#
|
||||
# OpenERP, Open Source Management Solution
|
||||
# Copyright (C) 2004-2010 OpenERP SA (<http://openerp.com>).
|
||||
#
|
||||
# This program is free software: you can redistribute it and/or modify
|
||||
# it under the terms of the GNU Affero General Public License as
|
||||
# published by the Free Software Foundation, either version 3 of the
|
||||
# License, or (at your option) any later version.
|
||||
#
|
||||
# This program is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU Affero General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU Affero General Public License
|
||||
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
#
|
||||
##############################################################################
|
||||
|
||||
"""
|
||||
Experimental script for conversion between OpenERP's XML serialization format
|
||||
and the new YAML serialization format introduced in OpenERP 6.0.
|
||||
Intended to be used as a quick preprocessor for converting data/test files, then
|
||||
to be fine-tuned manually.
|
||||
"""
|
||||
|
||||
import yaml
|
||||
import logging
|
||||
from lxml import etree
|
||||
|
||||
__VERSION__ = '0.0.2'
|
||||
|
||||
def toString(value):
|
||||
value='"' + value + '"'
|
||||
return value
|
||||
|
||||
class XmlTag(etree.ElementBase):
|
||||
def _to_yaml(self):
|
||||
child_tags = []
|
||||
for node in self:
|
||||
if hasattr(node, '_to_yaml'):
|
||||
child_tags.append(node._to_yaml())
|
||||
return self.tag(attrib=self.attrib, child_tags=child_tags)
|
||||
|
||||
class YamlTag(object):
|
||||
"""
|
||||
Superclass for constructors of custom tags defined in yaml file.
|
||||
"""
|
||||
def __init__(self, **kwargs):
|
||||
self.__dict__.update(kwargs)
|
||||
self.attrib = self.__dict__.get('attrib', {})
|
||||
self.child_tags = self.__dict__.get('child_tags', '')
|
||||
def __getitem__(self, key):
|
||||
return getattr(self, key)
|
||||
def __getattr__(self, attr):
|
||||
return None
|
||||
def __repr__(self):
|
||||
for k,v in self.attrib.iteritems():
|
||||
if str(v) and str(v)[0] in ['[', '{', '#', '*', '(']:
|
||||
self.attrib[k] = toString(self.attrib[k]).replace("'", '')
|
||||
st = self.yaml_tag + ' ' + str(self.attrib)
|
||||
return st
|
||||
|
||||
# attrib tags
|
||||
class ref(YamlTag):
|
||||
yaml_tag = u'!ref'
|
||||
def __init__(self, expr="False"):
|
||||
self.expr = expr
|
||||
def __repr__(self):
|
||||
return "'%s'"%str(self.expr)
|
||||
|
||||
class Eval(YamlTag):
|
||||
yaml_tag = u'!eval'
|
||||
def __init__(self, expr="False"):
|
||||
self.expr = expr
|
||||
def __repr__(self):
|
||||
value=str(self.expr)
|
||||
if value.find("6,") != -1:
|
||||
value = eval(str(eval(value)))
|
||||
value=value[0][2]
|
||||
value = [[value]]
|
||||
else:
|
||||
try:
|
||||
value=int(value)
|
||||
except:
|
||||
if value and value[0] in ['[', '{', '#', '*', '(']:
|
||||
value = value.replace('"', r'\"')
|
||||
value = toString(value)
|
||||
else:
|
||||
try:
|
||||
value = eval(value)
|
||||
except Exception:
|
||||
pass
|
||||
return value
|
||||
|
||||
class Search(YamlTag):
|
||||
yaml_tag = u'!ref'
|
||||
|
||||
# test tag
|
||||
class xml_test(XmlTag):
|
||||
def _to_yaml(self):
|
||||
expr = self.attrib.get('expr')
|
||||
text = self.text
|
||||
if text:
|
||||
expr = expr + ' == ' + '"%s"'%text
|
||||
return [[expr]]
|
||||
|
||||
class xml_data(etree.ElementBase):
|
||||
def _to_yaml(self):
|
||||
value = self.attrib.get('noupdate', "0")
|
||||
return data(value)
|
||||
|
||||
# field tag:
|
||||
class xml_field(etree.ElementBase):
|
||||
def _to_yaml(self):
|
||||
field = ' ' + self.attrib.pop('name','unknown')
|
||||
|
||||
if self.attrib.get('search', None):
|
||||
value = Search(attrib=self.attrib, child_tags='').__repr__()
|
||||
else:
|
||||
attr = (self.attrib.get('ref', None) and 'ref') or (self.attrib.get('eval', None) and 'eval') or 'None'
|
||||
value = Eval(self.attrib.get(attr, self.text)).__repr__() or ''
|
||||
return {field: value}
|
||||
|
||||
# value tag
|
||||
class xml_value(etree.ElementBase):
|
||||
def _to_yaml(self):
|
||||
|
||||
if self.attrib.get('eval', None):
|
||||
key, val = 'eval', '"'+self.attrib.get('eval')+'"'
|
||||
elif self.attrib.get('model', None):
|
||||
key, val = 'model', self.attrib.get('model')
|
||||
val=val.replace("'",'""')
|
||||
self.attrib.pop(key)
|
||||
d={}
|
||||
for k,v in self.attrib.iteritems():
|
||||
if k == 'search':
|
||||
v = '"' + v + '"'
|
||||
k='--' + k
|
||||
v=v.replace("'",'""')
|
||||
d[k] = v
|
||||
if d:
|
||||
ls=[[{key:val},dict(d)]]
|
||||
else:
|
||||
ls=[[{key:val}]]
|
||||
return ls
|
||||
|
||||
# data tag
|
||||
class data(YamlTag):
|
||||
yaml_tag = u'!context'
|
||||
def __init__(self, noupdate="0"):
|
||||
self.child_tags = {' noupdate':noupdate}
|
||||
def __repr__(self):
|
||||
return "!!context"
|
||||
|
||||
# Record tag
|
||||
class Record(YamlTag):
|
||||
yaml_tag = u'!record'
|
||||
class xml_record(XmlTag):
|
||||
tag=Record
|
||||
def _to_yaml(self):
|
||||
child_tags = {}
|
||||
for node in self:
|
||||
if hasattr(node, '_to_yaml'):
|
||||
child_tags.update(node._to_yaml())
|
||||
return Record(attrib=self.attrib, child_tags=child_tags)
|
||||
|
||||
# ir_set tag
|
||||
class Ir_Set(YamlTag):
|
||||
yaml_tag = u'!ir_set'
|
||||
def __repr__(self):
|
||||
st = self.yaml_tag
|
||||
return st
|
||||
class xml_ir_set(XmlTag):
|
||||
tag=Ir_Set
|
||||
def _to_yaml(self):
|
||||
child_tags = {}
|
||||
for node in self:
|
||||
if hasattr(node, '_to_yaml'):
|
||||
child_tags.update(node._to_yaml())
|
||||
return Ir_Set(attrib=self.attrib, child_tags=child_tags)
|
||||
|
||||
# workflow tag
|
||||
class Workflow(YamlTag):
|
||||
yaml_tag = u'!workflow'
|
||||
class xml_workflow(XmlTag):
|
||||
tag=Workflow
|
||||
|
||||
# function tag
|
||||
class Function(YamlTag):
|
||||
yaml_tag = u'!function'
|
||||
class xml_function(XmlTag):
|
||||
tag=Function
|
||||
|
||||
# function tag
|
||||
class Assert(YamlTag):
|
||||
yaml_tag = u'!assert'
|
||||
class xml_assert(XmlTag):
|
||||
tag=Assert
|
||||
|
||||
# menuitem tagresult.append(yaml.safe_dump(obj, default_flow_style=False, allow_unicode=True).replace("'",''))
|
||||
class MenuItem(YamlTag):
|
||||
yaml_tag = u'!menuitem'
|
||||
class xml_menuitem(XmlTag):
|
||||
tag=MenuItem
|
||||
|
||||
# act_window tag
|
||||
class ActWindow(YamlTag):
|
||||
yaml_tag = u'!act_window'
|
||||
class xml_act_window(XmlTag):
|
||||
tag=ActWindow
|
||||
|
||||
# report tag
|
||||
class Report(YamlTag):
|
||||
yaml_tag = u'!report'
|
||||
class xml_report(XmlTag):
|
||||
tag=Report
|
||||
|
||||
# deletes tag
|
||||
class Delete(YamlTag):
|
||||
yaml_tag = u'!delete'
|
||||
class xml_delete(XmlTag):
|
||||
tag=Delete
|
||||
|
||||
# python tag
|
||||
class Python(YamlTag):
|
||||
yaml_tag = u'!python'
|
||||
class xml_python(XmlTag):
|
||||
tag=Python
|
||||
|
||||
# context tag
|
||||
class Context(YamlTag):
|
||||
yaml_tag = u'!context'
|
||||
class xml_context(XmlTag):
|
||||
tag=Context
|
||||
|
||||
# url tag
|
||||
class Url(YamlTag):
|
||||
yaml_tag = u'!url'
|
||||
class xml_url(XmlTag):
|
||||
tag=Url
|
||||
|
||||
# delete tag
|
||||
class Delete(YamlTag):
|
||||
yaml_tag = u'!delete'
|
||||
class xml_delete(XmlTag):
|
||||
tag=Delete
|
||||
|
||||
def represent_data(dumper, data):
|
||||
return dumper.represent_mapping(u'tag:yaml.org,2002:map', [('!'+str(data), data.child_tags)])
|
||||
|
||||
yaml.SafeDumper.add_representer(Record, represent_data)
|
||||
yaml.SafeDumper.add_representer(data, represent_data)
|
||||
yaml.SafeDumper.add_representer(Workflow, represent_data)
|
||||
yaml.SafeDumper.add_representer(Function, represent_data)
|
||||
yaml.SafeDumper.add_representer(Assert, represent_data)
|
||||
yaml.SafeDumper.add_representer(MenuItem, represent_data)
|
||||
yaml.SafeDumper.add_representer(Ir_Set, represent_data)
|
||||
yaml.SafeDumper.add_representer(Python, represent_data)
|
||||
yaml.SafeDumper.add_representer(Context, represent_data)
|
||||
|
||||
class MyLookup(etree.CustomElementClassLookup):
|
||||
def lookup(self, node_type, document, namespace, name):
|
||||
if node_type=='element':
|
||||
return {
|
||||
'data': xml_data,
|
||||
'record': xml_record,
|
||||
'field': xml_field,
|
||||
'workflow': xml_workflow,
|
||||
'function': xml_function,
|
||||
'value': xml_value,
|
||||
'assert': xml_assert,
|
||||
'test': xml_test,
|
||||
'menuitem': xml_menuitem,
|
||||
'act_window': xml_act_window,
|
||||
'report': xml_report,
|
||||
'delete': xml_delete,
|
||||
'python': xml_python,
|
||||
'context': xml_context,
|
||||
'url': xml_url,
|
||||
'ir_set': xml_ir_set,
|
||||
}.get(name, None)
|
||||
elif node_type=='comment':
|
||||
return None#xml_comment
|
||||
return None
|
||||
|
||||
class xml_parse(object):
|
||||
def __init__(self):
|
||||
self.context = {}
|
||||
def parse(self, fname):
|
||||
parser = etree.XMLParser()
|
||||
parser.setElementClassLookup(MyLookup())
|
||||
result = []
|
||||
self.root = etree.XML(file(fname).read(), parser)
|
||||
for data in self.root:
|
||||
if hasattr(data, '_to_yaml'):
|
||||
obj = data._to_yaml()
|
||||
if obj.yaml_tag == '!context':
|
||||
result.append(yaml.dump(str(obj)).replace("'",'').split('\n')[0])
|
||||
result.append(yaml.dump(obj.child_tags, default_flow_style=False).replace("'",''))
|
||||
else:
|
||||
result.append(yaml.safe_dump(obj, default_flow_style=False, allow_unicode=True).replace("'",''))
|
||||
self.context.update(data.attrib)
|
||||
for tag in data:
|
||||
if tag.tag == etree.Comment:
|
||||
result.append(tag)
|
||||
else:
|
||||
if hasattr(tag, '_to_yaml'):
|
||||
obj = tag._to_yaml()
|
||||
if not obj.child_tags:
|
||||
result.append(yaml.dump('!'+str(obj), default_flow_style=False, allow_unicode=True, width=999).replace("'",''))
|
||||
else:
|
||||
result.append(yaml.safe_dump(obj, default_flow_style=False, allow_unicode=True, width=999).replace('\n:', ':\n').replace("'",''))
|
||||
print "# Experimental OpenERP xml-to-yml conversion! (v%s)"%__VERSION__
|
||||
print "# Please use this as a first conversion/preprocessing step,"
|
||||
print "# not as a production-ready tool!"
|
||||
for record in result:
|
||||
if type(record) != type(''):
|
||||
record=str(record)
|
||||
l= record.split("\n")
|
||||
for line in l:
|
||||
print '#' + str(line)
|
||||
continue
|
||||
record=str(record)
|
||||
record = record.replace('- --',' ') #for value tag
|
||||
record = record.replace('!!', '- \n !') #for all parent tags
|
||||
record = record.replace('- - -', ' -') #for many2many fields
|
||||
record = record.replace('? ', '') #for long expressions
|
||||
record = record.replace('""', "'") #for string-value under value tag
|
||||
print record
|
||||
|
||||
if __name__=='__main__':
|
||||
import optparse
|
||||
import sys
|
||||
parser = optparse.OptionParser(
|
||||
usage = '%s file.xml' % sys.argv[0])
|
||||
(opt, args) = parser.parse_args()
|
||||
if len(args) != 1:
|
||||
parser.error("incorrect number of arguments")
|
||||
fname = sys.argv[1]
|
||||
p = xml_parse()
|
||||
p.parse(fname)
|
||||
|
||||
|
||||
# vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4:
|
|
@ -1,89 +0,0 @@
|
|||
# X.509 Certificate options
|
||||
#
|
||||
# DN options
|
||||
|
||||
# The organization of the subject.
|
||||
organization = "Some organization."
|
||||
|
||||
# The organizational unit of the subject.
|
||||
unit = "ERP dept."
|
||||
|
||||
# The locality of the subject.
|
||||
# locality =
|
||||
|
||||
# The state of the certificate owner.
|
||||
state = "State"
|
||||
|
||||
# The country of the subject. Two letter code.
|
||||
country = BE
|
||||
|
||||
# The common name of the certificate owner.
|
||||
cn = "Some company"
|
||||
|
||||
# A user id of the certificate owner.
|
||||
#uid = "clauper"
|
||||
|
||||
# If the supported DN OIDs are not adequate you can set
|
||||
# any OID here.
|
||||
# For example set the X.520 Title and the X.520 Pseudonym
|
||||
# by using OID and string pairs.
|
||||
#dn_oid = "2.5.4.12" "Dr." "2.5.4.65" "jackal"
|
||||
|
||||
# This is deprecated and should not be used in new
|
||||
# certificates.
|
||||
# pkcs9_email = "none@none.org"
|
||||
|
||||
# The serial number of the certificate
|
||||
serial = 001
|
||||
|
||||
# In how many days, counting from today, this certificate will expire.
|
||||
expiration_days = 700
|
||||
|
||||
# X.509 v3 extensions
|
||||
|
||||
# A dnsname in case of a WWW server.
|
||||
#dns_name = "www.none.org"
|
||||
#dns_name = "www.morethanone.org"
|
||||
|
||||
# An IP address in case of a server.
|
||||
#ip_address = "192.168.1.1"
|
||||
|
||||
# An email in case of a person
|
||||
email = "none@none.org"
|
||||
|
||||
# An URL that has CRLs (certificate revocation lists)
|
||||
# available. Needed in CA certificates.
|
||||
#crl_dist_points = "http://www.getcrl.crl/getcrl/"
|
||||
|
||||
# Whether this is a CA certificate or not
|
||||
#ca
|
||||
|
||||
# Whether this certificate will be used for a TLS client
|
||||
#tls_www_client
|
||||
|
||||
# Whether this certificate will be used for a TLS server
|
||||
tls_www_server
|
||||
|
||||
# Whether this certificate will be used to sign data (needed
|
||||
# in TLS DHE ciphersuites).
|
||||
#signing_key
|
||||
|
||||
# Whether this certificate will be used to encrypt data (needed
|
||||
# in TLS RSA ciphersuites). Note that it is prefered to use different
|
||||
# keys for encryption and signing.
|
||||
encryption_key
|
||||
|
||||
# Whether this key will be used to sign other certificates.
|
||||
#cert_signing_key
|
||||
|
||||
# Whether this key will be used to sign CRLs.
|
||||
#crl_signing_key
|
||||
|
||||
# Whether this key will be used to sign code.
|
||||
#code_signing_key
|
||||
|
||||
# Whether this key will be used to sign OCSP data.
|
||||
#ocsp_signing_key
|
||||
|
||||
# Whether this key will be used for time stamping.
|
||||
#time_stamping_key
|
|
@ -0,0 +1,158 @@
|
|||
#!/usr/bin/env python2
|
||||
#----------------------------------------------------------
|
||||
# odoo cli
|
||||
#
|
||||
# To install your odoo development environement type:
|
||||
#
|
||||
# wget -O- https://raw.githubusercontent.com/odoo/odoo/master/odoo.py | python
|
||||
#
|
||||
# The setup_* subcommands used to boostrap odoo are defined here inline and may
|
||||
# only depends on the python 2.7 stdlib
|
||||
#
|
||||
# The rest of subcommands are defined in odoo/cli or in <module>/cli by
|
||||
# subclassing the Command object
|
||||
#
|
||||
# https://raw.githubusercontent.com/odoo-dev/odoo/master-odoo-cmd-fme/odoo.py
|
||||
#
|
||||
#----------------------------------------------------------
|
||||
import os
|
||||
import re
|
||||
import sys
|
||||
import subprocess
|
||||
|
||||
GIT_HOOKS_PRE_PUSH = """
|
||||
#!/usr/bin/env python2
|
||||
import re
|
||||
import sys
|
||||
if re.search('github.com[:/]odoo/odoo.git$', sys.argv[2]):
|
||||
print "Pushing to /odoo/odoo.git is forbidden, please push to odoo-dev, use -f to override"
|
||||
sys.exit(1)
|
||||
"""
|
||||
|
||||
def printf(f,*l):
|
||||
print "odoo:" + f % l
|
||||
|
||||
def run(*l):
|
||||
if isinstance(l[0], list):
|
||||
l = l[0]
|
||||
printf("running %s", " ".join(l))
|
||||
subprocess.check_call(l)
|
||||
|
||||
def git_locate():
|
||||
# Locate git dir
|
||||
# TODO add support for os.environ.get('GIT_DIR')
|
||||
|
||||
# check for an odoo child
|
||||
if os.path.isfile('odoo/.git/config'):
|
||||
os.chdir('odoo')
|
||||
|
||||
path = os.getcwd()
|
||||
while path != '/':
|
||||
gitconfig_path = os.path.join(path, '.git/config')
|
||||
if os.path.isfile(gitconfig_path):
|
||||
content = open(gitconfig_path).read()
|
||||
if re.search('github.com[:/]odoo/odoo.git', content):
|
||||
break
|
||||
path = os.path.dirname(path)
|
||||
if path == '/':
|
||||
path = None
|
||||
return path
|
||||
|
||||
def cmd_setup_git_init():
|
||||
git_dir = git_locate()
|
||||
if git_dir:
|
||||
printf('git repo found at %s',git_dir)
|
||||
else:
|
||||
run("git", "init", "odoo")
|
||||
git_dir = os.path.join(os.getcwd(), 'odoo')
|
||||
if git_dir:
|
||||
# sane push config for git < 2.0
|
||||
run('git','config','push.default','simple')
|
||||
# merge bzr style
|
||||
run('git','config','merge.ff','no')
|
||||
run('git','config','merge.commit','no')
|
||||
# push hooks
|
||||
pre_push_path = os.path.join(git_dir, '.git/hooks/pre-push')
|
||||
open(pre_push_path,'w').write(GIT_HOOKS_PRE_PUSH.strip())
|
||||
os.chmod(pre_push_path, 0755)
|
||||
# setup odoo remote
|
||||
run('git','config','remote.odoo.url','https://github.com/odoo/odoo.git')
|
||||
run('git','config','remote.odoo.pushurl','git@github.com:odoo/odoo.git')
|
||||
run('git','config','--add','remote.odoo.fetch','dummy')
|
||||
run('git','config','--unset-all','remote.odoo.fetch')
|
||||
run('git','config','--add','remote.odoo.fetch','+refs/heads/*:refs/remotes/odoo/heads/*')
|
||||
# setup odoo-dev remote
|
||||
run('git','config','remote.odoo-dev.url','https://github.com/odoo-dev/odoo.git')
|
||||
run('git','config','remote.odoo-dev.pushurl','git@github.com:odoo-dev/odoo.git')
|
||||
run('git','remote','update')
|
||||
# setup master branch
|
||||
run('git','config','branch.master.remote','odoo')
|
||||
run('git','config','branch.master.merge','refs/heads/master')
|
||||
run('git','checkout','master')
|
||||
else:
|
||||
printf('no git repo found')
|
||||
|
||||
def cmd_setup_git_odoo_dev():
|
||||
git_dir = git_locate()
|
||||
if git_dir:
|
||||
# setup odoo-dev remote
|
||||
run('git','config','--add','remote.odoo-dev.fetch','dummy')
|
||||
run('git','config','--unset-all','remote.odoo-dev.fetch')
|
||||
run('git','config','--add','remote.odoo-dev.fetch','+refs/heads/*:refs/remotes/odoo-dev/heads/*')
|
||||
run('git','config','--add','remote.odoo-dev.fetch','+refs/pull/*:refs/remotes/odoo-dev/pull/*')
|
||||
run('git','remote','update')
|
||||
|
||||
def cmd_setup_git_odoo_review():
|
||||
git_dir = git_locate()
|
||||
if git_dir:
|
||||
# setup odoo-dev remote
|
||||
run('git','config','--add','remote.odoo.fetch','dummy')
|
||||
run('git','config','--unset-all','remote.odoo.fetch')
|
||||
run('git','config','--add','remote.odoo.fetch','+refs/heads/*:refs/remotes/odoo/heads/*')
|
||||
run('git','config','--add','remote.odoo.fetch','+refs/tags/*:refs/remotes/odoo/tags/*')
|
||||
run('git','config','--add','remote.odoo.fetch','+refs/pull/*:refs/remotes/odoo/pull/*')
|
||||
|
||||
def setup_deps_debian(git_dir):
|
||||
debian_control_path = os.path.join(git_dir, 'debian/control')
|
||||
debian_control = open(debian_control_path).read()
|
||||
debs = re.findall('python-[0-9a-z]+',debian_control)
|
||||
proc = subprocess.Popen(['sudo','apt-get','install'] + debs, stdin=open('/dev/tty'))
|
||||
proc.communicate()
|
||||
|
||||
def cmd_setup_deps():
|
||||
git_dir = git_locate()
|
||||
if git_dir:
|
||||
if os.path.isfile('/etc/debian_version'):
|
||||
setup_deps_debian(git_dir)
|
||||
|
||||
def setup_pg_debian(git_dir):
|
||||
cmd = ['sudo','su','-','postgres','-c','createuser -s %s' % os.environ['USER']]
|
||||
subprocess.call(cmd)
|
||||
|
||||
def cmd_setup_pg():
|
||||
git_dir = git_locate()
|
||||
if git_dir:
|
||||
if os.path.isfile('/etc/debian_version'):
|
||||
setup_pg_debian(git_dir)
|
||||
|
||||
def cmd_setup():
|
||||
cmd_setup_git_init()
|
||||
cmd_setup_deps()
|
||||
cmd_setup_pg()
|
||||
|
||||
def main():
|
||||
# regsitry of commands
|
||||
g = globals()
|
||||
cmds = dict([(i[4:],g[i]) for i in g if i.startswith('cmd_')])
|
||||
# if curl URL | python2 then use command setup
|
||||
if len(sys.argv) == 1 and __file__ == '<stdin>':
|
||||
cmd_setup()
|
||||
elif len(sys.argv) == 2 and sys.argv[1] in cmds:
|
||||
cmds[sys.argv[1]]()
|
||||
else:
|
||||
import openerp
|
||||
openerp.cli.main()
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
|
8
oe
|
@ -1,8 +0,0 @@
|
|||
#! /usr/bin/env python2
|
||||
|
||||
if __name__ == '__main__':
|
||||
import sys
|
||||
if len(sys.argv) > 1 and sys.argv[1] == 'run-tests':
|
||||
sys.exit(0)
|
||||
import openerpcommand.main
|
||||
openerpcommand.main.run()
|
|
@ -1,63 +0,0 @@
|
|||
import argparse
|
||||
import textwrap
|
||||
|
||||
from .call import Call
|
||||
from .client import Open, Show, ConsumeNothing, ConsumeMemory, LeakMemory, ConsumeCPU
|
||||
from .benchmarks import Bench, BenchRead, BenchFieldsViewGet, BenchDummy, BenchLogin
|
||||
from .bench_sale_mrp import BenchSaleMrp
|
||||
from . import common
|
||||
|
||||
from . import conf # Not really server-side (in the `for` below).
|
||||
from . import cron
|
||||
from . import drop
|
||||
from . import initialize
|
||||
from . import model
|
||||
from . import module
|
||||
from . import read
|
||||
from . import scaffold
|
||||
from . import uninstall
|
||||
from . import update
|
||||
from . import web
|
||||
from . import grunt_tests
|
||||
|
||||
command_list_server = (conf, cron, drop, initialize, model, module, read,
|
||||
scaffold, uninstall, update, web, grunt_tests, )
|
||||
|
||||
command_list_client = (Call, Open, Show, ConsumeNothing, ConsumeMemory,
|
||||
LeakMemory, ConsumeCPU, Bench, BenchRead,
|
||||
BenchFieldsViewGet, BenchDummy, BenchLogin,
|
||||
BenchSaleMrp, )
|
||||
|
||||
def main_parser():
|
||||
parser = argparse.ArgumentParser(
|
||||
usage=argparse.SUPPRESS,
|
||||
description=textwrap.fill(textwrap.dedent("""\
|
||||
OpenERP Command provides a set of command-line tools around
|
||||
the OpenERP framework: openobject-server. All the tools are
|
||||
sub-commands of a single oe executable.""")),
|
||||
epilog="""Use <command> --help to get information about the command.""",
|
||||
formatter_class=argparse.RawDescriptionHelpFormatter,
|
||||
)
|
||||
description = []
|
||||
for x in command_list_server:
|
||||
description.append(x.__name__[len(__package__)+1:])
|
||||
if x.__doc__:
|
||||
description.extend([
|
||||
":\n",
|
||||
textwrap.fill(str(x.__doc__).strip(),
|
||||
subsequent_indent=' ',
|
||||
initial_indent=' '),
|
||||
])
|
||||
description.append("\n\n")
|
||||
subparsers = parser.add_subparsers(
|
||||
title="Available commands",
|
||||
help=argparse.SUPPRESS,
|
||||
description="".join(description[:-1]),
|
||||
)
|
||||
# Server-side commands.
|
||||
for x in command_list_server:
|
||||
x.add_parser(subparsers)
|
||||
# Client-side commands. TODO one per .py file.
|
||||
for x in command_list_client:
|
||||
x(subparsers)
|
||||
return parser
|
|
@ -1,3 +0,0 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
# Nothing here, the module provides only data.
|
||||
# vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4:
|
|
@ -1,15 +0,0 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
{
|
||||
'name': 'bench_sale_mrp',
|
||||
'version': '0.1',
|
||||
'category': 'Benchmarks',
|
||||
'description': """Prepare some data to run a benchmark.""",
|
||||
'author': 'OpenERP SA',
|
||||
'maintainer': 'OpenERP SA',
|
||||
'website': 'http://www.openerp.com',
|
||||
'depends': ['base', 'sale_mrp'],
|
||||
'data': ['data.yml'],
|
||||
'installable': True,
|
||||
'auto_install': False,
|
||||
}
|
||||
# vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4:
|
|
@ -1,41 +0,0 @@
|
|||
-
|
||||
This is a subset of `sale_mrp/test/sale_mrp.yml`.
|
||||
-
|
||||
I define a product category `Mobile Products Sellable`.
|
||||
-
|
||||
!record {model: product.category, id: my_product_category_0}:
|
||||
name: Mobile Products Sellable
|
||||
-
|
||||
I define a product `Slider Mobile`
|
||||
-
|
||||
!record {model: product.product, id: my_slider_mobile_0}:
|
||||
categ_id: my_product_category_0
|
||||
cost_method: standard
|
||||
list_price: 200.0
|
||||
mes_type: fixed
|
||||
name: Slider Mobile
|
||||
procure_method: make_to_order
|
||||
seller_delay: '1'
|
||||
seller_ids:
|
||||
- delay: 1
|
||||
name: base.res_partner_agrolait
|
||||
min_qty: 2.0
|
||||
qty: 5.0
|
||||
standard_price: 189.0
|
||||
supply_method: produce
|
||||
type: product
|
||||
uom_id: product.product_uom_unit
|
||||
uom_po_id: product.product_uom_unit
|
||||
-
|
||||
I create a Bill of Material for the `Slider Mobile` product.
|
||||
-
|
||||
!record {model: mrp.bom, id: mrp_bom_slidermobile0}:
|
||||
company_id: base.main_company
|
||||
name: Slider Mobile
|
||||
product_efficiency: 1.0
|
||||
product_id: my_slider_mobile_0
|
||||
product_qty: 1.0
|
||||
product_uom: product.product_uom_unit
|
||||
product_uos_qty: 0.0
|
||||
sequence: 0.0
|
||||
type: normal
|
|
@ -1,68 +0,0 @@
|
|||
"""
|
||||
Benchmark based on the `sale_mrp` addons (in `sale_mrp/test/sale_mrp.yml`).
|
||||
"""
|
||||
|
||||
import time
|
||||
|
||||
from .benchmarks import Bench
|
||||
|
||||
class BenchSaleMrp(Bench):
|
||||
"""\
|
||||
Similar to `sale_mrp/test/sale_mrp.yml`.
|
||||
|
||||
This benchmarks the OpenERP server `sale_mrp` module by creating and
|
||||
confirming a sale order. As it creates data in the server, it is necessary
|
||||
to ensure unique names for the newly created data. You can use the --seed
|
||||
argument to give a lower bound to those names. (The number of generated
|
||||
names is --jobs * --samples.)
|
||||
"""
|
||||
|
||||
command_name = 'bench-sale-mrp'
|
||||
bench_name = '`sale_mrp/test/sale_mrp.yml`'
|
||||
|
||||
def measure_once(self, i):
|
||||
if self.worker >= 0:
|
||||
i = int(self.args.seed) + i + (self.worker * int(self.args.samples))
|
||||
else:
|
||||
i = int(self.args.seed) + i
|
||||
|
||||
# Resolve a few external-ids (this has little impact on the running
|
||||
# time of the whole method).
|
||||
product_uom_unit = self.execute('ir.model.data', 'get_object_reference', 'product', 'product_uom_unit')[1]
|
||||
my_slider_mobile_0 = self.execute('ir.model.data', 'get_object_reference', 'bench_sale_mrp', 'my_slider_mobile_0')[1]
|
||||
res_partner_4 = self.execute('ir.model.data', 'get_object_reference', 'base', 'res_partner_4')[1]
|
||||
res_partner_address_7 = self.execute('ir.model.data', 'get_object_reference', 'base', 'res_partner_address_7')[1]
|
||||
list0 = self.execute('ir.model.data', 'get_object_reference', 'product', 'list0')[1]
|
||||
shop = self.execute('ir.model.data', 'get_object_reference', 'sale', 'shop')[1]
|
||||
|
||||
# Create a sale order for the product `Slider Mobile`.
|
||||
data = {
|
||||
'client_order_ref': 'ref_xxx_' + str(i).rjust(6, '0'),
|
||||
'date_order': time.strftime('%Y-%m-%d'),
|
||||
'invoice_quantity': 'order',
|
||||
'name': 'sale_order_ref_xxx_' + str(i).rjust(6, '0'),
|
||||
'order_line': [(0, 0, {
|
||||
'name': 'Slider Mobile',
|
||||
'price_unit': 2,
|
||||
'product_uom': product_uom_unit,
|
||||
'product_uom_qty': 5.0,
|
||||
'state': 'draft',
|
||||
'delay': 7.0,
|
||||
'product_id': my_slider_mobile_0,
|
||||
'product_uos_qty': 5,
|
||||
'type': 'make_to_order',
|
||||
})],
|
||||
'order_policy': 'manual',
|
||||
'partner_id': res_partner_4,
|
||||
'partner_invoice_id': res_partner_address_7,
|
||||
'partner_order_id': res_partner_address_7,
|
||||
'partner_shipping_id': res_partner_address_7,
|
||||
'picking_policy': 'direct',
|
||||
'pricelist_id': list0,
|
||||
'shop_id': shop,
|
||||
}
|
||||
sale_order_id = self.execute('sale.order', 'create', data, {})
|
||||
|
||||
# Confirm the sale order.
|
||||
self.object_proxy.exec_workflow(self.database, self.uid, self.password, 'sale.order', 'order_confirm', sale_order_id, {})
|
||||
|
|
@ -1,166 +0,0 @@
|
|||
"""
|
||||
Define a base class for client-side benchmarking.
|
||||
"""
|
||||
import hashlib
|
||||
import multiprocessing
|
||||
import sys
|
||||
import time
|
||||
|
||||
from .client import Client
|
||||
|
||||
class Bench(Client):
|
||||
"""
|
||||
Base class for concurrent benchmarks. The measure_once() method must be
|
||||
overriden.
|
||||
|
||||
Each sub-benchmark will be run in its own process then a report is done
|
||||
with all the results (shared with the main process using a
|
||||
`multiprocessing.Array`).
|
||||
"""
|
||||
|
||||
def __init__(self, subparsers=None):
|
||||
super(Bench, self).__init__(subparsers)
|
||||
self.parser.add_argument('-n', '--samples', metavar='INT',
|
||||
default=100, help='number of measurements to take')
|
||||
# TODO if -n <int>s is given (instead of -n <int>), run the
|
||||
# benchmark for <int> seconds and return the number of iterations.
|
||||
self.parser.add_argument('-o', '--output', metavar='PATH',
|
||||
required=True, help='path to save the generated report')
|
||||
self.parser.add_argument('--append', action='store_true',
|
||||
default=False, help='append the report to an existing file')
|
||||
self.parser.add_argument('-j', '--jobs', metavar='JOBS',
|
||||
default=1, help='number of concurrent workers')
|
||||
self.parser.add_argument('--seed', metavar='SEED',
|
||||
default=0, help='a value to ensure different runs can create unique data')
|
||||
self.worker = -1
|
||||
|
||||
def work(self, iarr=None):
|
||||
if iarr:
|
||||
# If an array is given, it means we are a worker process...
|
||||
self.work_slave(iarr)
|
||||
else:
|
||||
# ... else we are the main process and we will spawn workers,
|
||||
# passing them an array.
|
||||
self.work_master()
|
||||
|
||||
def work_master(self):
|
||||
N = int(self.args.samples)
|
||||
self.arrs = [(i, multiprocessing.Array('f', range(N)))
|
||||
for i in xrange(int(self.args.jobs))]
|
||||
ps = [multiprocessing.Process(target=self.run, args=(arr,))
|
||||
for arr in self.arrs]
|
||||
[p.start() for p in ps]
|
||||
[p.join() for p in ps]
|
||||
|
||||
self.report_html()
|
||||
|
||||
def work_slave(self, iarr):
|
||||
j, arr = iarr
|
||||
self.worker = j
|
||||
N = int(self.args.samples)
|
||||
total_t0 = time.time()
|
||||
for i in xrange(N):
|
||||
t0 = time.time()
|
||||
self.measure_once(i)
|
||||
t1 = time.time()
|
||||
arr[i] = t1 - t0
|
||||
print >> sys.stdout, '\r%s' % ('|' * (i * 60 / N)),
|
||||
print >> sys.stdout, '%s %s%%' % \
|
||||
(' ' * (60 - (i * 60 / N)), int(float(i+1)/N*100)),
|
||||
sys.stdout.flush()
|
||||
total_t1 = time.time()
|
||||
print '\nDone in %ss.' % (total_t1 - total_t0)
|
||||
|
||||
def report_html(self):
|
||||
series = []
|
||||
for arr in self.arrs:
|
||||
serie = """{
|
||||
data: %s,
|
||||
points: { show: true }
|
||||
}""" % ([[x, i] for i, x in enumerate(arr)],)
|
||||
series.append(serie)
|
||||
chart_id = hashlib.md5(" ".join(sys.argv)).hexdigest()
|
||||
HEADER = """<!doctype html>
|
||||
<title>Benchmarks</title>
|
||||
<meta charset=utf-8>
|
||||
<script type="text/javascript" src="js/jquery.min.js"></script>
|
||||
<script type="text/javascript" src="js/jquery.flot.js"></script>
|
||||
"""
|
||||
|
||||
CONTENT = """<h1>%s</h1>
|
||||
%s
|
||||
<div id='chart_%s' style='width:400px;height:300px;'>...</div>
|
||||
<script type="text/javascript">
|
||||
$.plot($("#chart_%s"), [%s],
|
||||
{yaxis: { ticks: false }});
|
||||
</script>""" % (self.bench_name, ' '.join(sys.argv), chart_id, chart_id,
|
||||
','.join(series))
|
||||
if self.args.append:
|
||||
with open(self.args.output, 'a') as f:
|
||||
f.write(CONTENT,)
|
||||
else:
|
||||
with open(self.args.output, 'w') as f:
|
||||
f.write(HEADER + CONTENT,)
|
||||
|
||||
def measure_once(self, i):
|
||||
"""
|
||||
The `measure_once` method is called --jobs times. A `i` argument is
|
||||
supplied to allow to create unique values for each execution (e.g. to
|
||||
supply fresh identifiers to a `create` method.
|
||||
"""
|
||||
pass
|
||||
|
||||
class BenchRead(Bench):
|
||||
"""Read a record repeatedly."""
|
||||
|
||||
command_name = 'bench-read'
|
||||
bench_name = 'res.users.read(1)'
|
||||
|
||||
def __init__(self, subparsers=None):
|
||||
super(BenchRead, self).__init__(subparsers)
|
||||
self.parser.add_argument('-m', '--model', metavar='MODEL',
|
||||
required=True, help='the model')
|
||||
self.parser.add_argument('-i', '--id', metavar='RECORDID',
|
||||
required=True, help='the record id')
|
||||
|
||||
def measure_once(self, i):
|
||||
self.execute(self.args.model, 'read', [self.args.id], [])
|
||||
|
||||
class BenchFieldsViewGet(Bench):
|
||||
"""Read a record's fields and view architecture repeatedly."""
|
||||
|
||||
command_name = 'bench-view'
|
||||
bench_name = 'res.users.fields_view_get(1)'
|
||||
|
||||
def __init__(self, subparsers=None):
|
||||
super(BenchFieldsViewGet, self).__init__(subparsers)
|
||||
self.parser.add_argument('-m', '--model', metavar='MODEL',
|
||||
required=True, help='the model')
|
||||
self.parser.add_argument('-i', '--id', metavar='RECORDID',
|
||||
required=True, help='the record id')
|
||||
|
||||
def measure_once(self, i):
|
||||
self.execute(self.args.model, 'fields_view_get', self.args.id)
|
||||
|
||||
class BenchDummy(Bench):
|
||||
"""Dummy (call test.limits.model.consume_nothing())."""
|
||||
|
||||
command_name = 'bench-dummy'
|
||||
bench_name = 'test.limits.model.consume_nothing()'
|
||||
|
||||
def __init__(self, subparsers=None):
|
||||
super(BenchDummy, self).__init__(subparsers)
|
||||
self.parser.add_argument('-a', '--args', metavar='ARGS',
|
||||
default='', help='some arguments to serialize')
|
||||
|
||||
def measure_once(self, i):
|
||||
self.execute('test.limits.model', 'consume_nothing')
|
||||
|
||||
class BenchLogin(Bench):
|
||||
"""Login (update res_users.date)."""
|
||||
|
||||
command_name = 'bench-login'
|
||||
bench_name = 'res.users.login(1)'
|
||||
|
||||
def measure_once(self, i):
|
||||
self.common_proxy.login(self.database, self.user, self.password)
|
|
@ -1,44 +0,0 @@
|
|||
"""
|
||||
Call an arbitrary model's method.
|
||||
"""
|
||||
import ast
|
||||
import os
|
||||
import pprint
|
||||
import sys
|
||||
import time
|
||||
import xmlrpclib
|
||||
|
||||
import client
|
||||
|
||||
class Call(client.Client):
|
||||
"""\
|
||||
Call an arbitrary model's method.
|
||||
|
||||
Example:
|
||||
> oe call res.users.read '[1, 3]' '[]' -u 1 -p admin
|
||||
"""
|
||||
# TODO The above docstring is completely borked in the
|
||||
# --help message.
|
||||
|
||||
command_name = 'call'
|
||||
|
||||
def __init__(self, subparsers=None):
|
||||
super(Call, self).__init__(subparsers)
|
||||
self.parser.add_argument('call', metavar='MODEL.METHOD',
|
||||
help='the model and the method to call, using the '
|
||||
'<model>.<method> format.')
|
||||
self.parser.add_argument('args', metavar='ARGUMENT',
|
||||
nargs='+',
|
||||
help='the argument for the method call, must be '
|
||||
'`ast.literal_eval` compatible. Can be repeated.')
|
||||
|
||||
def work(self):
|
||||
try:
|
||||
model, method = self.args.call.rsplit('.', 1)
|
||||
except:
|
||||
print "Invalid syntax `%s` must have the form <model>.<method>."
|
||||
sys.exit(1)
|
||||
args = tuple(map(ast.literal_eval, self.args.args)) if self.args.args else ()
|
||||
x = self.execute(model, method, *args)
|
||||
pprint.pprint(x, indent=4)
|
||||
|
|
@ -1,137 +0,0 @@
|
|||
"""
|
||||
Define a few common arguments for client-side command-line tools.
|
||||
"""
|
||||
import os
|
||||
import sys
|
||||
import time
|
||||
import xmlrpclib
|
||||
|
||||
import common
|
||||
|
||||
class Client(common.Command):
|
||||
"""
|
||||
Base class for XML-RPC command-line clients. It must be inherited and the
|
||||
work() method overriden.
|
||||
"""
|
||||
|
||||
def __init__(self, subparsers=None):
|
||||
super(Client, self).__init__(subparsers)
|
||||
required_or_default = common.required_or_default
|
||||
self.parser.add_argument('-H', '--host', metavar='HOST',
|
||||
**required_or_default('HOST', 'the server host'))
|
||||
self.parser.add_argument('-P', '--port', metavar='PORT',
|
||||
**required_or_default('PORT', 'the server port'))
|
||||
|
||||
def execute(self, *args):
|
||||
return self.object_proxy.execute(self.database, self.uid, self.password, *args)
|
||||
|
||||
def initialize(self):
|
||||
self.host = self.args.host
|
||||
self.port = int(self.args.port)
|
||||
self.database = self.args.database
|
||||
self.user = self.args.user
|
||||
self.password = self.args.password
|
||||
|
||||
self.url = 'http://%s:%d/xmlrpc/' % (self.host, self.port)
|
||||
self.common_proxy = xmlrpclib.ServerProxy(self.url + 'common')
|
||||
self.object_proxy = xmlrpclib.ServerProxy(self.url + 'object')
|
||||
|
||||
try:
|
||||
self.uid = int(self.user)
|
||||
except ValueError, e:
|
||||
self.uid = self.common_proxy.login(self.database, self.user, self.password)
|
||||
|
||||
def run(self, *args):
|
||||
self.initialize()
|
||||
self.work(*args)
|
||||
|
||||
def work(self, *args):
|
||||
pass
|
||||
|
||||
class Open(Client):
|
||||
"""Get the web client's URL to view a specific model."""
|
||||
|
||||
command_name = 'open'
|
||||
|
||||
def __init__(self, subparsers=None):
|
||||
super(Open, self).__init__(subparsers)
|
||||
self.parser.add_argument('-m', '--model', metavar='MODEL',
|
||||
required=True, help='the view type')
|
||||
self.parser.add_argument('-v', '--view-mode', metavar='VIEWMODE',
|
||||
default='tree', help='the view mode')
|
||||
|
||||
def work(self):
|
||||
ids = self.execute('ir.actions.act_window', 'search', [
|
||||
('res_model', '=', self.args.model),
|
||||
('view_mode', 'like', self.args.view_mode),
|
||||
])
|
||||
xs = self.execute('ir.actions.act_window', 'read', ids, [])
|
||||
for x in xs:
|
||||
print x['id'], x['name']
|
||||
d = {}
|
||||
d['host'] = self.host
|
||||
d['port'] = self.port
|
||||
d['action_id'] = x['id']
|
||||
print " http://%(host)s:%(port)s/web/webclient/home#action_id=%(action_id)s" % d
|
||||
|
||||
class Show(Client):
|
||||
"""Display a record."""
|
||||
|
||||
command_name = 'show'
|
||||
|
||||
def __init__(self, subparsers=None):
|
||||
super(Show, self).__init__(subparsers)
|
||||
self.parser.add_argument('-m', '--model', metavar='MODEL',
|
||||
required=True, help='the model')
|
||||
self.parser.add_argument('-i', '--id', metavar='RECORDID',
|
||||
required=True, help='the record id')
|
||||
|
||||
def work(self):
|
||||
xs = self.execute(self.args.model, 'read', [self.args.id], [])
|
||||
if xs:
|
||||
x = xs[0]
|
||||
print x['name']
|
||||
else:
|
||||
print "Record not found."
|
||||
|
||||
class ConsumeNothing(Client):
|
||||
"""Call test.limits.model.consume_nothing()."""
|
||||
|
||||
command_name = 'consume-nothing'
|
||||
|
||||
def work(self):
|
||||
xs = self.execute('test.limits.model', 'consume_nothing')
|
||||
|
||||
class ConsumeMemory(Client):
|
||||
"""Call test.limits.model.consume_memory()."""
|
||||
|
||||
command_name = 'consume-memory'
|
||||
|
||||
def __init__(self, subparsers=None):
|
||||
super(ConsumeMemory, self).__init__(subparsers)
|
||||
self.parser.add_argument('--size', metavar='SIZE',
|
||||
required=True, help='size of the list to allocate')
|
||||
|
||||
def work(self):
|
||||
xs = self.execute('test.limits.model', 'consume_memory', int(self.args.size))
|
||||
|
||||
class LeakMemory(ConsumeMemory):
|
||||
"""Call test.limits.model.leak_memory()."""
|
||||
|
||||
command_name = 'leak-memory'
|
||||
|
||||
def work(self):
|
||||
xs = self.execute('test.limits.model', 'leak_memory', int(self.args.size))
|
||||
|
||||
class ConsumeCPU(Client):
|
||||
"""Call test.limits.model.consume_cpu_time()."""
|
||||
|
||||
command_name = 'consume-cpu'
|
||||
|
||||
def __init__(self, subparsers=None):
|
||||
super(ConsumeCPU, self).__init__(subparsers)
|
||||
self.parser.add_argument('--seconds', metavar='INT',
|
||||
required=True, help='how much CPU time to consume')
|
||||
|
||||
def work(self):
|
||||
xs = self.execute('test.limits.model', 'consume_cpu_time', int(self.args.seconds))
|
|
@ -1,108 +0,0 @@
|
|||
"""
|
||||
Define a few common arguments for server-side command-line tools.
|
||||
"""
|
||||
import argparse
|
||||
import os
|
||||
try:
|
||||
from setproctitle import setproctitle
|
||||
except ImportError:
|
||||
setproctitle = lambda x: None
|
||||
import sys
|
||||
|
||||
def add_addons_argument(parser):
|
||||
"""
|
||||
Add a common --addons argument to a parser.
|
||||
"""
|
||||
parser.add_argument('--addons', metavar='ADDONS',
|
||||
**required_or_default('ADDONS',
|
||||
'colon-separated list of paths to addons'))
|
||||
def set_addons(args):
|
||||
"""
|
||||
Turn args.addons into a list instead of a column-separated strings.
|
||||
Set openerp.toools.config accordingly.
|
||||
"""
|
||||
import openerp.tools.config
|
||||
config = openerp.tools.config
|
||||
|
||||
assert hasattr(args, 'addons')
|
||||
if args.addons:
|
||||
args.addons = args.addons.split(':')
|
||||
else:
|
||||
args.addons = []
|
||||
|
||||
config['addons_path'] = ','.join(args.addons)
|
||||
|
||||
def get_addons_from_paths(paths, exclude):
|
||||
"""
|
||||
Build a list of available modules from a list of addons paths.
|
||||
"""
|
||||
exclude = exclude or []
|
||||
module_names = []
|
||||
for p in paths:
|
||||
if os.path.exists(p):
|
||||
names = [n for n in os.listdir(p) if os.path.isfile(os.path.join(p, n, '__openerp__.py')) and not n.startswith('.') and n not in exclude]
|
||||
names = filter(lambda a: os.path.isdir(os.path.join(p, a)), names)
|
||||
names = filter(lambda a: os.path.exists(os.path.join(p, a, '__openerp__.py')), names)
|
||||
module_names.extend(names)
|
||||
else:
|
||||
print "The addons path `%s` doesn't exist." % p
|
||||
sys.exit(1)
|
||||
return module_names
|
||||
|
||||
def required_or_default(name, h):
|
||||
"""
|
||||
Helper to define `argparse` arguments. If the name is the environment,
|
||||
the argument is optional and draw its value from the environment if not
|
||||
supplied on the command-line. If it is not in the environment, make it
|
||||
a mandatory argument.
|
||||
"""
|
||||
if os.environ.get('OPENERP_' + name.upper()):
|
||||
d = {'default': os.environ['OPENERP_' + name.upper()]}
|
||||
else:
|
||||
d = {'required': True}
|
||||
d['help'] = h + '. The environment variable OPENERP_' + \
|
||||
name.upper() + ' can be used instead.'
|
||||
return d
|
||||
|
||||
class Command(object):
|
||||
"""
|
||||
Base class to create command-line tools. It must be inherited and the
|
||||
run() method overriden.
|
||||
"""
|
||||
|
||||
command_name = 'stand-alone'
|
||||
|
||||
def __init__(self, subparsers=None):
|
||||
if subparsers:
|
||||
self.parser = parser = subparsers.add_parser(self.command_name,
|
||||
description=self.__class__.__doc__)
|
||||
else:
|
||||
self.parser = parser = argparse.ArgumentParser(
|
||||
description=self.__class__.__doc__)
|
||||
|
||||
parser.add_argument('-d', '--database', metavar='DATABASE',
|
||||
**required_or_default('DATABASE', 'the database to connect to'))
|
||||
parser.add_argument('-u', '--user', metavar='USER',
|
||||
**required_or_default('USER', 'the user login or ID. When using '
|
||||
'RPC, providing an ID avoid the login() step'))
|
||||
parser.add_argument('-p', '--password', metavar='PASSWORD',
|
||||
**required_or_default('PASSWORD', 'the user password')) # TODO read it from the command line or from file.
|
||||
|
||||
parser.set_defaults(run=self.run_with_args)
|
||||
|
||||
def run_with_args(self, args):
|
||||
self.args = args
|
||||
self.run()
|
||||
|
||||
def run(self):
|
||||
print 'Stub Command.run().'
|
||||
|
||||
@classmethod
|
||||
def stand_alone(cls):
|
||||
"""
|
||||
A single Command object is a complete command-line program. See
|
||||
`openerp-command/stand-alone` for an example.
|
||||
"""
|
||||
command = cls()
|
||||
args = command.parser.parse_args()
|
||||
args.run(args)
|
|
@ -1,25 +0,0 @@
|
|||
"""
|
||||
Display the currently used configuration. The configuration for any
|
||||
sub-command is normally given by options. But some options can be specified
|
||||
using environment variables. This sub-command shows those variables.
|
||||
A `set` sub-command should be provided when the configuration is in a real
|
||||
configuration file instead of environment variables.
|
||||
"""
|
||||
import os
|
||||
import sys
|
||||
import textwrap
|
||||
|
||||
def run(args):
|
||||
for x in ('database', 'addons', 'host', 'port'):
|
||||
x_ = ('openerp_' + x).upper()
|
||||
if x_ in os.environ:
|
||||
print '%s: %s' % (x, os.environ[x_])
|
||||
else:
|
||||
print '%s: <not set>' % (x, )
|
||||
os.environ['OPENERP_DATABASE'] = 'yeah'
|
||||
|
||||
def add_parser(subparsers):
|
||||
parser = subparsers.add_parser('conf',
|
||||
description='Display the currently used configuration.')
|
||||
|
||||
parser.set_defaults(run=run)
|
|
@ -1,46 +0,0 @@
|
|||
"""
|
||||
Run an OpenERP cron process.
|
||||
"""
|
||||
|
||||
import os
|
||||
|
||||
import common
|
||||
|
||||
def run(args):
|
||||
import openerp
|
||||
import openerp.cli.server
|
||||
import openerp.tools.config
|
||||
import openerp.service.cron
|
||||
config = openerp.tools.config
|
||||
|
||||
os.environ["TZ"] = "UTC"
|
||||
common.set_addons(args)
|
||||
args.database = args.database or []
|
||||
|
||||
config['log_handler'] = [':WARNING', 'openerp.addons.base.ir.ir_cron:DEBUG']
|
||||
|
||||
openerp.multi_process = True
|
||||
common.setproctitle('openerp-cron [%s]' % ', '.join(args.database))
|
||||
|
||||
openerp.cli.server.check_root_user()
|
||||
openerp.netsvc.init_logger()
|
||||
#openerp.cli.server.report_configuration()
|
||||
openerp.cli.server.setup_signal_handlers(openerp.cli.server.signal_handler)
|
||||
import openerp.addons.base
|
||||
if args.database:
|
||||
for db in args.database:
|
||||
openerp.cli.server.preload_registry(db)
|
||||
openerp.service.cron.start_service()
|
||||
openerp.cli.server.quit_on_signals()
|
||||
else:
|
||||
print "No database given."
|
||||
|
||||
|
||||
def add_parser(subparsers):
|
||||
parser = subparsers.add_parser('cron',
|
||||
description='Run an OpenERP cron process.')
|
||||
common.add_addons_argument(parser)
|
||||
parser.add_argument('--database', action='append',
|
||||
help='Database for which cron jobs are processed (can be repeated)')
|
||||
|
||||
parser.set_defaults(run=run)
|
|
@ -1,50 +0,0 @@
|
|||
"""
|
||||
Drop a database.
|
||||
"""
|
||||
|
||||
import common
|
||||
|
||||
# TODO turn template1 in a parameter
|
||||
# This should be exposed from openerp (currently in
|
||||
# openerp/service/web_services.py).
|
||||
def drop_database(database_name):
|
||||
import openerp
|
||||
openerp.netsvc.init_logger()
|
||||
db = openerp.sql_db.db_connect('template1')
|
||||
cr = db.cursor()
|
||||
cr.autocommit(True) # avoid transaction block
|
||||
try:
|
||||
# TODO option for doing this.
|
||||
# Try to terminate all other connections that might prevent
|
||||
# dropping the database
|
||||
|
||||
# PostgreSQL 9.2 renamed pg_stat_activity.procpid to pid:
|
||||
# http://www.postgresql.org/docs/9.2/static/release-9-2.html#AEN110389
|
||||
pid_col = 'pid' if cr._cnx.server_version >= 90200 else 'procpid'
|
||||
try:
|
||||
cr.execute("""SELECT pg_terminate_backend(%(pid_col)s)
|
||||
FROM pg_stat_activity
|
||||
WHERE datname = %%s AND
|
||||
%(pid_col)s != pg_backend_pid()""" % {'pid_col': pid_col},
|
||||
(database_name,))
|
||||
except Exception:
|
||||
pass
|
||||
|
||||
try:
|
||||
cr.execute('DROP DATABASE "%s"' % database_name, log_exceptions=False)
|
||||
except Exception, e:
|
||||
print "Can't drop %s" % (database_name,)
|
||||
finally:
|
||||
cr.close()
|
||||
|
||||
def run(args):
|
||||
assert args.database
|
||||
drop_database(args.database)
|
||||
|
||||
def add_parser(subparsers):
|
||||
parser = subparsers.add_parser('drop',
|
||||
description='Drop a database.')
|
||||
parser.add_argument('-d', '--database', metavar='DATABASE',
|
||||
**common.required_or_default('DATABASE', 'the database to create'))
|
||||
|
||||
parser.set_defaults(run=run)
|
|
@ -1,50 +0,0 @@
|
|||
"""
|
||||
Search for Gruntfile.js files in all the addons and launch them using the 'grunt test' command.
|
||||
"""
|
||||
|
||||
import common
|
||||
import fnmatch
|
||||
import os
|
||||
import re
|
||||
import sys
|
||||
import subprocess
|
||||
|
||||
def grunt_tester(directories, log = sys.stdout):
|
||||
result = 0
|
||||
|
||||
matches = []
|
||||
for direc in directories:
|
||||
for root, dirnames, filenames in os.walk(direc):
|
||||
for filename in fnmatch.filter(filenames, 'Gruntfile.js'):
|
||||
full = os.path.join(root, filename)
|
||||
if re.match(r"(^.*?/node_modules/.*$)|(^.*?/lib/.*$)", full):
|
||||
continue
|
||||
matches.append(full)
|
||||
|
||||
for file_ in matches:
|
||||
folder = os.path.dirname(file_)
|
||||
p = subprocess.Popen(['npm', 'install'], cwd=folder)
|
||||
if p.wait() != 0:
|
||||
raise Exception("Failed to install dependencies for Gruntfile located in folder %s" % folder)
|
||||
|
||||
p = subprocess.Popen(['grunt', 'test', '--no-color'], cwd=folder, stdout=log, stderr=log)
|
||||
if p.wait() != 0:
|
||||
result = 1
|
||||
|
||||
return result
|
||||
|
||||
def run(args):
|
||||
if args.addons:
|
||||
args.addons = args.addons.split(':')
|
||||
else:
|
||||
args.addons = []
|
||||
result = grunt_tester(args.addons)
|
||||
if result != 0:
|
||||
sys.exit(result)
|
||||
|
||||
def add_parser(subparsers):
|
||||
parser = subparsers.add_parser('grunt-tests',
|
||||
description='Run the tests contained in Gruntfile.js files.')
|
||||
common.add_addons_argument(parser)
|
||||
|
||||
parser.set_defaults(run=run)
|
|
@ -1,121 +0,0 @@
|
|||
"""
|
||||
Install OpenERP on a new (by default) database.
|
||||
"""
|
||||
import contextlib
|
||||
import errno
|
||||
import os
|
||||
import sys
|
||||
import time
|
||||
|
||||
import common
|
||||
|
||||
# From http://code.activestate.com/recipes/576572/
|
||||
@contextlib.contextmanager
|
||||
def lock_file(path, wait_delay=.1, max_try=600):
|
||||
attempt = 0
|
||||
while True:
|
||||
attempt += 1
|
||||
if attempt > max_try:
|
||||
raise IOError("Could not lock file %s." % path)
|
||||
try:
|
||||
fd = os.open(path, os.O_CREAT | os.O_EXCL | os.O_RDWR)
|
||||
except OSError, e:
|
||||
if e.errno != errno.EEXIST:
|
||||
raise
|
||||
time.sleep(wait_delay)
|
||||
continue
|
||||
else:
|
||||
break
|
||||
try:
|
||||
yield fd
|
||||
finally:
|
||||
os.close(fd)
|
||||
os.unlink(path)
|
||||
|
||||
def run(args):
|
||||
assert args.database
|
||||
assert not (args.module and args.all_modules)
|
||||
|
||||
import openerp
|
||||
|
||||
config = openerp.tools.config
|
||||
config['db_name'] = args.database
|
||||
|
||||
if args.tests:
|
||||
config['log_handler'] = [':INFO']
|
||||
config['test_enable'] = True
|
||||
config['without_demo'] = False
|
||||
if args.port:
|
||||
config['xmlrpc_port'] = int(args.port)
|
||||
else:
|
||||
config['log_handler'] = [':CRITICAL']
|
||||
config['test_enable'] = False
|
||||
config['without_demo'] = True
|
||||
|
||||
if args.addons:
|
||||
args.addons = args.addons.split(':')
|
||||
else:
|
||||
args.addons = []
|
||||
config['addons_path'] = ','.join(args.addons)
|
||||
|
||||
if args.all_modules:
|
||||
module_names = common.get_addons_from_paths(args.addons, args.exclude)
|
||||
elif args.module:
|
||||
module_names = args.module
|
||||
else:
|
||||
module_names = ['base']
|
||||
config['init'] = dict.fromkeys(module_names, 1)
|
||||
|
||||
if args.coverage:
|
||||
import coverage
|
||||
# Without the `include` kwarg, coverage generates 'memory:0xXXXXX'
|
||||
# filenames (which do not exist) and cause it to crash. No idea why.
|
||||
cov = coverage.coverage(branch=True, include='*.py')
|
||||
cov.start()
|
||||
openerp.netsvc.init_logger()
|
||||
|
||||
if not args.no_create:
|
||||
with lock_file('/tmp/global_openerp_create_database.lock'):
|
||||
openerp.service.db._create_empty_database(args.database)
|
||||
|
||||
config['workers'] = False
|
||||
|
||||
rc = openerp.service.server.start(preload=[args.database], stop=True)
|
||||
|
||||
if args.coverage:
|
||||
cov.stop()
|
||||
cov.html_report(directory='coverage')
|
||||
# If we wanted the report on stdout:
|
||||
# cov.report()
|
||||
|
||||
sys.exit(rc)
|
||||
|
||||
def add_parser(subparsers):
|
||||
parser = subparsers.add_parser('initialize',
|
||||
description='Create and initialize a new OpenERP database.')
|
||||
parser.add_argument('-d', '--database', metavar='DATABASE',
|
||||
**common.required_or_default('DATABASE', 'the database to create'))
|
||||
common.add_addons_argument(parser)
|
||||
parser.add_argument('-P', '--port', metavar='PORT',
|
||||
**common.required_or_default('PORT', 'the server port'))
|
||||
parser.add_argument('--module', metavar='MODULE', action='append',
|
||||
help='specify a module to install'
|
||||
' (this option can be repeated)')
|
||||
parser.add_argument('--all-modules', action='store_true',
|
||||
help='install all visible modules (not compatible with --module)')
|
||||
parser.add_argument('--no-create', action='store_true',
|
||||
help='do not create the database, only initialize it')
|
||||
parser.add_argument('--exclude', metavar='MODULE', action='append',
|
||||
help='exclude a module from installation'
|
||||
' (this option can be repeated)')
|
||||
parser.add_argument('--tests', action='store_true',
|
||||
help='run the tests as modules are installed'
|
||||
' (use the `run-tests` command to choose specific'
|
||||
' tests to run against an existing database).'
|
||||
' Demo data are installed.')
|
||||
parser.add_argument('--coverage', action='store_true',
|
||||
help='report code coverage (particularly useful with --tests).'
|
||||
' The report is generated in a coverage directory and you can'
|
||||
' then point your browser to coverage/index.html.')
|
||||
|
||||
parser.set_defaults(run=run)
|
|
@ -1,7 +0,0 @@
|
|||
import openerpcommand
|
||||
|
||||
def run():
|
||||
""" Main entry point for the openerp-command tool."""
|
||||
parser = openerpcommand.main_parser()
|
||||
args = parser.parse_args()
|
||||
args.run(args)
|
|
@ -1,61 +0,0 @@
|
|||
"""
|
||||
Display information about a given model.
|
||||
"""
|
||||
import os
|
||||
import sys
|
||||
import textwrap
|
||||
|
||||
def run(args):
|
||||
assert args.database
|
||||
assert args.model
|
||||
import openerp
|
||||
openerp.tools.config['log_level'] = 100
|
||||
openerp.netsvc.init_logger()
|
||||
registry = openerp.modules.registry.RegistryManager.get(
|
||||
args.database, update_module=False)
|
||||
model = registry[args.model]
|
||||
longest_k = 1
|
||||
longest_string = 1
|
||||
columns = model._columns
|
||||
|
||||
if args.field and args.field not in columns:
|
||||
print "No such field."
|
||||
sys.exit(1)
|
||||
|
||||
if args.field:
|
||||
columns = { args.field: columns[args.field] }
|
||||
else:
|
||||
print "Fields (model `%s`, database `%s`):" % (args.model, args.database)
|
||||
|
||||
for k, v in columns.items():
|
||||
longest_k = len(k) if longest_k < len(k) else longest_k
|
||||
longest_string = len(v.string) \
|
||||
if longest_string < len(v.string) else longest_string
|
||||
for k, v in sorted(columns.items()):
|
||||
attr = []
|
||||
if v.required:
|
||||
attr.append("Required")
|
||||
if v.readonly:
|
||||
attr.append("Read-only")
|
||||
attr = '/'.join(attr)
|
||||
attr = '(' + attr + ')' if attr else attr
|
||||
if args.verbose:
|
||||
print v.string, '-- ' + k + ', ' + v._type, attr
|
||||
else:
|
||||
print k.ljust(longest_k + 2), v._type, attr
|
||||
if args.verbose and v.help:
|
||||
print textwrap.fill(v.help, initial_indent=' ', subsequent_indent=' ')
|
||||
|
||||
def add_parser(subparsers):
|
||||
parser = subparsers.add_parser('model',
|
||||
description='Display information about a given model for an existing database.')
|
||||
parser.add_argument('-d', '--database', metavar='DATABASE', required=True,
|
||||
help='the database to connect to')
|
||||
parser.add_argument('-m', '--model', metavar='MODEL', required=True,
|
||||
help='the model for which information should be displayed')
|
||||
parser.add_argument('-v', '--verbose', action='store_true',
|
||||
help='display more information')
|
||||
parser.add_argument('-f', '--field', metavar='FIELD',
|
||||
help='display information only for this particular field')
|
||||
|
||||
parser.set_defaults(run=run)
|
|
@ -1,65 +0,0 @@
|
|||
"""
|
||||
Show module information for a given database or from the file-system.
|
||||
"""
|
||||
import os
|
||||
import sys
|
||||
import textwrap
|
||||
|
||||
from . import common
|
||||
|
||||
# TODO provide a --rpc flag to use XML-RPC (with a specific username) instead
|
||||
# of server-side library.
|
||||
def run(args):
|
||||
assert args.database
|
||||
import openerp
|
||||
|
||||
config = openerp.tools.config
|
||||
config['log_handler'] = [':CRITICAL']
|
||||
if args.addons:
|
||||
args.addons = args.addons.split(':')
|
||||
else:
|
||||
args.addons = []
|
||||
config['addons_path'] = ','.join(args.addons)
|
||||
openerp.netsvc.init_logger()
|
||||
|
||||
if args.filesystem:
|
||||
module_names = common.get_addons_from_paths(args.addons, [])
|
||||
print "Modules (addons path %s):" % (', '.join(args.addons),)
|
||||
for x in sorted(module_names):
|
||||
print x
|
||||
else:
|
||||
registry = openerp.modules.registry.RegistryManager.get(
|
||||
args.database, update_module=False)
|
||||
|
||||
xs = []
|
||||
ir_module_module = registry.get('ir.module.module')
|
||||
with registry.cursor() as cr:
|
||||
ids = ir_module_module.search(cr, openerp.SUPERUSER_ID, [], {})
|
||||
xs = ir_module_module.read(cr, openerp.SUPERUSER_ID, ids, [], {})
|
||||
|
||||
if xs:
|
||||
print "Modules (database `%s`):" % (args.database,)
|
||||
for x in xs:
|
||||
if args.short:
|
||||
print '%3d %s' % (x['id'], x['name'])
|
||||
else:
|
||||
print '%3d %s %s' % (x['id'], x['name'], {'installed': '(installed)'}.get(x['state'], ''))
|
||||
else:
|
||||
print "No module found (database `%s`)." % (args.database,)
|
||||
|
||||
def add_parser(subparsers):
|
||||
parser = subparsers.add_parser('module',
|
||||
description='Display modules known from a given database or on file-system.')
|
||||
parser.add_argument('-d', '--database', metavar='DATABASE',
|
||||
**common.required_or_default('DATABASE', 'the database to modify'))
|
||||
common.add_addons_argument(parser)
|
||||
parser.add_argument('-m', '--module', metavar='MODULE', required=False,
|
||||
help='the module for which information should be shown')
|
||||
parser.add_argument('-v', '--verbose', action='store_true',
|
||||
help='display more information')
|
||||
parser.add_argument('--short', action='store_true',
|
||||
help='display less information')
|
||||
parser.add_argument('-f', '--filesystem', action='store_true',
|
||||
help='display module in the addons path, not in db')
|
||||
|
||||
parser.set_defaults(run=run)
|
|
@ -1,89 +0,0 @@
|
|||
_oe()
|
||||
{
|
||||
local cur prev opts
|
||||
COMPREPLY=()
|
||||
cur="${COMP_WORDS[COMP_CWORD]}"
|
||||
prev="${COMP_WORDS[COMP_CWORD-1]}"
|
||||
|
||||
cmd="${COMP_WORDS[0]}"
|
||||
subcmd=""
|
||||
if [[ ${COMP_CWORD} > 0 ]] ; then
|
||||
subcmd="${COMP_WORDS[1]}"
|
||||
fi
|
||||
|
||||
# oe
|
||||
|
||||
opts="initialize model read run-tests scaffold update \
|
||||
call open show consume-nothing consume-memory leak-memory \
|
||||
consume-cpu bench-read bench-fields-view-get bench-dummy bench-login \
|
||||
bench-sale-mrp --help"
|
||||
|
||||
if [[ ${prev} == oe && ${cur} != -* ]] ; then
|
||||
COMPREPLY=( $(compgen -W "${opts}" -- ${cur}) )
|
||||
return 0
|
||||
fi
|
||||
|
||||
# oe call
|
||||
|
||||
opts="--database --user --password --host --port --help"
|
||||
|
||||
if [[ ${subcmd} == call ]] ; then
|
||||
COMPREPLY=( $(compgen -W "${opts}" -- ${cur}) )
|
||||
return 0
|
||||
fi
|
||||
|
||||
# oe initialize
|
||||
|
||||
opts="--database --addons --all-modules --exclude --no-create --help"
|
||||
|
||||
if [[ ${subcmd} == initialize ]] ; then
|
||||
COMPREPLY=( $(compgen -W "${opts}" -- ${cur}) )
|
||||
return 0
|
||||
fi
|
||||
|
||||
# oe model
|
||||
|
||||
opts="--database --model --field --verbose --help"
|
||||
|
||||
if [[ ${subcmd} == model ]] ; then
|
||||
COMPREPLY=( $(compgen -W "${opts}" -- ${cur}) )
|
||||
return 0
|
||||
fi
|
||||
|
||||
# oe read
|
||||
|
||||
opts="--database --model --id --field --verbose --short --help"
|
||||
|
||||
if [[ ${subcmd} == read ]] ; then
|
||||
COMPREPLY=( $(compgen -W "${opts}" -- ${cur}) )
|
||||
return 0
|
||||
fi
|
||||
|
||||
# oe run-tests
|
||||
|
||||
opts="--database --addons --module --dry-run --help"
|
||||
|
||||
if [[ ${subcmd} == run-tests ]] ; then
|
||||
COMPREPLY=( $(compgen -W "${opts}" -- ${cur}) )
|
||||
return 0
|
||||
fi
|
||||
|
||||
# oe scaffold
|
||||
|
||||
opts="--help"
|
||||
|
||||
if [[ ${subcmd} == scaffold ]] ; then
|
||||
COMPREPLY=( $(compgen -W "${opts}" -- ${cur}) )
|
||||
return 0
|
||||
fi
|
||||
|
||||
# fallback for unimplemented completion
|
||||
|
||||
opts="--help"
|
||||
|
||||
if [[ true ]] ; then
|
||||
COMPREPLY=( $(compgen -W "${opts}" -- ${cur}) )
|
||||
return 0
|
||||
fi
|
||||
}
|
||||
complete -F _oe oe
|
|
@ -1,61 +0,0 @@
|
|||
"""
|
||||
Read a record.
|
||||
"""
|
||||
import os
|
||||
import sys
|
||||
import textwrap
|
||||
|
||||
import common
|
||||
|
||||
# TODO provide a --rpc flag to use XML-RPC (with a specific username) instead
|
||||
# of server-side library.
|
||||
def run(args):
|
||||
assert args.database
|
||||
assert args.model
|
||||
import openerp
|
||||
config = openerp.tools.config
|
||||
config['log_handler'] = [':CRITICAL']
|
||||
common.set_addons(args)
|
||||
openerp.netsvc.init_logger()
|
||||
registry = openerp.modules.registry.RegistryManager.get(
|
||||
args.database, update_module=False)
|
||||
model = registry[args.model]
|
||||
field_names = [args.field] if args.field else []
|
||||
if args.short:
|
||||
# ignore --field
|
||||
field_names = ['name']
|
||||
with registry.cursor() as cr:
|
||||
xs = model.read(cr, 1, args.id, field_names, {})
|
||||
|
||||
if xs:
|
||||
print "Records (model `%s`, database `%s`):" % (args.model, args.database)
|
||||
x = xs[0]
|
||||
if args.short:
|
||||
print str(x['id']) + '.', x['name']
|
||||
else:
|
||||
longest_k = 1
|
||||
for k, v in x.items():
|
||||
longest_k = len(k) if longest_k < len(k) else longest_k
|
||||
for k, v in sorted(x.items()):
|
||||
print (k + ':').ljust(longest_k + 2), v
|
||||
else:
|
||||
print "Record not found."
|
||||
|
||||
def add_parser(subparsers):
|
||||
parser = subparsers.add_parser('read',
|
||||
description='Display a record.')
|
||||
parser.add_argument('-d', '--database', metavar='DATABASE',
|
||||
**common.required_or_default('DATABASE', 'the database to connect to'))
|
||||
common.add_addons_argument(parser)
|
||||
parser.add_argument('-m', '--model', metavar='MODEL', required=True,
|
||||
help='the model for which a record should be read')
|
||||
parser.add_argument('-i', '--id', metavar='RECORDID', required=True,
|
||||
help='the record id')
|
||||
parser.add_argument('-v', '--verbose', action='store_true',
|
||||
help='display more information')
|
||||
parser.add_argument('--short', action='store_true',
|
||||
help='display less information')
|
||||
parser.add_argument('-f', '--field', metavar='FIELD',
|
||||
help='display information only for this particular field')
|
||||
|
||||
parser.set_defaults(run=run)
|
|
@ -1,131 +0,0 @@
|
|||
"""
|
||||
Generate an OpenERP module skeleton.
|
||||
"""
|
||||
|
||||
import functools
|
||||
import keyword
|
||||
import os
|
||||
import re
|
||||
import sys
|
||||
|
||||
import jinja2
|
||||
|
||||
# FIXME: add logging
|
||||
def run(args):
|
||||
env = jinja2.Environment(loader=jinja2.PackageLoader(
|
||||
'openerpcommand', 'templates'))
|
||||
env.filters['snake'] = snake
|
||||
args.dependency = 'web' if args.controller else 'base'
|
||||
|
||||
module_name = snake(args.module)
|
||||
module = functools.partial(
|
||||
os.path.join, args.modules_dir, module_name)
|
||||
|
||||
if args.controller is True:
|
||||
args.controller = module_name
|
||||
|
||||
if args.model is True:
|
||||
args.model = module_name
|
||||
|
||||
if os.path.exists(module()):
|
||||
message = "The path `%s` already exists." % module()
|
||||
die(message)
|
||||
|
||||
dump(env, '__openerp__.jinja2', module('__openerp__.py'), config=args)
|
||||
dump(env, '__init__.jinja2', module('__init__.py'), modules=[
|
||||
args.controller and 'controllers',
|
||||
args.model and 'models'
|
||||
])
|
||||
dump(env, 'ir.model.access.jinja2', module('security', 'ir.model.access.csv'), config=args)
|
||||
|
||||
if args.controller:
|
||||
controller_module = snake(args.controller)
|
||||
dump(env, '__init__.jinja2', module('controllers', '__init__.py'), modules=[controller_module])
|
||||
dump(env, 'controllers.jinja2',
|
||||
module('controllers', '%s.py' % controller_module),
|
||||
config=args)
|
||||
|
||||
if args.model:
|
||||
model_module = snake(args.model)
|
||||
dump(env, '__init__.jinja2', module('models', '__init__.py'), modules=[model_module])
|
||||
dump(env, 'models.jinja2', module('models', '%s.py' % model_module), config=args)
|
||||
|
||||
def add_parser(subparsers):
|
||||
parser = subparsers.add_parser('scaffold',
|
||||
description='Generate an OpenERP module skeleton.')
|
||||
parser.add_argument('module', metavar='MODULE',
|
||||
help='the name of the generated module')
|
||||
parser.add_argument('modules_dir', metavar='DIRECTORY', type=directory,
|
||||
help="Modules directory in which the new module should be generated")
|
||||
|
||||
controller = parser.add_mutually_exclusive_group()
|
||||
controller.add_argument('--controller', type=identifier,
|
||||
help="The name of the controller to generate")
|
||||
controller.add_argument('--no-controller', dest='controller',
|
||||
action='store_const', const=None, help="Do not generate a controller")
|
||||
|
||||
model = parser.add_mutually_exclusive_group()
|
||||
model.add_argument('--model', type=identifier,
|
||||
help="The name of the model to generate")
|
||||
model.add_argument('--no-model', dest='model',
|
||||
action='store_const', const=None, help="Do not generate a model")
|
||||
|
||||
mod = parser.add_argument_group("Module information",
|
||||
"these are added to the module metadata and displayed on e.g. "
|
||||
"apps.openerp.com. For company-backed modules, the company "
|
||||
"information should be used")
|
||||
mod.add_argument('--name', dest='author_name', default="",
|
||||
help="Name of the module author")
|
||||
mod.add_argument('--website', dest='author_website', default="",
|
||||
help="Website of the module author")
|
||||
mod.add_argument('--category', default="Uncategorized",
|
||||
help="Broad categories to which the module belongs, used for "
|
||||
"filtering within OpenERP and on apps.openerp.com."
|
||||
"Defaults to %(default)s")
|
||||
mod.add_argument('--summary', default="",
|
||||
help="Short (1 phrase/line) summary of the module's purpose, used as "
|
||||
"subtitle on modules listing or apps.openerp.com")
|
||||
|
||||
parser.set_defaults(run=run, controller=True, model=True)
|
||||
|
||||
def snake(s):
|
||||
""" snake cases ``s``
|
||||
|
||||
:param str s:
|
||||
:return: str
|
||||
"""
|
||||
# insert a space before each uppercase character preceded by a
|
||||
# non-uppercase letter
|
||||
s = re.sub(r'(?<=[^A-Z])\B([A-Z])', r' \1', s)
|
||||
# lowercase everything, split on whitespace and join
|
||||
return '_'.join(s.lower().split())
|
||||
|
||||
def dump(env, template, dest, **kwargs):
|
||||
outdir = os.path.dirname(dest)
|
||||
if not os.path.exists(outdir):
|
||||
os.makedirs(outdir)
|
||||
env.get_template(template).stream(**kwargs).dump(dest)
|
||||
# add trailing newline which jinja removes
|
||||
with open(dest, 'a') as f:
|
||||
f.write('\n')
|
||||
|
||||
def identifier(s):
|
||||
if keyword.iskeyword(s):
|
||||
die("%s is a Python keyword and can not be used as a name" % s)
|
||||
if not re.match('[A-Za-z_][A-Za-z0-9_]*', s):
|
||||
die("%s is not a valid Python identifier" % s)
|
||||
return s
|
||||
|
||||
def directory(p):
|
||||
expanded = os.path.abspath(
|
||||
os.path.expanduser(
|
||||
os.path.expandvars(p)))
|
||||
if not os.path.exists(expanded):
|
||||
os.makedirs(expanded)
|
||||
if not os.path.isdir(expanded):
|
||||
die("%s exists but is not a directory" % p)
|
||||
return expanded
|
||||
|
||||
def die(message, code=1):
|
||||
print >>sys.stderr, message
|
||||
sys.exit(code)
|
|
@ -1,4 +0,0 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
{% for module in modules if module -%}
|
||||
import {{ module }}
|
||||
{% endfor %}
|
|
@ -1,24 +0,0 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
{
|
||||
'name': "{{ config.module }}",
|
||||
# short description, used as subtitles on modules listings
|
||||
'summary': "{{ config.summary }}",
|
||||
# long description of module purpose
|
||||
'description': """
|
||||
""",
|
||||
# Who you are
|
||||
'author': "{{ config.author_name }}",
|
||||
'website': "{{ config.author_website }}",
|
||||
|
||||
# categories can be used to filter modules in modules listing
|
||||
'category': '{{ config.category }}',
|
||||
'version': '0.1',
|
||||
|
||||
# any module necessary for this one to work correctly
|
||||
'depends': ['{{ config.dependency }}'],
|
||||
'data': [
|
||||
{{- "'security/ir.model.access.csv'" if config.model -}}
|
||||
],
|
||||
'tests': [
|
||||
],
|
||||
}
|
|
@ -1,9 +0,0 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
|
||||
from openerp import http
|
||||
from openerp.addons.web.controllers import main
|
||||
|
||||
class {{ config.controller }}(main.Home):
|
||||
@http.route('/', auth='none')
|
||||
def index(self):
|
||||
return "Hello, world!"
|
|
@ -1,6 +0,0 @@
|
|||
id,name,model_id:id,group_id:id,perm_read,perm_write,perm_create,perm_unlink
|
||||
{% if config.model -%}
|
||||
access_{{ config.module|snake }}_{{ config.model|snake }},{{- '' -}}
|
||||
access_{{ config.module|snake }}_{{ config.model|snake }},{{- '' -}}
|
||||
model_{{ config.module|snake }}_{{ config.model|snake }},,1,0,0,0
|
||||
{%- endif %}
|
|
@ -1,9 +0,0 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
from openerp.osv import orm, fields
|
||||
|
||||
class {{ config.model }}(orm.Model):
|
||||
_name = "{{ config.module|snake }}.{{ config.model|snake }}"
|
||||
|
||||
_columns = {
|
||||
'name': fields.char(),
|
||||
}
|
|
@ -1,63 +0,0 @@
|
|||
"""
|
||||
Install OpenERP on a new (by default) database.
|
||||
"""
|
||||
import os
|
||||
import sys
|
||||
|
||||
import common
|
||||
|
||||
# TODO turn template1 in a parameter
|
||||
# This should be exposed from openerp (currently in
|
||||
# openerp/service/web_services.py).
|
||||
def create_database(database_name):
|
||||
import openerp
|
||||
db = openerp.sql_db.db_connect('template1')
|
||||
cr = db.cursor() # TODO `with db as cr:`
|
||||
try:
|
||||
cr.autocommit(True)
|
||||
cr.execute("""CREATE DATABASE "%s"
|
||||
ENCODING 'unicode' TEMPLATE "template1" """ \
|
||||
% (database_name,))
|
||||
finally:
|
||||
cr.close()
|
||||
|
||||
def run(args):
|
||||
assert args.database
|
||||
assert args.module
|
||||
|
||||
import openerp
|
||||
|
||||
config = openerp.tools.config
|
||||
config['log_handler'] = [':CRITICAL']
|
||||
if args.addons:
|
||||
args.addons = args.addons.split(':')
|
||||
else:
|
||||
args.addons = []
|
||||
config['addons_path'] = ','.join(args.addons)
|
||||
openerp.netsvc.init_logger()
|
||||
|
||||
# Install the import hook, to import openerp.addons.<module>.
|
||||
openerp.modules.module.initialize_sys_path()
|
||||
|
||||
registry = openerp.modules.registry.RegistryManager.get(
|
||||
args.database, update_module=False)
|
||||
|
||||
ir_module_module = registry.get('ir.module.module')
|
||||
with registry.cursor() as cr:
|
||||
ids = ir_module_module.search(cr, openerp.SUPERUSER_ID, [('name', 'in', args.module), ('state', '=', 'installed')], {})
|
||||
if len(ids) == len(args.module):
|
||||
ir_module_module.button_immediate_uninstall(cr, openerp.SUPERUSER_ID, ids, {})
|
||||
else:
|
||||
print "At least one module not found (database `%s`)." % (args.database,)
|
||||
|
||||
def add_parser(subparsers):
|
||||
parser = subparsers.add_parser('uninstall',
|
||||
description='Uninstall some modules from an OpenERP database.')
|
||||
parser.add_argument('-d', '--database', metavar='DATABASE',
|
||||
**common.required_or_default('DATABASE', 'the database to modify'))
|
||||
common.add_addons_argument(parser)
|
||||
parser.add_argument('--module', metavar='MODULE', action='append',
|
||||
help='specify a module to uninstall'
|
||||
' (this option can be repeated)')
|
||||
|
||||
parser.set_defaults(run=run)
|
|
@ -1,19 +0,0 @@
|
|||
"""
|
||||
Update an existing OpenERP database.
|
||||
"""
|
||||
|
||||
def run(args):
|
||||
assert args.database
|
||||
import openerp
|
||||
config = openerp.tools.config
|
||||
config['update']['all'] = 1
|
||||
openerp.modules.registry.RegistryManager.get(
|
||||
args.database, update_module=True)
|
||||
|
||||
def add_parser(subparsers):
|
||||
parser = subparsers.add_parser('update',
|
||||
description='Update an existing OpenERP database.')
|
||||
parser.add_argument('-d', '--database', metavar='DATABASE', required=True,
|
||||
help='the database to update')
|
||||
|
||||
parser.set_defaults(run=run)
|
|
@ -1,93 +0,0 @@
|
|||
"""
|
||||
Run a normal OpenERP HTTP process.
|
||||
"""
|
||||
|
||||
import logging
|
||||
import os
|
||||
import signal
|
||||
|
||||
import common
|
||||
|
||||
_logger = logging.getLogger(__name__)
|
||||
|
||||
def mk_signal_handler(server):
|
||||
def signal_handler(sig, frame):
|
||||
"""
|
||||
Specialized signal handler for the evented process.
|
||||
"""
|
||||
print "\n\n\nStopping gevent HTTP server...\n\n\n"
|
||||
server.stop()
|
||||
return signal_handler
|
||||
|
||||
def setup_signal_handlers(signal_handler):
|
||||
SIGNALS = (signal.SIGINT, signal.SIGTERM)
|
||||
map(lambda sig: signal.signal(sig, signal_handler), SIGNALS)
|
||||
|
||||
def run(args):
|
||||
# Note that gevent monkey patching must be done before importing the
|
||||
# `threading` module, see http://stackoverflow.com/questions/8774958/.
|
||||
if args.gevent:
|
||||
import gevent
|
||||
import gevent.monkey
|
||||
import gevent.wsgi
|
||||
import psycogreen.gevent
|
||||
gevent.monkey.patch_all()
|
||||
psycogreen.gevent.patch_psycopg()
|
||||
import threading
|
||||
import openerp
|
||||
import openerp.cli.server
|
||||
import openerp.service.wsgi_server
|
||||
import openerp.tools.config
|
||||
config = openerp.tools.config
|
||||
|
||||
os.environ["TZ"] = "UTC"
|
||||
common.set_addons(args)
|
||||
|
||||
openerp.multi_process = True
|
||||
common.setproctitle('openerp-web')
|
||||
|
||||
openerp.cli.server.check_root_user()
|
||||
openerp.netsvc.init_logger()
|
||||
#openerp.cli.server.report_configuration()
|
||||
|
||||
target = openerp.service.wsgi_server.serve
|
||||
if not args.gevent:
|
||||
openerp.evented = False
|
||||
openerp.cli.server.setup_signal_handlers(openerp.cli.server.signal_handler)
|
||||
# TODO openerp.multi_process with a multi-threaded process probably
|
||||
# doesn't work very well (e.g. waiting for all threads to complete
|
||||
# before killing the process is not implemented).
|
||||
arg = (args.interface, int(args.port), args.threaded)
|
||||
threading.Thread(target=target, args=arg).start()
|
||||
openerp.cli.server.quit_on_signals()
|
||||
else:
|
||||
openerp.evented = True
|
||||
|
||||
app = openerp.service.wsgi_server.application
|
||||
server = gevent.wsgi.WSGIServer((args.interface, int(args.port)), app)
|
||||
setup_signal_handlers(mk_signal_handler(server))
|
||||
try:
|
||||
server.serve_forever()
|
||||
except KeyboardInterrupt:
|
||||
try:
|
||||
server.stop()
|
||||
gevent.shutdown()
|
||||
except KeyboardInterrupt:
|
||||
sys.stderr.write("Forced shutdown.\n")
|
||||
gevent.shutdown()
|
||||
|
||||
def add_parser(subparsers):
|
||||
parser = subparsers.add_parser('web',
|
||||
description='Run a normal OpenERP HTTP process. By default a '
|
||||
'singly-threaded Werkzeug server is used.')
|
||||
common.add_addons_argument(parser)
|
||||
parser.add_argument('--interface', default='0.0.0.0',
|
||||
help='HTTP interface to listen on (default is %(default)s)')
|
||||
parser.add_argument('--port', metavar='INT', default=8069,
|
||||
help='HTTP port to listen on (default is %(default)s)')
|
||||
parser.add_argument('--threaded', action='store_true',
|
||||
help='Use a multithreaded Werkzeug server (incompatible with --gevent)')
|
||||
parser.add_argument('--gevent', action='store_true',
|
||||
help="Use gevent's WSGI server (incompatible with --threaded)")
|
||||
|
||||
parser.set_defaults(run=run)
|
Before Width: | Height: | Size: 19 KiB After Width: | Height: | Size: 19 KiB |
Before Width: | Height: | Size: 12 KiB After Width: | Height: | Size: 12 KiB |
Before Width: | Height: | Size: 2.2 KiB After Width: | Height: | Size: 2.2 KiB |
Before Width: | Height: | Size: 1.6 KiB After Width: | Height: | Size: 1.6 KiB |
Before Width: | Height: | Size: 201 KiB After Width: | Height: | Size: 201 KiB |
Before Width: | Height: | Size: 139 KiB After Width: | Height: | Size: 139 KiB |
Before Width: | Height: | Size: 4.2 KiB After Width: | Height: | Size: 4.2 KiB |
Before Width: | Height: | Size: 14 KiB After Width: | Height: | Size: 14 KiB |