diff --git a/conf.py b/conf.py index dcad45257..e9613e02b 100644 --- a/conf.py +++ b/conf.py @@ -102,9 +102,6 @@ extensions = [ 'exercise_admonition', - # Build code from git patches - 'patchqueue', - # Redirection generator 'redirects', @@ -308,7 +305,7 @@ def _generate_alternate_urls(app, pagename, templatename, context, doctree): def _build_url(_version=None, _lang=None): if app.config.is_remote_build: - # Project root like https://odoo.com/documentation/14.0/fr + # Project root like https://www.odoo.com/documentation _root = app.config.project_root else: # Project root like .../documentation/_build/html/14.0/fr diff --git a/content/administration/install.rst b/content/administration/install.rst index e18956b88..692fcffe3 100644 --- a/content/administration/install.rst +++ b/content/administration/install.rst @@ -107,18 +107,7 @@ Linux Debian/Ubuntu ''''''''''''' -Odoo 13.0 'deb' package currently supports `Debian Buster`_, `Ubuntu 18.04`_ or above. - -Prepare -^^^^^^^ - -Odoo needs a `PostgreSQL`_ server to run properly. The default configuration for -the Odoo 'deb' package is to use the PostgreSQL server on the same host as your -Odoo instance. Execute the following command in order to install the PostgreSQL server: - -.. code-block:: console - - $ sudo apt install postgresql -y +Odoo 14.0 'deb' package currently supports `Debian Buster`_, `Ubuntu 20.04`_ or above. .. warning:: `wkhtmltopdf` is not installed through **pip** and must be installed manually in version `0.12.5 `_ for it to support headers and @@ -129,13 +118,13 @@ Repository ^^^^^^^^^^ Odoo S.A. provides a repository that can be used with Debian and Ubuntu distributions. It can be -used to install *Odoo Community Edition* by executing the following commands **as root**: +used to install *Odoo Community Edition* by executing the following commands: .. code-block:: console - # wget -O - https://nightly.odoo.com/odoo.key | apt-key add - - # echo "deb http://nightly.odoo.com/14.0/nightly/deb/ ./" >> /etc/apt/sources.list.d/odoo.list - # apt-get update && apt-get install odoo + $ wget -O - https://nightly.odoo.com/odoo.key | sudo apt-key add - + $ sudo sh -c 'echo "deb http://nightly.odoo.com/14.0/nightly/deb/ ./" >> /etc/apt/sources.list.d/odoo.list' + $ sudo apt-get update && sudo apt-get install odoo You can then use the usual `apt-get upgrade` command to keep your installation up-to-date. @@ -147,40 +136,25 @@ Deb Package Instead of using the repository as described above, the 'deb' packages for both the *Community* and *Enterprise* editions can be downloaded from the `official download page `_. -Next, execute the following commands **as root**: +Next, execute the following commands: .. code-block:: console - # dpkg -i # this probably fails with missing dependencies - # apt-get install -f # should install the missing dependencies - # dpkg -i + $ sudo apt-get install This will install Odoo as a service, create the necessary PostgreSQL_ user and automatically start the server. -.. warning:: The `python3-xlwt` Debian package does not exists in Debian Buster nor Ubuntu 18.04. - This python module is needed to export into xls format. - - If you need the feature, you can install it manually with: - - .. code-block:: console - - $ sudo pip3 install xlwt - -.. warning:: The `num2words` python package does not exists in Debian Buster nor Ubuntu 18.04. - Textual amounts will not be rendered by Odoo and this could cause problems with the - `l10n_mx_edi` module. - - If you need this feature, you can install manually with: - - .. code-block:: console - - $ sudo pip3 install num2words +.. note:: The recommended `postgresql-server` package will be installed by the Debian Odoo package. + The `--no-install-recommends` argument of the `apt-get` tool should be used if you plan + to install the Postgresql server on another machine. Fedora '''''' -Odoo 13.0 'rpm' package supports Fedora 30. +Odoo 14.0 'rpm' package supports Fedora 32. + +.. warning:: The Odoo 14.0 rpm package does not support Fedora 33 or above. Prepare ^^^^^^^ @@ -224,7 +198,7 @@ Once downloaded, the package can be installed using the 'dnf' package manager: .. code-block:: console - $ sudo dnf localinstall odoo_13.0.latest.noarch.rpm + $ sudo dnf localinstall odoo_14.0.latest.noarch.rpm $ sudo systemctl enable odoo $ sudo systemctl start odoo @@ -756,7 +730,7 @@ official Odoo `docker image `_ page. .. _demo: https://demo.odoo.com .. _docker: https://www.docker.com .. _download: https://www.odoo.com/page/download -.. _Ubuntu 18.04: http://releases.ubuntu.com/18.04/ +.. _Ubuntu 20.04: http://releases.ubuntu.com/20.04/ .. _EPEL: https://fedoraproject.org/wiki/EPEL .. _PostgreSQL: http://www.postgresql.org .. _the official installer: diff --git a/content/applications/finance/accounting/fiscal_localizations/localizations/argentina.rst b/content/applications/finance/accounting/fiscal_localizations/localizations/argentina.rst index c9d698bf8..2b5757b8a 100644 --- a/content/applications/finance/accounting/fiscal_localizations/localizations/argentina.rst +++ b/content/applications/finance/accounting/fiscal_localizations/localizations/argentina.rst @@ -8,6 +8,7 @@ Webinars Below you can find videos with a general description of the localization, and how to configure it. - `VIDEO WEBINAR OF A COMPLETE DEMO `_. +- `ECOMMERCE `_. Introduction ============ diff --git a/content/applications/finance/accounting/fiscal_localizations/localizations/peru.rst b/content/applications/finance/accounting/fiscal_localizations/localizations/peru.rst index 7a687e0e0..8cf890f6b 100644 --- a/content/applications/finance/accounting/fiscal_localizations/localizations/peru.rst +++ b/content/applications/finance/accounting/fiscal_localizations/localizations/peru.rst @@ -91,8 +91,8 @@ Here are some terms that are essential on the Peruvian localization: - **OSE**: Electronic Service Operator, `OSE SUNAT's definition `_. - **CDR**: Receipt certificate (Constancia de Recepción). -- **SOL Credentials**: User and password provided by the SUNAT and grants access to Online - Operations systems. +- **SOL Credentials**: Sunat Operaciones en Línea. User and password are provided by the SUNAT and + grant access to Online Operations systems. Signature Provider @@ -195,6 +195,9 @@ in your configuration. In this case you need to consider: - Provide you SOL credentials. +.. important:: + When using direct connection with the SUNAT, the SOL User must be set with the Company RUT + User + Id. Example: ``20121888549JOHNSMITH`` Testing environment ~~~~~~~~~~~~~~~~~~~ diff --git a/content/applications/productivity/iot/devices.rst b/content/applications/productivity/iot/devices.rst index 4e4082f7e..2c45a2552 100644 --- a/content/applications/productivity/iot/devices.rst +++ b/content/applications/productivity/iot/devices.rst @@ -10,3 +10,4 @@ Devices devices/camera devices/footswitch devices/printer + devices/scale diff --git a/content/applications/productivity/iot/devices/scale.rst b/content/applications/productivity/iot/devices/scale.rst new file mode 100644 index 000000000..38e01bb56 --- /dev/null +++ b/content/applications/productivity/iot/devices/scale.rst @@ -0,0 +1,55 @@ +=============== +Connect a Scale +=============== + +When using your **IoT Box** in Odoo, you could need to use a scale. Doing so is easy and convenient +as it can be done in a few steps. Then, you can use it in your **Point of Sale app** to weigh your +products, which is helpful if their price are based on it. + +Connection +========== + +To link the scale to the **IoT Box**, connect them with a cable. + +.. note:: + In some cases, a serial to USB adapter may be needed. + +If your scale is `compatibale with Odoo IoT Box `_, there +is no need to set up anything because it will be automatically detected as soon as it is connected. + +.. image:: scale/iot-choice.png + :align: center + :alt: IOT box auto detection. + +You may need to restart the box and download your scales’ drivers from the box in some cases. To do +so, go to the *IoT Box Home Page* and click on *drivers list*. Then, click on load drivers. + +.. image:: scale/driver-list.png + :align: center + :alt: View of the IoT box settings and driver list. + +Use a Scale in Point of Sale +============================ + +To use the scale in your *Point of Sale* app, go to :menuselection:`Point of Sale --> Configuration +--> Point of Sale`, open the one you want to configure, then click on *Edit* and enable the *IoT +Box* feature. + +.. image:: scale/iot-box-pos.png + :align: center + :alt: View of the IoT box feature inside of the PoS settings. + +Now, choose the *IoT Box* in the dropdown menu and check the *Electronic Scale* option. Then, you +hit save. + +.. image:: scale/electronic-scale-feature.png + :align: center + :alt: List of the external tools that can be used with PoS and the IoT box. + +The scale is now available in all your *PoS* sessions. Then, if a product has a price per weight +set, clicking on it on the *PoS* screen opens the scale screen, where the cashier can weigh the +product and add the correct price to the cart. + +.. image:: scale/scale-view.png + :align: center + :alt: Electronic Scale dashboard view when no items are being weighed. \ No newline at end of file diff --git a/content/applications/productivity/iot/devices/scale/driver-list.png b/content/applications/productivity/iot/devices/scale/driver-list.png new file mode 100644 index 000000000..d87a4cca9 Binary files /dev/null and b/content/applications/productivity/iot/devices/scale/driver-list.png differ diff --git a/content/applications/productivity/iot/devices/scale/electronic-scale-feature.png b/content/applications/productivity/iot/devices/scale/electronic-scale-feature.png new file mode 100644 index 000000000..6bd38692b Binary files /dev/null and b/content/applications/productivity/iot/devices/scale/electronic-scale-feature.png differ diff --git a/content/applications/productivity/iot/devices/scale/iot-box-pos.png b/content/applications/productivity/iot/devices/scale/iot-box-pos.png new file mode 100644 index 000000000..147fc16e2 Binary files /dev/null and b/content/applications/productivity/iot/devices/scale/iot-box-pos.png differ diff --git a/content/applications/productivity/iot/devices/scale/iot-choice.png b/content/applications/productivity/iot/devices/scale/iot-choice.png new file mode 100644 index 000000000..8021bcfaa Binary files /dev/null and b/content/applications/productivity/iot/devices/scale/iot-choice.png differ diff --git a/content/applications/productivity/iot/devices/scale/scale-view.png b/content/applications/productivity/iot/devices/scale/scale-view.png new file mode 100644 index 000000000..49437b31a Binary files /dev/null and b/content/applications/productivity/iot/devices/scale/scale-view.png differ diff --git a/content/applications/websites/ecommerce.rst b/content/applications/websites/ecommerce.rst index 16fc2b02c..1365f41e0 100644 --- a/content/applications/websites/ecommerce.rst +++ b/content/applications/websites/ecommerce.rst @@ -6,7 +6,6 @@ eCommerce .. toctree:: :titlesonly: - ecommerce/overview ecommerce/getting_started ecommerce/managing_products ecommerce/taxes diff --git a/content/applications/websites/ecommerce/overview.rst b/content/applications/websites/ecommerce/overview.rst deleted file mode 100644 index 820f9d47c..000000000 --- a/content/applications/websites/ecommerce/overview.rst +++ /dev/null @@ -1,9 +0,0 @@ -================= -Overview -================= - -.. toctree:: - :titlesonly: - - overview/introduction - diff --git a/content/applications/websites/ecommerce/overview/introduction.rst b/content/applications/websites/ecommerce/overview/introduction.rst deleted file mode 100644 index 1d34c7717..000000000 --- a/content/applications/websites/ecommerce/overview/introduction.rst +++ /dev/null @@ -1,24 +0,0 @@ -============================== -Introduction to Odoo eCommerce -============================== - -.. youtube:: tR0xandHlhU - :align: right - :width: 700 - :height: 394 - -The documentation will help you go live with your eCommerce website in no time. -The topics follow the buying process: - -* Product Page -* Shop Page -* Pricing -* Taxes -* Checkout process -* Upselling & cross-selling -* Payment -* Shipping & Tracking - -.. seealso:: - - * :doc:`../../website/publish/domain_name` diff --git a/content/applications/websites/ecommerce/shopper_experience/media/payment_capture.png b/content/applications/websites/ecommerce/shopper_experience/media/payment_capture.png deleted file mode 100644 index 6c221a835..000000000 Binary files a/content/applications/websites/ecommerce/shopper_experience/media/payment_capture.png and /dev/null differ diff --git a/content/applications/websites/ecommerce/shopper_experience/media/payment_capture_mode.png b/content/applications/websites/ecommerce/shopper_experience/media/payment_capture_mode.png deleted file mode 100644 index 26f5ef099..000000000 Binary files a/content/applications/websites/ecommerce/shopper_experience/media/payment_capture_mode.png and /dev/null differ diff --git a/content/applications/websites/ecommerce/shopper_experience/media/payment_invoice.png b/content/applications/websites/ecommerce/shopper_experience/media/payment_invoice.png deleted file mode 100644 index 5e57bb1bf..000000000 Binary files a/content/applications/websites/ecommerce/shopper_experience/media/payment_invoice.png and /dev/null differ diff --git a/content/applications/websites/ecommerce/shopper_experience/media/payment_transaction.png b/content/applications/websites/ecommerce/shopper_experience/media/payment_transaction.png deleted file mode 100644 index 1758e77bd..000000000 Binary files a/content/applications/websites/ecommerce/shopper_experience/media/payment_transaction.png and /dev/null differ diff --git a/content/applications/websites/ecommerce/shopper_experience/payment_acquirer.rst b/content/applications/websites/ecommerce/shopper_experience/payment_acquirer.rst index 6fc7dd9b3..9277b0b85 100644 --- a/content/applications/websites/ecommerce/shopper_experience/payment_acquirer.rst +++ b/content/applications/websites/ecommerce/shopper_experience/payment_acquirer.rst @@ -1,78 +1,62 @@ -=================================================== -How to manage orders paid with payment acquirers -=================================================== +========================================= +Manage orders paid with Payment Acquirers +========================================= -Odoo confirms orders automatically as soon as the payment is authorized -by a payment acquirer. This triggers the delivery. -If you invoice based on ordered quantities, -you are also requested to invoice the order. +The moment a payment is officially authorized by a Payment Acquirer, Odoo *automatically* confirms +the order, which triggers the delivery. And, if you invoice based on ordered quantities, you are +requested to invoice the order, as well. +Let’s take a closer look at how to manage orders paid with Payment Acquirers. -What are the payment status -=========================== -At anytime, the salesman can check the transaction status from the order. +Checking the status of a payment +================================ -.. image:: media/payment_transaction.png - :align: center +To check the status of a payment, go to :menuselection:`Website --> Orders --> Orders`. Then, simply +click on the order you wish to check on. -* *Draft*: transaction under processing. +Once you are on the Sales Order page, you will find the payment is confirmed with an automatic note +in the *Chatter*. -* *Pending*: the payment acquirer keeps the transaction on hold and you - need to authorize it from the acquirer interface. +.. image:: payment_acquirer/chatter-transaction.png + :align: center + :alt: payment is confirmed in the chatter of sales order -* *Authorized*: the payment has been authorized but not yet captured. - In Odoo, the order is already confirmed. Once the delivery done, you - can capture the amount from the acquirer interface (or from Odoo if you use - Authorize.net). +If the user decides to create an invoice, the payment is directly reconciled. This note in +the *Chatter* includes a link to the Payment entry, which contains various details about the +transaction, along with a link to the related Journal Entry. -* *Done*: the payment is authorized and captured. The order has been confirmed. - -* *Error*: an error has occured during the transaction. - The customer needs to retry the payment. - The order is still in draft. - -* *Cancelled*: when the customer cancels the payment in the payment acquirer form. - They are taken back to Odoo in order to modify the order. +.. image:: payment_acquirer/transaction-info.png + :align: center + :alt: page with details surrounding the specific transaction .. note:: Specific messages are provided to your customers for every - payment status, when they are redirected to Odoo after the transaction. - To edit such messages, go to the *Messages* tab of the payment + payment status whenever they are redirected to Odoo after the transaction. + To edit these messages, go to the *Messages* tab of the payment method. +Automatically generate invoices at order +======================================== -Auto-validate invoices at order -=============================== +When the order is confirmed, you can also choose to have an invoice automatically issued +and paid. This fully-automated feature is designed for businesses that invoice +orders right away. -When the order is confirmed you can also have an invoice automatically issued -and paid. This fully-automated made for businesses that invoice -orders straight on. +To do automatically generate invoices at order: + - Go to :menuselection:`Website --> Configuration --> Settings --> Invoicing`. + - Then, under the **Invoicing Policy** option, select *Invoice what is ordered*. + - Then activate *Automatic Invoices* and *Save*. -.. image:: media/payment_invoice.png - :align: center +.. image:: payment_acquirer/automatic-invoice.png + :align: center + :alt: example of automatic invoice -.. note:: - If you choose this mode you are requested to select a payment journal in order to record payments - in your books. This payment is automatically reconcilied with the invoice, marking it as paid. - Select your **bank account** if you get paid immediately on your bank account. If you don't you - can create a specific journal for the payment acquirer (type = Bank). That way, you can track - online payments in an intermediary account of your books until you get paid into your bank - account (see :doc:`../../../general/payment_acquirers/payment_acquirers`). +Capture payment after the delivery +================================== +If the acquirer handling the payment is configured to capture amounts manually, the order is +confirmed, but the amount is kept on hold. Once the delivery is processed, you can capture the +payment from the related Sales Order. -Capture the payment after the delivery -====================================== -With this mode, the order is confirmed but the amount is kept on hold. -Once the delivery processed, you can capture the payment from Odoo. -This mode is only available with Authorize.net. - -.. image:: media/payment_capture_mode.png - :align: center - -To capture the payment, open the transaction from the order. -Then click *Capture Transaction*. - -.. image:: media/payment_capture.png - :align: center - -With other payment acquirers, you can manage the capture in -their own interfaces, not from Odoo. \ No newline at end of file +.. seealso:: + - :doc:`../../../general/payment_acquirers/payment_acquirers` + - :ref:`Payment Acquirers: Place a hold on a card ` diff --git a/content/applications/websites/ecommerce/shopper_experience/payment_acquirer/automatic-invoice.png b/content/applications/websites/ecommerce/shopper_experience/payment_acquirer/automatic-invoice.png new file mode 100644 index 000000000..644eae1d7 Binary files /dev/null and b/content/applications/websites/ecommerce/shopper_experience/payment_acquirer/automatic-invoice.png differ diff --git a/content/applications/websites/ecommerce/shopper_experience/payment_acquirer/chatter-transaction.png b/content/applications/websites/ecommerce/shopper_experience/payment_acquirer/chatter-transaction.png new file mode 100644 index 000000000..2c7d5d750 Binary files /dev/null and b/content/applications/websites/ecommerce/shopper_experience/payment_acquirer/chatter-transaction.png differ diff --git a/content/applications/websites/ecommerce/shopper_experience/payment_acquirer/transaction-info.png b/content/applications/websites/ecommerce/shopper_experience/payment_acquirer/transaction-info.png new file mode 100644 index 000000000..5a8117006 Binary files /dev/null and b/content/applications/websites/ecommerce/shopper_experience/payment_acquirer/transaction-info.png differ diff --git a/content/developer/howtos/backend.rst b/content/developer/howtos/backend.rst index b241be704..09eac057f 100644 --- a/content/developer/howtos/backend.rst +++ b/content/developer/howtos/backend.rst @@ -1,6 +1,4 @@ -.. queue:: backend/series - .. _howto/base: .. _howto/module: @@ -114,14 +112,6 @@ or XML. The usage of most of those files will be explained along this tutorial. Use the command line above to create an empty module Open Academy, and install it in Odoo. - .. only:: solutions - - #. Invoke the command ``odoo-bin scaffold openacademy addons``. - #. Adapt the manifest file to your module. - #. Don't bother about the other files. - - .. patch:: - Object-Relational Mapping ------------------------- @@ -216,12 +206,6 @@ overridden by setting :attr:`~odoo.models.Model._rec_name`. Define a new data model *Course* in the *openacademy* module. A course has a title and a description. Courses must have a title. - .. only:: solutions - - Edit the file ``openacademy/models/models.py`` to include a *Course* class. - - .. patch:: - Data files ---------- @@ -260,12 +244,6 @@ be declared in the ``'data'`` list (always loaded) or in the ``'demo'`` list Create demonstration data filling the *Courses* model with a few demonstration courses. - .. only:: solutions - - Edit the file ``openacademy/demo/demo.xml`` to include some data. - - .. patch:: - .. tip:: The content of the data files is only loaded when a module is installed or updated. @@ -315,14 +293,6 @@ action more easily. - display a list of all the courses - create/modify courses - .. only:: solutions - - #. Create ``openacademy/views/openacademy.xml`` with an action and - the menus triggering the action - #. Add it to the ``data`` list of ``openacademy/__manifest__.py`` - - .. patch:: - Basic views =========== @@ -417,22 +387,12 @@ elements (groups, notebooks) and interactive elements (buttons and fields): Create your own form view for the Course object. Data displayed should be: the name and the description of the course. - .. only:: solutions - - .. patch:: - .. exercise:: Notebooks In the Course form view, put the description field under a tab, such that it will be easier to add other tabs later, containing additional information. - .. only:: solutions - - Modify the Course form view as follows: - - .. patch:: - Form views can also use plain HTML for more flexible layouts: .. code-block:: xml @@ -480,10 +440,6 @@ searching on the ``name`` field. Allow searching for courses based on their title or their description. - .. only:: solutions - - .. patch:: - Relations between models ======================== @@ -500,18 +456,6 @@ client data; it is also related to its sale order line records. duration and a number of seats. Add an action and a menu item to display them. Make the new model visible via a menu item. - .. only:: solutions - - #. Create the class *Session* in ``openacademy/models/models.py``. - #. Add access to the session object in ``openacademy/view/openacademy.xml``. - - .. patch:: - - .. note:: ``digits=(6, 2)`` specifies the precision of a float number: - 6 is the total number of digits, while 2 is the number of - digits after the comma. Note that it results in the number - digits before the comma is a maximum 4 - Relational fields ----------------- @@ -562,25 +506,11 @@ Relational field types are: of the model ``openacademy.course`` and is required. - Adapt the views. - .. only:: solutions - - #. Add the relevant ``Many2one`` fields to the models, and - #. add them in the views. - - .. patch:: - .. exercise:: Inverse one2many relations Using the inverse relational field one2many, modify the models to reflect the relation between courses and sessions. - .. only:: solutions - - #. Modify the ``Course`` class, and - #. add the field in the course form view. - - .. patch:: - .. exercise:: Multiple many2many relations Using the relational field many2many, modify the *Session* model to relate @@ -588,13 +518,6 @@ Relational field types are: partner records, so we will relate to the built-in model ``res.partner``. Adapt the views accordingly. - .. only:: solutions - - #. Modify the ``Session`` class, and - #. add the field in the form view. - - .. patch:: - Inheritance =========== @@ -694,21 +617,6 @@ instead of a single view its ``arch`` field is composed of any number of the session-partner relation * Using view inheritance, display this fields in the partner form view - .. only:: solutions - - .. note:: - - This is the opportunity to introduce the developer mode to - inspect the view, find its external ID and the place to put the - new field. - - #. Create a file ``openacademy/models/partner.py`` and import it in - ``__init__.py`` - #. Create a file ``openacademy/views/partner.xml`` and add it to - ``__manifest__.py`` - - .. patch:: - Domains ####### @@ -741,31 +649,12 @@ records for the relation when trying to select records in the client interface. When selecting the instructor for a *Session*, only instructors (partners with ``instructor`` set to ``True``) should be visible. - .. only:: solutions - - .. patch:: - - .. note:: - - A domain declared as a literal list is evaluated server-side and - can't refer to dynamic values on the right-hand side, a domain - declared as a string is evaluated client-side and allows - field names on the right-hand side - .. exercise:: More complex domains Create new partner categories *Teacher / Level 1* and *Teacher / Level 2*. The instructor for a session can be either an instructor or a teacher (of any level). - .. only:: solutions - - #. Modify the *Session* model's domain - #. Modify ``openacademy/view/partner.xml`` to get access to - *Partner categories*: - - .. patch:: - Computed fields and default values ================================== @@ -834,13 +723,6 @@ field whenever some of its dependencies have been modified:: * Display that field in the tree and form views * Display the field as a progress bar - .. only:: solutions - - #. Add a computed field to *Session* - #. Show the field in the *Session* view: - - .. patch:: - Default values -------------- @@ -871,15 +753,6 @@ float, string), or a function taking a recordset and returning a value:: * Add a field ``active`` in the class Session, and set sessions as active by default. - .. only:: solutions - - .. patch:: - - .. note:: - - Odoo has built-in rules making records with an ``active`` field set - to ``False`` invisible. - Onchange ======== @@ -925,10 +798,6 @@ the ``taken_seats`` progressbar is automatically updated. Add an explicit onchange to warn about invalid values, like a negative number of seats, or more participants than seats. - .. only:: solutions - - .. patch:: - Model constraints ================= @@ -956,10 +825,6 @@ raise an exception if its invariant is not satisfied:: Add a constraint that checks that the instructor is not present in the attendees of his/her own session. - .. only:: solutions - - .. patch:: - SQL constraints are defined through the model attribute :attr:`~odoo.models.Model._sql_constraints`. The latter is assigned to a list of triples of strings ``(name, sql_definition, message)``, where ``name`` is a @@ -974,10 +839,6 @@ and ``message`` is the error message. #. CHECK that the course description and the course title are different #. Make the Course's name UNIQUE - .. only:: solutions - - .. patch:: - .. exercise:: Exercise 6 - Add a duplicate option Since we added a constraint for the Course name uniqueness, it is not @@ -987,10 +848,6 @@ and ``message`` is the error message. Re-implement your own "copy" method which allows to duplicate the Course object, changing the original name into "Copy of [original name]". - .. only:: solutions - - .. patch:: - Advanced Views ============== @@ -1038,12 +895,6 @@ behavior: 5 days are colored blue, and the ones lasting more than 15 days are colored red. - .. only:: solutions - - Modify the session tree view: - - .. patch:: - Calendars --------- @@ -1073,19 +924,6 @@ their most common attributes are: Add a Calendar view to the *Session* model enabling the user to view the events associated to the Open Academy. - .. only:: solutions - - #. Add an ``end_date`` field computed from ``start_date`` and - ``duration`` - - .. tip:: the inverse function makes the field writable, and allows - moving the sessions (via drag and drop) in the calendar view - - #. Add a calendar view to the *Session* model - #. And add the calendar view to the *Session* model's actions - - .. patch:: - Search views ------------ @@ -1135,10 +973,6 @@ default and behave as booleans (they can only be enabled by default). responsible in the course search view. Make it selected by default. #. Add a button to group courses by responsible user. - .. only:: solutions - - .. patch:: - Gantt ----- @@ -1163,13 +997,6 @@ their root element is ````. Add a Gantt Chart enabling the user to view the sessions scheduling linked to the Open Academy module. The sessions should be grouped by instructor. - .. only:: solutions - - #. Add the gantt view's definition, and add the gantt view to the - *Session* model's action - - .. patch:: - Graph views ----------- @@ -1221,13 +1048,6 @@ the values: Add a Graph view in the Session object that displays, for each course, the number of attendees under the form of a bar chart. - .. only:: solutions - - #. Add the number of attendees as a stored computed field - #. Then add the relevant view - - .. patch:: - Kanban ------ @@ -1248,13 +1068,6 @@ Kanban views define the structure of each card as a mix of form elements Add a Kanban view that displays sessions grouped by course (columns are thus courses). - .. only:: solutions - - #. Add an integer ``color`` field to the *Session* model - #. Add the kanban view and update the action - - .. patch:: - Security ======== @@ -1291,16 +1104,6 @@ rights are usually created by a CSV file named after its model: Create a new user "John Smith". Then create a group "OpenAcademy / Session Read" with read access to the *Session* model. - .. only:: solutions - - #. Create a new user *John Smith* through - :menuselection:`Settings --> Users --> Users` - #. Create a new group ``session_read`` through - :menuselection:`Settings --> Users --> Groups`, it should have - read access on the *Session* model - #. Edit *John Smith* to make them a member of ``session_read`` - #. Log in as *John Smith* to check the access rights are correct - .. exercise:: Add access control through data files in your module Using data files, @@ -1309,17 +1112,6 @@ rights are usually created by a CSV file named after its model: OpenAcademy models * Make *Session* and *Course* readable by all users - .. only:: solutions - - #. Create a new file ``openacademy/security/security.xml`` to - hold the OpenAcademy Manager group - #. Edit the file ``openacademy/security/ir.model.access.csv`` with - the access rights to the models - #. Finally update ``openacademy/__manifest__.py`` to add the new data - files to it - - .. patch:: - Record rules ------------ @@ -1353,14 +1145,6 @@ the same convention as the method :meth:`~odoo.models.Model.write` of the ORM. to the responsible of a course. If a course has no responsible, all users of the group must be able to modify it. - .. only:: solutions - - Create a new rule in ``openacademy/security/security.xml``: - - .. patch:: - -.. _howto/module/wizard: - Wizards ======= @@ -1386,12 +1170,6 @@ session, or for a list of sessions at once. Create a wizard model with a many2one relationship with the *Session* model and a many2many relationship with the *Partner* model. - .. only:: solutions - - Add a new file ``openacademy/wizard.py``: - - .. patch:: - Launching wizards ----------------- @@ -1430,28 +1208,16 @@ the action is "bound" to. #. Define a default value for the session field in the wizard; use the context parameter ``self._context`` to retrieve the current session. - .. only:: solutions - - .. patch:: - .. exercise:: Register attendees Add buttons to the wizard, and implement the corresponding method for adding the attendees to the given session. - .. only:: solutions - - .. patch:: - .. exercise:: Register attendees to multiple sessions Modify the wizard model so that attendees can be registered to multiple sessions. - .. only:: solutions - - .. patch:: - Internationalization ==================== @@ -1491,39 +1257,6 @@ for editing and merging PO/POT files. Choose a second language for your Odoo installation. Translate your module using the facilities provided by Odoo. - .. only:: solutions - - #. Create a directory ``openacademy/i18n/`` - #. You will need to activate the developer mode - to access the menus mentioned below ( - :menuselection:`Settings --> Activate the developer mode` - ) - #. Install whichever language you want ( - :menuselection:`Settings --> Translations --> Languages`) - #. Generate the missing terms (:menuselection:`Settings --> - Translations --> Application Terms --> Generate Missing Terms`) - #. Create a template translation file by exporting ( - :menuselection:`Settings --> Translations --> Import/Export - --> Export Translation`) without specifying a language, save in - ``openacademy/i18n/`` - #. Create a translation file by exporting ( - :menuselection:`Settings --> Translations --> Import/Export - --> Export Translation`) and specifying a language. Save it in - ``openacademy/i18n/`` - #. Open the exported translation file (with a basic text editor or a - dedicated PO-file editor e.g. POEdit_ and translate the missing - terms - - #. In ``models.py``, add an import statement for the function - ``odoo._`` and mark missing strings as translatable - - #. Repeat steps 3-6 - - .. patch:: - - .. todo:: do we never reload translations? - - Reporting ========= @@ -1623,10 +1356,6 @@ http://localhost:8069/report/pdf/account.report_invoice/1. For each session, it should display session's name, its start and end, and list the session's attendees. - .. only:: solutions - - .. patch:: - Dashboards ---------- @@ -1638,21 +1367,6 @@ Dashboards and automatically displayed in the web client when the OpenAcademy main menu is selected. - .. only:: solutions - - #. Create a file ``openacademy/views/session_board.xml``. It should contain - the board view, the actions referenced in that view, an action to - open the dashboard and a re-definition of the main menu item to add - the dashboard action - - .. note:: Available dashboard styles are ``1``, ``1-1``, ``1-2``, - ``2-1`` and ``1-1-1`` - - #. Update ``openacademy/__manifest__.py`` to reference the new data - file - - .. patch:: - WebServices =========== diff --git a/content/developer/howtos/backend/exercise-access-rights b/content/developer/howtos/backend/exercise-access-rights deleted file mode 100644 index 465dffea8..000000000 --- a/content/developer/howtos/backend/exercise-access-rights +++ /dev/null @@ -1,40 +0,0 @@ -# HG changeset patch -# Parent 303a5f4f011822dcb42b5833d579eabd3f03f4bf - -Index: addons/openacademy/__manifest__.py -=================================================================== ---- addons.orig/openacademy/__manifest__.py 2014-08-26 17:26:18.143783102 +0200 -+++ addons/openacademy/__manifest__.py 2014-08-26 17:26:18.135783102 +0200 -@@ -25,7 +25,8 @@ - - # always loaded - 'data': [ -- # 'security/ir.model.access.csv', -+ 'security/security.xml', -+ 'security/ir.model.access.csv', - 'templates.xml', - 'views/openacademy.xml', - 'views/partner.xml', -Index: addons/openacademy/security/ir.model.access.csv -=================================================================== ---- addons.orig/openacademy/security/ir.model.access.csv 2014-08-26 17:26:18.143783102 +0200 -+++ addons/openacademy/security/ir.model.access.csv 2014-08-26 17:26:18.135783102 +0200 -@@ -1,2 +1,5 @@ - id,name,model_id/id,group_id/id,perm_read,perm_write,perm_create,perm_unlink --access_openacademy_openacademy,openacademy.openacademy,model_openacademy_openacademy,,1,0,0,0 -+course_manager,course manager,model_openacademy_course,group_manager,1,1,1,1 -+session_manager,session manager,model_openacademy_session,group_manager,1,1,1,1 -+course_read_all,course all,model_openacademy_course,,1,0,0,0 -+session_read_all,session all,model_openacademy_session,,1,0,0,0 -Index: addons/openacademy/security/security.xml -=================================================================== ---- /dev/null 1970-01-01 00:00:00.000000000 +0000 -+++ addons/openacademy/security/security.xml 2014-08-26 17:26:18.135783102 +0200 -@@ -0,0 +1,7 @@ -+ -+ -+ -+ OpenAcademy / Manager -+ -+ -+ diff --git a/content/developer/howtos/backend/exercise-access-rules b/content/developer/howtos/backend/exercise-access-rules deleted file mode 100644 index d6290dfdb..000000000 --- a/content/developer/howtos/backend/exercise-access-rules +++ /dev/null @@ -1,27 +0,0 @@ -# HG changeset patch -# Parent 0602022dc2a428f9995c886df33b699b6d3bcb69 - -Index: addons/openacademy/security/security.xml -=================================================================== ---- addons.orig/openacademy/security/security.xml 2014-08-26 17:26:18.971783090 +0200 -+++ addons/openacademy/security/security.xml 2014-08-26 17:26:18.967783090 +0200 -@@ -3,5 +3,19 @@ - - OpenAcademy / Manager - -+ -+ -+ Only Responsible can modify Course -+ -+ -+ -+ -+ -+ -+ -+ ['|', ('responsible_id','=',False), -+ ('responsible_id','=',user.id)] -+ -+ - - diff --git a/content/developer/howtos/backend/exercise-advanced-treeview b/content/developer/howtos/backend/exercise-advanced-treeview deleted file mode 100644 index 69ddd66cf..000000000 --- a/content/developer/howtos/backend/exercise-advanced-treeview +++ /dev/null @@ -1,19 +0,0 @@ -# HG changeset patch -# Parent f8d2422e87b3ff566dc947ad582608db3b15e077 - -Index: addons/openacademy/views/openacademy.xml -=================================================================== ---- addons.orig/openacademy/views/openacademy.xml 2014-08-26 17:26:09.283783234 +0200 -+++ addons/openacademy/views/openacademy.xml 2014-08-26 17:26:09.279783234 +0200 -@@ -115,9 +115,10 @@ - session.tree - openacademy.session - -- -+ - - -+ - - - diff --git a/content/developer/howtos/backend/exercise-basic-action b/content/developer/howtos/backend/exercise-basic-action deleted file mode 100644 index 65e788b80..000000000 --- a/content/developer/howtos/backend/exercise-basic-action +++ /dev/null @@ -1,53 +0,0 @@ -# HG changeset patch -# Parent 16e4cb131d9f7f3a72a8a1b0bc46c2ce9ac76435 -Index: addons/openacademy/__manifest__.py -=================================================================== ---- addons.orig/openacademy/__manifest__.py 2014-08-26 17:25:53.519783468 +0200 -+++ addons/openacademy/__manifest__.py 2014-08-26 17:25:53.511783468 +0200 -@@ -27,6 +27,7 @@ - 'data': [ - # 'security/ir.model.access.csv', - 'templates.xml', -+ 'views/openacademy.xml', - ], - # only loaded in demonstration mode - 'demo': [ -Index: addons/openacademy/views/openacademy.xml -=================================================================== ---- /dev/null 1970-01-01 00:00:00.000000000 +0000 -+++ addons/openacademy/views/openacademy.xml 2014-08-26 17:25:53.511783468 +0200 -@@ -0,0 +1,34 @@ -+ -+ -+ -+ -+ -+ -+ Courses -+ openacademy.course -+ tree,form -+ -+

Create the first course -+

-+
-+
-+ -+ -+ -+ -+ -+ -+ -+ -+ -+
diff --git a/content/developer/howtos/backend/exercise-calendar b/content/developer/howtos/backend/exercise-calendar deleted file mode 100644 index 56689b022..000000000 --- a/content/developer/howtos/backend/exercise-calendar +++ /dev/null @@ -1,77 +0,0 @@ -# HG changeset patch -# Parent 85a8d7317b9e13480f39ad739955442d15144451 -# Parent 16fcdc4c6462a7872636f3c19550c16879af5281 - -diff --git a/openacademy/models.py b/openacademy/models.py ---- a/openacademy/models.py -+++ b/openacademy/models.py -@@ -1,5 +1,6 @@ - # -*- coding: utf-8 -*- - -+from datetime import timedelta - from odoo import models, fields, api, exceptions - - class Course(models.Model): -@@ -57,6 +58,8 @@ class Session(models.Model): - attendee_ids = fields.Many2many('res.partner', string="Attendees") - - taken_seats = fields.Float(string="Taken seats", compute='_taken_seats') -+ end_date = fields.Date(string="End Date", store=True, -+ compute='_get_end_date', inverse='_set_end_date') - - @api.depends('seats', 'attendee_ids') - def _taken_seats(self): -@@ -83,6 +86,27 @@ class Session(models.Model): - }, - } - -+ @api.depends('start_date', 'duration') -+ def _get_end_date(self): -+ for r in self: -+ if not (r.start_date and r.duration): -+ r.end_date = r.start_date -+ continue -+ -+ # Add duration to start_date, but: Monday + 5 days = Saturday, so -+ # subtract one second to get on Friday instead -+ duration = timedelta(days=r.duration, seconds=-1) -+ r.end_date = r.start_date + duration -+ -+ def _set_end_date(self): -+ for r in self: -+ if not (r.start_date and r.end_date): -+ continue -+ -+ # Compute the difference between dates, but: Friday - Monday = 4 days, -+ # so add one day to get 5 days instead -+ r.duration = (r.end_date - r.start_date).days + 1 -+ - @api.constrains('instructor_id', 'attendee_ids') - def _check_instructor_not_in_attendees(self): - for r in self: -diff --git a/openacademy/views/openacademy.xml b/openacademy/views/openacademy.xml ---- a/openacademy/views/openacademy.xml -+++ b/openacademy/views/openacademy.xml -@@ -124,10 +124,21 @@ - - - -+ -+ -+ session.calendar -+ openacademy.session -+ -+ -+ -+ -+ -+ -+ - - Sessions - openacademy.session -- tree,form -+ tree,form,calendar - - - - - -+ - - - - diff --git a/content/developer/howtos/backend/exercise-constraint-python b/content/developer/howtos/backend/exercise-constraint-python deleted file mode 100644 index 248507a24..000000000 --- a/content/developer/howtos/backend/exercise-constraint-python +++ /dev/null @@ -1,25 +0,0 @@ -# HG changeset patch -# Parent 7a7d003fe38426a405ce0657a627a139133ec4dd -# Parent 52f54b46487c8224a5aade4b921be77360ed3eae - -diff --git a/openacademy/models.py b/openacademy/models.py ---- a/openacademy/models.py -+++ b/openacademy/models.py -@@ -1,6 +1,6 @@ - # -*- coding: utf-8 -*- - --from odoo import models, fields, api -+from odoo import models, fields, api, exceptions - - class Course(models.Model): - _name = 'openacademy.course' -@@ -58,3 +58,9 @@ class Session(models.Model): - 'message': "Increase seats or remove excess attendees", - }, - } -+ -+ @api.constrains('instructor_id', 'attendee_ids') -+ def _check_instructor_not_in_attendees(self): -+ for r in self: -+ if r.instructor_id and r.instructor_id in r.attendee_ids: -+ raise exceptions.ValidationError("A session's instructor can't be an attendee") diff --git a/content/developer/howtos/backend/exercise-constraint-sql b/content/developer/howtos/backend/exercise-constraint-sql deleted file mode 100644 index 5595a6db3..000000000 --- a/content/developer/howtos/backend/exercise-constraint-sql +++ /dev/null @@ -1,24 +0,0 @@ -# HG changeset patch -# Parent 121bbfe120be3007f5e04611dbc27038abafcce8 - -Index: addons/openacademy/models.py -=================================================================== ---- addons.orig/openacademy/models.py -+++ addons/openacademy/models.py -@@ -14,6 +14,16 @@ - session_ids = fields.One2many( - 'openacademy.session', 'course_id', string="Sessions") - -+ _sql_constraints = [ -+ ('name_description_check', -+ 'CHECK(name != description)', -+ "The title of the course should not be the description"), -+ -+ ('name_unique', -+ 'UNIQUE(name)', -+ "The course title must be unique"), -+ ] -+ - - class Session(models.Model): - _name = 'openacademy.session' diff --git a/content/developer/howtos/backend/exercise-copy-override b/content/developer/howtos/backend/exercise-copy-override deleted file mode 100644 index 119ed8ca2..000000000 --- a/content/developer/howtos/backend/exercise-copy-override +++ /dev/null @@ -1,27 +0,0 @@ -# HG changeset patch -# Parent 7d14b75cdfd4c7a272a13572947de5d47f3e851f -# Parent f400352a70963801f0b4732d33a0183e4f6800ff - -diff --git a/openacademy/models.py b/openacademy/models.py ---- a/openacademy/models.py -+++ b/openacademy/models.py -@@ -14,6 +14,19 @@ class Course(models.Model): - session_ids = fields.One2many( - 'openacademy.session', 'course_id', string="Sessions") - -+ def copy(self, default=None): -+ default = dict(default or {}) -+ -+ copied_count = self.search_count( -+ [('name', '=like', u"Copy of {}%".format(self.name))]) -+ if not copied_count: -+ new_name = u"Copy of {}".format(self.name) -+ else: -+ new_name = u"Copy of {} ({})".format(self.name, copied_count) -+ -+ default['name'] = new_name -+ return super(Course, self).copy(default) -+ - _sql_constraints = [ - ('name_description_check', - 'CHECK(name != description)', diff --git a/content/developer/howtos/backend/exercise-creation b/content/developer/howtos/backend/exercise-creation deleted file mode 100644 index f5510aa87..000000000 --- a/content/developer/howtos/backend/exercise-creation +++ /dev/null @@ -1,152 +0,0 @@ -# HG changeset patch -# Parent 0000000000000000000000000000000000000000 -Index: addons/openacademy/__manifest__.py -=================================================================== ---- /dev/null 1970-01-01 00:00:00.000000000 +0000 -+++ addons/openacademy/__manifest__.py 2014-08-26 17:25:49.787783523 +0200 -@@ -0,0 +1,35 @@ -+# -*- coding: utf-8 -*- -+{ -+ 'name': "Open Academy", -+ -+ 'summary': """Manage trainings""", -+ -+ 'description': """ -+ Open Academy module for managing trainings: -+ - training courses -+ - training sessions -+ - attendees registration -+ """, -+ -+ 'author': "My Company", -+ 'website': "http://www.yourcompany.com", -+ -+ # Categories can be used to filter modules in modules listing -+ # Check https://github.com/odoo/odoo/blob/14.0/odoo/addons/base/data/ir_module_category_data.xml -+ # for the full list -+ 'category': 'Test', -+ 'version': '0.1', -+ -+ # any module necessary for this one to work correctly -+ 'depends': ['base'], -+ -+ # always loaded -+ 'data': [ -+ # 'security/ir.model.access.csv', -+ 'templates.xml', -+ ], -+ # only loaded in demonstration mode -+ 'demo': [ -+ 'demo.xml', -+ ], -+} -Index: addons/openacademy/__init__.py -=================================================================== ---- /dev/null 1970-01-01 00:00:00.000000000 +0000 -+++ addons/openacademy/__init__.py 2014-08-26 17:25:49.791783523 +0200 -@@ -0,0 +1,3 @@ -+# -*- coding: utf-8 -*- -+from . import controllers -+from . import models -Index: addons/openacademy/controllers.py -=================================================================== ---- /dev/null 1970-01-01 00:00:00.000000000 +0000 -+++ addons/openacademy/controllers.py 2014-08-26 17:25:49.791783523 +0200 -@@ -0,0 +1,20 @@ -+# -*- coding: utf-8 -*- -+from odoo import http -+ -+# class Openacademy(http.Controller): -+# @http.route('/openacademy/openacademy/', auth='public') -+# def index(self, **kw): -+# return "Hello, world" -+ -+# @http.route('/openacademy/openacademy/objects/', auth='public') -+# def list(self, **kw): -+# return http.request.render('openacademy.listing', { -+# 'root': '/openacademy/openacademy', -+# 'objects': http.request.env['openacademy.openacademy'].search([]), -+# }) -+ -+# @http.route('/openacademy/openacademy/objects//', auth='public') -+# def object(self, obj, **kw): -+# return http.request.render('openacademy.object', { -+# 'object': obj -+# }) -Index: addons/openacademy/demo.xml -=================================================================== ---- /dev/null 1970-01-01 00:00:00.000000000 +0000 -+++ addons/openacademy/demo.xml 2014-08-26 17:25:49.791783523 +0200 -@@ -0,0 +1,25 @@ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -Index: addons/openacademy/models.py -=================================================================== ---- /dev/null 1970-01-01 00:00:00.000000000 +0000 -+++ addons/openacademy/models.py 2014-08-26 17:25:49.791783523 +0200 -@@ -0,0 +1,8 @@ -+# -*- coding: utf-8 -*- -+ -+from odoo import models, fields, api -+ -+# class openacademy(models.Model): -+# _name = 'openacademy.openacademy' -+ -+# name = fields.Char() -Index: addons/openacademy/security/ir.model.access.csv -=================================================================== ---- /dev/null 1970-01-01 00:00:00.000000000 +0000 -+++ addons/openacademy/security/ir.model.access.csv 2014-08-26 17:25:49.791783523 +0200 -@@ -0,0 +1,2 @@ -+id,name,model_id/id,group_id/id,perm_read,perm_write,perm_create,perm_unlink -+access_openacademy_openacademy,openacademy.openacademy,model_openacademy_openacademy,,1,0,0,0 -Index: addons/openacademy/templates.xml -=================================================================== ---- /dev/null 1970-01-01 00:00:00.000000000 +0000 -+++ addons/openacademy/templates.xml 2014-08-26 17:25:49.791783523 +0200 -@@ -0,0 +1,22 @@ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ diff --git a/content/developer/howtos/backend/exercise-dashboard b/content/developer/howtos/backend/exercise-dashboard deleted file mode 100644 index 9e5eb41b7..000000000 --- a/content/developer/howtos/backend/exercise-dashboard +++ /dev/null @@ -1,91 +0,0 @@ -# HG changeset patch -# Parent 643813940cbea07bec792f9e1c60022a9292fa90 - -Index: addons/openacademy/__manifest__.py -=================================================================== ---- addons.orig/openacademy/__manifest__.py 2014-08-26 17:26:21.535783052 +0200 -+++ addons/openacademy/__manifest__.py 2014-08-26 17:26:21.531783052 +0200 -@@ -21,7 +21,7 @@ - 'version': '0.1', - - # any module necessary for this one to work correctly -- 'depends': ['base'], -+ 'depends': ['base', 'board'], - - # always loaded - 'data': [ -@@ -30,6 +30,7 @@ - 'templates.xml', - 'views/openacademy.xml', - 'views/partner.xml', -+ 'views/session_board.xml', - 'reports.xml', - ], - # only loaded in demonstration mode -Index: addons/openacademy/views/session_board.xml -=================================================================== ---- /dev/null 1970-01-01 00:00:00.000000000 +0000 -+++ addons/openacademy/views/session_board.xml 2014-08-26 17:26:21.531783052 +0200 -@@ -0,0 +1,62 @@ -+ -+ -+ -+ -+ Attendees by course -+ openacademy.session -+ graph -+ -+ -+ -+ Sessions -+ openacademy.session -+ calendar -+ -+ -+ -+ Courses -+ openacademy.course -+ tree,form -+ -+ -+ Session Dashboard Form -+ board.board -+ form -+ -+
-+ -+ -+ -+ -+ -+ -+ -+ -+ -+
-+
-+
-+ -+ Session Dashboard -+ board.board -+ form -+ menu -+ -+ -+ -+ -+ -+
diff --git a/content/developer/howtos/backend/exercise-defaults b/content/developer/howtos/backend/exercise-defaults deleted file mode 100644 index 6365f4417..000000000 --- a/content/developer/howtos/backend/exercise-defaults +++ /dev/null @@ -1,28 +0,0 @@ -Index: addons/openacademy/models.py -=================================================================== ---- addons.orig/openacademy/models.py -+++ addons/openacademy/models.py -@@ -20,9 +20,10 @@ - _description = "OpenAcademy Sessions" - - name = fields.Char(required=True) -- start_date = fields.Date() -+ start_date = fields.Date(default=fields.Date.today) - duration = fields.Float(digits=(6, 2), help="Duration in days") - seats = fields.Integer(string="Number of seats") -+ active = fields.Boolean(default=True) - - instructor_id = fields.Many2one('res.partner', string="Instructor", - domain=['|', ('instructor', '=', True), -Index: addons/openacademy/views/openacademy.xml -=================================================================== ---- addons.orig/openacademy/views/openacademy.xml -+++ addons/openacademy/views/openacademy.xml -@@ -94,6 +94,7 @@ - - - -+ - - - diff --git a/content/developer/howtos/backend/exercise-demo b/content/developer/howtos/backend/exercise-demo deleted file mode 100644 index 50a956442..000000000 --- a/content/developer/howtos/backend/exercise-demo +++ /dev/null @@ -1,48 +0,0 @@ -# HG changeset patch -# Parent 84e2b0b43fc61fd0bcbb44c1929755d44ee58ae5 - -Index: addons/openacademy/demo.xml -=================================================================== ---- addons.orig/openacademy/demo.xml 2014-08-26 17:25:52.683783480 +0200 -+++ addons/openacademy/demo.xml 2014-08-26 17:25:52.679783480 +0200 -@@ -1,25 +1,19 @@ - - -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -+ -+ Course 0 -+ Course 0's description -+ -+Can have multiple lines -+ -+ -+ -+ Course 1 -+ -+ -+ -+ Course 2 -+ Course 2's description -+ - - diff --git a/content/developer/howtos/backend/exercise-domain-advanced b/content/developer/howtos/backend/exercise-domain-advanced deleted file mode 100644 index 9b96a4459..000000000 --- a/content/developer/howtos/backend/exercise-domain-advanced +++ /dev/null @@ -1,42 +0,0 @@ -# HG changeset patch -# Parent 69d1f2d359eb8ef304a9d99f17790c78b35eda1a - -Index: addons/openacademy/models.py -=================================================================== ---- addons.orig/openacademy/models.py -+++ addons/openacademy/models.py -@@ -25,7 +25,8 @@ - seats = fields.Integer(string="Number of seats") - - instructor_id = fields.Many2one('res.partner', string="Instructor", -- domain=[('instructor', '=', True)]) -+ domain=['|', ('instructor', '=', True), -+ ('category_id.name', 'ilike', "Teacher")]) - course_id = fields.Many2one('openacademy.course', - ondelete='cascade', string="Course", required=True) - attendee_ids = fields.Many2many('res.partner', string="Attendees") -Index: addons/openacademy/views/partner.xml -=================================================================== ---- addons.orig/openacademy/views/partner.xml -+++ addons/openacademy/views/partner.xml -@@ -29,4 +29,20 @@ - parent="configuration_menu" - action="contact_list_action"/> - -+ -+ Contact Tags -+ res.partner.category -+ tree,form -+ -+ -+ -+ -+ Teacher / Level 1 -+ -+ -+ Teacher / Level 2 -+ -+ - diff --git a/content/developer/howtos/backend/exercise-domain-basic b/content/developer/howtos/backend/exercise-domain-basic deleted file mode 100644 index 71ee608cf..000000000 --- a/content/developer/howtos/backend/exercise-domain-basic +++ /dev/null @@ -1,17 +0,0 @@ -# HG changeset patch -# Parent 142c5065ff1b7266d944d4ef5239e814ae22f0df - -Index: addons/openacademy/models.py -=================================================================== ---- addons.orig/openacademy/models.py -+++ addons/openacademy/models.py -@@ -24,7 +24,8 @@ - duration = fields.Float(digits=(6, 2), help="Duration in days") - seats = fields.Integer(string="Number of seats") - -- instructor_id = fields.Many2one('res.partner', string="Instructor") -+ instructor_id = fields.Many2one('res.partner', string="Instructor", -+ domain=[('instructor', '=', True)]) - course_id = fields.Many2one('openacademy.course', - ondelete='cascade', string="Course", required=True) - attendee_ids = fields.Many2many('res.partner', string="Attendees") diff --git a/content/developer/howtos/backend/exercise-formview b/content/developer/howtos/backend/exercise-formview deleted file mode 100644 index 9f80bc2f7..000000000 --- a/content/developer/howtos/backend/exercise-formview +++ /dev/null @@ -1,29 +0,0 @@ -# HG changeset patch -# Parent 4a0db1d29257764f4df5cb1ee0be7e59e8c8d0d8 - -Index: addons/openacademy/views/openacademy.xml -=================================================================== ---- addons.orig/openacademy/views/openacademy.xml 2014-08-26 17:25:54.291783456 +0200 -+++ addons/openacademy/views/openacademy.xml 2014-08-26 17:25:54.283783457 +0200 -@@ -1,6 +1,21 @@ - - - -+ -+ course.form -+ openacademy.course -+ -+
-+ -+ -+ -+ -+ -+ -+
-+
-+
-+ - - -+
-+ -+
    -+
  • -+ Delete -+
  • -+
  • -+
      -+ -+
    -+
-+
-+ -+
-+ -+ Session name: -+ -+
-+ Start date: -+ -+
-+ duration: -+ -+
-+ -+ -+ -+ -+
-+ -+ - - Sessions - openacademy.session -- tree,form,calendar,gantt,graph -+ tree,form,calendar,gantt,graph,kanban - - - -
- -+
- - -+ -+ -+ course.tree -+ openacademy.course -+ -+ -+ -+ -+ -+ -+ -+ - - -+ -+ session.tree -+ openacademy.session -+ -+ -+ -+ -+ -+ -+ -+ - - Sessions - openacademy.session diff --git a/content/developer/howtos/backend/exercise-model b/content/developer/howtos/backend/exercise-model deleted file mode 100644 index 6180794d1..000000000 --- a/content/developer/howtos/backend/exercise-model +++ /dev/null @@ -1,19 +0,0 @@ -# HG changeset patch -# Parent e3bb12713a6d38c28f50d46e8c1bab74ac40c1be -Index: addons/openacademy/models.py -=================================================================== ---- addons.orig/openacademy/models.py -+++ addons/openacademy/models.py -@@ -2,7 +2,9 @@ - - from odoo import models, fields, api - --# class openacademy(models.Model): --# _name = 'openacademy.openacademy' -+class Course(models.Model): -+ _name = 'openacademy.course' -+ _description = "OpenAcademy Courses" - --# name = fields.Char() -+ name = fields.Char(string="Title", required=True) -+ description = fields.Text() diff --git a/content/developer/howtos/backend/exercise-model-inheritance b/content/developer/howtos/backend/exercise-model-inheritance deleted file mode 100644 index 073d706c8..000000000 --- a/content/developer/howtos/backend/exercise-model-inheritance +++ /dev/null @@ -1,78 +0,0 @@ -# HG changeset patch -# Parent d903c828fb10f2b38e5f43e9ceaeae0a9db7f858 - -Index: addons/openacademy/__init__.py -=================================================================== ---- addons.orig/openacademy/__init__.py 2014-08-26 17:26:01.227783353 +0200 -+++ addons/openacademy/__init__.py 2014-08-26 17:26:01.219783354 +0200 -@@ -1,3 +1,4 @@ - # -*- coding: utf-8 -*- - from . import controllers - from . import models -+from . import partner -Index: addons/openacademy/__manifest__.py -=================================================================== ---- addons.orig/openacademy/__manifest__.py 2014-08-26 17:26:01.227783353 +0200 -+++ addons/openacademy/__manifest__.py 2014-08-26 17:26:01.223783354 +0200 -@@ -28,6 +28,7 @@ - # 'security/ir.model.access.csv', - 'templates.xml', - 'views/openacademy.xml', -+ 'views/partner.xml', - ], - # only loaded in demonstration mode - 'demo': [ -Index: addons/openacademy/partner.py -=================================================================== ---- /dev/null 1970-01-01 00:00:00.000000000 +0000 -+++ addons/openacademy/partner.py 2014-08-26 17:26:01.223783354 +0200 -@@ -0,0 +1,12 @@ -+# -*- coding: utf-8 -*- -+from odoo import fields, models -+ -+class Partner(models.Model): -+ _inherit = 'res.partner' -+ -+ # Add a new column to the res.partner model, by default partners are not -+ # instructors -+ instructor = fields.Boolean("Instructor", default=False) -+ -+ session_ids = fields.Many2many('openacademy.session', -+ string="Attended Sessions", readonly=True) -Index: addons/openacademy/views/partner.xml -=================================================================== ---- /dev/null 1970-01-01 00:00:00.000000000 +0000 -+++ addons/openacademy/views/partner.xml 2014-08-26 17:26:01.223783354 +0200 -@@ -0,0 +1,32 @@ -+ -+ -+ -+ -+ -+ partner.instructor -+ res.partner -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ Contacts -+ res.partner -+ tree,form -+ -+ -+ -+ -+ diff --git a/content/developer/howtos/backend/exercise-onchange b/content/developer/howtos/backend/exercise-onchange deleted file mode 100644 index 355e5ad70..000000000 --- a/content/developer/howtos/backend/exercise-onchange +++ /dev/null @@ -1,28 +0,0 @@ -# HG changeset patch -# Parent 8d5573b704b2867788dd6895503f1871c2976a29 -# Parent 9eb163e5da677a0d09e01a354ba56697b576a4bc - -diff --git a/openacademy/models.py b/openacademy/models.py ---- a/openacademy/models.py -+++ b/openacademy/models.py -@@ -41,3 +41,20 @@ class Session(models.Model): - r.taken_seats = 0.0 - else: - r.taken_seats = 100.0 * len(r.attendee_ids) / r.seats -+ -+ @api.onchange('seats', 'attendee_ids') -+ def _verify_valid_seats(self): -+ if self.seats < 0: -+ return { -+ 'warning': { -+ 'title': "Incorrect 'seats' value", -+ 'message': "The number of available seats may not be negative", -+ }, -+ } -+ if self.seats < len(self.attendee_ids): -+ return { -+ 'warning': { -+ 'title': "Too many attendees", -+ 'message': "Increase seats or remove excess attendees", -+ }, -+ } diff --git a/content/developer/howtos/backend/exercise-one2many b/content/developer/howtos/backend/exercise-one2many deleted file mode 100644 index 67ded32da..000000000 --- a/content/developer/howtos/backend/exercise-one2many +++ /dev/null @@ -1,36 +0,0 @@ -# HG changeset patch -# Parent cb05882d4fe73e97b9d34a69190ced14d1a50c24 - -Index: addons/openacademy/models.py -=================================================================== ---- addons.orig/openacademy/models.py -+++ addons/openacademy/models.py -@@ -11,6 +11,8 @@ - - responsible_id = fields.Many2one('res.users', - ondelete='set null', string="Responsible", index=True) -+ session_ids = fields.One2many( -+ 'openacademy.session', 'course_id', string="Sessions") - - - class Session(models.Model): -Index: addons/openacademy/views/openacademy.xml -=================================================================== ---- addons.orig/openacademy/views/openacademy.xml -+++ addons/openacademy/views/openacademy.xml -@@ -15,8 +15,13 @@ - - - -- -- This is an example of notebooks -+ -+ -+ -+ -+ -+ -+ - - - diff --git a/content/developer/howtos/backend/exercise-report b/content/developer/howtos/backend/exercise-report deleted file mode 100644 index 9cf3e990f..000000000 --- a/content/developer/howtos/backend/exercise-report +++ /dev/null @@ -1,52 +0,0 @@ -# HG changeset patch -# Parent c140f0a861a08881d8737bca0ffb83904a2059a3 - -Index: addons/openacademy/__manifest__.py -=================================================================== ---- addons.orig/openacademy/__manifest__.py 2014-08-29 08:39:43.975536806 +0200 -+++ addons/openacademy/__manifest__.py 2014-08-29 08:39:52.000000000 +0200 -@@ -30,6 +30,7 @@ - 'templates.xml', - 'views/openacademy.xml', - 'views/partner.xml', -+ 'reports.xml', - ], - # only loaded in demonstration mode - 'demo': [ -Index: foo/openacademy/reports.xml -=================================================================== ---- /dev/null -+++ foo/openacademy/reports.xml -@@ -0,0 +1,32 @@ -+ -+ -+ -+ Session Report -+ openacademy.session -+ qweb-pdf -+ openacademy.report_session_view -+ openacademy.report_session -+ -+ report -+ -+ -+ -+ -+ diff --git a/content/developer/howtos/backend/exercise-searchview b/content/developer/howtos/backend/exercise-searchview deleted file mode 100644 index 1f7ef805c..000000000 --- a/content/developer/howtos/backend/exercise-searchview +++ /dev/null @@ -1,28 +0,0 @@ -# HG changeset patch -# Parent 93a45ab8dd0a76c131cb5eeca6e44b71dca9f100 - -Index: addons/openacademy/views/openacademy.xml -=================================================================== ---- addons.orig/openacademy/views/openacademy.xml 2014-08-28 14:01:45.299033618 +0200 -+++ addons/openacademy/views/openacademy.xml 2014-08-28 14:18:58.847018275 +0200 -@@ -36,6 +36,12 @@ - - - -+ -+ -+ -+ - -
- -@@ -61,6 +67,7 @@ - Courses - openacademy.course - tree,form -+ - -

Create the first course -

diff --git a/content/developer/howtos/backend/exercise-searchview-basic b/content/developer/howtos/backend/exercise-searchview-basic deleted file mode 100644 index a04813351..000000000 --- a/content/developer/howtos/backend/exercise-searchview-basic +++ /dev/null @@ -1,24 +0,0 @@ -# HG changeset patch -# Parent b9bfc8929e0ffc3eb153641e14952fe5d99eb908 -Index: addons/openacademy/views/openacademy.xml -=================================================================== ---- addons.orig/openacademy/views/openacademy.xml 2014-08-26 17:25:55.807783434 +0200 -+++ addons/openacademy/views/openacademy.xml 2014-08-26 17:25:55.799783434 +0200 -@@ -23,6 +23,17 @@ -
- - -+ -+ course.search -+ openacademy.course -+ -+ -+ -+ -+ -+ -+ -+ - - - -+ -+ -+ session.form -+ openacademy.session -+ -+
-+ -+ -+ -+ -+ -+ -+ -+ -+
-+
-+
-+ -+ -+ Sessions -+ openacademy.session -+ tree,form -+ -+ -+ -+ - diff --git a/content/developer/howtos/backend/exercise-translations b/content/developer/howtos/backend/exercise-translations deleted file mode 100644 index 02b656efe..000000000 --- a/content/developer/howtos/backend/exercise-translations +++ /dev/null @@ -1,56 +0,0 @@ -# HG changeset patch -# Parent 7c95aad3b60e4c2006c5f706bd157e8e05318bfa - -diff --git a/openacademy/models.py b/openacademy/models.py ---- a/openacademy/models.py -+++ b/openacademy/models.py -@@ -1,7 +1,7 @@ - # -*- coding: utf-8 -*- - - from datetime import timedelta --from odoo import models, fields, api, exceptions -+from odoo import models, fields, api, exceptions, _ - - class Course(models.Model): - _name = 'openacademy.course' -@@ -20,11 +20,11 @@ class Course(models.Model): - default = dict(default or {}) - - copied_count = self.search_count( -- [('name', '=like', u"Copy of {}%".format(self.name))]) -+ [('name', '=like', _(u"Copy of {}%").format(self.name))]) - if not copied_count: -- new_name = u"Copy of {}".format(self.name) -+ new_name = _(u"Copy of {}").format(self.name) - else: -- new_name = u"Copy of {} ({})".format(self.name, copied_count) -+ new_name = _(u"Copy of {} ({})").format(self.name, copied_count) - - default['name'] = new_name - return super(Course, self).copy(default) -@@ -81,15 +81,15 @@ class Session(models.Model): - if self.seats < 0: - return { - 'warning': { -- 'title': "Incorrect 'seats' value", -- 'message': "The number of available seats may not be negative", -+ 'title': _("Incorrect 'seats' value"), -+ 'message': _("The number of available seats may not be negative"), - }, - } - if self.seats < len(self.attendee_ids): - return { - 'warning': { -- 'title': "Too many attendees", -- 'message': "Increase seats or remove excess attendees", -+ 'title': _("Too many attendees"), -+ 'message': _("Increase seats or remove excess attendees"), - }, - } - -@@ -114,4 +114,4 @@ class Session(models.Model): - def _check_instructor_not_in_attendees(self): - for r in self: - if r.instructor_id and r.instructor_id in r.attendee_ids: -- raise exceptions.ValidationError("A session's instructor can't be an attendee") -+ raise exceptions.ValidationError(_("A session's instructor can't be an attendee")) diff --git a/content/developer/howtos/backend/exercise-wizard b/content/developer/howtos/backend/exercise-wizard deleted file mode 100644 index 9c44d71ca..000000000 --- a/content/developer/howtos/backend/exercise-wizard +++ /dev/null @@ -1,36 +0,0 @@ -Index: addons/openacademy/__init__.py -=================================================================== ---- addons.orig/openacademy/__init__.py -+++ addons/openacademy/__init__.py -@@ -2,3 +2,4 @@ - from . import controllers - from . import models - from . import partner -+from . import wizard -Index: addons/openacademy/wizard.py -=================================================================== ---- /dev/null -+++ addons/openacademy/wizard.py -@@ -0,0 +1,11 @@ -+# -*- coding: utf-8 -*- -+ -+from odoo import models, fields, api -+ -+class Wizard(models.TransientModel): -+ _name = 'openacademy.wizard' -+ _description = "Wizard: Quick Registration of Attendees to Sessions" -+ -+ session_id = fields.Many2one('openacademy.session', -+ string="Session", required=True) -+ attendee_ids = fields.Many2many('res.partner', string="Attendees") -Index: foo/openacademy/security/ir.model.access.csv -=================================================================== ---- foo.orig/openacademy/security/ir.model.access.csv -+++ foo/openacademy/security/ir.model.access.csv -@@ -1,5 +1,6 @@ - id,name,model_id/id,group_id/id,perm_read,perm_write,perm_create,perm_unlink - course_manager,course manager,model_openacademy_course,group_manager,1,1,1,1 - session_manager,session manager,model_openacademy_session,group_manager,1,1,1,1 -+wizard_manager,wizard session manager,model_openacademy_wizard,group_manager,1,1,1,1 - course_read_all,course all,model_openacademy_course,,1,0,0,0 - session_read_all,session all,model_openacademy_session,,1,0,0,0 diff --git a/content/developer/howtos/backend/exercise-wizard-action b/content/developer/howtos/backend/exercise-wizard-action deleted file mode 100644 index 305b5859b..000000000 --- a/content/developer/howtos/backend/exercise-wizard-action +++ /dev/null @@ -1,29 +0,0 @@ -Index: addons/openacademy/views/openacademy.xml -=================================================================== ---- addons.orig/openacademy/views/openacademy.xml -+++ addons/openacademy/views/openacademy.xml -@@ -232,6 +232,12 @@ - - - -+
-+
- -
- -Index: addons/openacademy/wizard.py -=================================================================== ---- addons.orig/openacademy/wizard.py -+++ addons/openacademy/wizard.py -@@ -12,3 +12,7 @@ - session_id = fields.Many2one('openacademy.session', - string="Session", required=True, default=_default_session) - attendee_ids = fields.Many2many('res.partner', string="Attendees") -+ -+ def subscribe(self): -+ self.session_id.attendee_ids |= self.attendee_ids -+ return {} diff --git a/content/developer/howtos/backend/exercise-wizard-launch b/content/developer/howtos/backend/exercise-wizard-launch deleted file mode 100644 index 65b7be845..000000000 --- a/content/developer/howtos/backend/exercise-wizard-launch +++ /dev/null @@ -1,45 +0,0 @@ -Index: addons/openacademy/wizard.py -=================================================================== ---- addons.orig/openacademy/wizard.py -+++ addons/openacademy/wizard.py -@@ -6,6 +6,9 @@ class Wizard(models.TransientModel): - _name = 'openacademy.wizard' - _description = "Wizard: Quick Registration of Attendees to Sessions" - -+ def _default_session(self): -+ return self.env['openacademy.session'].browse(self._context.get('active_id')) -+ - session_id = fields.Many2one('openacademy.session', -- string="Session", required=True) -+ string="Session", required=True, default=_default_session) - attendee_ids = fields.Many2many('res.partner', string="Attendees") -Index: foo/openacademy/views/openacademy.xml -=================================================================== ---- foo.orig/openacademy/views/openacademy.xml -+++ foo/openacademy/views/openacademy.xml -@@ -220,4 +220,25 @@ - parent="openacademy_menu" - action="session_list_action"/> - -+ -+ wizard.form -+ openacademy.wizard -+ -+
-+ -+ -+ -+ -+
-+
-+
-+ -+ -+ Add Attendees -+ openacademy.wizard -+ form -+ new -+ -+ -+ - diff --git a/content/developer/howtos/backend/exercise-wizard-multi b/content/developer/howtos/backend/exercise-wizard-multi deleted file mode 100644 index 8e8805e52..000000000 --- a/content/developer/howtos/backend/exercise-wizard-multi +++ /dev/null @@ -1,37 +0,0 @@ -Index: addons/openacademy/views/openacademy.xml -=================================================================== ---- addons.orig/openacademy/views/openacademy.xml -+++ addons/openacademy/views/openacademy.xml -@@ -229,7 +229,7 @@ - -
- -- -+ - - -