EnCoach Odoo 19 custom modules

Full backend implementation with custom Odoo modules:
- encoach_api: Core API, user management, JWT auth
- encoach_exam: Exam generation (reading, writing, listening, speaking)
- encoach_evaluate: AI-powered evaluation (writing, speaking)
- encoach_training: Training tips and walkthrough
- encoach_storage: File storage management
- encoach_payment: Stripe, PayPal, Paymob integration
- encoach_mail: Email notifications

Made-with: Cursor
This commit is contained in:
Talal Sharabi
2026-03-14 16:46:46 +04:00
commit f5b627256f
168 changed files with 13428 additions and 0 deletions

View File

@@ -0,0 +1 @@
from . import models

View File

@@ -0,0 +1,15 @@
{
"name": "EnCoach Ticket",
"version": "19.0.1.0.0",
"category": "Education",
"summary": "Support ticket system for EnCoach",
"description": "Feedback, bug reports, and help requests from users.",
"depends": ["encoach_core"],
"data": [
"security/ir.model.access.csv",
"security/rules.xml",
"views/ticket_views.xml",
],
"installable": True,
"license": "LGPL-3",
}

View File

@@ -0,0 +1 @@
from . import ticket

View File

@@ -0,0 +1,53 @@
from odoo import models, fields
class EncoachTicket(models.Model):
_name = "encoach.ticket"
_description = "EnCoach Support Ticket"
reporter_name = fields.Char()
reporter_email = fields.Char()
subject = fields.Char(required=True)
description = fields.Text()
ticket_type = fields.Selection(
[
("feedback", "Feedback"),
("bug", "Bug Report"),
("help", "Help Request"),
],
string="Type",
)
status = fields.Selection(
[
("open", "Open"),
("in_progress", "In Progress"),
("resolved", "Resolved"),
("closed", "Closed"),
],
default="open",
)
exam_information = fields.Json()
assigned_to_id = fields.Many2one(
"res.users", string="Assigned To", ondelete="set null",
)
created_at = fields.Datetime(default=fields.Datetime.now)
legacy_id = fields.Char(index=True)
def to_encoach_dict(self):
self.ensure_one()
return {
"id": self.id,
"reporterName": self.reporter_name or "",
"reporterEmail": self.reporter_email or "",
"subject": self.subject or "",
"description": self.description or "",
"type": self.ticket_type or "",
"status": self.status,
"examInformation": self.exam_information,
"assignedTo": (
self.assigned_to_id.name if self.assigned_to_id else None
),
"createdAt": (
self.created_at.isoformat() if self.created_at else None
),
}

View File

@@ -0,0 +1,5 @@
id,name,model_id:id,group_id:id,perm_read,perm_write,perm_create,perm_unlink
access_encoach_ticket_student,encoach.ticket.student,model_encoach_ticket,encoach_core.group_encoach_student,1,0,1,0
access_encoach_ticket_teacher,encoach.ticket.teacher,model_encoach_ticket,encoach_core.group_encoach_teacher,1,1,1,0
access_encoach_ticket_admin,encoach.ticket.admin,model_encoach_ticket,encoach_core.group_encoach_admin,1,1,1,1
access_encoach_ticket_sysadmin,encoach.ticket.sysadmin,model_encoach_ticket,base.group_system,1,1,1,1
1 id name model_id:id group_id:id perm_read perm_write perm_create perm_unlink
2 access_encoach_ticket_student encoach.ticket.student model_encoach_ticket encoach_core.group_encoach_student 1 0 1 0
3 access_encoach_ticket_teacher encoach.ticket.teacher model_encoach_ticket encoach_core.group_encoach_teacher 1 1 1 0
4 access_encoach_ticket_admin encoach.ticket.admin model_encoach_ticket encoach_core.group_encoach_admin 1 1 1 1
5 access_encoach_ticket_sysadmin encoach.ticket.sysadmin model_encoach_ticket base.group_system 1 1 1 1

View File

@@ -0,0 +1,17 @@
<?xml version="1.0" encoding="UTF-8"?>
<odoo>
<data noupdate="1">
<record id="rule_ticket_student_own" model="ir.rule">
<field name="name">Students see own tickets</field>
<field name="model_id" ref="model_encoach_ticket"/>
<field name="groups" eval="[(4, ref('encoach_core.group_encoach_student'))]"/>
<field name="domain_force">[('create_uid', '=', user.id)]</field>
</record>
<record id="rule_ticket_admin" model="ir.rule">
<field name="name">Admins see all tickets</field>
<field name="model_id" ref="model_encoach_ticket"/>
<field name="groups" eval="[(4, ref('encoach_core.group_encoach_admin'))]"/>
<field name="domain_force">[(1, '=', 1)]</field>
</record>
</data>
</odoo>

View File

@@ -0,0 +1,74 @@
<?xml version="1.0" encoding="UTF-8"?>
<odoo>
<record id="view_encoach_ticket_tree" model="ir.ui.view">
<field name="name">encoach.ticket.tree</field>
<field name="model">encoach.ticket</field>
<field name="arch" type="xml">
<list string="Tickets"
decoration-danger="status == 'open'"
decoration-warning="status == 'in_progress'"
decoration-success="status == 'resolved'">
<field name="subject"/>
<field name="reporter_name"/>
<field name="reporter_email"/>
<field name="ticket_type"/>
<field name="status"/>
<field name="assigned_to_id"/>
<field name="created_at"/>
</list>
</field>
</record>
<record id="view_encoach_ticket_form" model="ir.ui.view">
<field name="name">encoach.ticket.form</field>
<field name="model">encoach.ticket</field>
<field name="arch" type="xml">
<form string="Ticket">
<header>
<field name="status" widget="statusbar" statusbar_visible="open,in_progress,resolved,closed" options="{'clickable': 1}"/>
</header>
<sheet>
<group>
<group>
<field name="subject"/>
<field name="reporter_name"/>
<field name="reporter_email"/>
</group>
<group>
<field name="ticket_type"/>
<field name="assigned_to_id"/>
</group>
</group>
<group>
<field name="description" widget="text"/>
</group>
<notebook>
<page string="Exam Information" name="exam_information">
<field name="exam_information" widget="json"/>
</page>
</notebook>
</sheet>
</form>
</field>
</record>
<record id="action_encoach_ticket" model="ir.actions.act_window">
<field name="name">Tickets</field>
<field name="res_model">encoach.ticket</field>
<field name="view_mode">list,form</field>
<field name="help" type="html">
<p class="o_view_nocontent_smiling_face">
No support tickets yet.
</p>
<p>
Tickets handle feedback, bug reports, and help requests from users.
</p>
</field>
</record>
<menuitem id="menu_encoach_tickets"
name="Tickets"
parent="encoach_core.menu_encoach_support"
action="action_encoach_ticket"
sequence="10"/>
</odoo>