Compare commits

..

58 Commits

Author SHA1 Message Date
b2efe86375 Tower: upload bi_print_journal_entries 19.0.0.0 (via marketplace) 2026-05-01 13:58:35 +00:00
9d9b59c5d3 Tower: upload bi_print_journal_entries 19.0.0.0 (via marketplace) 2026-05-01 13:58:34 +00:00
e8ffe910ee Tower: upload bi_print_journal_entries 19.0.0.0 (via marketplace) 2026-05-01 13:58:34 +00:00
727fc0e844 Tower: upload bi_print_journal_entries 19.0.0.0 (via marketplace) 2026-05-01 13:58:33 +00:00
f7982d1a0e Tower: upload bi_print_journal_entries 19.0.0.0 (via marketplace) 2026-05-01 13:58:32 +00:00
ee132a00a2 Tower: upload bi_print_journal_entries 19.0.0.0 (via marketplace) 2026-05-01 13:58:31 +00:00
1861912af3 Tower: upload bi_print_journal_entries 19.0.0.0 (via marketplace) 2026-05-01 13:58:31 +00:00
b86139b373 Tower: upload bi_print_journal_entries 19.0.0.0 (via marketplace) 2026-05-01 13:58:30 +00:00
b8c54e3f8e Tower: upload bi_print_journal_entries 19.0.0.0 (via marketplace) 2026-05-01 13:58:29 +00:00
1b57e46bb1 Tower: upload bi_print_journal_entries 19.0.0.0 (via marketplace) 2026-05-01 13:58:28 +00:00
dd948951db Tower: upload bi_print_journal_entries 19.0.0.0 (via marketplace) 2026-05-01 13:58:27 +00:00
3bd6e77fa2 Tower: upload bi_print_journal_entries 19.0.0.0 (via marketplace) 2026-05-01 13:58:27 +00:00
193600db34 Tower: upload bi_print_journal_entries 19.0.0.0 (via marketplace) 2026-05-01 13:58:26 +00:00
3d2024ab9f Tower: upload bi_print_journal_entries 19.0.0.0 (via marketplace) 2026-05-01 13:58:25 +00:00
2c163771d4 Tower: upload bi_print_journal_entries 19.0.0.0 (via marketplace) 2026-05-01 13:58:24 +00:00
26f24c3022 Tower: upload bi_print_journal_entries 19.0.0.0 (via marketplace) 2026-05-01 13:58:23 +00:00
f61e5f7e77 Tower: upload bi_print_journal_entries 19.0.0.0 (via marketplace) 2026-05-01 13:58:23 +00:00
3391a4c864 Tower: upload bi_print_journal_entries 19.0.0.0 (via marketplace) 2026-05-01 13:58:21 +00:00
48ad67fc95 Tower: upload bi_print_journal_entries 19.0.0.0 (via marketplace) 2026-05-01 13:58:21 +00:00
cb0fcf1b06 Tower: upload bi_print_journal_entries 19.0.0.0 (via marketplace) 2026-05-01 13:58:20 +00:00
2e42407d45 Tower: upload bi_print_journal_entries 19.0.0.0 (via marketplace) 2026-05-01 13:58:19 +00:00
695188ae0f Tower: upload bi_print_journal_entries 19.0.0.0 (via marketplace) 2026-05-01 13:58:18 +00:00
9df8e641fd Tower: upload bi_print_journal_entries 19.0.0.0 (via marketplace) 2026-05-01 13:58:18 +00:00
20806189b9 Tower: upload bi_print_journal_entries 19.0.0.0 (via marketplace) 2026-05-01 13:58:17 +00:00
e85bcbd0a1 Tower: upload bi_print_journal_entries 19.0.0.0 (via marketplace) 2026-05-01 13:58:16 +00:00
a9ab2991d7 Tower: upload bi_print_journal_entries 19.0.0.0 (via marketplace) 2026-05-01 13:58:15 +00:00
9dca096734 Tower: upload bi_print_journal_entries 19.0.0.0 (via marketplace) 2026-05-01 13:58:14 +00:00
657f3e4fb4 Tower: upload bi_print_journal_entries 19.0.0.0 (via marketplace) 2026-05-01 13:58:13 +00:00
80bbd23379 Tower: upload bi_print_journal_entries 19.0.0.0 (via marketplace) 2026-05-01 13:58:12 +00:00
50ce4f41a2 Tower: upload bi_print_journal_entries 19.0.0.0 (via marketplace) 2026-05-01 13:58:11 +00:00
d57b15f9dd Tower: upload bi_print_journal_entries 19.0.0.0 (via marketplace) 2026-05-01 13:58:11 +00:00
db8cd58f31 Tower: upload bi_print_journal_entries 19.0.0.0 (via marketplace) 2026-05-01 13:58:10 +00:00
f0745cda51 Tower: upload bi_print_journal_entries 19.0.0.0 (via marketplace) 2026-05-01 13:58:09 +00:00
6752ab966e Tower: upload bi_print_journal_entries 19.0.0.0 (via marketplace) 2026-05-01 13:58:09 +00:00
8ddc02a7c8 Tower: upload bi_print_journal_entries 19.0.0.0 (via marketplace) 2026-05-01 13:58:08 +00:00
db033c9d8b Tower: upload bi_print_journal_entries 19.0.0.0 (via marketplace) 2026-05-01 13:58:07 +00:00
d97e5cecb2 Tower: upload bi_print_journal_entries 19.0.0.0 (via marketplace) 2026-05-01 13:58:06 +00:00
46ab5fa005 Tower: upload bi_print_journal_entries 19.0.0.0 (via marketplace) 2026-05-01 13:58:06 +00:00
147f237c6f Tower: upload bi_print_journal_entries 19.0.0.0 (via marketplace) 2026-05-01 13:58:05 +00:00
a488d64cb3 Tower: upload bi_print_journal_entries 19.0.0.0 (via marketplace) 2026-05-01 13:58:04 +00:00
6b5ccf1604 Tower: upload bi_print_journal_entries 19.0.0.0 (via marketplace) 2026-05-01 13:58:03 +00:00
516e1a3bb4 Tower: upload bi_print_journal_entries 19.0.0.0 (via marketplace) 2026-05-01 13:58:03 +00:00
4c42f51869 Tower: upload bi_print_journal_entries 19.0.0.0 (via marketplace) 2026-05-01 13:58:02 +00:00
9d8cbe6868 Tower: upload bi_print_journal_entries 19.0.0.0 (via marketplace) 2026-05-01 13:58:01 +00:00
23dace1045 Tower: upload bi_print_journal_entries 19.0.0.0 (via marketplace) 2026-05-01 13:58:00 +00:00
c2cb157124 Tower: upload bi_print_journal_entries 19.0.0.0 (via marketplace) 2026-05-01 13:57:59 +00:00
070f8187f1 Tower: upload bi_print_journal_entries 19.0.0.0 (via marketplace) 2026-05-01 13:57:58 +00:00
b4f4024174 Tower: upload bi_print_journal_entries 19.0.0.0 (via marketplace) 2026-05-01 13:57:58 +00:00
91e6814c2f Tower: upload bi_print_journal_entries 19.0.0.0 (via marketplace) 2026-05-01 13:57:57 +00:00
ef39e07904 Tower: upload bi_print_journal_entries 19.0.0.0 (via marketplace) 2026-05-01 13:57:56 +00:00
71d4b51e09 Tower: upload bi_print_journal_entries 19.0.0.0 (via marketplace) 2026-05-01 13:57:55 +00:00
1a83db992c Tower: upload bi_print_journal_entries 19.0.0.0 (via marketplace) 2026-05-01 13:57:55 +00:00
76ce02f7fa Tower: upload bi_print_journal_entries 19.0.0.0 (via marketplace) 2026-05-01 13:57:54 +00:00
4de59a7674 Tower: upload bi_print_journal_entries 19.0.0.0 (via marketplace) 2026-05-01 13:57:53 +00:00
dc0417b263 Tower: upload bi_print_journal_entries 19.0.0.0 (via marketplace) 2026-05-01 13:57:52 +00:00
5684de0fbc Tower: upload bi_print_journal_entries 19.0.0.0 (via marketplace) 2026-05-01 13:57:51 +00:00
3fae6c3b2d Tower: upload bi_print_journal_entries 19.0.0.0 (via marketplace) 2026-05-01 13:57:51 +00:00
9a89e53ec4 Tower: upload bi_print_journal_entries 19.0.0.0 (via marketplace) 2026-05-01 13:57:50 +00:00
76 changed files with 934 additions and 1434 deletions

View 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.

View 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"],
}

View File

@@ -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>

View File

@@ -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>

Binary file not shown.

After

Width:  |  Height:  |  Size: 217 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 114 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 267 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 111 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 41 KiB

File diff suppressed because one or more lines are too long

After

Width:  |  Height:  |  Size: 108 KiB

File diff suppressed because one or more lines are too long

After

Width:  |  Height:  |  Size: 111 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.5 MiB

File diff suppressed because one or more lines are too long

After

Width:  |  Height:  |  Size: 76 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 25 KiB

File diff suppressed because one or more lines are too long

After

Width:  |  Height:  |  Size: 66 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.4 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 15 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 38 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 282 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.9 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 486 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.7 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 639 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.6 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 60 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 882 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 96 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 286 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 332 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 9.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 510 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 26 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 257 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 113 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 333 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 168 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 109 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 32 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 25 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 17 KiB

View 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&#160;&#160;&#160;|&#160;</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>

Binary file not shown.

After

Width:  |  Height:  |  Size: 221 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.2 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 431 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 123 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.1 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 83 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 10 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 90 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 681 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 62 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 225 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 991 KiB

View File

@@ -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.

View File

@@ -1,4 +0,0 @@
# Copyright 2025 Cetmix OÜ
# License LGPL-3.0 or later (http://www.gnu.org/licenses/lgpl).
from . import models

View File

@@ -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,
}

View File

@@ -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 ""

View File

@@ -1,4 +0,0 @@
# Copyright 2025 Cetmix OÜ
# License LGPL-3.0 or later (http://www.gnu.org/licenses/lgpl).
from . import res_users

View File

@@ -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)

View File

@@ -1,3 +0,0 @@
[build-system]
requires = ["whool"]
build-backend = "whool.buildapi"

View File

@@ -1,2 +0,0 @@
* Cetmix

View File

@@ -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**.

View File

@@ -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],
)
```

Binary file not shown.

Before

Width:  |  Height:  |  Size: 204 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 9.4 KiB

View File

@@ -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">[&quot;form&quot;, &quot;kanban&quot;]</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">&quot;sales_team.group_sale_salesman&quot;</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">&quot;res.users&quot;</span><span class="p">]</span><span class="o">.</span><span class="n">search</span><span class="p">([(</span><span class="s2">&quot;groups_id&quot;</span><span class="p">,</span> <span class="s2">&quot;in&quot;</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">&quot;crm.lead&quot;</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">&quot;kanban&quot;</span><span class="p">,</span> <span class="s2">&quot;form&quot;</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>

View File

@@ -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);
},
});

View File

@@ -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);
},
});

View File

@@ -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);
},
});

View File

@@ -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

View File

@@ -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"], [])