Compare commits
58 Commits
cx_web_ref
...
bi_print_j
| Author | SHA1 | Date | |
|---|---|---|---|
| b2efe86375 | |||
| 9d9b59c5d3 | |||
| e8ffe910ee | |||
| 727fc0e844 | |||
| f7982d1a0e | |||
| ee132a00a2 | |||
| 1861912af3 | |||
| b86139b373 | |||
| b8c54e3f8e | |||
| 1b57e46bb1 | |||
| dd948951db | |||
| 3bd6e77fa2 | |||
| 193600db34 | |||
| 3d2024ab9f | |||
| 2c163771d4 | |||
| 26f24c3022 | |||
| f61e5f7e77 | |||
| 3391a4c864 | |||
| 48ad67fc95 | |||
| cb0fcf1b06 | |||
| 2e42407d45 | |||
| 695188ae0f | |||
| 9df8e641fd | |||
| 20806189b9 | |||
| e85bcbd0a1 | |||
| a9ab2991d7 | |||
| 9dca096734 | |||
| 657f3e4fb4 | |||
| 80bbd23379 | |||
| 50ce4f41a2 | |||
| d57b15f9dd | |||
| db8cd58f31 | |||
| f0745cda51 | |||
| 6752ab966e | |||
| 8ddc02a7c8 | |||
| db033c9d8b | |||
| d97e5cecb2 | |||
| 46ab5fa005 | |||
| 147f237c6f | |||
| a488d64cb3 | |||
| 6b5ccf1604 | |||
| 516e1a3bb4 | |||
| 4c42f51869 | |||
| 9d8cbe6868 | |||
| 23dace1045 | |||
| c2cb157124 | |||
| 070f8187f1 | |||
| b4f4024174 | |||
| 91e6814c2f | |||
| ef39e07904 | |||
| 71d4b51e09 | |||
| 1a83db992c | |||
| 76ce02f7fa | |||
| 4de59a7674 | |||
| dc0417b263 | |||
| 5684de0fbc | |||
| 3fae6c3b2d | |||
| 9a89e53ec4 |
28
addons/bi_print_journal_entries/LICENSE
Normal file
@@ -0,0 +1,28 @@
|
||||
Odoo Proprietary License v1.0
|
||||
|
||||
This software and associated files (the "Software") may only be used (executed,
|
||||
modified, executed after modifications) if you have purchased a valid license
|
||||
from the authors, typically via Odoo Apps, or if you have received a written
|
||||
agreement from the authors of the Software (see the COPYRIGHT file).
|
||||
|
||||
You may develop Odoo modules that use the Software as a library (typically
|
||||
by depending on it, importing it and using its resources), but without copying
|
||||
any source code or material from the Software. You may distribute those
|
||||
modules under the license of your choice, provided that this license is
|
||||
compatible with the terms of the Odoo Proprietary License (For example:
|
||||
LGPL, MIT, or proprietary licenses similar to this one).
|
||||
|
||||
It is forbidden to publish, distribute, sublicense, or sell copies of the Software
|
||||
or modified copies of the Software.
|
||||
|
||||
The above copyright notice and this permission notice must be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
|
||||
IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
|
||||
DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
|
||||
ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
|
||||
DEALINGS IN THE SOFTWARE.
|
||||
|
||||
0
addons/bi_print_journal_entries/ReadME.txt
Normal file
0
addons/bi_print_journal_entries/__init__.py
Normal file
35
addons/bi_print_journal_entries/__manifest__.py
Normal file
@@ -0,0 +1,35 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
# Part of BrowseInfo. See LICENSE file for full copyright and licensing details.
|
||||
{
|
||||
'name': 'Print Journal Entries Report in Odoo',
|
||||
'version': '19.0.0.0',
|
||||
'category': 'Accounting',
|
||||
'license': 'OPL-1',
|
||||
'summary': 'Allow to print pdf report of Journal Entries.',
|
||||
'description': """
|
||||
Allow to print pdf report of Journal Entries.
|
||||
journal entry
|
||||
print journal entry
|
||||
journal entries
|
||||
print journal entry reports
|
||||
account journal entry reports
|
||||
journal reports
|
||||
account entry reports
|
||||
|
||||
|
||||
""",
|
||||
'price': 000,
|
||||
'currency': 'EUR',
|
||||
'author': 'BROWSEINFO',
|
||||
'website': 'https://www.browseinfo.com/demo-request?app=bi_print_journal_entries&version=19&edition=Community',
|
||||
'depends': ['base','account'],
|
||||
'data': [
|
||||
'report/report_journal_entries.xml',
|
||||
'report/report_journal_entries_view.xml',
|
||||
],
|
||||
'installable': True,
|
||||
'auto_install': False,
|
||||
'live_test_url':'https://www.browseinfo.com/demo-request?app=bi_print_journal_entries&version=19&edition=Community',
|
||||
"images":["static/description/Banner.gif"],
|
||||
}
|
||||
|
||||
@@ -0,0 +1,17 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<odoo>
|
||||
<data>
|
||||
|
||||
<record id="journal_entry_report_id" model="ir.actions.report">
|
||||
<field name="name">Print Journal Entries</field>
|
||||
<field name="model">account.move</field>
|
||||
<field name="report_type">qweb-pdf</field>
|
||||
<field name="report_name">bi_print_journal_entries.journal_entry_report_template_id</field>
|
||||
<field name="report_file">bi_print_journal_entries.journal_entry_report_template_id</field>
|
||||
<field name="print_report_name">'Journal Entry'</field>
|
||||
<field name="binding_model_id" ref="account.model_account_move"/>
|
||||
<field name="binding_type">report</field>
|
||||
</record>
|
||||
|
||||
</data>
|
||||
</odoo>
|
||||
@@ -0,0 +1,127 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
|
||||
<odoo>
|
||||
<data>
|
||||
|
||||
<template id="journal_entry_template_report_view">
|
||||
<t t-call="web.external_layout">
|
||||
<t t-set="o" t-value="o.with_context({'lang':o.partner_id.lang})"/>
|
||||
<div class="page">
|
||||
<br/>
|
||||
<div class="oe_structure"/>
|
||||
<div>
|
||||
<h2 class="text-center mb32">
|
||||
<strong>Journal Entry</strong>
|
||||
</h2>
|
||||
</div>
|
||||
<br/>
|
||||
<table style="width:100%;margin:0px auto;background:white;border:1px solid #e1e1e1;" class="table table-condensed">
|
||||
<tbody>
|
||||
<tr>
|
||||
<td><strong>Journal Entry : </strong></td>
|
||||
<td><p t-field="o.name"/></td>
|
||||
<td><strong>Journal :</strong></td>
|
||||
<td><p t-field="o.journal_id"/></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><strong>Date :</strong></td>
|
||||
<td><p t-field="o.date"/></td>
|
||||
<td><strong>Reference :</strong></td>
|
||||
<td><p t-field="o.ref"/></td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
<br/>
|
||||
|
||||
<table style="width:100%;margin:0px auto;background:white;border:1px solid #e1e1e1;" class="table table-condensed">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Partner</th>
|
||||
<th>Account</th>
|
||||
<th>Label</th>
|
||||
<th>Due Date</th>
|
||||
<th>Analytic Account</th>
|
||||
<th>Taxes(%)</th>
|
||||
<th>Debit</th>
|
||||
<th>Credit</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody class="consumed_tbody">
|
||||
<t t-set="total" t-value="0.0"/>
|
||||
<t t-set="total1" t-value="0.0"/>
|
||||
<t t-foreach="o.line_ids" t-as="line">
|
||||
<tr>
|
||||
<td>
|
||||
<span t-field="line.partner_id" />
|
||||
</td>
|
||||
<td>
|
||||
<span t-field="line.account_id" />
|
||||
</td>
|
||||
<td>
|
||||
<span t-field="line.name" />
|
||||
</td>
|
||||
<td>
|
||||
<span t-field="line.date_maturity" />
|
||||
</td>
|
||||
<td>
|
||||
<span>
|
||||
<div t-foreach="line.analytic_distribution" t-as="distribution">
|
||||
<t t-set="distributions" t-value="distribution.split(',')"/>
|
||||
<t t-foreach="distributions" t-as="dist">
|
||||
<t t-if="line.env['account.analytic.account'].browse(int(dist))[0].partner_id">
|
||||
<t t-esc="line.env['account.analytic.account'].browse(int(dist))[0].name"/>-
|
||||
<t t-esc="line.env['account.analytic.account'].browse(int(dist))[0].partner_id.name"/>,
|
||||
<t t-esc="line.analytic_distribution.get(dist)"/>
|
||||
</t>
|
||||
<t t-else="">
|
||||
<t t-esc="line.env['account.analytic.account'].browse(int(dist))[0].name"/>,
|
||||
<t t-esc="line.analytic_distribution.get(dist)"/>
|
||||
</t>
|
||||
<br/>
|
||||
</t>
|
||||
</div>
|
||||
</span>
|
||||
</td>
|
||||
<td>
|
||||
<span t-field="line.tax_ids" />
|
||||
</td>
|
||||
<td>
|
||||
<span t-field="line.debit" />
|
||||
</td>
|
||||
<td>
|
||||
<span t-field="line.credit" />
|
||||
</td>
|
||||
</tr>
|
||||
<t t-set="total" t-value="total + line.debit"/>
|
||||
<t t-set="total1" t-value="total1 + line.credit"/>
|
||||
</t>
|
||||
<tr>
|
||||
<td></td>
|
||||
<td></td>
|
||||
<td></td>
|
||||
<td></td>
|
||||
<td></td>
|
||||
<td><strong>Total:</strong></td>
|
||||
<td width="10%"><strong>
|
||||
<span><t t-esc="total"/></span>
|
||||
</strong></td>
|
||||
<td width="10%"><strong>
|
||||
<span><t t-esc="total1"/></span>
|
||||
</strong></td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</t>
|
||||
</template>
|
||||
|
||||
<template id="journal_entry_report_template_id">
|
||||
<t t-call="web.html_container">
|
||||
<t t-foreach="docs" t-as="o">
|
||||
<t t-call="bi_print_journal_entries.journal_entry_template_report_view" t-lang="o.partner_id.lang"/>
|
||||
</t>
|
||||
</t>
|
||||
</template>
|
||||
|
||||
</data>
|
||||
</odoo>
|
||||
|
After Width: | Height: | Size: 217 KiB |
|
After Width: | Height: | Size: 114 KiB |
|
After Width: | Height: | Size: 267 KiB |
BIN
addons/bi_print_journal_entries/static/description/Banner.gif
Normal file
|
After Width: | Height: | Size: 111 KiB |
BIN
addons/bi_print_journal_entries/static/description/assets/1.png
Normal file
|
After Width: | Height: | Size: 41 KiB |
|
After Width: | Height: | Size: 108 KiB |
|
After Width: | Height: | Size: 111 KiB |
|
After Width: | Height: | Size: 1.5 MiB |
|
After Width: | Height: | Size: 76 KiB |
|
After Width: | Height: | Size: 25 KiB |
|
After Width: | Height: | Size: 66 KiB |
|
After Width: | Height: | Size: 1.4 MiB |
|
After Width: | Height: | Size: 15 KiB |
|
After Width: | Height: | Size: 38 KiB |
|
After Width: | Height: | Size: 3.8 KiB |
|
After Width: | Height: | Size: 282 KiB |
|
After Width: | Height: | Size: 3.5 KiB |
|
After Width: | Height: | Size: 1.9 MiB |
|
After Width: | Height: | Size: 486 KiB |
|
After Width: | Height: | Size: 1.7 MiB |
|
After Width: | Height: | Size: 639 B |
|
After Width: | Height: | Size: 1.6 MiB |
|
After Width: | Height: | Size: 4.6 KiB |
BIN
addons/bi_print_journal_entries/static/description/bi_logo.png
Normal file
|
After Width: | Height: | Size: 60 KiB |
|
After Width: | Height: | Size: 882 KiB |
|
After Width: | Height: | Size: 96 KiB |
|
After Width: | Height: | Size: 286 KiB |
|
After Width: | Height: | Size: 332 KiB |
|
After Width: | Height: | Size: 9.2 KiB |
|
After Width: | Height: | Size: 510 KiB |
|
After Width: | Height: | Size: 26 KiB |
|
After Width: | Height: | Size: 257 KiB |
|
After Width: | Height: | Size: 113 KiB |
|
After Width: | Height: | Size: 333 KiB |
|
After Width: | Height: | Size: 168 KiB |
|
After Width: | Height: | Size: 109 KiB |
|
After Width: | Height: | Size: 32 KiB |
|
After Width: | Height: | Size: 25 KiB |
BIN
addons/bi_print_journal_entries/static/description/icon.png
Normal file
|
After Width: | Height: | Size: 17 KiB |
691
addons/bi_print_journal_entries/static/description/index.html
Normal file
@@ -0,0 +1,691 @@
|
||||
<!doctype html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.0.2/dist/css/bootstrap.min.css" rel="stylesheet" integrity="sha384-EVSTQN3/azprG1Anm3QDgpJLIm9Nao0Yz1ztcQTwFspd3yD65VohhpuuCOmLASjC" crossorigin="anonymous">
|
||||
<link href='https://fonts.googleapis.com/css?family=Inter' rel='stylesheet'>
|
||||
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.5.0/css/all.min.css" integrity="sha512-..." crossorigin="anonymous" referrerpolicy="no-referrer"/>
|
||||
<script type="text/javascript" src="https://cdnjs.cloudflare.com/ajax/libs/twitter-bootstrap/5.1.1/js/bootstrap.min.js"></script>
|
||||
</head>
|
||||
<body>
|
||||
<section>
|
||||
<div class="bg-white mt-4">
|
||||
<div class="position-relative overflow-hidden" style="border-radius:20px;">
|
||||
<img src="assets/main_background.jpg" alt="Snow" class="h-100 w-100 position-absolute" loading="lazy">
|
||||
<br><br>
|
||||
|
||||
<!-- Header Logo -->
|
||||
<div style="text-align: center;">
|
||||
<div class="position-relative">
|
||||
<img src="assets/bi_header_logo.png" alt="Odoo Gold Partner" style="width: 85%;"/>
|
||||
<img src="assets/best_partner.png" alt="Odoo Best Partner" class="position-absolute" style="width: 25%; margin-left: -53%;"/>
|
||||
</div>
|
||||
<div class="row">
|
||||
<div class="col-6 zn-0 position-relative" style="margin-left: 20px;">
|
||||
<img src="assets/in_flag.gif" style="width: 4%;"/>
|
||||
<span style="font-size: 1.4vw;">India   | </span>
|
||||
<img src="assets/ml_flag.gif" style="width: 4%;"/>
|
||||
<span style="font-size: 1.4vw;">Malaysia</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div>
|
||||
<div class="text-center position-relative justify-content-center d-flex mt-5">
|
||||
<div class="col-6">
|
||||
<span style="font-size: 3.2vw;">
|
||||
<b>"The Epitome Of Odoo App Excellence."</b>
|
||||
<img src="assets/yoyo.png" style="width: 7%;"/>
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
<img src="assets/Header_Banner.png" class="position-relative mb-3 d-block" alt="Browseinfo" style="margin:0px auto; width: 40%;"/>
|
||||
</div>
|
||||
<div class="container position-relative" style="width: 70%;">
|
||||
<div class="row p-lg-5 p-md-3 p-sm-1 pt-0">
|
||||
<div class="col-4 zn-1">
|
||||
<img class="w-100" src="assets/1.png">
|
||||
</div>
|
||||
<div class="col-4 zn-1">
|
||||
<img class="w-100" src="assets/2.svg">
|
||||
</div>
|
||||
<div class="col-4 zn-1">
|
||||
<img class="w-100" src="assets/3.svg">
|
||||
</div>
|
||||
</div>
|
||||
<div class="text-center pb-lg-4 pb-md-2 pb-sm-1">
|
||||
<span class="p-1 text-white" style="font-size: 1.4vw; border-radius: 15px; background-color: #714B67; padding: 0.5% 3% 0.5% 3% !important;">
|
||||
<b>Best Odoo Gold Partner in India 2024 - Trusted Expertise</b>
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="row position-relative pt-lg-3 pb-lg-3 px-lg-5 p-md-4 d-flex w-100 gx-0">
|
||||
<section class="bg-transparent">
|
||||
<div class="container mt-3 px-3">
|
||||
<div class="row shadow gx-0" style="border-radius: 35px;">
|
||||
<div style="border-radius: 20px; padding: 1%; background-color:#fff;">
|
||||
<img src="assets/web_icon.png" alt="Browseinfo" class="mt-3" style="margin-left: 1%; width: 40px;">
|
||||
|
||||
<!-- App Description -->
|
||||
<div class="mt-lg-4 text-center">
|
||||
<div class="row px-lg-5 px-2">
|
||||
<div class="col-12 pb-lg-5 pb-3 d-flex justify-content-center">
|
||||
<div class="d-flex justify-content-center" style="width: 45%;">
|
||||
<div class="text-center p-1" style="border:1px solid #008596; font-size: 1.4vw; color:#fff; margin:0 3px; border-radius: 20px; background-color: #008596;">
|
||||
<span>Community</span>
|
||||
</div>
|
||||
<div class="text-center p-1" style="border:1px solid #FFC107; font-size: 1.4vw; color:#fff; margin:0 3px; border-radius: 20px; background-color: #FFC107;">
|
||||
<span>Enterprise</span>
|
||||
</div>
|
||||
<div class="text-center p-1" style="border:1px solid #5082c4; font-size: 1.4vw; color:#fff; margin: 0 3px; border-radius: 20px; background-color: #5082c4;">
|
||||
<span>Odoo.sh</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-12">
|
||||
<h1 class="text-center mb-4" style="font-size: clamp(14px, 6vw, 40px); color: #5082c4; font-weight: bold;">
|
||||
Print Journal Entries Report Odoo Apps
|
||||
</h1>
|
||||
</div>
|
||||
<div class="row gx-0 pb-3">
|
||||
<div class="col-lg-8 col-12 align-content-center">
|
||||
<div>
|
||||
<h2 class="m-2 opacity-50" style="font-size: clamp(14px, 4vw, 23px);"><b>
|
||||
Print Accounting Journal Report
|
||||
</b></h2>
|
||||
<h2 class="m-2 opacity-50" style="font-size: clamp(14px, 4vw, 23px);"><b>
|
||||
Export Journal Entries Report Odoo
|
||||
</b></h2>
|
||||
<h3 class="m-2 opacity-50" style="font-size: clamp(14px, 4vw, 23px);"><b>
|
||||
Journal Entries Report Download Odoo
|
||||
</b></h3>
|
||||
<h3 class="m-2 opacity-50" style="font-size: clamp(14px, 4vw, 23px);"><b>
|
||||
Journal Entry Report Print Tool Odoo Apps
|
||||
</b></h3>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-lg-4 col-12">
|
||||
<img class="w-50" src="assets/accounting.gif" alt="Browseinfo">
|
||||
</div>
|
||||
</div>
|
||||
<p style="font-family:Inter; font-weight:normal; font-size:18px; text-align: left;">
|
||||
Allow to print PDF report of Journal Entries.
|
||||
</p>
|
||||
|
||||
|
||||
|
||||
<!-- Demo & Video Button -->
|
||||
<div class="row">
|
||||
<div class="col-6 text-lg-end p-lg-5 px-lg-2 pt-3 pb-3">
|
||||
<a class="button shadow p-lg-3 p-2" target="_blank" href="mailto:sales@browseinfo.co.in?subject=Demo Request for Print Journal Entries Report Odoo Apps" style="border-radius:15px; font-weight:bold; color:#fff; background-color: #5082c4; font-size: clamp(10px, 1vw, 25px);">
|
||||
DEMO REQUEST
|
||||
</a>
|
||||
</div>
|
||||
<div class="col-6 text-lg-start p-lg-5 px-lg-2 pt-3 pb-3">
|
||||
<a class="button shadow p-lg-3 p-2" target="_blank" href="https://youtu.be/qehLT4WOWPs" style="border-radius:15px; font-weight:bold; color:#fff; background-color: #5082c4; font-size: clamp(10px, 1vw, 25px);">
|
||||
VIDEO TUTORIAL
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<!-- Menu Bar -->
|
||||
<section class="container mt-5 bg-transparent text-center">
|
||||
<ul role="tablist" class="nav nav-tabs justify-content-around p-2 align-items-center" data-tabs="tabs" style="border:none; background-color: #3f8ebacf; border-radius: 35px; margin:0 auto;">
|
||||
<li class="nav-item" style="width: max(100px, 15vw);">
|
||||
<a href="#feature_tab" data-bs-toggle="tab" aria-expanded="true" class="show nav-link active" style="font-size: clamp(14px, 4vw, 20px); border-radius:25px; color:#000; border:1px solid transparent">Features</a>
|
||||
</li>
|
||||
<li class="nav-item" style="width: max(100px, 15vw);">
|
||||
<a href="#release_note_tab" data-bs-toggle="tab" aria-expanded="true" class="show nav-link" style="font-size: clamp(14px, 4vw, 20px); border-radius:25px; color:#000; border:1px solid transparent">Release Note</a>
|
||||
</li>
|
||||
<li class="nav-item" style="width: max(100px, 15vw);">
|
||||
<a href="#faq_tab" data-bs-toggle="tab" aria-expanded="true" class="show nav-link" style="font-size: clamp(14px, 4vw, 20px); border-radius:25px; color:#000; border:1px solid transparent">FAQs</a>
|
||||
</li>
|
||||
</ul>
|
||||
|
||||
<div class="tab-content">
|
||||
<!-- Features -->
|
||||
<div id="feature_tab" class="tab-pane active">
|
||||
|
||||
<!-- 1-2 -->
|
||||
<div class="row justify-content-between d-inline-flex ms-3 ms-md-0" style="width: 95%;">
|
||||
<div class="col-lg-6 col-sm-12 p-0 bg-white shadow-lg row" style="margin-top: 5%; border-radius: 15vw;">
|
||||
<div class="shadow-lg col-2 d-flex align-items-center justify-content-center gx-0" style="background-color: #E91E63; border-top-left-radius: 25px; border-bottom-left-radius: 25px;">
|
||||
<img src="features/1fet.png" style="width: 60%;"/>
|
||||
</div>
|
||||
<div class="col-1 p-0 shadow-lg" style="background-color: #B0174B; width: 3%;">
|
||||
</div>
|
||||
<div class="col-6 d-grid align-items-center" style="padding: 10px; width: 63%;">
|
||||
<h2 style="color: #2A2C32; margin: 0; font-size: 1.4em;">
|
||||
Allow to print PDF report from Journal Entry
|
||||
</h2>
|
||||
<p style="margin: 10px 0 0 0; color: #666; font-size: 1em;">
|
||||
User can generate PDF report from Journal Entry.
|
||||
</p>
|
||||
</div>
|
||||
<div class="col-2 d-flex align-items-center shadow-lg" style="border-radius: 0px 150px 150px 0; background-color: #CF1B58; margin: 2% 0%; width: 15%;">
|
||||
<div class="circle1s shadow-lg" style="height: 70%; width: 85%; border-radius: 0px 100px 100px 0; background-color: #E91E63;"></div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="col-lg-6 col-sm-12 p-0 bg-white shadow-lg row d-inline-flex" style="margin-top: 5%; border-radius: 15vw;">
|
||||
<div class="shadow-lg col-2 d-flex align-items-center justify-content-center gx-0" style="background-color: #2196F3; border-top-left-radius: 25px; border-bottom-left-radius: 25px;">
|
||||
<img src="features/2fet.png" style="width: 60%;"/>
|
||||
</div>
|
||||
<div class="col-1 p-0 shadow-lg" style="background-color: #3A7EB6; width: 3%;">
|
||||
</div>
|
||||
<div class="col-6 d-grid align-items-center" style="padding: 10px; width: 63%;">
|
||||
<h2 style="color: #2A2C32; margin: 0; font-size: 1.4em;">
|
||||
Report with total Credit and total Debit
|
||||
</h2>
|
||||
<p style="margin: 10px 0 0 0; color: #666; font-size: 1em;">
|
||||
Show total Credit and total Debit of Journal Entry.
|
||||
</p>
|
||||
</div>
|
||||
<div class="col-2 d-flex align-items-center shadow-lg" style="border-radius: 0px 150px 150px 0; background-color: #1F89DD; margin: 2% 0%; width: 15%;">
|
||||
<div class="circle1s shadow-lg" style="height: 70%; width: 85%; border-radius: 0px 100px 100px 0; background-color: #2196F3;"></div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
|
||||
<div class="row text-dark mt-5">
|
||||
<!-- 1 -->
|
||||
<div class="col-12 mt-5">
|
||||
<div>
|
||||
<h2>
|
||||
<b>Print Journal Entries button</b>
|
||||
</h2>
|
||||
<p class="mt-4 h4" style="font-size: larger; font-weight: 300;">
|
||||
On clicking "Print Journal Entries" PDF report for journal entry will get generated.
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-12 mt-3 mb-5 d-flex justify-content-center">
|
||||
<img class="w-100 p-lg-0 p-3" src="1_print_menu.png" style="border-radius: 20px;">
|
||||
</div>
|
||||
<hr class="d-lg-none">
|
||||
|
||||
<!-- 2 -->
|
||||
<div class="col-12 mt-3">
|
||||
<div>
|
||||
<h2>
|
||||
<b>Journal Entry PDF report</b>
|
||||
</h2>
|
||||
<p class="mt-4 h4" style="font-size: larger; font-weight: 300;">
|
||||
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-12 mt-3 mb-5 d-flex justify-content-center">
|
||||
<img src="2_entry_report.png" class="w-100 p-lg-0 p-3" style="border-radius: 20px;">
|
||||
</div>
|
||||
<hr class="d-lg-none">
|
||||
<div class="col-12 mt-3">
|
||||
<div>
|
||||
<h2>
|
||||
<b>Print report for multiple Journal Entries</b>
|
||||
</h2>
|
||||
<p class="mt-4 h4" style="font-size: larger; font-weight: 300;">
|
||||
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-12 mt-3 mb-5 d-flex justify-content-center">
|
||||
<img src="3_print_menu.png" class="w-100 p-lg-0 p-3" style="border-radius: 20px;">
|
||||
</div>
|
||||
<hr class="d-lg-none">
|
||||
|
||||
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Release Note -->
|
||||
<div id="release_note_tab" class="container tab-pane fade">
|
||||
<div class="" style="margin-top: 30px; border-radius: 20px;">
|
||||
<div class="col-12 px-0 pb-lg-5 position-relative overflow-hidden" style="border-radius: 20px;">
|
||||
<div class="row position-relative pt-lg-5 px-lg-5 pb-lg-3 p-md-4 p-3 mx-auto d-flex align-items-center" style="color: #333333; font-weight: 500; font-size: 16px; width: 95%;">
|
||||
<div class="container" style="max-width: 1320px;">
|
||||
<div class="row" style="display: flex; flex-wrap: wrap; padding: 35px 35px;">
|
||||
<!-- Version Start -->
|
||||
<div class="row mb-3 pt-3 pb-3 text-center">
|
||||
<div class="col-md-6 themed-grid-col">
|
||||
<h3 class="mbr-section-subtitle mbr-fonts-style display-5" style="line-height: 139%; font-size: 1.5rem; word-break: break-word; word-wrap: break-word; font-weight: 400;">
|
||||
<strong style="font-weight: bold; line-height: 139%;"> Version :
|
||||
<code style="font-size: 1.3rem; color: red;">
|
||||
18.0.0.0
|
||||
</code>
|
||||
</strong>
|
||||
<br>
|
||||
<span class="font-italic" style="font-style: italic;">
|
||||
30 October 2024
|
||||
</span>
|
||||
</h3>
|
||||
</div>
|
||||
<div class="col-md-6 themed-grid-col">
|
||||
<p style="font-style:normal; font-weight:normal; font-size:18px; line-height:162.5%; text-align:center; color:#000000">
|
||||
Initial release of the app.
|
||||
</p>
|
||||
</div>
|
||||
<hr style="height: 0.3rem; width: 90%; color: black; margin-left: 75px;">
|
||||
</div>
|
||||
<!-- Version End -->
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
<!-- FAQs -->
|
||||
<div id="faq_tab" class="container tab-pane fade">
|
||||
<div id="faq" style="margin-top:40px; border-radius:20px">
|
||||
<div class="col-12 px-0 pb-lg-5 position-relative overflow-hidden" style="border-radius:20px;">
|
||||
<div class="row position-relative px-lg-5 pb-lg-3 p-md-4 p-3 mx-auto d-flex align-items-center" style="color:#333333; font-weight:500; font-size:16px; width:95%">
|
||||
<div class="container" style="max-width: 1320px;">
|
||||
<div class="m-4">
|
||||
<div class="accordion" id="bi_faq">
|
||||
<div class="accordion-item" style="background-color: transparent; border: none;">
|
||||
<h2 class="accordion-header" id="faq_one">
|
||||
<a class="accordion-button collapsed" data-bs-toggle="collapse" data-bs-target="#faq_one_ans" aria-expanded="false" aria-controls="faq_one_ans" style="background-color: transparent;text-decoration: none !important;">
|
||||
<span style="font-style:normal; font-weight:normal; font-size:18px; line-height:162.5%; text-align:center; color:#000000"> Can I Use this module with Odoo Online ? </span>
|
||||
</a>
|
||||
</h2>
|
||||
<div id="faq_one_ans" class="accordion-collapse collapse" data-parent="#accordion" aria-labelledby="faq_one">
|
||||
<div class="accordion-body" style=" font-style:normal; font-weight:normal; font-size:18px; line-height:162.5%; color:#000000; background: #ffff;text-align: left;"> This Odoo app can not be used with Odoo Online (SaaS). </div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="accordion-item" style="background-color: transparent; border: none;">
|
||||
<h2 class="accordion-header" id="faq_two">
|
||||
<a class="accordion-button collapsed" data-bs-toggle="collapse" data-bs-target="#faq_two_ans" aria-expanded="false" aria-controls="faq_two_ans" style="background-color: transparent !important; text-decoration: none !important;">
|
||||
<span style="font-style:normal; font-weight:normal; font-size:18px; line-height:162.5%; text-align:center; color:#000000">Do I got free support when I buy an App ? </span>
|
||||
</a>
|
||||
</h2>
|
||||
<div id="faq_two_ans" class="accordion-collapse collapse" data-parent="#accordion" aria-labelledby="faq_two">
|
||||
<div class="accordion-body" style=" font-style:normal; font-weight:normal; font-size:18px; line-height:162.5%; color:#000000; background: #ffff;text-align: left;"> Yes, <strong> 60 Days Free Support</strong> Is provided with module in case any bug or issue with module. </div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="accordion-item" style="background-color: transparent; border: none;">
|
||||
<h2 class="accordion-header" id="faq_three">
|
||||
<a class="accordion-button collapsed" data-bs-toggle="collapse" data-bs-target="#faq_three_ans" aria-expanded="false" aria-controls="faq_three_ans" style="background-color: transparent !important; text-decoration: none !important;">
|
||||
<span style="font-style:normal; font-weight:normal; font-size:18px; line-height:162.5%; text-align:center; color:#000000"> Do I have to buy module for each version ? </span>
|
||||
</a>
|
||||
</h2>
|
||||
<div id="faq_three_ans" class="accordion-collapse collapse" data-parent="#accordion" aria-labelledby="faq_three">
|
||||
<div class="accordion-body" style=" font-style:normal; font-weight:normal; font-size:18px; line-height:162.5%; color:#000000; background: #ffff; text-align: left;">
|
||||
<strong>Yes,</strong> Starting version 13.0, Every version of the module is sold separately.
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="accordion-item" style="background-color: transparent; border: none;">
|
||||
<h2 class="accordion-header" id="faq_four">
|
||||
<a class="accordion-button collapsed" data-bs-toggle="collapse" data-bs-target="#faq_four_ans" aria-expanded="false" aria-controls="faq_four_ans" style=" background-color: transparent !important; text-decoration: none !important;">
|
||||
<span style="font-style:normal; font-weight:normal; font-size:18px; line-height:162.5%; text-align:center; color:#000000"> How can I install the module I bought ? </span>
|
||||
</a>
|
||||
</h2>
|
||||
<div id="faq_four_ans" class="accordion-collapse collapse" data-parent="#accordion" aria-labelledby="faq_four">
|
||||
<div class="accordion-body" style=" font-style:normal; font-weight:normal; font-size:18px; line-height:162.5%; color:#000000; background: #ffff; text-align: left;"> You need to copy paste the module that is inside the zip file to your addons folder. If you are installing the module into an existing database, you need to enable the Technical Features to click on Apps > Update Modules List. Your new module will now appear in the list of modules that you can install. </div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="accordion-item" style=" background-color: transparent; border: none;">
|
||||
<h2 class="accordion-header" id="faq_five">
|
||||
<a class="accordion-button collapsed" data-bs-toggle="collapse" data-bs-target="#faq_five_ans" aria-expanded="false" aria-controls="faq_five_ans" style=" background-color: transparent !important; text-decoration: none !important;">
|
||||
<span style="font-style:normal; font-weight:normal; font-size:18px; line-height:162.5%; text-align:center; color:#000000"> The module I bought doesn't work, What should I do ? </span>
|
||||
</a>
|
||||
</h2>
|
||||
<div id="faq_five_ans" class="accordion-collapse collapse" data-parent="#accordion" aria-labelledby="faq_five">
|
||||
<div class="accordion-body" style="font-style:normal; font-weight:normal; font-size:18px; line-height:162.5%; color:#000000; background: #ffff; text-align: left;"> Please raise your ticket to use on <a href="mailto:ticket@browseinfo.co.in" style="color:blue;">
|
||||
<strong>ticket@browseinfo.co.in</strong>
|
||||
</a> We will back to you shortly. </div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<!-- Support -->
|
||||
<section class="mt-5 bg-transparent">
|
||||
<div class="container">
|
||||
<div class="gx-0 row shadow mt-5 bg-white" style="border-radius: 20px;">
|
||||
<div class="col-lg-6 col-sm-12 ps-5" style="text-align: left;">
|
||||
<div class="mt-5">
|
||||
<img src="assets/best_partner.png" style="width: 60%;">
|
||||
</div>
|
||||
<div class="row mt-5">
|
||||
<div class="rectangle" style="text-align: right; height: 14px; width: 2px; background-color: #3497DB; margin-top: 10px; margin-left: 21px;"></div>
|
||||
<div class="col-10 mb-3">
|
||||
<h2 class="text-left" style="text-align: left; font-size: 30px; color: #111827; margin-left: -6px;">Get Support, For Free! </h2>
|
||||
</div>
|
||||
</div>
|
||||
<h2 class="text-left" style="font-weight: bold; font-size: 35px; color: #111827;"> 60 Days Free Support </h2>
|
||||
<br />
|
||||
<p class="detail-box lh-base" style="font-size: 20px; color: #111827;">In case any bugs or issue (Except data recovery)</p>
|
||||
<p class="detail-box lh-base" style="font-size: 16px; color: #111827;"> At Browseinfo we offer end to end solution for Odoo services. Which includes analysis & consultation on the workflows and integration part. </p>
|
||||
<div class="container mt-5 ps-0">
|
||||
<div class="row">
|
||||
<div class="col-2">
|
||||
<img src="assets/call.png" width="50" height="50">
|
||||
</div>
|
||||
<div class="col-8" style="text-align: left;">
|
||||
<h4 style="font-size: 18px;font-weight: 700;margin: 0 0 10px 0; color: #7D7D7D;">Have Any Question ?</h4>
|
||||
<p style="color: black;">+91-6351620270</p>
|
||||
</div>
|
||||
</div>
|
||||
<div class="row mt-5 mb-5">
|
||||
<div class="col-2">
|
||||
<img src="assets/mail.png" width="50" height="50">
|
||||
</div>
|
||||
<div class="col-8" style="text-align: left;">
|
||||
<h4 style="font-size: 18px;font-weight: 700;margin: 0 0 10px 0; color: #7D7D7D;">Write Email</h4>
|
||||
<p style="color: black;"> Sales : sales@browseinfo.co.in <br /> Support : ticket@browseinfo.co.in </p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-lg-6 col-lg-6 text-center pb-5">
|
||||
<img src="assets/support_image.gif" style="width: 75%;">
|
||||
<br />
|
||||
<div class="row align-items-center mt-3">
|
||||
<div class="col-8">
|
||||
<span style="color: black; font-size: 18px;">Need Functional Help? Scan Here!</span>
|
||||
</div>
|
||||
<div class="col-4">
|
||||
<img src="assets/Functional_QR.svg" class="float-start h-auto img-fluid w-auto" style="">
|
||||
</div>
|
||||
<div class="w-100"></div>
|
||||
<div class="col-8">
|
||||
<span style="color: black; font-size: 18px;">Create Support Ticket</span>
|
||||
</div>
|
||||
<div class="col-4 mt-4">
|
||||
<img src="assets/Support_QR.svg" class="float-start h-auto img-fluid w-auto" style="">
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
|
||||
|
||||
<!-- Our Services -->
|
||||
<section class="bg-transparent">
|
||||
<div class="container">
|
||||
<div class="row align-items-center shadow bg-white mt-5" style="border-radius: 20px; ">
|
||||
<img src="assets/BI_services.gif" alt="Snow" style="width:100%;">
|
||||
<div class="centered position-absolute d-lg-inline d-none" style="margin-bottom: 15%; color: black; text-wrap: balance; width: 90%; margin-left: 6%;">
|
||||
Explore comprehensive Odoo solutions with our expert team at Browseinfo. As your trusted Odoo Gold Partner, we provide top-tier services including Odoo customization, development, migration, implementation, training, and ongoing support.
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
|
||||
<!-- Must Have Apps -->
|
||||
<section class="container shadow bg-transparent" style="background-color: #fafbff; padding: 15px; border-radius: 20px; margin-top: 40px !important;">
|
||||
<br>
|
||||
<br>
|
||||
<div class="container align-items-center" style="margin-bottom:10px; margin-top: 10px;">
|
||||
<h2 class="text-center" style="font-weight:bold; font-size:calc(1.1rem + 1vw); color:#4F9ACF;">
|
||||
Must-Have
|
||||
<span style="color:#000000; font-weight:600; font-size:calc(1.1rem + 1vw)">
|
||||
Apps
|
||||
</span>
|
||||
</h2>
|
||||
<div class="divider-custom justify-content-center d-flex w-100">
|
||||
<div class="divider-custom-line" style="width: 100%; max-width: 7rem; height: 0.25rem; background-color: #000000; border-radius: 1rem; border-color: #000000;"></div>
|
||||
<div style="font-size: 2rem; margin-top:-22px;" class="divider-custom-icon">
|
||||
<span style="display: inline-block; height: 1em; overflow: visible; vertical-align: -0.125em; padding-left: 25px; padding-right: 25px; color:black;" class="fa fa-plus-circle"></span>
|
||||
</div>
|
||||
<div style=" width: 100%; max-width: 7rem;height: 0.25rem; background-color: #000000;border-radius: 1rem; border-color: #000000;" class="divider-custom-line"></div>
|
||||
</div>
|
||||
</div>
|
||||
<div id="RelatedAppCarouselOne" class="row carousel slide mt64 mb32" data-bs-ride="carousel" data-interval="80000">
|
||||
<div class="carousel-inner text-center">
|
||||
<div class="carousel-item active">
|
||||
<div class="col-xs-12 col-sm-4 col-md-4 mb16 mt16" style="float:left">
|
||||
<a href="https://apps.odoo.com/apps/modules/18.0/bi_invoice_tripple_approval/" target="_blank">
|
||||
<img src="related/bi_invoice_tripple_approval.gif" style="width: 90%;">
|
||||
</a>
|
||||
</div>
|
||||
<div class="col-xs-12 col-sm-4 col-md-4 mb16 mt16" style="float:left">
|
||||
<a href="https://apps.odoo.com/apps/modules/18.0/bi_accounting_report_excel/" target="_blank">
|
||||
<img src="related/bi_accounting_report_excel.gif" style="width: 90%;">
|
||||
</a>
|
||||
</div>
|
||||
<div class="col-xs-12 col-sm-4 col-md-4 mb16 mt16" style="float:left">
|
||||
<a href="https://apps.odoo.com/apps/modules/18.0/bi_account_payable_report/" target="_blank">
|
||||
<img src="related/bi_account_payable_report.gif" style="width: 90%;">
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
<div class="carousel-item">
|
||||
<div class="col-xs-12 col-sm-4 col-md-4 mb16 mt16" style="float:left">
|
||||
<a href="https://apps.odoo.com/apps/modules/18.0/account_statement/" target="_blank">
|
||||
<img src="related/account_statement.gif" style="width: 90%;">
|
||||
</a>
|
||||
</div>
|
||||
<div class="col-xs-12 col-sm-4 col-md-4 mb16 mt16" style="float:left">
|
||||
<a href="https://apps.odoo.com/apps/modules/18.0/bi_account_receivable_report/" target="_blank">
|
||||
<img src="related/bi_account_receivable_report.gif" style="width: 90%;">
|
||||
</a>
|
||||
</div>
|
||||
<div class="col-xs-12 col-sm-4 col-md-4 mb16 mt16" style="float:left">
|
||||
<a href="https://apps.odoo.com/apps/modules/18.0/bi_all_in_one_invoice_reports/" target="_blank">
|
||||
<img src="related/bi_all_in_one_invoice_reports.gif" style="width: 90%;">
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
<a class="carousel-control-prev" href="#RelatedAppCarouselOne" data-bs-slide="prev" style="width:35px;">
|
||||
<span class="">
|
||||
<i class="fa fa-chevron-left" style="font-size:24px"></i>
|
||||
</span>
|
||||
</a>
|
||||
<a class="carousel-control-next" href="#RelatedAppCarouselOne" data-bs-slide="next" style="width:35px;">
|
||||
<span class="">
|
||||
<i class="fa fa-chevron-right" style="font-size:24px"></i>
|
||||
</span>
|
||||
</a>
|
||||
</div>
|
||||
<ol class="carousel-indicators position-relative" style="margin: 1% 0%;">
|
||||
<li data-bs-target="#RelatedAppCarouselOne" data-bs-slide-to="0" class="active" style="background-color: #000; width: 10px; border-radius: 15px; height: 10px; border-top: 0px; border-bottom: 0px;"></li>
|
||||
<li data-bs-target="#RelatedAppCarouselOne" data-bs-slide-to="1" style="background-color: #000; width: 10px; border-radius: 15px; height: 10px; border-top: 0px; border-bottom: 0px;"></li>
|
||||
</ol>
|
||||
</div>
|
||||
<div id="RelatedAppCarouselTwo" class="row carousel slide mt64 mb32" data-bs-ride="carousel" data-interval="80000">
|
||||
<div class="carousel-inner text-center">
|
||||
<div class="carousel-item active">
|
||||
<div class="col-xs-12 col-sm-4 col-md-4 mb16 mt16" style="float:left">
|
||||
<a href="https://apps.odoo.com/apps/modules/18.0/bi_create_single_invoice/" target="_blank">
|
||||
<img src="related/bi_create_single_invoice.gif" style="width: 90%;">
|
||||
</a>
|
||||
</div>
|
||||
<div class="col-xs-12 col-sm-4 col-md-4 mb16 mt16" style="float:left">
|
||||
<a href="https://apps.odoo.com/apps/modules/18.0/bi_customer_supplier_approve/" target="_blank">
|
||||
<img src="related/bi_customer_supplier_approve.gif" style="width: 90%;">
|
||||
</a>
|
||||
</div>
|
||||
<div class="col-xs-12 col-sm-4 col-md-4 mb16 mt16" style="float:left">
|
||||
<a href="https://apps.odoo.com/apps/modules/18.0/bi_general_ledger_filter/" target="_blank">
|
||||
<img src="related/bi_general_ledger_filter.gif" style="width: 90%;">
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
<div class="carousel-item">
|
||||
<div class="col-xs-12 col-sm-4 col-md-4 mb16 mt16" style="float:left">
|
||||
<a href="https://apps.odoo.com/apps/modules/18.0/bi_report_analytic_account_filter/" target="_blank">
|
||||
<img src="related/bi_report_analytic_account_filter.gif" style="width: 90%;">
|
||||
</a>
|
||||
</div>
|
||||
<div class="col-xs-12 col-sm-4 col-md-4 mb16 mt16" style="float:left">
|
||||
<a href="https://apps.odoo.com/apps/modules/18.0/customer_account_payment_followup/" target="_blank">
|
||||
<img src="related/customer_account_payment_followup.gif" style="width: 90%;">
|
||||
</a>
|
||||
</div>
|
||||
<div class="col-xs-12 col-sm-4 col-md-4 mb16 mt16" style="float:left">
|
||||
<a href="https://apps.odoo.com/apps/modules/18.0/website_rental_management/" target="_blank">
|
||||
<img src="related/website_rental_management.gif" style="width: 90%;">
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
<a class="carousel-control-prev" href="#RelatedAppCarouselTwo" data-bs-slide="prev" style="width:35px;">
|
||||
<span class="">
|
||||
<i class="fa fa-chevron-left" style="font-size:24px"></i>
|
||||
</span>
|
||||
</a>
|
||||
<a class="carousel-control-next" href="#RelatedAppCarouselTwo" data-bs-slide="next" style="width:35px;">
|
||||
<span class="">
|
||||
<i class="fa fa-chevron-right" style="font-size:24px"></i>
|
||||
</span>
|
||||
</a>
|
||||
</div>
|
||||
<ol class="carousel-indicators position-relative" style="margin: 1% 0%;">
|
||||
<li data-bs-target="#RelatedAppCarouselTwo" data-bs-slide-to="0" class="active" style="background-color: #000; width: 10px; border-radius: 15px; height:10px; border-top: 0px; border-bottom: 0px;"></li>
|
||||
<li data-bs-target="#RelatedAppCarouselTwo" data-bs-slide-to="1" style="background-color: #000; width: 10px; border-radius: 15px; height:10px; border-top: 0px; border-bottom: 0px;"></li>
|
||||
</ol>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
|
||||
<!-- Best of the best Apps -->
|
||||
<section class="container shadow bg-transparent" style="background-color: #fafbff; padding: 15px; border-radius: 20px; margin-top: 40px !important;">
|
||||
<br>
|
||||
<br>
|
||||
<div class="container align-items-center" style="margin-bottom:10px;">
|
||||
<h2 class="text-center" style="font-weight:bold; font-size:calc(1.1rem + 1vw); color:#4F9ACF;">
|
||||
Best of the
|
||||
<span style="color:#000000; font-weight:600; font-size:calc(1.1rem + 1vw)">
|
||||
Best Apps
|
||||
</span>
|
||||
</h2>
|
||||
<div class="divider-custom d-flex justify-content-center w-100">
|
||||
<div class="divider-custom-line" style=" width: 100%; max-width: 7rem; height: 0.25rem; background-color: #000000; border-radius: 1rem; border-color: #000000;"></div>
|
||||
<div style="font-size: 2rem; margin-top:-22px;" class="divider-custom-icon">
|
||||
<span style="display: inline-block; height: 1em; overflow: visible; vertical-align: -0.125em; padding-left: 25px; padding-right: 25px; color:black;" class="fa fa-check-square"></span>
|
||||
</div>
|
||||
<div style=" width: 100%; max-width: 7rem;height: 0.25rem; background-color: #000000;border-radius: 1rem; border-color: #000000;" class="divider-custom-line"></div>
|
||||
</div>
|
||||
</div>
|
||||
<div id="DemandedAppCarouselOne" class="row carousel slide mt64 mb32" data-bs-ride="carousel" data-interval="80000">
|
||||
<div class="carousel-inner text-center">
|
||||
<div class="carousel-item active">
|
||||
<div class="col-xs-12 col-sm-4 col-md-4 mb16 mt16" style="float:left">
|
||||
<a href="https://apps.odoo.com/apps/modules/18.0/pos_orders_all/" target="_blank">
|
||||
<img src="demanded/pos_orders_all.gif" style="width: 90%;">
|
||||
</a>
|
||||
</div>
|
||||
<div class="col-xs-12 col-sm-4 col-md-4 mb16 mt16" style="float:left">
|
||||
<a href="https://apps.odoo.com/apps/modules/18.0/bi_customer_overdue_statement/" target="_blank">
|
||||
<img src="demanded/bi_customer_overdue_statement.gif" style="width: 90%;">
|
||||
</a>
|
||||
</div>
|
||||
<div class="col-xs-12 col-sm-4 col-md-4 mb16 mt16" style="float:left">
|
||||
<a href="https://apps.odoo.com/apps/modules/18.0/generic_excel_reports/" target="_blank">
|
||||
<img src="demanded/generic_excel_reports.gif" style="width: 90%;">
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
<div class="carousel-item">
|
||||
<div class="col-xs-12 col-sm-4 col-md-4 mb16 mt16" style="float:left">
|
||||
<a href="https://apps.odoo.com/apps/modules/18.0/product_bundle_all/" target="_blank">
|
||||
<img src="demanded/product_bundle_all.gif" style="width: 90%;">
|
||||
</a>
|
||||
</div>
|
||||
<div class="col-xs-12 col-sm-4 col-md-4 mb16 mt16" style="float:left">
|
||||
<a href="https://apps.odoo.com/apps/modules/18.0/car_repair_industry/" target="_blank">
|
||||
<img src="demanded/car_repair_industry.gif" style="width: 90%;">
|
||||
</a>
|
||||
</div>
|
||||
<div class="col-xs-12 col-sm-4 col-md-4 mb16 mt16" style="float:left">
|
||||
<a href="https://apps.odoo.com/apps/modules/18.0/odoo_website_wallet/" target="_blank">
|
||||
<img src="demanded/odoo_website_wallet.gif" style="width: 90%;">
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
<a class="carousel-control-prev" href="#DemandedAppCarouselOne" data-bs-slide="prev" style="width:35px;">
|
||||
<span class="">
|
||||
<i class="fa fa-chevron-left" style="font-size:24px"></i>
|
||||
</span>
|
||||
</a>
|
||||
<a class="carousel-control-next" href="#DemandedAppCarouselOne" data-bs-slide="next" style="width:35px;">
|
||||
<span class="">
|
||||
<i class="fa fa-chevron-right" style="font-size:24px"></i>
|
||||
</span>
|
||||
</a>
|
||||
</div>
|
||||
<ol class="carousel-indicators position-relative" style="margin: 1% 0%;">
|
||||
<li data-bs-target="#DemandedAppCarouselOne" data-bs-slide-to="0" class="active" style="background-color: #000; width: 10px; border-radius: 15px; height:10px; border-top: 0px; border-bottom: 0px;"></li>
|
||||
<li data-bs-target="#DemandedAppCarouselOne" data-bs-slide-to="1" style="background-color: #000; width: 10px; border-radius: 15px; height:10px; border-top: 0px; border-bottom: 0px;"></li>
|
||||
</ol>
|
||||
</div>
|
||||
<div id="DemandedAppCarouselTwo" class="row carousel slide mt64 mb32" data-bs-ride="carousel" data-interval="80000">
|
||||
<div class="carousel-inner text-center">
|
||||
<div class="carousel-item active">
|
||||
<div class="col-xs-12 col-sm-4 col-md-4 mb16 mt16" style="float:left">
|
||||
<a href="https://apps.odoo.com/apps/modules/18.0/sales_commission_generic/" target="_blank">
|
||||
<img src="demanded/sales_commission_generic.gif" style="width: 90%;">
|
||||
</a>
|
||||
</div>
|
||||
<div class="col-xs-12 col-sm-4 col-md-4 mb16 mt16" style="float:left">
|
||||
<a href="https://apps.odoo.com/apps/modules/18.0/bi_generic_import/" target="_blank">
|
||||
<img src="demanded/bi_generic_import.gif" style="width: 90%;">
|
||||
</a>
|
||||
</div>
|
||||
<div class="col-xs-12 col-sm-4 col-md-4 mb16 mt16" style="float:left">
|
||||
<a href="https://apps.odoo.com/apps/modules/18.0/branch/" target="_blank">
|
||||
<img src="demanded/branch.gif" style="width: 90%;">
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
<div class="carousel-item">
|
||||
<div class="col-xs-12 col-sm-4 col-md-4 mb16 mt16" style="float:left">
|
||||
<a href="https://apps.odoo.com/apps/modules/18.0/bi_inter_company_transfer/" target="_blank">
|
||||
<img src="demanded/bi_inter_company_transfer.gif" style="width: 90%;">
|
||||
</a>
|
||||
</div>
|
||||
<div class="col-xs-12 col-sm-4 col-md-4 mb16 mt16" style="float:left">
|
||||
<a href="https://apps.odoo.com/apps/modules/18.0/bi_loyalty_generic/" target="_blank">
|
||||
<img src="demanded/bi_loyalty_generic.gif" style="width: 90%;">
|
||||
</a>
|
||||
</div>
|
||||
<div class="col-xs-12 col-sm-4 col-md-4 mb16 mt16" style="float:left">
|
||||
<a href="https://apps.odoo.com/apps/modules/18.0/margin/" target="_blank">
|
||||
<img src="demanded/margin.gif" style="width: 90%;">
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
<a class="carousel-control-prev" href="#DemandedAppCarouselTwo" data-bs-slide="prev" style="width:35px;">
|
||||
<span class="">
|
||||
<i class="fa fa-chevron-left" style="font-size:24px"></i>
|
||||
</span>
|
||||
</a>
|
||||
<a class="carousel-control-next" href="#DemandedAppCarouselTwo" data-bs-slide="next" style="width:35px;">
|
||||
<span class="">
|
||||
<i class="fa fa-chevron-right" style="font-size:24px"></i>
|
||||
</span>
|
||||
</a>
|
||||
</div>
|
||||
<ol class="carousel-indicators position-relative" style="margin: 1% 0%;">
|
||||
<li data-bs-target="#DemandedAppCarouselTwo" data-bs-slide-to="0" class="active" style="background-color: #000; width: 10px; border-radius: 15px; height:10px; border-top: 0px; border-bottom: 0px;"></li>
|
||||
<li data-bs-target="#DemandedAppCarouselTwo" data-bs-slide-to="1" style="background-color: #000; width: 10px; border-radius: 15px; height:10px; border-top: 0px; border-bottom: 0px;"></li>
|
||||
</ol>
|
||||
</div>
|
||||
</section>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
</body>
|
||||
</html>
|
||||
|
After Width: | Height: | Size: 221 KiB |
|
After Width: | Height: | Size: 1.2 MiB |
|
After Width: | Height: | Size: 431 KiB |
|
After Width: | Height: | Size: 123 KiB |
|
After Width: | Height: | Size: 1.1 MiB |
|
After Width: | Height: | Size: 83 KiB |
|
After Width: | Height: | Size: 10 KiB |
|
After Width: | Height: | Size: 90 KiB |
|
After Width: | Height: | Size: 681 KiB |
|
After Width: | Height: | Size: 62 KiB |
|
After Width: | Height: | Size: 225 KiB |
|
After Width: | Height: | Size: 991 KiB |
@@ -1,127 +0,0 @@
|
||||
========================
|
||||
Web Refresh From Backend
|
||||
========================
|
||||
|
||||
..
|
||||
!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
|
||||
!! This file is generated by oca-gen-addon-readme !!
|
||||
!! changes will be overwritten. !!
|
||||
!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
|
||||
!! source digest: sha256:e841ff66d3bfff0a3de22c9be5dc40f1ca539739f5487a9162fdf887fc5ac6ad
|
||||
!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
|
||||
|
||||
.. |badge1| image:: https://img.shields.io/badge/maturity-Beta-yellow.png
|
||||
:target: https://odoo-community.org/page/development-status
|
||||
:alt: Beta
|
||||
.. |badge2| image:: https://img.shields.io/badge/license-LGPL--3-blue.png
|
||||
:target: http://www.gnu.org/licenses/lgpl-3.0-standalone.html
|
||||
:alt: License: LGPL-3
|
||||
.. |badge3| image:: https://img.shields.io/badge/github-cetmix%2Fcetmix--tower-lightgray.png?logo=github
|
||||
:target: https://github.com/cetmix/cetmix-tower/tree/16.0/cx_web_refresh_from_backend
|
||||
:alt: cetmix/cetmix-tower
|
||||
|
||||
|badge1| |badge2| |badge3|
|
||||
|
||||
Backend UI Reload Module
|
||||
========================
|
||||
|
||||
This is a **technical module** that allows triggering a **UI reload**
|
||||
from the backend. It enables triggering the reload action for selected
|
||||
users and record IDs.
|
||||
|
||||
--------------
|
||||
|
||||
🔧 Helper Function: ``reload_views``
|
||||
------------------------------------
|
||||
|
||||
A special helper function ``reload_views`` is added to the ``res.users``
|
||||
model.
|
||||
|
||||
**Arguments**
|
||||
~~~~~~~~~~~~~
|
||||
|
||||
+----------------+--------------------------+--------------------------+
|
||||
| Argument | Type | Description |
|
||||
+================+==========================+==========================+
|
||||
| **model** | ``Char`` | Model name, e.g. |
|
||||
| | | ``'res.partner'`` |
|
||||
+----------------+--------------------------+--------------------------+
|
||||
| **view_types** | ``List of Char`` | View types to reload, |
|
||||
| | *(optional)* | e.g. |
|
||||
| | | ``["form", "kanban"]``. |
|
||||
| | | Leave blank to reload |
|
||||
| | | all views. |
|
||||
+----------------+--------------------------+--------------------------+
|
||||
| **rec_ids** | ``List of Integer`` | The view will be |
|
||||
| | *(optional)* | reloaded only if a |
|
||||
| | | record with an ID from |
|
||||
| | | this list is present in |
|
||||
| | | the view. |
|
||||
+----------------+--------------------------+--------------------------+
|
||||
|
||||
--------------
|
||||
|
||||
⚠️ Important Notes
|
||||
------------------
|
||||
|
||||
Use this function **wisely**.
|
||||
|
||||
When reloading **form views**, be aware that if a user is currently
|
||||
editing a record, **their unsaved updates may be lost**.
|
||||
|
||||
**Table of contents**
|
||||
|
||||
.. contents::
|
||||
:local:
|
||||
|
||||
Usage
|
||||
=====
|
||||
|
||||
🧩 Example Usage
|
||||
----------------
|
||||
|
||||
Below is a code snippet showing how to use the ``reload_views`` helper
|
||||
function.
|
||||
|
||||
.. code:: python
|
||||
|
||||
# Reload the kanban and form views for all salespeople when an opportunity is won
|
||||
# Will reload views only if the current opportunity is being displayed
|
||||
|
||||
group_id = self.env.ref("sales_team.group_sale_salesman").id
|
||||
users_to_reload = self.env["res.users"].search([("groups_id", "in", [group_id])])
|
||||
users_to_reload.reload_views(
|
||||
model="crm.lead",
|
||||
view_types=["kanban", "form"],
|
||||
rec_ids=[self.id],
|
||||
)
|
||||
|
||||
Bug Tracker
|
||||
===========
|
||||
|
||||
Bugs are tracked on `GitHub Issues <https://github.com/cetmix/cetmix-tower/issues>`_.
|
||||
In case of trouble, please check there if your issue has already been reported.
|
||||
If you spotted it first, help us to smash it by providing a detailed and welcomed
|
||||
`feedback <https://github.com/cetmix/cetmix-tower/issues/new?body=module:%20cx_web_refresh_from_backend%0Aversion:%2016.0%0A%0A**Steps%20to%20reproduce**%0A-%20...%0A%0A**Current%20behavior**%0A%0A**Expected%20behavior**>`_.
|
||||
|
||||
Do not contact contributors directly about support or help with technical issues.
|
||||
|
||||
Credits
|
||||
=======
|
||||
|
||||
Authors
|
||||
-------
|
||||
|
||||
* Cetmix
|
||||
|
||||
Contributors
|
||||
------------
|
||||
|
||||
- Cetmix
|
||||
|
||||
Maintainers
|
||||
-----------
|
||||
|
||||
This module is part of the `cetmix/cetmix-tower <https://github.com/cetmix/cetmix-tower/tree/16.0/cx_web_refresh_from_backend>`_ project on GitHub.
|
||||
|
||||
You are welcome to contribute.
|
||||
@@ -1,4 +0,0 @@
|
||||
# Copyright 2025 Cetmix OÜ
|
||||
# License LGPL-3.0 or later (http://www.gnu.org/licenses/lgpl).
|
||||
|
||||
from . import models
|
||||
@@ -1,26 +0,0 @@
|
||||
# Copyright 2025 Cetmix OÜ
|
||||
# License LGPL-3.0 or later (http://www.gnu.org/licenses/lgpl).
|
||||
|
||||
# Mail is required: its ir.websocket override subscribes the partner channel to the
|
||||
# bus, so users receive web.refresh_view notifications.
|
||||
|
||||
{
|
||||
"name": "Web Refresh From Backend",
|
||||
"summary": "Refresh frontend views from backend",
|
||||
"version": "16.0.1.0.0",
|
||||
"category": "Web",
|
||||
"license": "LGPL-3",
|
||||
"author": "Cetmix",
|
||||
"website": "https://tower.cetmix.com",
|
||||
"images": ["static/description/banner.png"],
|
||||
"depends": ["mail"],
|
||||
"assets": {
|
||||
"web.assets_backend": [
|
||||
"cx_web_refresh_from_backend/static/src/views/list/list_controller_patch.esm.js",
|
||||
"cx_web_refresh_from_backend/static/src/views/kanban/kanban_controller_patch.esm.js",
|
||||
"cx_web_refresh_from_backend/static/src/views/form/form_controller_patch.esm.js",
|
||||
],
|
||||
},
|
||||
"installable": True,
|
||||
"auto_install": False,
|
||||
}
|
||||
@@ -1,97 +0,0 @@
|
||||
# Translation of Odoo Server.
|
||||
# This file contains the translation of the following modules:
|
||||
# * cx_web_refresh_from_backend
|
||||
#
|
||||
msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: Odoo Server 16.0\n"
|
||||
"Report-Msgid-Bugs-To: \n"
|
||||
"Last-Translator: \n"
|
||||
"Language-Team: \n"
|
||||
"MIME-Version: 1.0\n"
|
||||
"Content-Type: text/plain; charset=UTF-8\n"
|
||||
"Content-Transfer-Encoding: \n"
|
||||
"Plural-Forms: \n"
|
||||
|
||||
#. module: cx_web_refresh_from_backend
|
||||
#. odoo-javascript
|
||||
#: code:addons/cx_web_refresh_from_backend/static/src/views/form/form_controller_patch.esm.js:0
|
||||
#, python-format
|
||||
msgid "All unsaved changes will be lost! Continue?"
|
||||
msgstr ""
|
||||
|
||||
#. module: cx_web_refresh_from_backend
|
||||
#. odoo-javascript
|
||||
#: code:addons/cx_web_refresh_from_backend/static/src/views/form/form_controller_patch.esm.js:0
|
||||
#: code:addons/cx_web_refresh_from_backend/static/src/views/list/list_controller_patch.esm.js:0
|
||||
#, python-format
|
||||
msgid "Cancel"
|
||||
msgstr ""
|
||||
|
||||
#. module: cx_web_refresh_from_backend
|
||||
#. odoo-javascript
|
||||
#: code:addons/cx_web_refresh_from_backend/static/src/views/form/form_controller_patch.esm.js:0
|
||||
#, python-format
|
||||
msgid "Continue"
|
||||
msgstr ""
|
||||
|
||||
#. module: cx_web_refresh_from_backend
|
||||
#. odoo-javascript
|
||||
#: code:addons/cx_web_refresh_from_backend/static/src/views/form/form_controller_patch.esm.js:0
|
||||
#, python-format
|
||||
msgid "Could not reload form. "
|
||||
msgstr ""
|
||||
|
||||
#. module: cx_web_refresh_from_backend
|
||||
#. odoo-javascript
|
||||
#: code:addons/cx_web_refresh_from_backend/static/src/views/kanban/kanban_controller_patch.esm.js:0
|
||||
#, python-format
|
||||
msgid "Could not reload kanban. "
|
||||
msgstr ""
|
||||
|
||||
#. module: cx_web_refresh_from_backend
|
||||
#. odoo-javascript
|
||||
#: code:addons/cx_web_refresh_from_backend/static/src/views/list/list_controller_patch.esm.js:0
|
||||
#, python-format
|
||||
msgid "Could not reload list. "
|
||||
msgstr ""
|
||||
|
||||
#. module: cx_web_refresh_from_backend
|
||||
#. odoo-javascript
|
||||
#: code:addons/cx_web_refresh_from_backend/static/src/views/list/list_controller_patch.esm.js:0
|
||||
#, python-format
|
||||
msgid "Could not save record. "
|
||||
msgstr ""
|
||||
|
||||
#. module: cx_web_refresh_from_backend
|
||||
#. odoo-javascript
|
||||
#: code:addons/cx_web_refresh_from_backend/static/src/views/form/form_controller_patch.esm.js:0
|
||||
#, python-format
|
||||
msgid "Form is being refreshed from backend"
|
||||
msgstr ""
|
||||
|
||||
#. module: cx_web_refresh_from_backend
|
||||
#. odoo-javascript
|
||||
#: code:addons/cx_web_refresh_from_backend/static/src/views/list/list_controller_patch.esm.js:0
|
||||
#, python-format
|
||||
msgid "List is being refreshed from backend"
|
||||
msgstr ""
|
||||
|
||||
#. module: cx_web_refresh_from_backend
|
||||
#. odoo-javascript
|
||||
#: code:addons/cx_web_refresh_from_backend/static/src/views/list/list_controller_patch.esm.js:0
|
||||
#, python-format
|
||||
msgid "Save & Refresh"
|
||||
msgstr ""
|
||||
|
||||
#. module: cx_web_refresh_from_backend
|
||||
#: model:ir.model,name:cx_web_refresh_from_backend.model_res_users
|
||||
msgid "User"
|
||||
msgstr ""
|
||||
|
||||
#. module: cx_web_refresh_from_backend
|
||||
#. odoo-javascript
|
||||
#: code:addons/cx_web_refresh_from_backend/static/src/views/list/list_controller_patch.esm.js:0
|
||||
#, python-format
|
||||
msgid "You have unsaved edits. Save them before refreshing?"
|
||||
msgstr ""
|
||||
@@ -1,4 +0,0 @@
|
||||
# Copyright 2025 Cetmix OÜ
|
||||
# License LGPL-3.0 or later (http://www.gnu.org/licenses/lgpl).
|
||||
|
||||
from . import res_users
|
||||
@@ -1,50 +0,0 @@
|
||||
# Copyright 2025 Cetmix OÜ
|
||||
# License LGPL-3.0 or later (http://www.gnu.org/licenses/lgpl).
|
||||
|
||||
from odoo import models
|
||||
|
||||
|
||||
class ResUsers(models.Model):
|
||||
_inherit = "res.users"
|
||||
|
||||
def reload_views(self, model, view_types=None, rec_ids=None):
|
||||
"""
|
||||
Trigger UI reload for selected users and record IDs.
|
||||
|
||||
This method allows to reload specific views from the backend.
|
||||
Be aware that when reloading form views, if a user is currently
|
||||
doing some updates, those updates may be lost.
|
||||
|
||||
:param model: str, Model name (e.g., 'res.partner')
|
||||
:param view_types: list of str, optional, View types to reload
|
||||
(e.g., ['form', 'kanban']). Leave blank to reload all views.
|
||||
:param rec_ids: list of int, optional, View will be reloaded only if a record
|
||||
with id from the list is present in the view.
|
||||
|
||||
Example usage:
|
||||
# Reload the kanban and form views for all salespeople
|
||||
# when an opportunity is won.
|
||||
# Will reload views only if the current opportunity is being displayed
|
||||
group_id = self.env.ref("sales_team.group_sale_salesman").id
|
||||
users_to_reload = self.env["res.users"].search(
|
||||
[("groups_id", "in", [group_id])]
|
||||
)
|
||||
users_to_reload.reload_views(
|
||||
model="crm.lead",
|
||||
view_types=["kanban", "form"],
|
||||
rec_ids=[self.id]
|
||||
)
|
||||
"""
|
||||
|
||||
# Prepare the message payload
|
||||
bus_message = {
|
||||
"model": model,
|
||||
"view_types": view_types or [],
|
||||
"rec_ids": rec_ids or [],
|
||||
}
|
||||
|
||||
# Send notification to each user's partner
|
||||
notifications = [
|
||||
[user.partner_id, "web.refresh_view", bus_message] for user in self
|
||||
]
|
||||
self.env["bus.bus"]._sendmany(notifications)
|
||||
@@ -1,3 +0,0 @@
|
||||
[build-system]
|
||||
requires = ["whool"]
|
||||
build-backend = "whool.buildapi"
|
||||
@@ -1,2 +0,0 @@
|
||||
* Cetmix
|
||||
|
||||
@@ -1,27 +0,0 @@
|
||||
# Backend UI Reload Module
|
||||
|
||||
This is a **technical module** that allows triggering a **UI reload** from the backend.
|
||||
It enables triggering the reload action for selected users and record IDs.
|
||||
|
||||
---
|
||||
|
||||
## 🔧 Helper Function: `reload_views`
|
||||
|
||||
A special helper function `reload_views` is added to the `res.users` model.
|
||||
|
||||
### **Arguments**
|
||||
|
||||
| Argument | Type | Description |
|
||||
|-----------|------|-------------|
|
||||
| **model** | `Char` | Model name, e.g. `'res.partner'` |
|
||||
| **view_types** | `List of Char` *(optional)* | View types to reload, e.g. `["form", "kanban"]`. Leave blank to reload all views. |
|
||||
| **rec_ids** | `List of Integer` *(optional)* | The view will be reloaded only if a record with an ID from this list is present in the view. |
|
||||
|
||||
---
|
||||
|
||||
## ⚠️ Important Notes
|
||||
|
||||
Use this function **wisely**.
|
||||
|
||||
When reloading **form views**, be aware that if a user is currently editing a record,
|
||||
**their unsaved updates may be lost**.
|
||||
@@ -1,16 +0,0 @@
|
||||
## 🧩 Example Usage
|
||||
|
||||
Below is a code snippet showing how to use the `reload_views` helper function.
|
||||
|
||||
```python
|
||||
# Reload the kanban and form views for all salespeople when an opportunity is won
|
||||
# Will reload views only if the current opportunity is being displayed
|
||||
|
||||
group_id = self.env.ref("sales_team.group_sale_salesman").id
|
||||
users_to_reload = self.env["res.users"].search([("groups_id", "in", [group_id])])
|
||||
users_to_reload.reload_views(
|
||||
model="crm.lead",
|
||||
view_types=["kanban", "form"],
|
||||
rec_ids=[self.id],
|
||||
)
|
||||
```
|
||||
|
Before Width: | Height: | Size: 204 KiB |
|
Before Width: | Height: | Size: 9.4 KiB |
@@ -1,484 +0,0 @@
|
||||
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
|
||||
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">
|
||||
<head>
|
||||
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
|
||||
<meta name="generator" content="Docutils: https://docutils.sourceforge.io/" />
|
||||
<title>Web Refresh From Backend</title>
|
||||
<style type="text/css">
|
||||
|
||||
/*
|
||||
:Author: David Goodger (goodger@python.org)
|
||||
:Id: $Id: html4css1.css 9511 2024-01-13 09:50:07Z milde $
|
||||
:Copyright: This stylesheet has been placed in the public domain.
|
||||
|
||||
Default cascading style sheet for the HTML output of Docutils.
|
||||
Despite the name, some widely supported CSS2 features are used.
|
||||
|
||||
See https://docutils.sourceforge.io/docs/howto/html-stylesheets.html for how to
|
||||
customize this style sheet.
|
||||
*/
|
||||
|
||||
/* used to remove borders from tables and images */
|
||||
.borderless, table.borderless td, table.borderless th {
|
||||
border: 0 }
|
||||
|
||||
table.borderless td, table.borderless th {
|
||||
/* Override padding for "table.docutils td" with "! important".
|
||||
The right padding separates the table cells. */
|
||||
padding: 0 0.5em 0 0 ! important }
|
||||
|
||||
.first {
|
||||
/* Override more specific margin styles with "! important". */
|
||||
margin-top: 0 ! important }
|
||||
|
||||
.last, .with-subtitle {
|
||||
margin-bottom: 0 ! important }
|
||||
|
||||
.hidden {
|
||||
display: none }
|
||||
|
||||
.subscript {
|
||||
vertical-align: sub;
|
||||
font-size: smaller }
|
||||
|
||||
.superscript {
|
||||
vertical-align: super;
|
||||
font-size: smaller }
|
||||
|
||||
a.toc-backref {
|
||||
text-decoration: none ;
|
||||
color: black }
|
||||
|
||||
blockquote.epigraph {
|
||||
margin: 2em 5em ; }
|
||||
|
||||
dl.docutils dd {
|
||||
margin-bottom: 0.5em }
|
||||
|
||||
object[type="image/svg+xml"], object[type="application/x-shockwave-flash"] {
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
/* Uncomment (and remove this text!) to get bold-faced definition list terms
|
||||
dl.docutils dt {
|
||||
font-weight: bold }
|
||||
*/
|
||||
|
||||
div.abstract {
|
||||
margin: 2em 5em }
|
||||
|
||||
div.abstract p.topic-title {
|
||||
font-weight: bold ;
|
||||
text-align: center }
|
||||
|
||||
div.admonition, div.attention, div.caution, div.danger, div.error,
|
||||
div.hint, div.important, div.note, div.tip, div.warning {
|
||||
margin: 2em ;
|
||||
border: medium outset ;
|
||||
padding: 1em }
|
||||
|
||||
div.admonition p.admonition-title, div.hint p.admonition-title,
|
||||
div.important p.admonition-title, div.note p.admonition-title,
|
||||
div.tip p.admonition-title {
|
||||
font-weight: bold ;
|
||||
font-family: sans-serif }
|
||||
|
||||
div.attention p.admonition-title, div.caution p.admonition-title,
|
||||
div.danger p.admonition-title, div.error p.admonition-title,
|
||||
div.warning p.admonition-title, .code .error {
|
||||
color: red ;
|
||||
font-weight: bold ;
|
||||
font-family: sans-serif }
|
||||
|
||||
/* Uncomment (and remove this text!) to get reduced vertical space in
|
||||
compound paragraphs.
|
||||
div.compound .compound-first, div.compound .compound-middle {
|
||||
margin-bottom: 0.5em }
|
||||
|
||||
div.compound .compound-last, div.compound .compound-middle {
|
||||
margin-top: 0.5em }
|
||||
*/
|
||||
|
||||
div.dedication {
|
||||
margin: 2em 5em ;
|
||||
text-align: center ;
|
||||
font-style: italic }
|
||||
|
||||
div.dedication p.topic-title {
|
||||
font-weight: bold ;
|
||||
font-style: normal }
|
||||
|
||||
div.figure {
|
||||
margin-left: 2em ;
|
||||
margin-right: 2em }
|
||||
|
||||
div.footer, div.header {
|
||||
clear: both;
|
||||
font-size: smaller }
|
||||
|
||||
div.line-block {
|
||||
display: block ;
|
||||
margin-top: 1em ;
|
||||
margin-bottom: 1em }
|
||||
|
||||
div.line-block div.line-block {
|
||||
margin-top: 0 ;
|
||||
margin-bottom: 0 ;
|
||||
margin-left: 1.5em }
|
||||
|
||||
div.sidebar {
|
||||
margin: 0 0 0.5em 1em ;
|
||||
border: medium outset ;
|
||||
padding: 1em ;
|
||||
background-color: #ffffee ;
|
||||
width: 40% ;
|
||||
float: right ;
|
||||
clear: right }
|
||||
|
||||
div.sidebar p.rubric {
|
||||
font-family: sans-serif ;
|
||||
font-size: medium }
|
||||
|
||||
div.system-messages {
|
||||
margin: 5em }
|
||||
|
||||
div.system-messages h1 {
|
||||
color: red }
|
||||
|
||||
div.system-message {
|
||||
border: medium outset ;
|
||||
padding: 1em }
|
||||
|
||||
div.system-message p.system-message-title {
|
||||
color: red ;
|
||||
font-weight: bold }
|
||||
|
||||
div.topic {
|
||||
margin: 2em }
|
||||
|
||||
h1.section-subtitle, h2.section-subtitle, h3.section-subtitle,
|
||||
h4.section-subtitle, h5.section-subtitle, h6.section-subtitle {
|
||||
margin-top: 0.4em }
|
||||
|
||||
h1.title {
|
||||
text-align: center }
|
||||
|
||||
h2.subtitle {
|
||||
text-align: center }
|
||||
|
||||
hr.docutils {
|
||||
width: 75% }
|
||||
|
||||
img.align-left, .figure.align-left, object.align-left, table.align-left {
|
||||
clear: left ;
|
||||
float: left ;
|
||||
margin-right: 1em }
|
||||
|
||||
img.align-right, .figure.align-right, object.align-right, table.align-right {
|
||||
clear: right ;
|
||||
float: right ;
|
||||
margin-left: 1em }
|
||||
|
||||
img.align-center, .figure.align-center, object.align-center {
|
||||
display: block;
|
||||
margin-left: auto;
|
||||
margin-right: auto;
|
||||
}
|
||||
|
||||
table.align-center {
|
||||
margin-left: auto;
|
||||
margin-right: auto;
|
||||
}
|
||||
|
||||
.align-left {
|
||||
text-align: left }
|
||||
|
||||
.align-center {
|
||||
clear: both ;
|
||||
text-align: center }
|
||||
|
||||
.align-right {
|
||||
text-align: right }
|
||||
|
||||
/* reset inner alignment in figures */
|
||||
div.align-right {
|
||||
text-align: inherit }
|
||||
|
||||
/* div.align-center * { */
|
||||
/* text-align: left } */
|
||||
|
||||
.align-top {
|
||||
vertical-align: top }
|
||||
|
||||
.align-middle {
|
||||
vertical-align: middle }
|
||||
|
||||
.align-bottom {
|
||||
vertical-align: bottom }
|
||||
|
||||
ol.simple, ul.simple {
|
||||
margin-bottom: 1em }
|
||||
|
||||
ol.arabic {
|
||||
list-style: decimal }
|
||||
|
||||
ol.loweralpha {
|
||||
list-style: lower-alpha }
|
||||
|
||||
ol.upperalpha {
|
||||
list-style: upper-alpha }
|
||||
|
||||
ol.lowerroman {
|
||||
list-style: lower-roman }
|
||||
|
||||
ol.upperroman {
|
||||
list-style: upper-roman }
|
||||
|
||||
p.attribution {
|
||||
text-align: right ;
|
||||
margin-left: 50% }
|
||||
|
||||
p.caption {
|
||||
font-style: italic }
|
||||
|
||||
p.credits {
|
||||
font-style: italic ;
|
||||
font-size: smaller }
|
||||
|
||||
p.label {
|
||||
white-space: nowrap }
|
||||
|
||||
p.rubric {
|
||||
font-weight: bold ;
|
||||
font-size: larger ;
|
||||
color: maroon ;
|
||||
text-align: center }
|
||||
|
||||
p.sidebar-title {
|
||||
font-family: sans-serif ;
|
||||
font-weight: bold ;
|
||||
font-size: larger }
|
||||
|
||||
p.sidebar-subtitle {
|
||||
font-family: sans-serif ;
|
||||
font-weight: bold }
|
||||
|
||||
p.topic-title {
|
||||
font-weight: bold }
|
||||
|
||||
pre.address {
|
||||
margin-bottom: 0 ;
|
||||
margin-top: 0 ;
|
||||
font: inherit }
|
||||
|
||||
pre.literal-block, pre.doctest-block, pre.math, pre.code {
|
||||
margin-left: 2em ;
|
||||
margin-right: 2em }
|
||||
|
||||
pre.code .ln { color: gray; } /* line numbers */
|
||||
pre.code, code { background-color: #eeeeee }
|
||||
pre.code .comment, code .comment { color: #5C6576 }
|
||||
pre.code .keyword, code .keyword { color: #3B0D06; font-weight: bold }
|
||||
pre.code .literal.string, code .literal.string { color: #0C5404 }
|
||||
pre.code .name.builtin, code .name.builtin { color: #352B84 }
|
||||
pre.code .deleted, code .deleted { background-color: #DEB0A1}
|
||||
pre.code .inserted, code .inserted { background-color: #A3D289}
|
||||
|
||||
span.classifier {
|
||||
font-family: sans-serif ;
|
||||
font-style: oblique }
|
||||
|
||||
span.classifier-delimiter {
|
||||
font-family: sans-serif ;
|
||||
font-weight: bold }
|
||||
|
||||
span.interpreted {
|
||||
font-family: sans-serif }
|
||||
|
||||
span.option {
|
||||
white-space: nowrap }
|
||||
|
||||
span.pre {
|
||||
white-space: pre }
|
||||
|
||||
span.problematic, pre.problematic {
|
||||
color: red }
|
||||
|
||||
span.section-subtitle {
|
||||
/* font-size relative to parent (h1..h6 element) */
|
||||
font-size: 80% }
|
||||
|
||||
table.citation {
|
||||
border-left: solid 1px gray;
|
||||
margin-left: 1px }
|
||||
|
||||
table.docinfo {
|
||||
margin: 2em 4em }
|
||||
|
||||
table.docutils {
|
||||
margin-top: 0.5em ;
|
||||
margin-bottom: 0.5em }
|
||||
|
||||
table.footnote {
|
||||
border-left: solid 1px black;
|
||||
margin-left: 1px }
|
||||
|
||||
table.docutils td, table.docutils th,
|
||||
table.docinfo td, table.docinfo th {
|
||||
padding-left: 0.5em ;
|
||||
padding-right: 0.5em ;
|
||||
vertical-align: top }
|
||||
|
||||
table.docutils th.field-name, table.docinfo th.docinfo-name {
|
||||
font-weight: bold ;
|
||||
text-align: left ;
|
||||
white-space: nowrap ;
|
||||
padding-left: 0 }
|
||||
|
||||
/* "booktabs" style (no vertical lines) */
|
||||
table.docutils.booktabs {
|
||||
border: 0px;
|
||||
border-top: 2px solid;
|
||||
border-bottom: 2px solid;
|
||||
border-collapse: collapse;
|
||||
}
|
||||
table.docutils.booktabs * {
|
||||
border: 0px;
|
||||
}
|
||||
table.docutils.booktabs th {
|
||||
border-bottom: thin solid;
|
||||
text-align: left;
|
||||
}
|
||||
|
||||
h1 tt.docutils, h2 tt.docutils, h3 tt.docutils,
|
||||
h4 tt.docutils, h5 tt.docutils, h6 tt.docutils {
|
||||
font-size: 100% }
|
||||
|
||||
ul.auto-toc {
|
||||
list-style-type: none }
|
||||
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<div class="document" id="web-refresh-from-backend">
|
||||
<h1 class="title">Web Refresh From Backend</h1>
|
||||
|
||||
<!-- !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
|
||||
!! This file is generated by oca-gen-addon-readme !!
|
||||
!! changes will be overwritten. !!
|
||||
!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
|
||||
!! source digest: sha256:e841ff66d3bfff0a3de22c9be5dc40f1ca539739f5487a9162fdf887fc5ac6ad
|
||||
!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! -->
|
||||
<p><a class="reference external image-reference" href="https://odoo-community.org/page/development-status"><img alt="Beta" src="https://img.shields.io/badge/maturity-Beta-yellow.png" /></a> <a class="reference external image-reference" href="http://www.gnu.org/licenses/lgpl-3.0-standalone.html"><img alt="License: LGPL-3" src="https://img.shields.io/badge/license-LGPL--3-blue.png" /></a> <a class="reference external image-reference" href="https://github.com/cetmix/cetmix-tower/tree/16.0/cx_web_refresh_from_backend"><img alt="cetmix/cetmix-tower" src="https://img.shields.io/badge/github-cetmix%2Fcetmix--tower-lightgray.png?logo=github" /></a></p>
|
||||
<div class="section" id="backend-ui-reload-module">
|
||||
<h1>Backend UI Reload Module</h1>
|
||||
<p>This is a <strong>technical module</strong> that allows triggering a <strong>UI reload</strong>
|
||||
from the backend. It enables triggering the reload action for selected
|
||||
users and record IDs.</p>
|
||||
<hr class="docutils" />
|
||||
<div class="section" id="helper-function-reload-views">
|
||||
<h2>🔧 Helper Function: <tt class="docutils literal">reload_views</tt></h2>
|
||||
<p>A special helper function <tt class="docutils literal">reload_views</tt> is added to the <tt class="docutils literal">res.users</tt>
|
||||
model.</p>
|
||||
<div class="section" id="arguments">
|
||||
<h3><strong>Arguments</strong></h3>
|
||||
<table border="1" class="docutils">
|
||||
<colgroup>
|
||||
<col width="24%" />
|
||||
<col width="38%" />
|
||||
<col width="38%" />
|
||||
</colgroup>
|
||||
<thead valign="bottom">
|
||||
<tr><th class="head">Argument</th>
|
||||
<th class="head">Type</th>
|
||||
<th class="head">Description</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody valign="top">
|
||||
<tr><td><strong>model</strong></td>
|
||||
<td><tt class="docutils literal">Char</tt></td>
|
||||
<td>Model name, e.g.
|
||||
<tt class="docutils literal">'res.partner'</tt></td>
|
||||
</tr>
|
||||
<tr><td><strong>view_types</strong></td>
|
||||
<td><tt class="docutils literal">List of Char</tt>
|
||||
<em>(optional)</em></td>
|
||||
<td>View types to reload,
|
||||
e.g.
|
||||
<tt class="docutils literal">["form", "kanban"]</tt>.
|
||||
Leave blank to reload
|
||||
all views.</td>
|
||||
</tr>
|
||||
<tr><td><strong>rec_ids</strong></td>
|
||||
<td><tt class="docutils literal">List of Integer</tt>
|
||||
<em>(optional)</em></td>
|
||||
<td>The view will be
|
||||
reloaded only if a
|
||||
record with an ID from
|
||||
this list is present in
|
||||
the view.</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
<hr class="docutils" />
|
||||
<div class="section" id="important-notes">
|
||||
<h2>⚠️ Important Notes</h2>
|
||||
<p>Use this function <strong>wisely</strong>.</p>
|
||||
<p>When reloading <strong>form views</strong>, be aware that if a user is currently
|
||||
editing a record, <strong>their unsaved updates may be lost</strong>.</p>
|
||||
<p><strong>Table of contents</strong></p>
|
||||
</div>
|
||||
</div>
|
||||
<div class="section" id="usage">
|
||||
<h1>Usage</h1>
|
||||
<div class="section" id="example-usage">
|
||||
<h2>🧩 Example Usage</h2>
|
||||
<p>Below is a code snippet showing how to use the <tt class="docutils literal">reload_views</tt> helper
|
||||
function.</p>
|
||||
<pre class="code python literal-block">
|
||||
<span class="c1"># Reload the kanban and form views for all salespeople when an opportunity is won</span><span class="w">
|
||||
</span><span class="c1"># Will reload views only if the current opportunity is being displayed</span><span class="w">
|
||||
|
||||
</span><span class="n">group_id</span> <span class="o">=</span> <span class="bp">self</span><span class="o">.</span><span class="n">env</span><span class="o">.</span><span class="n">ref</span><span class="p">(</span><span class="s2">"sales_team.group_sale_salesman"</span><span class="p">)</span><span class="o">.</span><span class="n">id</span><span class="w">
|
||||
</span><span class="n">users_to_reload</span> <span class="o">=</span> <span class="bp">self</span><span class="o">.</span><span class="n">env</span><span class="p">[</span><span class="s2">"res.users"</span><span class="p">]</span><span class="o">.</span><span class="n">search</span><span class="p">([(</span><span class="s2">"groups_id"</span><span class="p">,</span> <span class="s2">"in"</span><span class="p">,</span> <span class="p">[</span><span class="n">group_id</span><span class="p">])])</span><span class="w">
|
||||
</span><span class="n">users_to_reload</span><span class="o">.</span><span class="n">reload_views</span><span class="p">(</span><span class="w">
|
||||
</span> <span class="n">model</span><span class="o">=</span><span class="s2">"crm.lead"</span><span class="p">,</span><span class="w">
|
||||
</span> <span class="n">view_types</span><span class="o">=</span><span class="p">[</span><span class="s2">"kanban"</span><span class="p">,</span> <span class="s2">"form"</span><span class="p">],</span><span class="w">
|
||||
</span> <span class="n">rec_ids</span><span class="o">=</span><span class="p">[</span><span class="bp">self</span><span class="o">.</span><span class="n">id</span><span class="p">],</span><span class="w">
|
||||
</span><span class="p">)</span>
|
||||
</pre>
|
||||
</div>
|
||||
</div>
|
||||
<div class="section" id="bug-tracker">
|
||||
<h1>Bug Tracker</h1>
|
||||
<p>Bugs are tracked on <a class="reference external" href="https://github.com/cetmix/cetmix-tower/issues">GitHub Issues</a>.
|
||||
In case of trouble, please check there if your issue has already been reported.
|
||||
If you spotted it first, help us to smash it by providing a detailed and welcomed
|
||||
<a class="reference external" href="https://github.com/cetmix/cetmix-tower/issues/new?body=module:%20cx_web_refresh_from_backend%0Aversion:%2016.0%0A%0A**Steps%20to%20reproduce**%0A-%20...%0A%0A**Current%20behavior**%0A%0A**Expected%20behavior**">feedback</a>.</p>
|
||||
<p>Do not contact contributors directly about support or help with technical issues.</p>
|
||||
</div>
|
||||
<div class="section" id="credits">
|
||||
<h1>Credits</h1>
|
||||
<div class="section" id="authors">
|
||||
<h2>Authors</h2>
|
||||
<ul class="simple">
|
||||
<li>Cetmix</li>
|
||||
</ul>
|
||||
</div>
|
||||
<div class="section" id="contributors">
|
||||
<h2>Contributors</h2>
|
||||
<ul class="simple">
|
||||
<li>Cetmix</li>
|
||||
</ul>
|
||||
</div>
|
||||
<div class="section" id="maintainers">
|
||||
<h2>Maintainers</h2>
|
||||
<p>This module is part of the <a class="reference external" href="https://github.com/cetmix/cetmix-tower/tree/16.0/cx_web_refresh_from_backend">cetmix/cetmix-tower</a> project on GitHub.</p>
|
||||
<p>You are welcome to contribute.</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
||||
@@ -1,181 +0,0 @@
|
||||
/** @odoo-module **/
|
||||
|
||||
import {FormController} from "@web/views/form/form_controller";
|
||||
import {patch} from "@web/core/utils/patch";
|
||||
import {useService} from "@web/core/utils/hooks";
|
||||
import {onWillUnmount} from "@odoo/owl";
|
||||
import {ConfirmationDialog} from "@web/core/confirmation_dialog/confirmation_dialog";
|
||||
|
||||
// Patch the standard FormController to react on bus notifications
|
||||
patch(FormController.prototype, "cx_web_refresh_from_backend.FormController", {
|
||||
setup() {
|
||||
// Call original setup logic
|
||||
this._super(...arguments);
|
||||
|
||||
// Get core services used by this behavior
|
||||
this.busService = useService("bus_service");
|
||||
this.actionService = useService("action");
|
||||
this.notificationService = useService("notification");
|
||||
|
||||
// Timestamp of last local save (used to avoid immediate auto-refresh)
|
||||
this._lastLocalSave = null;
|
||||
|
||||
// Bind the handler to keep reference for cleanup
|
||||
this._boundBusHandler = this._onBusNotification.bind(this);
|
||||
|
||||
// Subscribe to bus notifications
|
||||
this.busService.addEventListener("notification", this._boundBusHandler);
|
||||
|
||||
// Cleanup subscription on component unmount
|
||||
onWillUnmount(() => {
|
||||
if (this.busService && this._boundBusHandler) {
|
||||
this.busService.removeEventListener(
|
||||
"notification",
|
||||
this._boundBusHandler
|
||||
);
|
||||
}
|
||||
});
|
||||
},
|
||||
|
||||
/**
|
||||
* Handle bus notification for view refresh.
|
||||
* Listens for notifications with type "web.refresh_view" and delegates
|
||||
* processing to _handleViewRefresh.
|
||||
*
|
||||
* @param {Event} event - Bus notification event
|
||||
*/
|
||||
async _onBusNotification({detail: notifications}) {
|
||||
// Check if component is still alive
|
||||
if (!this.model || !this.model.root) {
|
||||
return;
|
||||
}
|
||||
|
||||
for (const {payload, type} of notifications) {
|
||||
if (type === "web.refresh_view") {
|
||||
await this._handleViewRefresh(payload);
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* Handle view refresh notification.
|
||||
*
|
||||
* Only refreshes when:
|
||||
* - model matches current form model
|
||||
* - requested view types include "form" (if specified)
|
||||
* - record id matches current record (if specified)
|
||||
*
|
||||
* @param {Object} notification - Notification payload
|
||||
*/
|
||||
async _handleViewRefresh(notification) {
|
||||
const {model, view_types = [], rec_ids = []} = notification;
|
||||
|
||||
// Check if the model matches current form model
|
||||
if (this.props.resModel !== model) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Check if view_type matches (if specified in notification)
|
||||
if (view_types.length > 0 && !view_types.includes("form")) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Check if record ID matches (if rec_ids is specified)
|
||||
const currentResId = this.model && this.model.root && this.model.root.resId;
|
||||
if (rec_ids.length > 0 && (!currentResId || !rec_ids.includes(currentResId))) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Skip refresh when form is in a dialog or when a wizard is on top of the stack.
|
||||
// Refreshing in that context can leave wizard/confirmation dialogs stuck open
|
||||
// (e.g. confirm="..." in wizard view).
|
||||
if (this.env.inDialog) {
|
||||
return;
|
||||
}
|
||||
const currentController = this.actionService.currentController;
|
||||
const currentAction = currentController && currentController.action;
|
||||
if (currentAction && currentAction.target === "new") {
|
||||
return;
|
||||
}
|
||||
|
||||
await this.refreshForm();
|
||||
},
|
||||
|
||||
/**
|
||||
* Refresh the form with actual data from server.
|
||||
*
|
||||
* For normal forms:
|
||||
* - if record is clean: perform a soft_reload action
|
||||
* - if record has unsaved changes: ask for confirmation, then reload
|
||||
*
|
||||
* For wizards (dialogs, target="new"):
|
||||
* - reload only the current record without full action reload
|
||||
*
|
||||
* @returns {Promise<void>}
|
||||
*/
|
||||
async refreshForm() {
|
||||
// Do not refresh immediately after an explicit save (debounce window)
|
||||
if (this._lastLocalSave && Date.now() - this._lastLocalSave < 1000) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (!this.model || !this.model.root) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Check if this form is opened as a wizard (dialog)
|
||||
const currentController = this.actionService.currentController;
|
||||
const action = currentController && currentController.action;
|
||||
const isWizard = action && action.target === "new";
|
||||
|
||||
const record = this.model.root;
|
||||
|
||||
if (!isWizard && record.isDirty) {
|
||||
// Ask user whether to discard unsaved changes before refreshing
|
||||
const confirmed = await new Promise((resolve) => {
|
||||
this.dialogService.add(ConfirmationDialog, {
|
||||
title: this.env._t("Form is being refreshed from backend"),
|
||||
body: this.env._t("All unsaved changes will be lost! Continue?"),
|
||||
confirm: () => resolve(true),
|
||||
cancel: () => resolve(false),
|
||||
confirmLabel: this.env._t("Continue"),
|
||||
cancelLabel: this.env._t("Cancel"),
|
||||
});
|
||||
});
|
||||
|
||||
if (!confirmed) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
try {
|
||||
await record.load();
|
||||
} catch (error) {
|
||||
const message =
|
||||
(error && error.data && error.data.message) ||
|
||||
(error && error.message) ||
|
||||
String(error);
|
||||
this.notificationService.add(
|
||||
this.env._t("Could not reload form. ") + message,
|
||||
{type: "danger"}
|
||||
);
|
||||
return;
|
||||
}
|
||||
|
||||
// Update the view (only if component is still mounted)
|
||||
if (this.model && this.model.root) {
|
||||
this.render(true);
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* Override of save button handler.
|
||||
*
|
||||
* Stores timestamp of last local save to avoid immediate auto-refresh
|
||||
* triggered by our own changes.
|
||||
*/
|
||||
async saveButtonClicked() {
|
||||
this._lastLocalSave = Date.now();
|
||||
return await this._super(...arguments);
|
||||
},
|
||||
});
|
||||
@@ -1,137 +0,0 @@
|
||||
/** @odoo-module **/
|
||||
|
||||
import {KanbanController} from "@web/views/kanban/kanban_controller";
|
||||
import {patch} from "@web/core/utils/patch";
|
||||
import {useService} from "@web/core/utils/hooks";
|
||||
import {onWillUnmount} from "@odoo/owl";
|
||||
|
||||
patch(KanbanController.prototype, "cx_web_refresh_from_backend.KanbanController", {
|
||||
setup() {
|
||||
this._super(...arguments);
|
||||
this.busService = useService("bus_service");
|
||||
this.notificationService = useService("notification");
|
||||
|
||||
// Bind the handler to keep reference for cleanup
|
||||
this._boundBusHandler = this._onBusNotification.bind(this);
|
||||
|
||||
// Subscribe to bus notifications
|
||||
this.busService.addEventListener("notification", this._boundBusHandler);
|
||||
|
||||
// Cleanup on unmount
|
||||
onWillUnmount(() => {
|
||||
if (this.busService && this._boundBusHandler) {
|
||||
this.busService.removeEventListener(
|
||||
"notification",
|
||||
this._boundBusHandler
|
||||
);
|
||||
}
|
||||
});
|
||||
},
|
||||
|
||||
/**
|
||||
* Handle bus notification for view refresh
|
||||
* @param {Event} event - Bus notification event
|
||||
*/
|
||||
async _onBusNotification({detail: notifications}) {
|
||||
// Check if component is still alive
|
||||
if (!this.model || !this.model.root) {
|
||||
return;
|
||||
}
|
||||
|
||||
for (const {payload, type} of notifications) {
|
||||
if (type === "web.refresh_view") {
|
||||
await this._handleViewRefresh(payload);
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* Handle view refresh notification
|
||||
* @param {Object} notification - Notification payload
|
||||
*/
|
||||
async _handleViewRefresh(notification) {
|
||||
const {model, view_types = [], rec_ids = []} = notification;
|
||||
|
||||
// Check if the model matches
|
||||
if (this.props.resModel !== model) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Check if view_type matches (if specified)
|
||||
if (view_types.length > 0 && !view_types.includes("kanban")) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Check if record ID matches (if rec_ids is specified)
|
||||
if (rec_ids.length > 0) {
|
||||
const loadedIds = this.getLoadedRecordIds();
|
||||
const shouldReload = loadedIds.some((id) => rec_ids.includes(id));
|
||||
|
||||
if (!shouldReload) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
await this.refreshList();
|
||||
},
|
||||
|
||||
/**
|
||||
* Refresh the kanban with actual data from server
|
||||
* @returns {Promise<void>}
|
||||
*/
|
||||
async refreshList() {
|
||||
// Safety check: component might be destroyed
|
||||
if (!this.model || !this.model.root) {
|
||||
return;
|
||||
}
|
||||
|
||||
const list = this.model.root;
|
||||
|
||||
// Reload data from server
|
||||
try {
|
||||
await list.load();
|
||||
} catch (error) {
|
||||
const message =
|
||||
(error && error.data && error.data.message) ||
|
||||
(error && error.message) ||
|
||||
String(error);
|
||||
this.notificationService.add(
|
||||
this.env._t("Could not reload kanban. ") + message,
|
||||
{type: "danger"}
|
||||
);
|
||||
return;
|
||||
}
|
||||
|
||||
// Update the view (only if component is still mounted)
|
||||
if (this.model && this.model.root) {
|
||||
this.render(true);
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* Get IDs of all loaded records on the current page
|
||||
* @returns {Array<Number>} Array of record IDs
|
||||
*/
|
||||
getLoadedRecordIds() {
|
||||
const list = this.model.root;
|
||||
|
||||
if (list.isGrouped) {
|
||||
// For grouped kanban, collect IDs from all groups
|
||||
const recordIds = [];
|
||||
const collectIds = (groups) => {
|
||||
for (const group of groups) {
|
||||
if (group.list && group.list.records) {
|
||||
recordIds.push(...group.list.records.map((r) => r.resId));
|
||||
}
|
||||
if (group.groups) {
|
||||
collectIds(group.groups);
|
||||
}
|
||||
}
|
||||
};
|
||||
collectIds(list.groups);
|
||||
return recordIds;
|
||||
}
|
||||
// For regular kanban, return IDs of all records
|
||||
return list.records.map((record) => record.resId);
|
||||
},
|
||||
});
|
||||
@@ -1,177 +0,0 @@
|
||||
/** @odoo-module **/
|
||||
|
||||
import {ListController} from "@web/views/list/list_controller";
|
||||
import {patch} from "@web/core/utils/patch";
|
||||
import {useService} from "@web/core/utils/hooks";
|
||||
import {onWillUnmount} from "@odoo/owl";
|
||||
import {ConfirmationDialog} from "@web/core/confirmation_dialog/confirmation_dialog";
|
||||
|
||||
patch(ListController.prototype, "cx_web_refresh_from_backend.ListController", {
|
||||
setup() {
|
||||
this._super(...arguments);
|
||||
this.busService = useService("bus_service");
|
||||
this.dialogService = useService("dialog");
|
||||
this.notificationService = useService("notification");
|
||||
|
||||
// Bind the handler to keep reference for cleanup
|
||||
this._boundBusHandler = this._onBusNotification.bind(this);
|
||||
|
||||
// Subscribe to bus notifications
|
||||
this.busService.addEventListener("notification", this._boundBusHandler);
|
||||
|
||||
// Cleanup on unmount
|
||||
onWillUnmount(() => {
|
||||
if (this.busService && this._boundBusHandler) {
|
||||
this.busService.removeEventListener(
|
||||
"notification",
|
||||
this._boundBusHandler
|
||||
);
|
||||
}
|
||||
});
|
||||
},
|
||||
|
||||
/**
|
||||
* Handle bus notification for view refresh
|
||||
* @param {Event} event - Bus notification event
|
||||
*/
|
||||
async _onBusNotification({detail: notifications}) {
|
||||
// Check if component is still alive
|
||||
if (!this.model || !this.model.root) {
|
||||
return;
|
||||
}
|
||||
|
||||
for (const {payload, type} of notifications) {
|
||||
if (type === "web.refresh_view") {
|
||||
await this._handleViewRefresh(payload);
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* Handle view refresh notification
|
||||
* @param {Object} notification - Notification payload
|
||||
*/
|
||||
async _handleViewRefresh(notification) {
|
||||
const {model, view_types = [], rec_ids = []} = notification;
|
||||
|
||||
// Check if the model matches
|
||||
if (this.props.resModel !== model) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Check if view_type matches (if specified)
|
||||
if (
|
||||
view_types.length > 0 &&
|
||||
!view_types.includes("list") &&
|
||||
!view_types.includes("tree")
|
||||
) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Check if record ID matches (if rec_ids is specified)
|
||||
if (rec_ids.length > 0) {
|
||||
const loadedIds = this.getLoadedRecordIds();
|
||||
const shouldReload = loadedIds.some((id) => rec_ids.includes(id));
|
||||
|
||||
if (!shouldReload) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
await this.refreshList();
|
||||
},
|
||||
|
||||
/**
|
||||
* Refresh the list with actual data from server.
|
||||
* If there is an edited record, asks the user to save or cancel.
|
||||
*
|
||||
* @returns {Promise<void>}
|
||||
*/
|
||||
async refreshList() {
|
||||
// Safety check: component might be destroyed
|
||||
if (!this.model || !this.model.root) {
|
||||
return;
|
||||
}
|
||||
|
||||
const list = this.model.root;
|
||||
|
||||
if (list.editedRecord) {
|
||||
const confirmed = await new Promise((resolve) => {
|
||||
this.dialogService.add(ConfirmationDialog, {
|
||||
title: this.env._t("List is being refreshed from backend"),
|
||||
body: this.env._t(
|
||||
"You have unsaved edits. Save them before refreshing?"
|
||||
),
|
||||
confirm: () => resolve(true),
|
||||
cancel: () => resolve(false),
|
||||
confirmLabel: this.env._t("Save & Refresh"),
|
||||
cancelLabel: this.env._t("Cancel"),
|
||||
});
|
||||
});
|
||||
|
||||
if (!confirmed) {
|
||||
return;
|
||||
}
|
||||
try {
|
||||
await list.editedRecord.save();
|
||||
} catch (error) {
|
||||
const message =
|
||||
(error && error.data && error.data.message) ||
|
||||
(error && error.message) ||
|
||||
String(error);
|
||||
this.notificationService.add(
|
||||
this.env._t("Could not save record. ") + message,
|
||||
{type: "danger"}
|
||||
);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
// Reload data from server
|
||||
try {
|
||||
await list.load();
|
||||
} catch (error) {
|
||||
const message =
|
||||
(error && error.data && error.data.message) ||
|
||||
(error && error.message) ||
|
||||
String(error);
|
||||
this.notificationService.add(
|
||||
this.env._t("Could not reload list. ") + message,
|
||||
{type: "danger"}
|
||||
);
|
||||
return;
|
||||
}
|
||||
|
||||
// Update the view (only if component is still mounted)
|
||||
if (this.model && this.model.root) {
|
||||
this.render(true);
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* Get IDs of all loaded records on the current page
|
||||
* @returns {Array<Number>} Array of record IDs
|
||||
*/
|
||||
getLoadedRecordIds() {
|
||||
const list = this.model.root;
|
||||
|
||||
if (list.isGrouped) {
|
||||
// For grouped list, collect IDs from all groups
|
||||
const recordIds = [];
|
||||
const collectIds = (groups) => {
|
||||
for (const group of groups) {
|
||||
if (group.list && group.list.records) {
|
||||
recordIds.push(...group.list.records.map((r) => r.resId));
|
||||
}
|
||||
if (group.groups) {
|
||||
collectIds(group.groups);
|
||||
}
|
||||
}
|
||||
};
|
||||
collectIds(list.groups);
|
||||
return recordIds;
|
||||
}
|
||||
// For regular list, return IDs of all records
|
||||
return list.records.map((record) => record.resId);
|
||||
},
|
||||
});
|
||||
@@ -1,4 +0,0 @@
|
||||
# Copyright 2025 Cetmix OÜ
|
||||
# License LGPL-3.0 or later (http://www.gnu.org/licenses/lgpl).
|
||||
|
||||
from . import test_reload_views
|
||||
@@ -1,95 +0,0 @@
|
||||
# Copyright 2025 Cetmix OÜ
|
||||
# License LGPL-3.0 or later (http://www.gnu.org/licenses/lgpl).
|
||||
|
||||
from unittest.mock import patch
|
||||
|
||||
from odoo.tests import tagged
|
||||
from odoo.tests.common import TransactionCase
|
||||
|
||||
|
||||
@tagged("post_install", "-at_install")
|
||||
class TestReloadViews(TransactionCase):
|
||||
@classmethod
|
||||
def setUpClass(cls):
|
||||
super().setUpClass()
|
||||
cls.user_admin = cls.env.ref("base.user_admin")
|
||||
cls.user_demo = cls.env.ref("base.user_demo")
|
||||
cls.partner = cls.env["res.partner"].create(
|
||||
{
|
||||
"name": "Test Partner",
|
||||
}
|
||||
)
|
||||
|
||||
def test_reload_views_basic(self):
|
||||
"""Test basic reload_views call without parameters"""
|
||||
with patch.object(type(self.env["bus.bus"]), "_sendmany") as mock_sendmany:
|
||||
self.user_admin.reload_views(model="res.partner")
|
||||
|
||||
mock_sendmany.assert_called_once()
|
||||
# Get the notifications list - it's the first positional argument
|
||||
notifications = mock_sendmany.call_args[0][0]
|
||||
self.assertEqual(len(notifications), 1)
|
||||
|
||||
partner, channel, message = notifications[0]
|
||||
self.assertEqual(partner, self.user_admin.partner_id)
|
||||
self.assertEqual(channel, "web.refresh_view")
|
||||
self.assertEqual(message["model"], "res.partner")
|
||||
self.assertEqual(message["view_types"], [])
|
||||
self.assertEqual(message["rec_ids"], [])
|
||||
|
||||
def test_reload_views_with_params(self):
|
||||
"""Test reload_views with view_types and rec_ids parameters"""
|
||||
with patch.object(type(self.env["bus.bus"]), "_sendmany") as mock_sendmany:
|
||||
self.user_admin.reload_views(
|
||||
model="res.partner",
|
||||
view_types=["form", "kanban"],
|
||||
rec_ids=[self.partner.id],
|
||||
)
|
||||
|
||||
notifications = mock_sendmany.call_args[0][0]
|
||||
message = notifications[0][2]
|
||||
self.assertEqual(message["view_types"], ["form", "kanban"])
|
||||
self.assertEqual(message["rec_ids"], [self.partner.id])
|
||||
|
||||
def test_reload_views_multiple_users(self):
|
||||
"""Test reload_views for multiple users at once"""
|
||||
users = self.user_admin | self.user_demo
|
||||
|
||||
with patch.object(type(self.env["bus.bus"]), "_sendmany") as mock_sendmany:
|
||||
users.reload_views(model="res.partner")
|
||||
|
||||
notifications = mock_sendmany.call_args[0][0]
|
||||
self.assertEqual(len(notifications), 2)
|
||||
|
||||
# Verify both users' partners are notified
|
||||
notified_partners = {n[0] for n in notifications}
|
||||
expected_partners = {self.user_admin.partner_id, self.user_demo.partner_id}
|
||||
self.assertEqual(notified_partners, expected_partners)
|
||||
|
||||
def test_reload_views_recordset(self):
|
||||
"""Test reload_views on a multi-record user recordset.
|
||||
|
||||
Ensures that calling reload_views on a recordset still results in a
|
||||
single _sendmany call, with one notification entry per user.
|
||||
"""
|
||||
users = self.user_admin | self.user_demo
|
||||
|
||||
with patch.object(type(self.env["bus.bus"]), "_sendmany") as mock_sendmany:
|
||||
users.reload_views(model="res.partner")
|
||||
|
||||
# _sendmany should be called only once for the whole recordset
|
||||
mock_sendmany.assert_called_once()
|
||||
|
||||
notifications = mock_sendmany.call_args[0][0]
|
||||
# We expect one notification tuple per user in the recordset
|
||||
self.assertEqual(len(notifications), 2)
|
||||
|
||||
# Verify both users' partners are notified and payload is correct
|
||||
for partner, channel, message in notifications:
|
||||
self.assertIn(
|
||||
partner, {self.user_admin.partner_id, self.user_demo.partner_id}
|
||||
)
|
||||
self.assertEqual(channel, "web.refresh_view")
|
||||
self.assertEqual(message["model"], "res.partner")
|
||||
self.assertEqual(message["view_types"], [])
|
||||
self.assertEqual(message["rec_ids"], [])
|
||||