Odoo表单布局模块
This commit is contained in:
@@ -0,0 +1 @@
|
||||
from . import models
|
||||
@@ -0,0 +1,27 @@
|
||||
{
|
||||
"name": "房地产管理系统", # 模块名称
|
||||
"summary": "管理房地产属性、优惠和标签", # 模块摘要
|
||||
"description": "用于管理房地产属性、报价和标签的模块。", # 模块描述
|
||||
"version": "1.0", # 版本号
|
||||
"category": "MyAddons/Estate", # 模块分类
|
||||
"data": [
|
||||
"security/ir.model.access.csv",
|
||||
"views/estate_property_offer_views.xml", # 视图文件
|
||||
"views/estate_property_tag_views.xml", # 视图文件
|
||||
"views/estate_property_type_views.xml", # 视图文件
|
||||
"views/estate_property_views.xml", # 视图文件
|
||||
"views/res_users_views.xml", # 用户视图文件
|
||||
"views/report_actions.xml", # 报表操作文件
|
||||
"views/report_template.xml", # 报表模板文件
|
||||
"views/estate_menus.xml", # 菜单文件
|
||||
], # 数据文件列表
|
||||
"assets": {
|
||||
"web.assets_backend": [
|
||||
"estate/static/src/scss/estate.scss",
|
||||
]
|
||||
},
|
||||
"depends": ["base", "mail"], # 依赖的模块列表
|
||||
"license": "LGPL-3", # 许可证类型
|
||||
"installable": True, # 是否可安装
|
||||
"application": True, # 是否为应用程序
|
||||
}
|
||||
@@ -0,0 +1,5 @@
|
||||
from . import estate_property
|
||||
from . import estate_property_type
|
||||
from . import estate_property_tag
|
||||
from . import estate_property_offer
|
||||
from . import res_users
|
||||
@@ -0,0 +1,181 @@
|
||||
from odoo import models, fields, api
|
||||
from datetime import timedelta
|
||||
from odoo.exceptions import UserError, ValidationError
|
||||
|
||||
# 导入异常类
|
||||
|
||||
|
||||
class EstateProperty(models.Model):
|
||||
_name = "estate.property"
|
||||
_description = "房地产属性"
|
||||
# 继承邮件线程和活动混合类
|
||||
_inherit = ["mail.thread", "mail.activity.mixin"]
|
||||
# 默认按ID降序排列
|
||||
_order = "id desc"
|
||||
|
||||
name = fields.Char(string="名称", required=True)
|
||||
description = fields.Text(string="描述")
|
||||
postcode = fields.Char(string="邮政编码")
|
||||
# 默认日期为三个月后
|
||||
date_availability = fields.Date(
|
||||
string="可用日期",
|
||||
copy=False,
|
||||
default=lambda self: fields.Date.today() + timedelta(days=90),
|
||||
)
|
||||
expected_price = fields.Float(string="预期价格", required=True)
|
||||
selling_price = fields.Float(string="销售价格", copy=False)
|
||||
bedrooms = fields.Integer(string="卧室数量", default=2)
|
||||
living_area = fields.Integer(string="居住面积")
|
||||
facades = fields.Integer(string="立面")
|
||||
garage = fields.Boolean(string="是否有车库")
|
||||
garden = fields.Boolean(string="是否有花园")
|
||||
garden_area = fields.Integer(string="花园面积")
|
||||
garden_orientation = fields.Selection(
|
||||
[("north", "北"), ("south", "南"), ("east", "东"), ("west", "西")],
|
||||
string="花园朝向",
|
||||
)
|
||||
# 默认货币为公司货币
|
||||
currency_id = fields.Many2one(
|
||||
"res.currency",
|
||||
string="货币",
|
||||
required=True,
|
||||
default=lambda self: self.env.ref("base.CNY").id,
|
||||
)
|
||||
|
||||
active = fields.Boolean(string="是否归档", default=True)
|
||||
# 房屋类型
|
||||
state = fields.Selection(
|
||||
[
|
||||
("new", "新"),
|
||||
("offer_received", "已收到报价"),
|
||||
("offer_accepted", "已接受报价"),
|
||||
("sold", "已售出"),
|
||||
("canceled", "已取消"),
|
||||
],
|
||||
string="状态",
|
||||
default="new",
|
||||
required=True,
|
||||
copy=False,
|
||||
)
|
||||
versionflog = fields.Integer(string="换版", default=1)
|
||||
# 关联房屋类型模型
|
||||
property_type_id = fields.Many2one(
|
||||
"estate.property.type", string="房屋类型", required=True
|
||||
)
|
||||
# 买家
|
||||
buyer_id = fields.Many2one("res.partner", string="买家", copy=False)
|
||||
# 关联联系人模型卖家
|
||||
seller_id = fields.Many2one(
|
||||
"res.users", string="卖家", required=True, default=lambda self: self.env.user
|
||||
)
|
||||
# 房屋标签
|
||||
tag_ids = fields.Many2many("estate.property.tag", string="房屋标签")
|
||||
# 房屋报价
|
||||
offer_ids = fields.One2many(
|
||||
"estate.property.offer", "property_id", string="房屋报价"
|
||||
)
|
||||
# 房屋总面积
|
||||
total_area = fields.Float(
|
||||
string="总面积", compute="_compute_total_area", store=True
|
||||
)
|
||||
# 最佳报价
|
||||
best_price = fields.Float(
|
||||
string="最佳报价", compute="_compute_best_price", store=True
|
||||
)
|
||||
# 房产预期价格必须严格为正数
|
||||
_sql_constraints = [
|
||||
("check_expected_price", "CHECK(expected_price > 0)", "预期价格必须为正数"),
|
||||
("check_selling_price", "CHECK(selling_price > 0)", "销售价格必须为正数"),
|
||||
("check_bedrooms", "CHECK(bedrooms > 0)", "卧室数量必须为正数"),
|
||||
("check_living_area", "CHECK(living_area > 0)", "居住面积必须为正数"),
|
||||
("check_facades", "CHECK(facades > 0)", "立面数量必须为正数"),
|
||||
]
|
||||
|
||||
# 无法接受低于预期价格 90% 的报价
|
||||
@api.constrains("selling_price", "expected_price") # 添加约束
|
||||
def _check_selling_price(self):
|
||||
for record in self:
|
||||
if record.selling_price < record.expected_price * 0.9:
|
||||
raise ValidationError("销售价格不能低于预期价格的90%")
|
||||
|
||||
@api.depends("living_area", "garden_area") # 计算字段
|
||||
def _compute_total_area(self):
|
||||
"""计算房屋总面积"""
|
||||
for record in self:
|
||||
record.total_area = record.living_area + record.garden_area
|
||||
|
||||
@api.depends("offer_ids.price") # 计算字段
|
||||
def _compute_best_price(self):
|
||||
"""计算最佳报价"""
|
||||
for record in self:
|
||||
best_price = 0.0
|
||||
if record.offer_ids:
|
||||
best_price = max(record.offer_ids.mapped("price"))
|
||||
record.best_price = best_price
|
||||
|
||||
def action_sold(self):
|
||||
"""标记为已售(需先接受报价)"""
|
||||
if self.state != "offer_accepted":
|
||||
raise UserError("必须接受报价后才能标记已售")
|
||||
return self.write({"state": "sold"})
|
||||
|
||||
def action_cancel(self):
|
||||
"""取消交易"""
|
||||
if self.state == "sold":
|
||||
raise UserError("已售房产不可取消")
|
||||
return self.write({"state": "canceled"})
|
||||
|
||||
# 如果有花园,设置默认花园面积为50,朝向为南,若没有花园,则设置花园面积为0,朝向为无
|
||||
@api.onchange("garden")
|
||||
def _onchange_garden(self):
|
||||
if self.garden:
|
||||
self.garden_area = 50
|
||||
self.garden_orientation = "south"
|
||||
else:
|
||||
self.garden_area = 0
|
||||
self.garden_orientation = False
|
||||
|
||||
# 若取消选择车库则弹出警告
|
||||
@api.onchange("garage")
|
||||
def _onchange_garage(self):
|
||||
if not self.garage == False:
|
||||
return {"warning": {"title": "警告", "message": "确定取消车库吗?"}}
|
||||
|
||||
# •如果房产的状态不是‘新建’或‘已取消’,则阻止删除房产
|
||||
@api.ondelete(at_uninstall=True)
|
||||
def _unlink_except_new_canceled(self):
|
||||
"""删除房产"""
|
||||
for record in self:
|
||||
if record.state not in ["new", "canceled"]:
|
||||
raise UserError("只能删除新建或已取消的房产")
|
||||
|
||||
# 设置版本日志
|
||||
def setversionflog(self):
|
||||
# 空函数,不做任何操作
|
||||
pass
|
||||
|
||||
def write(self, vals):
|
||||
vals["create_uid"] = self.create_uid
|
||||
return super(EstateProperty, self).write(vals)
|
||||
|
||||
# 定义按钮
|
||||
def action_set_offer_received(self):
|
||||
"""设置为已收到报价"""
|
||||
return self.write({"state": "offer_received"})
|
||||
|
||||
def action_set_offer_accepted(self):
|
||||
"""设置为已接受报价"""
|
||||
return self.write({"state": "offer_accepted"})
|
||||
def action_set_sold(self):
|
||||
"""设置为已售出"""
|
||||
# 将状态设置为已售出
|
||||
return self.write({"state": "sold"})
|
||||
|
||||
def do_reject_new(self):
|
||||
"""拒绝新建报价"""
|
||||
# 将状态设置为"new"
|
||||
return self.write({"state": "new"})
|
||||
|
||||
def do_reject_offer(self):
|
||||
"""拒绝报价"""
|
||||
return self.write({"state": "offer_received"})
|
||||
@@ -0,0 +1,77 @@
|
||||
from odoo import models,fields,api;# 导入Odoo模型、字段和API模块
|
||||
from datetime import timedelta;# 导入时间模块
|
||||
from odoo.exceptions import UserError;# 导入用户错误模块
|
||||
from odoo.exceptions import ValidationError;# 导入验证错误模块
|
||||
# 房屋报价模型
|
||||
class EstatePropertyOffer(models.Model):
|
||||
_name = 'estate.property.offer';
|
||||
_description = '房屋报价'
|
||||
_order = "price desc";# 默认按价格降序排列
|
||||
|
||||
price = fields.Float(string='报价', required=True);
|
||||
status = fields.Selection([('Accepted', '已接受'),('Refused', '已拒绝')], string='状态',copy=False);
|
||||
partner_id = fields.Many2one('res.partner', string='客户', required=True);
|
||||
property_id = fields.Many2one('estate.property', string='房屋', required=True);
|
||||
validity = fields.Integer(string='有效期', default=7);# 默认有效期为7天
|
||||
date_deadline = fields.Date(string='截止日期', compute='_compute_date_deadline',inverse='_inverse_validity', store=True);# 计算字段,存储在数据库中
|
||||
property_type_id = fields.Many2one(related='property_id.property_type_id', string='房屋类型', store=True);# 关联房屋类型模型
|
||||
|
||||
|
||||
# 保价必须为严格正数
|
||||
_sql_constraints = [
|
||||
('check_price', 'CHECK(price > 0)', '报价必须为正数')
|
||||
]
|
||||
@api.depends('validity',"create_date")# 计算字段
|
||||
def _compute_date_deadline(self):
|
||||
for record in self:
|
||||
if record.create_date:
|
||||
# 计算截止日期
|
||||
record.date_deadline = record.create_date + timedelta(days=record.validity);
|
||||
else:
|
||||
# 如果创建日期为空,则为当前日期
|
||||
record.date_deadline = fields.Date.today()+ timedelta(days=record.validity);
|
||||
# 反向计算有效期,inverse方法
|
||||
def _inverse_validity(self):
|
||||
for record in self:
|
||||
if record.date_deadline:
|
||||
if record.create_date:
|
||||
validity = (record.date_deadline - record.create_date.date()).days;
|
||||
# 如果有效期小于0,则抛出异常
|
||||
if validity < 0:
|
||||
raise UserError("有效期不能小于0天")
|
||||
record.validity = validity;
|
||||
else:
|
||||
validity = (record.date_deadline - fields.Datetime.now().date()).days;
|
||||
# 如果有效期小于0,则抛出异常
|
||||
if validity < 0:
|
||||
raise UserError("有效期不能小于0天")
|
||||
record.validity = validity;
|
||||
# 接受报价
|
||||
def action_accept(self):
|
||||
# 将房屋状态改为已接受报价
|
||||
self.property_id.state = 'offer_accepted';
|
||||
# 将房屋的销售价格设置为报价价格
|
||||
self.property_id.selling_price = self.price;
|
||||
# 将买家设置为报价的客户
|
||||
self.property_id.buyer_id = self.partner_id.id;
|
||||
# 将报价的状态设置为已接受
|
||||
self.status = 'Accepted';
|
||||
# 拒绝报价
|
||||
def action_refuse(self):
|
||||
# 将房屋状态改为已收到报价
|
||||
self.property_id.state = 'offer_received';
|
||||
# 将报价的状态设置为已拒绝
|
||||
self.status = 'Refused';
|
||||
|
||||
# 创建报价时,将房产状态设置为‘收到报价’。如果用户尝试创建价格低于现有报价的报价,则引发错误。
|
||||
@api.model_create_multi
|
||||
def create(self, vals_list):
|
||||
for vals in vals_list:
|
||||
# 检查房产状态是否为‘收到报价’
|
||||
property_id = vals.get('property_id')
|
||||
property_record = self.env['estate.property'].browse(property_id);
|
||||
property_record.state = 'offer_received';
|
||||
price = vals.get('price')
|
||||
if price<property_record.best_price:
|
||||
raise UserError("报价必须高于现有报价")
|
||||
return super(EstatePropertyOffer, self).create(vals_list);
|
||||
@@ -0,0 +1,13 @@
|
||||
from odoo import models,fields;
|
||||
class EstatePropertyTag(models.Model):
|
||||
_name = 'estate.property.tag';
|
||||
_description = '房屋标签'
|
||||
_order = "name";# 默认按名称升序排列
|
||||
|
||||
name = fields.Char(string='房屋标签', required=True);
|
||||
color = fields.Integer(string='颜色',default=0);# 颜色字段
|
||||
|
||||
# 房屋标签必须唯一
|
||||
_sql_constraints = [
|
||||
('check_name', 'UNIQUE(name)', '房屋标签必须唯一')
|
||||
]
|
||||
@@ -0,0 +1,20 @@
|
||||
from odoo import models,fields,api;
|
||||
class EstatePropertyType(models.Model):
|
||||
_name = 'estate.property.type';
|
||||
_description = '房屋类型'
|
||||
_order = 'sequence';# 排序字段
|
||||
|
||||
name = fields.Char(string='房屋类型', required=True);
|
||||
property_ids = fields.One2many('estate.property', 'property_type_id', string='房屋');# 关联房屋模型
|
||||
sequence= fields.Integer(string='排序', default=1);# 排序字段
|
||||
offer_ids = fields.One2many('estate.property.offer', 'property_type_id', string='房屋报价');# 关联房屋报价模型
|
||||
offer_count = fields.Integer(string='报价数量', compute='_compute_offer_count', store=True);# 计算字段,存储在数据库中
|
||||
# 房屋类型必须唯一
|
||||
_sql_constraints = [
|
||||
('check_name', 'UNIQUE(name)', '房屋类型必须唯一')
|
||||
]
|
||||
# 计算报价数量
|
||||
@api.depends('offer_ids')
|
||||
def _compute_offer_count(self):
|
||||
for record in self:
|
||||
record.offer_count = len(record.offer_ids);# 计算报价数量
|
||||
@@ -0,0 +1,5 @@
|
||||
from odoo import models, fields, api;# 导入模型、字段和api模块
|
||||
class ResUsers(models.Model):
|
||||
_inherit = 'res.users'
|
||||
|
||||
property_ids = fields.One2many('estate.property', 'seller_id', string='我的房产',domain="[('state', '!=', 'sold')]")# 关联房屋模型,排除已售房产
|
||||
@@ -0,0 +1,5 @@
|
||||
id,name,model_id:id,group_id:id,perm_read,perm_write,perm_create,perm_unlink
|
||||
estate.access_estate_property,access_estate_property,estate.model_estate_property,base.group_user,1,1,1,1
|
||||
estate.access_estate_property_type,access_estate_property_type,estate.model_estate_property_type,base.group_user,1,1,1,1
|
||||
estate.access_estate_property_tag,access_estate_property_tag,estate.model_estate_property_tag,base.group_user,1,1,1,1
|
||||
estate.access_estate_property_offer,access_estate_property_offer,estate.model_estate_property_offer,base.group_user,1,1,1,1
|
||||
|
Binary file not shown.
|
After Width: | Height: | Size: 2.9 KiB |
@@ -0,0 +1,11 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<svg
|
||||
class="icon"
|
||||
viewBox="0 0 1024 1024"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
>
|
||||
<path
|
||||
d="M618.2 340.5H220.9l181.6-190.8 134.2 144.1c8.7 9.3 23.4 9.9 32.8 1.2 9.4-8.7 9.9-23.4 1.2-32.7l-151-162.2c-4.4-4.7-10.5-7.4-16.9-7.4-6.5 0.5-12.5 2.6-17 7.2L150 347.7c-4.1 4.3-6.4 10-6.4 16v534.8c0 12.8 10.4 23.2 23.2 23.2h451.4c12.8 0 23.2-10.4 23.2-23.2V363.7c0-12.8-10.3-23.2-23.2-23.2zM595 875.3H190V386.8h405v488.5z m262.3-440H699.5c-12.8 0-23.2 10.4-23.2 23.2 0 12.8 10.4 23.2 23.2 23.2H834v393.9H699.5c-12.8 0-23.2 10.4-23.2 23.2 0 12.8 10.4 23.2 23.2 23.2h157.7c12.8 0 23.2-10.4 23.2-23.2V458.4c0.1-12.8-10.3-23.1-23.1-23.1z m-339 21.2h-78.8v78.6h78.8v-78.6z m-172.7 0h-78.8v78.6h78.8v-78.6z m172.7 139.2h-78.8v78.6h78.8v-78.6z m-172.7 0h-78.8v78.6h78.8v-78.6z m172.7 139.1h-78.8v78.6h78.8v-78.6z m-172.7 0h-78.8v78.6h78.8v-78.6z"
|
||||
fill="currentColor"
|
||||
/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 918 B |
@@ -0,0 +1,20 @@
|
||||
.estate_form .o_box.stat-card {
|
||||
background: #fff;
|
||||
box-shadow: 0 2px 6px rgba(0, 0, 0, 0.1);
|
||||
transition: transform 0.2s ease-in-out;
|
||||
}
|
||||
|
||||
.estate_form .o_box.stat-card:hover {
|
||||
transform: translateY(-3px);
|
||||
}
|
||||
|
||||
.estate_form .o_box.stat-card h3 {
|
||||
font-size: 18px;
|
||||
font-weight: bold;
|
||||
margin: 5px 0 0 0;
|
||||
}
|
||||
|
||||
.estate_form .o_box.stat-card span {
|
||||
font-size: 14px;
|
||||
color: #888;
|
||||
}
|
||||
@@ -0,0 +1,13 @@
|
||||
<odoo>
|
||||
<menuitem id="estate_property_menu_root" name="房地产信息" web_icon="estate,static/description/icon.png">
|
||||
<menuitem id="estate_property_first_level_property" name="房屋信息">
|
||||
<menuitem id="estate_property_model_menu_action" action="estate_property_model_action" />
|
||||
</menuitem>
|
||||
<menuitem id="estate_property_first_level_setting" name="设置">
|
||||
<menuitem id="estate_property_type_model_menu_action"
|
||||
action="estate_property_type_model_action" />
|
||||
<menuitem id="estate_property_tag_model_menu_action"
|
||||
action="estate_property_tag_model_action" />
|
||||
</menuitem>
|
||||
</menuitem>
|
||||
</odoo>
|
||||
@@ -0,0 +1,38 @@
|
||||
<odoo>
|
||||
<record id="estate_property_offer_model_action" model="ir.actions.act_window">
|
||||
<field name="name">房屋报价</field>
|
||||
<field name="res_model">estate.property.offer</field>
|
||||
<field name="view_mode">tree</field>
|
||||
<field name="domain">[('property_type_id', '=', active_id)]</field>
|
||||
</record>
|
||||
<!-- 列表视图配置 -->
|
||||
<record id="estate_property_offer_view_tree" model="ir.ui.view">
|
||||
<field name="name">estate.property.offer.tree</field>
|
||||
<field name="model">estate.property.offer</field>
|
||||
<field name="arch" type="xml">
|
||||
<tree editable="bottom" string="房屋报价"
|
||||
decoration-danger="status == 'Refused'"
|
||||
decoration-success="status == 'Accepted'"
|
||||
>
|
||||
<field name="price" />
|
||||
<field name="status" />
|
||||
<field name="validity" />
|
||||
<field name="date_deadline" />
|
||||
<field name="partner_id" />
|
||||
<field name="property_id" />
|
||||
<button name="action_accept" string="接受报价" type="object" icon="fa-check" invisible="status" />
|
||||
<button name="action_refuse" string="拒绝报价" type="object" icon="fa-times" invisible="status" />
|
||||
</tree>
|
||||
</field>
|
||||
</record>
|
||||
<!-- 搜索视图配置 -->
|
||||
<record id="estate_property_tag_search_view" model="ir.ui.view">
|
||||
<field name="name">estate.property.tag.search</field>
|
||||
<field name="model">estate.property.tag</field>
|
||||
<field name="arch" type="xml">
|
||||
<search string="搜索">
|
||||
<field name="name" />
|
||||
</search>
|
||||
</field>
|
||||
</record>
|
||||
</odoo>
|
||||
@@ -0,0 +1,28 @@
|
||||
<odoo>
|
||||
<record id="estate_property_tag_model_action" model="ir.actions.act_window">
|
||||
<field name="name">房屋标签</field>
|
||||
<field name="res_model">estate.property.tag</field>
|
||||
<field name="view_mode">tree</field>
|
||||
</record>
|
||||
<!-- 列表视图配置 -->
|
||||
<record id="estate_property_tag_view_tree" model="ir.ui.view">
|
||||
<field name="name">estate.property.tag.tree</field>
|
||||
<field name="model">estate.property.tag</field>
|
||||
<field name="arch" type="xml">
|
||||
<tree editable="bottom">
|
||||
<field name="name"/>
|
||||
<field name="color"/>
|
||||
</tree>
|
||||
</field>
|
||||
</record>
|
||||
<!-- 搜索视图配置 -->
|
||||
<record id="estate_property_tag_search_view" model="ir.ui.view">
|
||||
<field name="name">estate.property.tag.search</field>
|
||||
<field name="model">estate.property.tag</field>
|
||||
<field name="arch" type="xml">
|
||||
<search string="搜索">
|
||||
<field name="name" />
|
||||
</search>
|
||||
</field>
|
||||
</record>
|
||||
</odoo>
|
||||
@@ -0,0 +1,80 @@
|
||||
<odoo>
|
||||
<record id="estate_property_type_model_action" model="ir.actions.act_window">
|
||||
<field name="name">房屋类型</field>
|
||||
<field name="res_model">estate.property.type</field>
|
||||
<field name="view_mode">tree,form</field>
|
||||
</record>
|
||||
<!-- 列表视图配置 -->
|
||||
<record id="estate_property_type_view_tree" model="ir.ui.view">
|
||||
<field name="name">estate.property.type.tree</field>
|
||||
<field name="model">estate.property.type</field>
|
||||
<field name="arch" type="xml">
|
||||
<tree string="房屋类型">
|
||||
<field name="sequence" widget="handle" />
|
||||
<field name="name" string="房屋类型" />
|
||||
</tree>
|
||||
</field>
|
||||
</record>
|
||||
<!-- 表单视图配置 -->
|
||||
<record id="estate_property_type_form_view" model="ir.ui.view">
|
||||
<field name="name">estate.property.type.form</field>
|
||||
<field name="model">estate.property.type</field>
|
||||
<field name="arch" type="xml">
|
||||
<form string="房屋类型">
|
||||
<!-- 头部状态栏 -->
|
||||
<header>
|
||||
<!-- 按钮放在最右边 -->
|
||||
<button
|
||||
class="oe_stat_button "
|
||||
name="%(estate_property_offer_model_action)d"
|
||||
type="action"
|
||||
icon="fa-bars"
|
||||
>
|
||||
<div class="o_stat_info">
|
||||
<span class="o_stat_value">
|
||||
<field name="offer_count" />
|
||||
</span>
|
||||
<span class="o_stat_text">
|
||||
报价数量
|
||||
</span>
|
||||
</div>
|
||||
</button>
|
||||
</header>
|
||||
<sheet>
|
||||
<group>
|
||||
<h1>
|
||||
<field name="name" string="房屋类型" />
|
||||
</h1>
|
||||
<br/>
|
||||
<field name="offer_count" />
|
||||
</group>
|
||||
<notebook>
|
||||
<page string="房产">
|
||||
<field name="property_ids">
|
||||
<tree>
|
||||
<field name="name" string="房产名称" />
|
||||
<field name="expected_price" string="预期价格" />
|
||||
<field name="state" string="状态" />
|
||||
<field name="date_availability" string="可用日期" />
|
||||
</tree>
|
||||
</field>
|
||||
</page>
|
||||
<page string="房屋报价">
|
||||
<field name="offer_ids"></field>
|
||||
</page>
|
||||
</notebook>
|
||||
</sheet>
|
||||
</form>
|
||||
</field>
|
||||
</record>
|
||||
<!-- 搜索视图配置 -->
|
||||
<record id="estate_property_type_search_view" model="ir.ui.view">
|
||||
<field name="name">estate.property.type.search</field>
|
||||
<field name="model">estate.property.type</field>
|
||||
<field name="arch" type="xml">
|
||||
<search string="搜索">
|
||||
<field name="name" />
|
||||
</search>
|
||||
</field>
|
||||
</record>
|
||||
</odoo>
|
||||
@@ -0,0 +1,165 @@
|
||||
<odoo>
|
||||
<record id="estate_property_model_action" model="ir.actions.act_window">
|
||||
<field name="name">房地产信息</field>
|
||||
<field name="res_model">estate.property</field>
|
||||
<field name="view_mode">tree,form,kanban</field>
|
||||
<field name="context">{}</field>
|
||||
</record>
|
||||
<!-- 列表视图配置 -->
|
||||
<record id="estate_property_view_tree" model="ir.ui.view">
|
||||
<field name="name">estate.property.tree</field>
|
||||
<field name="model">estate.property</field>
|
||||
<field name="arch" type="xml">
|
||||
<tree string="房地产列表" decoration-danger="state == 'canceled'" decoration-success="state in ['offer_received', 'offer_accepted']" decoration-bf="state == 'offer_accepted'" decoration-muted="state == 'sold'">
|
||||
<field name="state" />
|
||||
<field name="name" string="房产名称" />
|
||||
<field name="expected_price" string="预期价格" />
|
||||
<field name="selling_price" string="成交价格" />
|
||||
<field name="bedrooms" string="卧室数" />
|
||||
<field name="living_area" string="面积(㎡)" />
|
||||
<field name="state" string="状态" />
|
||||
<field name="date_availability" string="可用日期" optional="True" />
|
||||
</tree>
|
||||
</field>
|
||||
</record>
|
||||
<!-- 表单视图配置 -->
|
||||
<record id="view_estate_property_form_custom" model="ir.ui.view">
|
||||
<field name="name">estate.property.form.custom</field>
|
||||
<field name="model">estate.property</field>
|
||||
<field name="arch" type="xml">
|
||||
<form string="房地产信息" class="estate_form">
|
||||
<header>
|
||||
<field name="state" widget="statusbar" statusbar_visible="new,offer_received,offer_accepted,sold,canceled" statusbar_colors='{"sold":"success","canceled":"danger"}' class="oe_statusbar_buttons" />
|
||||
<!-- 标记已售按钮 -->
|
||||
<button name="action_sold" type="object" string="标记已售" class="oe_highlight" confirm="确认完成交易?" invisible="state in ['sold','canceled']" domain="[('state','=','offer_accepted')]">
|
||||
<field name="state" invisible="1" />
|
||||
</button>
|
||||
<!-- 取消交易按钮 -->
|
||||
<button name="action_cancel" type="object" string="取消交易" class="oe_unlink" confirm="确认取消该交易?" invisible="state in ['sold','canceled']" domain="[('state','!=','canceled')]">
|
||||
<field name="state" invisible="1" />
|
||||
</button>
|
||||
</header>
|
||||
<sheet>
|
||||
<!-- 标题 -->
|
||||
<div class="oe_title">
|
||||
<h1 style="font-size:24px; font-weight:700; color:#1E90FF; margin-bottom:12px;">
|
||||
<field name="name"/>
|
||||
</h1>
|
||||
</div>
|
||||
<div class="o_row" style="margin-top: 20px;">
|
||||
<!-- 房屋报价 -->
|
||||
<div class="o_box" style="flex:1; margin-right:10px; padding:15px; border:1px solid #ddd; border-radius:6px;">
|
||||
<h3 style="border-bottom:2px solid #4CAF50; padding-bottom:6px; margin-bottom:12px; font-weight:600; color:#333;">📑 房屋报价</h3>
|
||||
<field name="offer_ids" context="{'default_property_id': id}" options="{'no_create': True, 'no_open': True}" readonly='state in ["offer_accepted","sold","canceled"]' />
|
||||
</div>
|
||||
</div>
|
||||
<!-- 两栏布局:基础信息 + 费用信息 -->
|
||||
<div class="o_row" style="margin-top: 20px; display:flex; align-items:stretch;">
|
||||
<!-- 基础信息 -->
|
||||
<div class="o_box" style="flex:1; margin-right:10px; padding:15px; border:1px solid #ddd; border-radius:6px; display:flex; flex-direction:column;">
|
||||
<h3 style="border-bottom:2px solid #2196F3; padding-bottom:6px; margin-bottom:12px; font-weight:600; color:#333;">📅 基础信息</h3>
|
||||
<group col="2" style="flex:1;">
|
||||
<field name="postcode" placeholder="6位邮政编码" />
|
||||
<field name="date_availability" widget="daterange" options="{'endDateField': 'date_availability'}" />
|
||||
<field name="property_type_id" options="{'no_create': True, 'no_open': True}" />
|
||||
<field name="tag_ids" widget="many2many_tags" options="{'color_field': 'color'}" />
|
||||
<field name="garage" widget="boolean_toggle" options='{"terminology": {"active": "有车库","inactive": "无车库"}}' />
|
||||
<field name="active" widget="boolean_toggle" options='{"terminology": {"active": "可用","inactive": "不可用"}}' />
|
||||
<field name="garden" />
|
||||
<field name="garden_orientation" widget="selection_badge" options="{'horizontal': True, 'max_items': 4}" invisible="not garden" />
|
||||
</group>
|
||||
</div>
|
||||
<!-- 费用信息 -->
|
||||
<div class="o_box" style="flex:2; margin-left:10px; padding:15px; border:1px solid #ddd; border-radius:6px; display:flex; flex-direction:column;">
|
||||
<h3 style="border-bottom:2px solid #FF9800; padding-bottom:6px; margin-bottom:12px; font-weight:600; color:#333;">💰 费用信息</h3>
|
||||
<group col="2" style="flex:1;">
|
||||
<field name="bedrooms" default="2" />
|
||||
<field name="living_area" string="面积(㎡)" />
|
||||
<field name="garden_area" invisible="not garden" />
|
||||
<field name="facades" />
|
||||
<field name="seller_id" string="销售专员" />
|
||||
<field name="buyer_id" string="客户名称" />
|
||||
</group>
|
||||
<!-- 金额卡片展示区 -->
|
||||
<div class="o_row" style="margin-top:20px; display:flex; flex:0;">
|
||||
<div class="o_box stat-card" style="flex:1; margin:5px; padding:15px; text-align:center; border-radius:8px; border:1px solid #ddd; background:#f9fafb;">
|
||||
<span style="font-size:14px; color:#555;">总面积 (㎡)</span>
|
||||
<h3 style="margin-top:8px; color:#333;">
|
||||
<field name="total_area"/>
|
||||
</h3>
|
||||
</div>
|
||||
<div class="o_box stat-card" style="flex:1; margin:5px; padding:15px; text-align:center; border-radius:8px; border:1px solid #ddd; background:#f9fafb;">
|
||||
<span style="font-size:14px; color:#555;">销售价格</span>
|
||||
<h3 style="margin-top:8px; color:blue;">
|
||||
<field name="selling_price" widget="monetary" options="{'currency_field': 'currency_id'}"/>
|
||||
</h3>
|
||||
</div>
|
||||
<div class="o_box stat-card" style="flex:1; margin:5px; padding:15px; text-align:center; border-radius:8px; border:1px solid #ddd; background:#f9fafb;">
|
||||
<span style="font-size:14px; color:#555;">预期价格</span>
|
||||
<h3 style="margin-top:8px; color:red;">
|
||||
<field name="expected_price" widget="monetary" options="{'currency_field': 'currency_id'}"/>
|
||||
</h3>
|
||||
</div>
|
||||
<div class="o_box stat-card" style="flex:1; margin:5px; padding:15px; text-align:center; border-radius:8px; border:1px solid #ddd; background:#f9fafb;">
|
||||
<span style="font-size:14px; color:#555;">最佳报价</span>
|
||||
<h3 style="margin-top:8px; color:#333;">
|
||||
<field name="best_price" widget="monetary" options="{'currency_field': 'currency_id'}"/>
|
||||
</h3>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</sheet>
|
||||
</form>
|
||||
</field>
|
||||
</record>
|
||||
<!-- 搜索视图配置 -->
|
||||
<record id="estate_property_search_view" model="ir.ui.view">
|
||||
<field name="name">estate.property.search</field>
|
||||
<field name="model">estate.property</field>
|
||||
<field name="arch" type="xml">
|
||||
<search string="搜索">
|
||||
<field name="name" />
|
||||
<field name="postcode" />
|
||||
<field name="expected_price" />
|
||||
<field name="bedrooms" />
|
||||
<field name="living_area" string="面积至少为(㎡)" filter_domain="[('living_area','>=',self)]" />
|
||||
<field name="facades" />
|
||||
<field name="garage" />
|
||||
<separator />
|
||||
<filter string="可用的" name="available" domain="['|',('state', '=', 'new'),('state', '=', 'offer_received')]" />
|
||||
<group expand="1" string="Group By">
|
||||
<filter string="邮编" name="postcode" context="{'group_by':'postcode'}" />
|
||||
</group>
|
||||
</search>
|
||||
</field>
|
||||
</record>
|
||||
<!-- 看板试图配置 -->
|
||||
<record id="estate_property_kanban" model="ir.ui.view">
|
||||
<field name="model">estate.property</field>
|
||||
<field name="arch" type="xml">
|
||||
<kanban default_group_by="property_type_id" string="房地产信息" records_draggable="1">
|
||||
<!-- 新增字段声明 tag也要显示-->
|
||||
<field name="state" />
|
||||
<templates>
|
||||
<t t-name="kanban-box">
|
||||
<div>
|
||||
<div class="o_kanban_details">
|
||||
<strong>
|
||||
<field name="name" />
|
||||
</strong>
|
||||
<div class="o_kanban_description"> 预期价格:
|
||||
<field name="expected_price" widget="monetary" options="{'currency_field': 'currency_id'}" />
|
||||
<field name="state" widget="statusbar" statusbar_visible="new,offer_received,offer_accepted,sold,canceled" />
|
||||
</div>
|
||||
<div class="o_kanban_tags" style="margin-top: 5px;">
|
||||
<field name="tag_ids" />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</t>
|
||||
</templates>
|
||||
</kanban>
|
||||
</field>
|
||||
</record>
|
||||
</odoo>
|
||||
@@ -0,0 +1,12 @@
|
||||
<odoo>
|
||||
<record id="action_property_pdf_report" model="ir.actions.report">
|
||||
<field name="name">房产详情报告</field>
|
||||
<field name="model">estate.property</field>
|
||||
<field name="binding_model_id" ref="model_estate_property"/>
|
||||
<field name="report_type">qweb-pdf</field>
|
||||
<field name="report_name">estate.report_property_template</field>
|
||||
<field name="print_report_name">'房产报告-%s' % object.name</field>
|
||||
<field name="binding_model_id" ref="model_estate_property" />
|
||||
<field name="binding_type">report</field>
|
||||
</record>
|
||||
</odoo>
|
||||
@@ -0,0 +1,68 @@
|
||||
<odoo>
|
||||
<template id="report_property_template">
|
||||
<t t-call="web.html_container">
|
||||
<!-- 假设单记录报表 -->
|
||||
<t t-set="object" t-value="docs" />
|
||||
<t t-debug="pdb"/>
|
||||
<t t-call="web.external_layout">
|
||||
<div class="page">
|
||||
<!-- 头部信息 -->
|
||||
<div>
|
||||
<h2>房产基本信息</h2>
|
||||
<table class="table table-bordered">
|
||||
<tr>
|
||||
<td>名称:</td>
|
||||
<td>
|
||||
<span t-field="object.name" />
|
||||
</td>
|
||||
<td>状态:</td>
|
||||
<td>
|
||||
<span t-field="object.state" />
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>预期价格:</td>
|
||||
<td>
|
||||
<span t-field="object.expected_price"
|
||||
t-options='{"widget": "monetary", "display_currency": object.currency_id}' />
|
||||
</td>
|
||||
<td>最佳报价:</td>
|
||||
<td>
|
||||
<span t-field="object.best_price"
|
||||
t-options='{"widget": "monetary", "display_currency": object.currency_id}' />
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
</div>
|
||||
|
||||
<!-- 报价明细 -->
|
||||
<div t-if="object.offer_ids">
|
||||
<h3>报价记录</h3>
|
||||
<table class="table table-striped">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>买家</th>
|
||||
<th>报价金额</th>
|
||||
<th>创建日期</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr t-foreach="object.offer_ids" t-as="offer">
|
||||
<td>
|
||||
<span t-field="offer.partner_id.name" />
|
||||
</td>
|
||||
<td>
|
||||
<span t-field="offer.price" />
|
||||
</td>
|
||||
<td>
|
||||
<span t-field="offer.create_date" t-options='{"format": "yyyy-MM-dd"}' />
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
</t>
|
||||
</t>
|
||||
</template>
|
||||
</odoo>
|
||||
@@ -0,0 +1,14 @@
|
||||
<odoo>
|
||||
<record id="res_users_view_form_estate" model="ir.ui.view">
|
||||
<field name="name">res.users.view.form.estate</field>
|
||||
<field name="model">res.users</field>
|
||||
<field name="inherit_id" ref="base.view_users_form" />
|
||||
<field name="arch" type="xml">
|
||||
<page name="preferences" position="after">
|
||||
<page string="房地产信息" name="estate_property_info">
|
||||
<field name='property_ids' widget="one2many_list" />
|
||||
</page>
|
||||
</page>
|
||||
</field>
|
||||
</record>
|
||||
</odoo>
|
||||
Reference in New Issue
Block a user