@ -0,0 +1,52 @@ |
|||
.. image:: https://img.shields.io/badge/license-AGPL--3-blue.svg |
|||
:target: https://www.gnu.org/licenses/agpl-3.0-standalone.html |
|||
:alt: License: AGPL-3 |
|||
|
|||
Event Ticket QR Code Scanner |
|||
============================ |
|||
This module helps to create QR codes for event tickets, |
|||
scan them, and manage invalid tickets. |
|||
|
|||
Configuration |
|||
============= |
|||
* Install qrcode package. |
|||
|
|||
License |
|||
------- |
|||
Affero General Public License v3.0 (AGPL v3) |
|||
(https://www.gnu.org/licenses/agpl-3.0-standalone.html) |
|||
|
|||
Company |
|||
------- |
|||
* `Cybrosys Techno Solutions <https://cybrosys.com/>`__ |
|||
|
|||
Credits |
|||
------- |
|||
* Developers: (V15) Mufeeda Shirin , |
|||
(V16) Anagha S, |
|||
(V18) Mruthul Raj, |
|||
* Contact: odoo@cybrosys.com |
|||
|
|||
|
|||
Contacts |
|||
-------- |
|||
* Mail Contact : odoo@cybrosys.com |
|||
* Website : https://cybrosys.com |
|||
|
|||
Bug Tracker |
|||
----------- |
|||
Bugs are tracked on GitHub Issues. In case of trouble, please check there if |
|||
your issue has already been reported. |
|||
|
|||
Maintainer |
|||
========== |
|||
.. image:: https://cybrosys.com/images/logo.png |
|||
:target: https://cybrosys.com |
|||
|
|||
This module is maintained by Cybrosys Technologies. |
|||
|
|||
For support and more information, please visit `Our Website <https://cybrosys.com/>`__ |
|||
|
|||
Further information |
|||
=================== |
|||
HTML Description: `<static/description/index.html>`__ |
@ -0,0 +1,22 @@ |
|||
# -*- coding: utf-8 -*- |
|||
############################################################################### |
|||
# |
|||
# Cybrosys Technologies Pvt. Ltd. |
|||
# |
|||
# Copyright (C) 2025-TODAY Cybrosys Technologies(<https://www.cybrosys.com>) |
|||
# Author: Cybrosys Techno Solutions (odoo@cybrosys.com) |
|||
# |
|||
# You can modify it under the terms of the GNU AFFERO |
|||
# GENERAL PUBLIC LICENSE (AGPL v3), Version 3. |
|||
# |
|||
# This program is distributed in the hope that it will be useful, |
|||
# but WITHOUT ANY WARRANTY; without even the implied warranty of |
|||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
|||
# GNU AFFERO GENERAL PUBLIC LICENSE (AGPL v3) for more details. |
|||
# |
|||
# You should have received a copy of the GNU AFFERO GENERAL PUBLIC LICENSE |
|||
# (AGPL v3) along with this program. |
|||
# If not, see <http://www.gnu.org/licenses/>. |
|||
# |
|||
############################################################################### |
|||
from . import models |
@ -0,0 +1,52 @@ |
|||
# -*- coding: utf-8 -*- |
|||
############################################################################### |
|||
# |
|||
# Cybrosys Technologies Pvt. Ltd. |
|||
# |
|||
# Copyright (C) 2025-TODAY Cybrosys Technologies(<https://www.cybrosys.com>) |
|||
# Author: Cybrosys Techno Solutions (odoo@cybrosys.com) |
|||
# |
|||
# You can modify it under the terms of the GNU AFFERO |
|||
# GENERAL PUBLIC LICENSE (AGPL v3), Version 3. |
|||
# |
|||
# This program is distributed in the hope that it will be useful, |
|||
# but WITHOUT ANY WARRANTY; without even the implied warranty of |
|||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
|||
# GNU AFFERO GENERAL PUBLIC LICENSE (AGPL v3) for more details. |
|||
# |
|||
# You should have received a copy of the GNU AFFERO GENERAL PUBLIC LICENSE |
|||
# (AGPL v3) along with this program. |
|||
# If not, see <http://www.gnu.org/licenses/>. |
|||
# |
|||
############################################################################### |
|||
{ |
|||
'name': 'Event Ticket QR Code Scanner', |
|||
'version': '18.0.1.0.0', |
|||
'category': 'Marketing', |
|||
'summary': 'QR Code Scanner for Event Ticket', |
|||
'description': """This module helps to create QR codes for event tickets, |
|||
scan them, and manage invalid tickets.""", |
|||
'author': 'Cybrosys Techno Solutions', |
|||
'company': 'Cybrosys Techno Solutions', |
|||
'maintainer': 'Cybrosys Techno Solutions', |
|||
'website': "https://www.cybrosys.com", |
|||
'depends': ['website_event', 'sale_management'], |
|||
'data': ['views/event_event_ticket.xml', |
|||
'views/event_event_views.xml', |
|||
'views/event_template.xml', |
|||
'report/event_event_templates.xml', |
|||
], |
|||
'assets': { |
|||
'web.assets_backend': [ |
|||
'event_ticket_qr_scanner/static/src/**/*', |
|||
], |
|||
}, |
|||
'external_dependencies': { |
|||
'python': ['qrcode'] |
|||
}, |
|||
'images': ['static/description/banner.jpg'], |
|||
'license': 'AGPL-3', |
|||
'installable': True, |
|||
'application': False, |
|||
'auto_install': False, |
|||
} |
@ -0,0 +1,6 @@ |
|||
## Module <event_ticket_qr_scanner> |
|||
|
|||
#### 13.03.2025 |
|||
#### Version 18.0.1.0.0 |
|||
#### ADD |
|||
- Initial commit for Event Ticket QR Code Scanner |
@ -0,0 +1,22 @@ |
|||
# -*- coding: utf-8 -*- |
|||
############################################################################### |
|||
# |
|||
# Cybrosys Technologies Pvt. Ltd. |
|||
# |
|||
# Copyright (C) 2025-TODAY Cybrosys Technologies(<https://www.cybrosys.com>) |
|||
# Author: Cybrosys Techno Solutions (odoo@cybrosys.com) |
|||
# |
|||
# You can modify it under the terms of the GNU AFFERO |
|||
# GENERAL PUBLIC LICENSE (AGPL v3), Version 3. |
|||
# |
|||
# This program is distributed in the hope that it will be useful, |
|||
# but WITHOUT ANY WARRANTY; without even the implied warranty of |
|||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
|||
# GNU AFFERO GENERAL PUBLIC LICENSE (AGPL v3) for more details. |
|||
# |
|||
# You should have received a copy of the GNU AFFERO GENERAL PUBLIC LICENSE |
|||
# (AGPL v3) along with this program. |
|||
# If not, see <http://www.gnu.org/licenses/>. |
|||
# |
|||
############################################################################### |
|||
from . import event_event_ticket |
@ -0,0 +1,68 @@ |
|||
# -*- coding: utf-8 -*- |
|||
############################################################################### |
|||
# |
|||
# Cybrosys Technologies Pvt. Ltd. |
|||
# |
|||
# Copyright (C) 2025-TODAY Cybrosys Technologies(<https://www.cybrosys.com>) |
|||
# Author: Cybrosys Techno Solutions (odoo@cybrosys.com) |
|||
# |
|||
# You can modify it under the terms of the GNU AFFERO |
|||
# GENERAL PUBLIC LICENSE (AGPL v3), Version 3. |
|||
# |
|||
# This program is distributed in the hope that it will be useful, |
|||
# but WITHOUT ANY WARRANTY; without even the implied warranty of |
|||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
|||
# GNU AFFERO GENERAL PUBLIC LICENSE (AGPL v3) for more details. |
|||
# |
|||
# You should have received a copy of the GNU AFFERO GENERAL PUBLIC LICENSE |
|||
# (AGPL v3) along with this program. |
|||
# If not, see <http://www.gnu.org/licenses/>. |
|||
# |
|||
############################################################################### |
|||
import base64 |
|||
from io import BytesIO |
|||
|
|||
from odoo import fields, models |
|||
|
|||
try: |
|||
import qrcode |
|||
except ImportError: |
|||
qrcode = None |
|||
|
|||
|
|||
class EventEventTicket(models.Model): |
|||
"""This class extends the 'event.event.ticket' model to include |
|||
the generation of QR codes for event tickets.""" |
|||
_inherit = 'event.event.ticket' |
|||
|
|||
ticket_qr_code_image = fields.Binary(string='QRcode', attachment=False, |
|||
help='QR code for Event Tickets.', |
|||
readonly=True) |
|||
|
|||
def create(self, vals): |
|||
"""Generate qrcode when create new product.""" |
|||
res = super(EventEventTicket, self).create(vals) |
|||
ean = self.generate_ticket_qr(res.id) |
|||
res.ticket_qr_code_image = ean |
|||
return res |
|||
|
|||
def generate_ticket_qr(self, val_id): |
|||
"""Action to generate QR code for Tickets. |
|||
:param int val_id: id of the event. |
|||
:return: event ticket qrcode image.""" |
|||
data = self.browse(val_id) |
|||
if qrcode and base64: |
|||
code = qrcode.QRCode( |
|||
version=1, |
|||
error_correction=qrcode.constants.ERROR_CORRECT_L, |
|||
box_size=3, border=4) |
|||
code.add_data(f"Event: {data.event_id.name},") |
|||
code.add_data(f"code: {data.event_id.id},") |
|||
code.add_data(f"Ticket: {val_id},") |
|||
code.add_data(f"Price: {data.price}") |
|||
code.make(fit=True) |
|||
img = code.make_image() |
|||
temp = BytesIO() |
|||
img.save(temp, format="PNG") |
|||
qr_image = base64.b64encode(temp.getvalue()) |
|||
return qr_image |
@ -0,0 +1,15 @@ |
|||
<?xml version="1.0" encoding="utf-8"?> |
|||
<odoo> |
|||
<!-- QR Code On Full page ticket --> |
|||
<template id="event_report_template_full_page_ticket_inherited" |
|||
inherit_id="event.event_report_template_full_page_ticket"> |
|||
<xpath expr="//div[hasclass('o_event_full_page_left_details_top')]" |
|||
position="inside"> |
|||
<t t-if="attendee"> |
|||
<h5>Scan The QR Code for Tickets</h5> |
|||
<img style="height: 100px; width: 100px;" |
|||
t-attf-src="data:image/png;base64,{{attendee.event_ticket_id.ticket_qr_code_image}}"/> |
|||
</t> |
|||
</xpath> |
|||
</template> |
|||
</odoo> |
After Width: | Height: | Size: 1.1 KiB |
After Width: | Height: | Size: 210 KiB |
After Width: | Height: | Size: 209 KiB |
After Width: | Height: | Size: 109 KiB |
After Width: | Height: | Size: 495 B |
After Width: | Height: | Size: 1.0 KiB |
After Width: | Height: | Size: 624 B |
After Width: | Height: | Size: 136 KiB |
After Width: | Height: | Size: 214 KiB |
After Width: | Height: | Size: 36 KiB |
After Width: | Height: | Size: 3.6 KiB |
After Width: | Height: | Size: 310 B |
After Width: | Height: | Size: 929 B |
After Width: | Height: | Size: 1.3 KiB |
After Width: | Height: | Size: 3.3 KiB |
After Width: | Height: | Size: 1.4 KiB |
After Width: | Height: | Size: 17 KiB |
After Width: | Height: | Size: 542 B |
After Width: | Height: | Size: 576 B |
After Width: | Height: | Size: 733 B |
After Width: | Height: | Size: 4.3 KiB |
After Width: | Height: | Size: 1.2 KiB |
After Width: | Height: | Size: 4.0 KiB |
After Width: | Height: | Size: 1.7 KiB |
After Width: | Height: | Size: 738 KiB |
After Width: | Height: | Size: 2.2 KiB |
After Width: | Height: | Size: 911 B |
After Width: | Height: | Size: 1.1 KiB |
After Width: | Height: | Size: 1.2 KiB |
After Width: | Height: | Size: 1.2 KiB |
After Width: | Height: | Size: 600 B |
After Width: | Height: | Size: 673 B |
After Width: | Height: | Size: 2.0 KiB |
After Width: | Height: | Size: 462 B |
After Width: | Height: | Size: 2.1 KiB |
After Width: | Height: | Size: 926 B |
After Width: | Height: | Size: 9.0 KiB |
After Width: | Height: | Size: 23 KiB |
After Width: | Height: | Size: 7.0 KiB |
After Width: | Height: | Size: 878 B |
After Width: | Height: | Size: 1.2 KiB |
After Width: | Height: | Size: 653 B |
After Width: | Height: | Size: 800 B |
After Width: | Height: | Size: 905 B |
After Width: | Height: | Size: 189 KiB |
After Width: | Height: | Size: 4.3 KiB |
After Width: | Height: | Size: 839 B |
After Width: | Height: | Size: 1.7 KiB |
After Width: | Height: | Size: 5.9 KiB |
After Width: | Height: | Size: 1.6 KiB |
After Width: | Height: | Size: 34 KiB |
After Width: | Height: | Size: 26 KiB |
After Width: | Height: | Size: 3.8 KiB |
After Width: | Height: | Size: 23 KiB |
After Width: | Height: | Size: 1.9 KiB |
After Width: | Height: | Size: 2.3 KiB |
After Width: | Height: | Size: 427 B |
After Width: | Height: | Size: 627 B |
After Width: | Height: | Size: 1.1 KiB |
After Width: | Height: | Size: 1.2 KiB |
After Width: | Height: | Size: 988 B |
After Width: | Height: | Size: 3.7 KiB |
After Width: | Height: | Size: 5.0 KiB |
After Width: | Height: | Size: 875 B |
After Width: | Height: | Size: 1.2 KiB |
After Width: | Height: | Size: 767 KiB |
After Width: | Height: | Size: 138 KiB |
After Width: | Height: | Size: 697 KiB |
After Width: | Height: | Size: 89 KiB |
After Width: | Height: | Size: 126 KiB |
After Width: | Height: | Size: 776 KiB |
After Width: | Height: | Size: 154 KiB |
After Width: | Height: | Size: 103 KiB |
After Width: | Height: | Size: 138 KiB |
After Width: | Height: | Size: 301 KiB |
After Width: | Height: | Size: 144 KiB |
After Width: | Height: | Size: 105 KiB |
After Width: | Height: | Size: 117 KiB |
After Width: | Height: | Size: 122 KiB |
After Width: | Height: | Size: 287 KiB |
After Width: | Height: | Size: 103 KiB |
After Width: | Height: | Size: 115 KiB |
After Width: | Height: | Size: 363 KiB |
After Width: | Height: | Size: 743 KiB |
After Width: | Height: | Size: 46 KiB |
@ -0,0 +1,94 @@ |
|||
/** @odoo-module **/ |
|||
import { registry } from "@web/core/registry"; |
|||
import { useService } from "@web/core/utils/hooks"; |
|||
import { Component, onMounted, useRef } from "@odoo/owl"; |
|||
import { loadJS } from "@web/core/assets"; |
|||
|
|||
export class QrcodeScanner extends Component { |
|||
setup() { |
|||
super.setup(); |
|||
this.result = useRef("result"); |
|||
this.reader = useRef("reader"); |
|||
this.orm = useService("orm"); |
|||
|
|||
// Ensure QR scanner loads only when the component is mounted
|
|||
onMounted(() => { |
|||
this.loadQRScanner(); |
|||
}); |
|||
} |
|||
|
|||
async loadQRScanner() { |
|||
await loadJS("https://cdn.jsdelivr.net/npm/html5-qrcode@2.3.8/html5-qrcode.min.js"); |
|||
this.startQRScanner(); |
|||
} |
|||
|
|||
startQRScanner() { |
|||
if (!this.reader.el) { |
|||
console.error("❌ QR Scanner Error: Element with ID 'reader' not found!"); |
|||
return; |
|||
} |
|||
|
|||
this.qrScanner = new Html5QrcodeScanner("reader", { |
|||
qrbox: { width: 250, height: 250 }, |
|||
fps: 20, |
|||
}); |
|||
|
|||
this.qrScanner.render( |
|||
(data) => this.onScanSuccess(data), |
|||
(err) => console.warn("QR Scan Error:", err) |
|||
); |
|||
} |
|||
|
|||
async onScanSuccess(data) { |
|||
console.log("✅ Scanned Data:", data); |
|||
this.qrScanner.clear(); |
|||
this.reader.el.classList.add("d-none"); |
|||
|
|||
// Parsing scanned QR data
|
|||
const keyValuePairs = data.split(','); |
|||
const result = {}; |
|||
for (const pair of keyValuePairs) { |
|||
const [key, value] = pair.split(':'); |
|||
if (value) result[key.trim()] = value.trim(); |
|||
} |
|||
|
|||
if (!result['Event'] || !result['Ticket'] || !result['code']) { |
|||
this.result.el.innerHTML = `<h2 style="color:red;">Invalid QR Code!</h2>`; |
|||
return; |
|||
} |
|||
|
|||
var domain = [['event_ticket_id', '=', parseInt(result['Ticket'])], |
|||
['event_id', '=', parseInt(result['code'])], ['state', '=', 'open']]; |
|||
var fields = ['name', 'partner_id']; |
|||
|
|||
const attendees = await this.orm.call('event.registration', 'search_read', [domain, fields]); |
|||
|
|||
let successHTML = `<h2 style="color:green;">Success! Ticket Scanned</h2>
|
|||
<p>Event: <strong>${result['Event']}</strong></p>`; |
|||
|
|||
if (attendees.length === 0) { |
|||
successHTML += `<p style="color:red;">No Attendee Found!</p>`; |
|||
} else { |
|||
successHTML += `<label for="select_attendee_id">Select Attendee:</label>
|
|||
<select id="select_attendee_id" class="attendee_selection">`;
|
|||
attendees.forEach(attendee => { |
|||
successHTML += `<option value="${attendee.id}">${attendee.name}</option>`; |
|||
}); |
|||
successHTML += `</select><br/><br/>
|
|||
<button id="mark_as_attending" class="btn btn-primary">Mark as Attending</button>`; |
|||
} |
|||
|
|||
this.result.el.innerHTML = successHTML; |
|||
|
|||
if (attendees.length > 0) { |
|||
document.getElementById("mark_as_attending").addEventListener("click", async () => { |
|||
const selectedId = document.getElementById("select_attendee_id").value; |
|||
await this.orm.call('event.registration', 'action_set_done', [parseInt(selectedId)]); |
|||
location.reload(); |
|||
}); |
|||
} |
|||
} |
|||
} |
|||
|
|||
QrcodeScanner.template = "event_ticket_qr_scanner.QrCodeScanner"; |
|||
registry.category("actions").add("js_function", QrcodeScanner); |
@ -0,0 +1,14 @@ |
|||
<?xml version="1.0" encoding="utf-8" ?> |
|||
<templates id="template" xml:space="preserve" owl="1"> |
|||
<!-- Qr Code Scanner --> |
|||
<t t-name="event_ticket_qr_scanner.QrCodeScanner"> |
|||
<t t-set="_activeClasses">border-primary</t> |
|||
<div> |
|||
<div id="reader" style="width: 600px; |
|||
margin-left: 182px;" t-ref="reader"> |
|||
</div> |
|||
<div id="result" style="text-align: center; font-size: 1.5rem;" t-ref="result"> |
|||
</div> |
|||
</div> |
|||
</t> |
|||
</templates> |
@ -0,0 +1,19 @@ |
|||
<?xml version="1.0" encoding="UTF-8"?> |
|||
<odoo> |
|||
<!-- Qr code image on event ticket form view --> |
|||
<record id="event_event_ticket_view_tree_from_event" model="ir.ui.view"> |
|||
<field name="name"> |
|||
event.event.ticket.view.tree.inherit.event.ticket.qr.scanner |
|||
</field> |
|||
<field name="model">event.event.ticket</field> |
|||
<field name="inherit_id" |
|||
ref="event.event_event_ticket_view_tree_from_event"/> |
|||
<field name="arch" type="xml"> |
|||
<field name="description" position="after"> |
|||
<field name="ticket_qr_code_image" widget="image" |
|||
class="oe_avatar" |
|||
options='{"zoom": true, "size": [30, 40]}'/> |
|||
</field> |
|||
</field> |
|||
</record> |
|||
</odoo> |
@ -0,0 +1,28 @@ |
|||
<?xml version="1.0" encoding="UTF-8"?> |
|||
<odoo> |
|||
<!-- Open Scanner--> |
|||
<record id="scan_ticket_qrcode" model="ir.actions.client"> |
|||
<field name="name">Scan Ticket Qr Code</field> |
|||
<field name="tag">js_function</field> |
|||
<field name="target">new</field> |
|||
</record> |
|||
<!-- New Button on Event form to scan Ticket QR code--> |
|||
<record id="view_event_form" model="ir.ui.view"> |
|||
<field name="name"> |
|||
event.event.view.form.inherit.event.ticket.qr.scanner |
|||
</field> |
|||
<field name="model">event.event</field> |
|||
<field name="inherit_id" ref="event.view_event_form"/> |
|||
<field name="priority" eval="20"/> |
|||
<field name="arch" type="xml"> |
|||
<header position="inside"> |
|||
<button type="action" |
|||
name="%(event_ticket_qr_scanner.scan_ticket_qrcode)d" |
|||
string="Scan Tickets" icon="fa-qrcode" |
|||
class="btn btn-primary scan_tickets" |
|||
invisible="seats_taken == 0"/> |
|||
<field name="seats_taken" invisible="1"/> |
|||
</header> |
|||
</field> |
|||
</record> |
|||
</odoo> |
@ -0,0 +1,16 @@ |
|||
<?xml version="1.0" encoding="utf-8"?> |
|||
<odoo> |
|||
<!-- Add a QR Code on Event registration --> |
|||
<template id="registration_complete" |
|||
inherit_id="website_event.registration_complete"> |
|||
<xpath expr="//div[hasclass('o_wereg_confirmed')]/div/div" |
|||
position="inside"> |
|||
<t t-if="attendees.event_ticket_id"> |
|||
<div id="qr_code_image"> |
|||
<img style="height: 100px; width: 100px;" |
|||
t-attf-src="data:image/png;base64,{{attendees.event_ticket_id.ticket_qr_code_image}}"/> |
|||
</div> |
|||
</t> |
|||
</xpath> |
|||
</template> |
|||
</odoo> |