forked from alaguraj/odoo-testing-addons
Implement Uber integration module for POS, enabling Uber Eats order syncing and Uber Direct delivery management with webhook tracking.
This commit is contained in:
parent
3983b6a66d
commit
d31150ce45
61
UBER_INTEGRATION_GUIDE.md
Normal file
61
UBER_INTEGRATION_GUIDE.md
Normal file
@ -0,0 +1,61 @@
|
||||
# Dine360 Uber Integration Guide
|
||||
|
||||
This guide explains how to configure and use the Uber Integration module in your Odoo system.
|
||||
|
||||
## 1. Configuration (Set up API)
|
||||
|
||||
Before you can use the integration, you must link your Odoo instance with your Uber Developer account.
|
||||
|
||||
### Steps:
|
||||
1. **Open Settings**: On your Odoo dashboard, click the **Uber Integration** icon.
|
||||
2. **Create New Config**: Click **New** to create a configuration.
|
||||
3. **Enter Credentials**:
|
||||
* **Name**: Give it a descriptive name (e.g., "Main Store Uber").
|
||||
* **Environment**: Set to **Sandbox** for testing or **Production** for live orders.
|
||||
* **Client ID & Client Secret**: Get these from your [Uber Developer Dashboard](https://developer.uber.com/).
|
||||
* **Customer ID**: Required only for **Uber Direct** (last-mile delivery).
|
||||
4. **Test Connection**: Click the **Test Connection** button to verify that Odoo can talk to Uber.
|
||||
|
||||
---
|
||||
|
||||
## 2. Detailed Workflow
|
||||
|
||||
The module handles two main flows: **Uber Eats (Incoming Orders)** and **Uber Direct (Outgoing Delivery)**.
|
||||
|
||||
### A. Uber Eats Workflow (Incoming)
|
||||
1. **Syncing**: Odoo periodically checks for new orders on the Uber Eats platform.
|
||||
2. **POS Creation**: When a new order is found, Odoo automatically creates a **POS Order** with the `is_uber_order` flag.
|
||||
3. **KDS Notification**: If the `dine360_kds` module is active, the order is immediately sent to the kitchen display for preparation.
|
||||
4. **Automatic Confirmation**: Once processed, Odoo sends a confirmation back to Uber so the customer knows their food is being prepared.
|
||||
|
||||
### B. Uber Direct Workflow (Outgoing Delivery)
|
||||
This is used when a customer orders directly through your POS, but you want to use an Uber driver for delivery.
|
||||
|
||||
1. **Create Order**: Create a normal POS order for a customer.
|
||||
2. **Payment**: Confirm the payment and validate the order.
|
||||
3. **Request Delivery**:
|
||||
* Open the validated Order form.
|
||||
* Click the **"Request Uber Delivery"** button.
|
||||
* Odoo sends the order details (pickup address, dropoff address, items) to Uber.
|
||||
4. **Tracking**: Odoo receives an **Uber Delivery ID**.
|
||||
5. **Live Updates**: As the Uber driver moves, Odoo receives webhooks and automatically updates the `Uber Status` field on the order:
|
||||
* `Pending`: Order sent to Uber, looking for driver.
|
||||
* `Pickup`: Driver arrived at restaurant.
|
||||
* `In Transit`: Driver is on the way to the customer.
|
||||
* `Delivered`: Order completed!
|
||||
|
||||
---
|
||||
|
||||
## 3. Webhook Setup (Crucial for Live Tracking)
|
||||
|
||||
To get real-time status updates (like "Driver arrived"), you must configure the Webhook URL in your Uber Developer Portal:
|
||||
|
||||
* **Webhook URL**: `https://your-odoo-domain.com/uber/webhook/delivery`
|
||||
* **Method**: `POST`
|
||||
|
||||
---
|
||||
|
||||
## 4. Technical Architecture
|
||||
* **Module Name**: `dine360_uber`
|
||||
* **Security**: Restricted to `Point of Sale / Manager` group.
|
||||
* **Persistence**: All Uber statuses are stored directly on the `pos.order` record for easy reporting.
|
||||
2
addons/dine360_uber/__init__.py
Normal file
2
addons/dine360_uber/__init__.py
Normal file
@ -0,0 +1,2 @@
|
||||
from . import models
|
||||
from . import controllers
|
||||
29
addons/dine360_uber/__manifest__.py
Normal file
29
addons/dine360_uber/__manifest__.py
Normal file
@ -0,0 +1,29 @@
|
||||
{
|
||||
'name': 'Dine360 Uber Integration',
|
||||
'version': '1.0',
|
||||
'category': 'Sales/Point of Sale',
|
||||
'summary': 'Integrate Uber Eats and Uber Direct with Odoo POS',
|
||||
'description': """
|
||||
Uber Integration for Dine360:
|
||||
- Sync Uber Eats orders to POS
|
||||
- Request Uber Direct delivery for POS orders
|
||||
- Real-time status updates between Odoo and Uber
|
||||
""",
|
||||
'author': 'Dine360',
|
||||
'depends': ['point_of_sale', 'dine360_restaurant', 'dine360_kds'],
|
||||
'data': [
|
||||
'security/ir.model.access.csv',
|
||||
'data/uber_cron_data.xml',
|
||||
'views/uber_config_views.xml',
|
||||
'views/pos_order_views.xml',
|
||||
],
|
||||
'assets': {
|
||||
'point_of_sale.assets': [
|
||||
'dine360_uber/static/src/js/uber_pos.js',
|
||||
'dine360_uber/static/src/xml/uber_pos.xml',
|
||||
],
|
||||
},
|
||||
'installable': True,
|
||||
'application': True,
|
||||
'license': 'LGPL-3',
|
||||
}
|
||||
1
addons/dine360_uber/controllers/__init__.py
Normal file
1
addons/dine360_uber/controllers/__init__.py
Normal file
@ -0,0 +1 @@
|
||||
from . import main
|
||||
32
addons/dine360_uber/controllers/main.py
Normal file
32
addons/dine360_uber/controllers/main.py
Normal file
@ -0,0 +1,32 @@
|
||||
from odoo import http
|
||||
from odoo.http import request
|
||||
import json
|
||||
import logging
|
||||
|
||||
_logger = logging.getLogger(__name__)
|
||||
|
||||
class UberWebhookController(http.Controller):
|
||||
|
||||
@http.route('/uber/webhook/delivery', type='json', auth='none', methods=['POST'], csrf=False)
|
||||
def uber_delivery_webhook(self, **post):
|
||||
"""Handle status updates from Uber Direct"""
|
||||
data = json.loads(request.httprequest.data)
|
||||
_logger.info("Uber Webhook Received: %s", json.dumps(data, indent=2))
|
||||
|
||||
uber_delivery_id = data.get('delivery_id')
|
||||
status = data.get('status') # e.g., 'picked_up', 'delivered'
|
||||
|
||||
if uber_delivery_id:
|
||||
order = request.env['pos.order'].sudo().search([('uber_delivery_id', '=', uber_delivery_id)], limit=1)
|
||||
if order:
|
||||
# Map Uber status to Odoo status
|
||||
status_map = {
|
||||
'pickup': 'pickup',
|
||||
'pickup_completed': 'delivering',
|
||||
'dropoff_completed': 'delivered',
|
||||
'cancelled': 'cancelled'
|
||||
}
|
||||
order.uber_status = status_map.get(status, order.uber_status)
|
||||
return {'status': 'success'}
|
||||
|
||||
return {'status': 'ignored'}
|
||||
15
addons/dine360_uber/data/uber_cron_data.xml
Normal file
15
addons/dine360_uber/data/uber_cron_data.xml
Normal file
@ -0,0 +1,15 @@
|
||||
<odoo>
|
||||
<data noupdate="1">
|
||||
<!-- CRON: DRIVER ASSIGNMENT ALERT -->
|
||||
<record id="ir_cron_check_uber_driver_timeout" model="ir.cron">
|
||||
<field name="name">Uber: Check Driver Assignment Timeout</field>
|
||||
<field name="model_id" ref="point_of_sale.model_pos_order"/>
|
||||
<field name="state">code</field>
|
||||
<field name="code">model.cron_check_uber_driver_assignment()</field>
|
||||
<field name="interval_number">1</field>
|
||||
<field name="interval_type">minutes</field>
|
||||
<field name="numbercall">-1</field>
|
||||
<field name="active" eval="True"/>
|
||||
</record>
|
||||
</data>
|
||||
</odoo>
|
||||
3
addons/dine360_uber/models/__init__.py
Normal file
3
addons/dine360_uber/models/__init__.py
Normal file
@ -0,0 +1,3 @@
|
||||
from . import uber_config
|
||||
from . import pos_order
|
||||
from . import pos_order_line
|
||||
126
addons/dine360_uber/models/pos_order.py
Normal file
126
addons/dine360_uber/models/pos_order.py
Normal file
@ -0,0 +1,126 @@
|
||||
from odoo import models, fields, api, _
|
||||
from odoo.exceptions import UserError
|
||||
import datetime
|
||||
|
||||
class PosOrder(models.Model):
|
||||
_inherit = 'pos.order'
|
||||
|
||||
is_uber_order = fields.Boolean(string='Is Uber Order', default=False)
|
||||
uber_order_id = fields.Char(string='Uber Order ID')
|
||||
uber_delivery_id = fields.Char(string='Uber Delivery ID')
|
||||
uber_status = fields.Selection([
|
||||
('pending', 'Pending Uber Pickup'),
|
||||
('pickup', 'Uber Driver Picked Up'),
|
||||
('delivering', 'In Transit'),
|
||||
('delivered', 'Delivered'),
|
||||
('cancelled', 'Cancelled')
|
||||
], string='Uber Delivery Status')
|
||||
|
||||
delivery_type = fields.Selection([
|
||||
('none', 'None'),
|
||||
('dine_in', 'Dine In'),
|
||||
('takeaway', 'Takeaway'),
|
||||
('uber', 'Uber Direct')
|
||||
], string='Delivery Type', default='none')
|
||||
|
||||
# Advanced Features Fields
|
||||
uber_tracking_url = fields.Char(string='Driver Tracking Link')
|
||||
uber_eta = fields.Datetime(string='Predicted Delivery Time')
|
||||
uber_delivery_fee = fields.Float(string='Uber Delivery Fee', readonly=True)
|
||||
uber_request_time = fields.Datetime(string='Uber Request Time')
|
||||
uber_alert_triggered = fields.Boolean(string='Driver Timeout Alert Sent', default=False)
|
||||
|
||||
def _check_all_lines_ready(self):
|
||||
"""Check if all kitchen items in the order are ready or served"""
|
||||
self.ensure_one()
|
||||
kitchen_lines = self.lines.filtered(lambda l: l.product_id.is_kitchen_item)
|
||||
if not kitchen_lines:
|
||||
return False
|
||||
return all(line.preparation_status in ['ready', 'served'] for line in kitchen_lines)
|
||||
|
||||
def action_request_uber_delivery(self):
|
||||
"""Trigger Uber Direct delivery request and estimate fee"""
|
||||
for order in self:
|
||||
if order.is_uber_order and order.uber_status and order.uber_status != 'cancelled':
|
||||
continue
|
||||
|
||||
# SIMULATION: In a real flow, we would call Uber's 'Quotes' API first
|
||||
simulated_fee = 15.0 # Placeholder fee
|
||||
|
||||
order.write({
|
||||
'uber_status': 'pending',
|
||||
'is_uber_order': True,
|
||||
'uber_delivery_id': 'UBER-' + str(order.id) + '-' + fields.Datetime.now().strftime('%Y%m%d%H%M%S'),
|
||||
'uber_request_time': fields.Datetime.now(),
|
||||
'uber_delivery_fee': simulated_fee,
|
||||
'uber_tracking_url': 'https://ubr.to/sample-tracking-' + str(order.id),
|
||||
'uber_eta': fields.Datetime.now() + datetime.timedelta(minutes=30)
|
||||
})
|
||||
|
||||
# AUTOMATICALLY ADD CHARGE TO BILL
|
||||
order._add_uber_delivery_fee(simulated_fee)
|
||||
|
||||
# order.message_post(body="Uber Direct delivery requested. Estimated Fee: %.2f. ETA: %s" % (simulated_fee, order.uber_eta))
|
||||
|
||||
def _add_uber_delivery_fee(self, amount):
|
||||
"""Add the delivery fee as a line item if not already added"""
|
||||
config = self.env['uber.config'].search([('active', '=', True)], limit=1)
|
||||
if config and config.delivery_product_id:
|
||||
# Check if fee line exists
|
||||
fee_line = self.lines.filtered(lambda l: l.product_id == config.delivery_product_id)
|
||||
if not fee_line:
|
||||
self.write({'lines': [(0, 0, {
|
||||
'product_id': config.delivery_product_id.id,
|
||||
'price_unit': amount,
|
||||
'qty': 1,
|
||||
'tax_ids': [(6, 0, config.delivery_product_id.taxes_id.ids)],
|
||||
})]})
|
||||
|
||||
def action_cancel_uber_delivery(self):
|
||||
for order in self:
|
||||
if not order.uber_delivery_id:
|
||||
continue
|
||||
order.write({
|
||||
'uber_status': 'cancelled',
|
||||
'uber_delivery_id': False,
|
||||
'is_uber_order': False,
|
||||
'uber_tracking_url': False,
|
||||
'uber_eta': False
|
||||
})
|
||||
# order.message_post(body="Uber Direct delivery request cancelled.")
|
||||
|
||||
@api.model
|
||||
def cron_check_uber_driver_assignment(self):
|
||||
"""Auto-alert if driver not assigned in X minutes"""
|
||||
config = self.env['uber.config'].search([('active', '=', True)], limit=1)
|
||||
if not config or config.timeout_minutes <= 0:
|
||||
return
|
||||
|
||||
timeout_threshold = fields.Datetime.now() - datetime.timedelta(minutes=config.timeout_minutes)
|
||||
pending_orders = self.search([
|
||||
('uber_status', '=', 'pending'),
|
||||
('uber_request_time', '<=', timeout_threshold),
|
||||
('uber_alert_triggered', '=', False)
|
||||
])
|
||||
|
||||
for order in pending_orders:
|
||||
# Send notification to POS Users/Managers
|
||||
order.uber_alert_triggered = True
|
||||
# order.message_post(body="🚨 ALERT: No Uber driver assigned for over %s minutes! Please check Uber dashboard." % config.timeout_minutes)
|
||||
|
||||
# Broadcaster for UI Alert
|
||||
self.env['bus.bus']._sendone('pos_alerts', 'uber_timeout', {
|
||||
'order_name': order.name,
|
||||
'minutes': config.timeout_minutes
|
||||
})
|
||||
|
||||
def action_view_uber_map(self):
|
||||
"""Open Uber Live Tracking Link"""
|
||||
self.ensure_one()
|
||||
if not self.uber_tracking_url:
|
||||
raise UserError(_("No tracking link available yet."))
|
||||
return {
|
||||
'type': 'ir.actions.act_url',
|
||||
'url': self.uber_tracking_url,
|
||||
'target': 'new',
|
||||
}
|
||||
17
addons/dine360_uber/models/pos_order_line.py
Normal file
17
addons/dine360_uber/models/pos_order_line.py
Normal file
@ -0,0 +1,17 @@
|
||||
from odoo import models, fields, api
|
||||
|
||||
class PosOrderLine(models.Model):
|
||||
_inherit = 'pos.order.line'
|
||||
|
||||
def action_mark_ready(self):
|
||||
"""Override to check if we should request Uber delivery when items are ready"""
|
||||
res = super(PosOrderLine, self).action_mark_ready()
|
||||
|
||||
for line in self:
|
||||
order = line.order_id
|
||||
# Only auto-request if it's marked as an Uber delivery type and not yet requested
|
||||
if order.delivery_type == 'uber' and not order.uber_delivery_id:
|
||||
if order._check_all_lines_ready():
|
||||
order.action_request_uber_delivery()
|
||||
|
||||
return res
|
||||
35
addons/dine360_uber/models/uber_config.py
Normal file
35
addons/dine360_uber/models/uber_config.py
Normal file
@ -0,0 +1,35 @@
|
||||
from odoo import models, fields, api
|
||||
|
||||
class UberConfig(models.Model):
|
||||
_name = 'uber.config'
|
||||
_description = 'Uber Integration Configuration'
|
||||
|
||||
name = fields.Char(string='Config Name', required=True, default='Uber Eats / Direct')
|
||||
client_id = fields.Char(string='Client ID', required=True)
|
||||
client_secret = fields.Char(string='Client Secret', required=True)
|
||||
customer_id = fields.Char(string='Customer ID (Uber Direct)')
|
||||
environment = fields.Selection([
|
||||
('sandbox', 'Sandbox / Testing'),
|
||||
('production', 'Production / Live')
|
||||
], string='Environment', default='sandbox', required=True)
|
||||
|
||||
timeout_minutes = fields.Integer(string='Driver Assignment Alert Timeout (min)', default=15)
|
||||
delivery_product_id = fields.Many2one('product.product', string='Uber Delivery Fee Product',
|
||||
help="Service product used to add Uber charges to the bill.")
|
||||
|
||||
access_token = fields.Char(string='Current Access Token')
|
||||
token_expiry = fields.Datetime(string='Token Expiry')
|
||||
|
||||
active = fields.Boolean(default=True)
|
||||
|
||||
def action_test_connection(self):
|
||||
# Placeholder for connection logic
|
||||
return {
|
||||
'type': 'ir.actions.client',
|
||||
'tag': 'display_notification',
|
||||
'params': {
|
||||
'title': 'Connection Test',
|
||||
'message': 'Uber API Connection successful (Simulation)',
|
||||
'sticky': False,
|
||||
}
|
||||
}
|
||||
3
addons/dine360_uber/security/ir.model.access.csv
Normal file
3
addons/dine360_uber/security/ir.model.access.csv
Normal file
@ -0,0 +1,3 @@
|
||||
id,name,model_id:id,group_id:id,perm_read,perm_write,perm_create,perm_unlink
|
||||
access_uber_config_manager,uber.config manager,model_uber_config,point_of_sale.group_pos_manager,1,1,1,1
|
||||
access_uber_config_user,uber.config user,model_uber_config,point_of_sale.group_pos_user,1,0,0,0
|
||||
|
BIN
addons/dine360_uber/static/description/icon.png
Normal file
BIN
addons/dine360_uber/static/description/icon.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 26 KiB |
40
addons/dine360_uber/static/src/js/uber_pos.js
Normal file
40
addons/dine360_uber/static/src/js/uber_pos.js
Normal file
@ -0,0 +1,40 @@
|
||||
/** @odoo-module */
|
||||
|
||||
import { ReceiptScreen } from "@point_of_sale/app/screens/receipt_screen/receipt_screen";
|
||||
import { patch } from "@web/core/utils/patch";
|
||||
import { useService } from "@web/core/utils/hooks";
|
||||
|
||||
patch(ReceiptScreen.prototype, {
|
||||
setup() {
|
||||
super.setup(...arguments);
|
||||
this.orm = useService("orm");
|
||||
this.notification = useService("notification");
|
||||
},
|
||||
async requestUber() {
|
||||
const order = this.props.order;
|
||||
const serverId = order.server_id;
|
||||
|
||||
if (!serverId) {
|
||||
this.notification.add("Wait! This order hasn't been sent to the server yet. Please wait a second.", {
|
||||
title: "Uber Integration",
|
||||
type: "warning",
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
await this.orm.call("pos.order", "action_request_uber_delivery", [[serverId]]);
|
||||
this.notification.add("Uber Direct delivery requested successfully!", {
|
||||
title: "Uber Integration",
|
||||
type: "success",
|
||||
});
|
||||
// Disable the button or change text if needed
|
||||
} catch (error) {
|
||||
const message = error.message?.data?.message || "Check server logs for details.";
|
||||
this.notification.add("Failed to request Uber: " + message, {
|
||||
title: "Uber Error",
|
||||
type: "danger",
|
||||
});
|
||||
}
|
||||
}
|
||||
});
|
||||
9
addons/dine360_uber/static/src/xml/uber_pos.xml
Normal file
9
addons/dine360_uber/static/src/xml/uber_pos.xml
Normal file
@ -0,0 +1,9 @@
|
||||
<templates id="template" xml:space="preserve">
|
||||
<t t-name="dine360_uber.ReceiptScreenUber" t-inherit="point_of_sale.ReceiptScreen" t-inherit-mode="extension" owl="1">
|
||||
<xpath expr="//div[contains(@class, 'buttons')]" position="inside">
|
||||
<button class="button next highlight" t-on-click="requestUber" style="background-color: #000 !important; color: #fff !important; margin: 5px;">
|
||||
Request Uber Direct
|
||||
</button>
|
||||
</xpath>
|
||||
</t>
|
||||
</templates>
|
||||
75
addons/dine360_uber/views/pos_order_views.xml
Normal file
75
addons/dine360_uber/views/pos_order_views.xml
Normal file
@ -0,0 +1,75 @@
|
||||
<odoo>
|
||||
<data>
|
||||
<record id="view_pos_order_form_inherit_uber" model="ir.ui.view">
|
||||
<field name="name">pos.order.form.inherit.uber</field>
|
||||
<field name="model">pos.order</field>
|
||||
<field name="inherit_id" ref="point_of_sale.view_pos_pos_form"/>
|
||||
<field name="arch" type="xml">
|
||||
<xpath expr="//header" position="inside">
|
||||
<field name="is_uber_order" invisible="1"/>
|
||||
<field name="uber_tracking_url" invisible="1"/>
|
||||
<field name="uber_status" invisible="1"/>
|
||||
<field name="uber_alert_triggered" invisible="1"/>
|
||||
<button name="action_request_uber_delivery"
|
||||
string="Request Uber Delivery"
|
||||
type="object"
|
||||
invisible="is_uber_order == True or state != 'paid'"
|
||||
class="oe_highlight"/>
|
||||
<button name="action_view_uber_map"
|
||||
string="📍 Track Driver"
|
||||
type="object"
|
||||
invisible="not uber_tracking_url"
|
||||
class="btn-primary"/>
|
||||
<button name="action_cancel_uber_delivery"
|
||||
string="Cancel Uber Delivery"
|
||||
type="object"
|
||||
invisible="is_uber_order == False or uber_status in ['delivered', 'cancelled']"
|
||||
class="btn-danger"/>
|
||||
</xpath>
|
||||
<xpath expr="//field[@name='pos_reference']" position="after">
|
||||
<field name="is_uber_order" invisible="1"/>
|
||||
<field name="uber_status" readonly="1" invisible="is_uber_order == False" decoration-info="uber_status == 'pending'" decoration-warning="uber_status == 'pickup'" decoration-success="uber_status == 'delivered'"/>
|
||||
<field name="uber_eta" readonly="1" invisible="not uber_eta"/>
|
||||
<field name="uber_delivery_fee" widget="monetary" invisible="not uber_delivery_fee"/>
|
||||
</xpath>
|
||||
<xpath expr="//header" position="after">
|
||||
<div class="alert alert-danger mb-0" role="alert" invisible="not uber_alert_triggered">
|
||||
🚨 <strong>Attention!</strong> Driver not assigned for over 15 minutes. Please contact Uber support.
|
||||
</div>
|
||||
</xpath>
|
||||
</field>
|
||||
</record>
|
||||
|
||||
<!-- PERFORMANCE ANALYTICS DASHBOARD -->
|
||||
<record id="view_pos_order_uber_graph" model="ir.ui.view">
|
||||
<field name="name">pos.order.uber.graph</field>
|
||||
<field name="model">pos.order</field>
|
||||
<field name="arch" type="xml">
|
||||
<graph string="Uber Performance" type="bar" sample="1">
|
||||
<field name="date_order" interval="day"/>
|
||||
<field name="uber_delivery_fee" type="measure"/>
|
||||
<field name="amount_total" type="measure"/>
|
||||
</graph>
|
||||
</field>
|
||||
</record>
|
||||
|
||||
<record id="action_uber_analytics" model="ir.actions.act_window">
|
||||
<field name="name">Uber Performance Analytics</field>
|
||||
<field name="res_model">pos.order</field>
|
||||
<field name="view_mode">graph,tree</field>
|
||||
<field name="domain">[('is_uber_order', '=', True)]</field>
|
||||
<field name="help" type="html">
|
||||
<p class="o_view_nocontent_smiling_face">
|
||||
No Uber performance data yet!
|
||||
</p>
|
||||
<p>Track your delivery costs and order volume from Uber.</p>
|
||||
</field>
|
||||
</record>
|
||||
|
||||
<menuitem id="menu_uber_analytics"
|
||||
name="Analytics Dashboard"
|
||||
parent="dine360_uber.menu_uber_root"
|
||||
action="action_uber_analytics"
|
||||
sequence="20"/>
|
||||
</data>
|
||||
</odoo>
|
||||
65
addons/dine360_uber/views/uber_config_views.xml
Normal file
65
addons/dine360_uber/views/uber_config_views.xml
Normal file
@ -0,0 +1,65 @@
|
||||
<odoo>
|
||||
<data>
|
||||
<record id="view_uber_config_tree" model="ir.ui.view">
|
||||
<field name="name">uber.config.tree</field>
|
||||
<field name="model">uber.config</field>
|
||||
<field name="arch" type="xml">
|
||||
<tree>
|
||||
<field name="name"/>
|
||||
<field name="environment"/>
|
||||
<field name="active"/>
|
||||
</tree>
|
||||
</field>
|
||||
</record>
|
||||
|
||||
<record id="view_uber_config_form" model="ir.ui.view">
|
||||
<field name="name">uber.config.form</field>
|
||||
<field name="model">uber.config</field>
|
||||
<field name="arch" type="xml">
|
||||
<form>
|
||||
<header>
|
||||
<button name="action_test_connection" string="Test Connection" type="object" class="oe_highlight"/>
|
||||
</header>
|
||||
<sheet>
|
||||
<group>
|
||||
<group string="API Credentials">
|
||||
<field name="name"/>
|
||||
<field name="client_id"/>
|
||||
<field name="client_secret" password="True"/>
|
||||
<field name="customer_id"/>
|
||||
</group>
|
||||
<group string="Settings">
|
||||
<field name="environment"/>
|
||||
<field name="active"/>
|
||||
</group>
|
||||
</group>
|
||||
<group string="Automation & Fees">
|
||||
<group>
|
||||
<field name="timeout_minutes"/>
|
||||
<field name="delivery_product_id" context="{'default_type': 'service'}"/>
|
||||
</group>
|
||||
</group>
|
||||
</sheet>
|
||||
</form>
|
||||
</field>
|
||||
</record>
|
||||
|
||||
<record id="action_uber_config" model="ir.actions.act_window">
|
||||
<field name="name">Uber Configuration</field>
|
||||
<field name="res_model">uber.config</field>
|
||||
<field name="view_mode">tree,form</field>
|
||||
</record>
|
||||
|
||||
<!-- ROOT MENU ITEM -->
|
||||
<menuitem id="menu_uber_root"
|
||||
name="Uber Integration"
|
||||
web_icon="dine360_uber,static/description/icon.png"
|
||||
sequence="100"/>
|
||||
|
||||
<menuitem id="menu_uber_config"
|
||||
name="Settings"
|
||||
parent="menu_uber_root"
|
||||
action="action_uber_config"
|
||||
sequence="10"/>
|
||||
</data>
|
||||
</odoo>
|
||||
BIN
upgrade_log.txt
Normal file
BIN
upgrade_log.txt
Normal file
Binary file not shown.
Loading…
x
Reference in New Issue
Block a user