Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

ahta - Server framework 101 #209

Open
wants to merge 21 commits into
base: 18.0
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from 15 commits
Commits
Show all changes
21 commits
Select commit Hold shift + click to select a range
a946f85
[IMP] estate: Finish Chapter 1: Architecture Overview, Chapter 2: A N…
ataha1 Dec 16, 2024
b926817
[IMP] estate: Finish Chapter 4: Security - A Brief Introduction
ataha1 Dec 16, 2024
b83d00c
[IMP] estate: Chapter 5: Finally, Some UI To Play With
ataha1 Dec 17, 2024
6a57c68
[IMP] estate: use linter to format files
ataha1 Dec 17, 2024
b33e023
[IMP] estate: add list and form views
ataha1 Dec 17, 2024
c043cf5
[IMP] estate: add search view
ataha1 Dec 18, 2024
627f7df
[IMP] estate: add the real estate property type table
ataha1 Dec 18, 2024
835ea34
[IMP] estate: Add the Real Estate Property Tag table.
ataha1 Dec 18, 2024
31110d2
[IMP] estate: Add the Real Estate Property Offer table.
ataha1 Dec 18, 2024
7f92485
[IMP] estate: Use Computed Fields And Onchanges
ataha1 Dec 19, 2024
a981754
[IMP] estate: Use Actions To Cancel and set a property as sold
ataha1 Dec 19, 2024
ebc6946
[IMP] estate: Resolve Warnings & Issues
ataha1 Dec 20, 2024
a7af1bb
[IMP] estate: Add Constraints
ataha1 Dec 20, 2024
b78a6d2
[IMP] estate: Add Inline View, Widget and List Order
ataha1 Dec 20, 2024
63f1ad1
[IMP] estate: Add The Sprinkles
ataha1 Dec 23, 2024
19a5ba9
[IMP] estate: Add Inheritance to Real Estate Module
ataha1 Dec 24, 2024
124e92e
[IMP] estate: Interact With Invoicing App
ataha1 Dec 26, 2024
3a20c9f
[IMP] estate: Add Kaban View
ataha1 Dec 26, 2024
956d380
[IMP] awesome_owl: Finish Chapter 1 Sections 1-5
ataha1 Dec 26, 2024
cce23d4
[IMP] awesome_owl: Finish Chapter 1 Sections 6-9
ataha1 Dec 27, 2024
01b39f1
[IMP] awesome_owl: Finish Chapter 1 Sections 10-11
ataha1 Dec 27, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -127,3 +127,6 @@ dmypy.json

# Pyre type checker
.pyre/

# vscode settings
.vscode/
3 changes: 3 additions & 0 deletions .vscode/settings.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
{
"cSpell.words": ["odoo"]
ataha1 marked this conversation as resolved.
Show resolved Hide resolved
}
1 change: 1 addition & 0 deletions estate/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
from . import models
18 changes: 18 additions & 0 deletions estate/__manifest__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
{
"name": "Real Estate",
"author": "Odoo",
"website": "https://www.odoo.com/page/realestate",
"version": "0.1",
"application": True,
"installable": True,
"depends": ["base"],
"data": [
"security/ir.model.access.csv",
"views/estate_property_type_views.xml",
"views/estate_property_tag_views.xml",
"views/estate_property_offer_views.xml",
"views/estate_property_views.xml",
"views/estate_menus.xml",
],
"license": "LGPL-3",
}
4 changes: 4 additions & 0 deletions estate/models/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
from . import estate_property
from . import estate_property_type
from . import estate_property_tag
from . import estate_property_offer
121 changes: 121 additions & 0 deletions estate/models/estate_property.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,121 @@
from odoo import api, fields, models
from odoo.exceptions import UserError
from odoo.tools.float_utils import float_is_zero, float_compare


class EstateProperty(models.Model):
_name = "estate.property"
_description = "Estate Property"
_order = "id desc"

name = fields.Char(required=True)
description = fields.Text()
postcode = fields.Char()
date_availability = fields.Date(
copy=False,
default=lambda x: fields.Datetime.add(fields.Datetime.today(), months=3),
)
expected_price = fields.Float(required=True)
selling_price = fields.Float(readonly=True, copy=False)
bedrooms = fields.Integer(default=2)
living_area = fields.Integer()
facades = fields.Integer()
garage = fields.Boolean()
garden = fields.Boolean()
garden_area = fields.Integer()
garden_orientation = fields.Selection(
string="Garden Orientation",
selection=[
("north", "North"),
("south", "South"),
("east", "East"),
("west", "West"),
],
)
active = fields.Boolean(default=True)
state = fields.Selection(
string="State",
selection=[
("new", "New"),
("offer received", "Offer Received"),
("offer accepted", "Offer Accepted"),
("sold", "Sold"),
("canceled", "Canceled"),
ataha1 marked this conversation as resolved.
Show resolved Hide resolved
],
default="new",
copy=False,
)
property_type_id = fields.Many2one("estate.property.type", string="Property Type")
buyer = fields.Many2one("res.partner", string="Buyer", copy=False)
salesperson = fields.Many2one(
"res.users", string="Salesperson", default=lambda self: self.env.user
)
tags_ids = fields.Many2many("estate.property.tag", string="Tags")
offer_ids = fields.One2many("estate.property.offer", "property_id", string="Offers")
total_area = fields.Float(compute="_compute_total_area")
best_price = fields.Float(compute="_compute_best_price")

_sql_constraints = [
(
"check_positive_expected_price",
"CHECK(expected_price>0)",
"The expected price should be positive.",
),
(
"check_positive_selling_price",
"CHECK(selling_price>0)",
"The selling price should be positive.",
),
]

@api.depends("garden_area", "living_area")
def _compute_total_area(self):
for record in self:
record.total_area = record.garden_area + record.living_area

@api.depends("offer_ids.price")
def _compute_best_price(self):
for record in self:
prices = record.offer_ids.mapped("price")
record.best_price = max(prices, default=0)

@api.onchange("garden")
def _onchange_garden(self):
if self.garden:
self.garden_area = 10
self.garden_orientation = "north"
else:
self.garden_area = 0
self.garden_orientation = False

def action_sold(self):
for record in self:
ataha1 marked this conversation as resolved.
Show resolved Hide resolved
if record.state != "canceled":
record.state = "sold"
else:
raise UserError("Canceled properties can't be sold")
return True

def action_cancel(self):
for record in self:
if record.state != "sold":
record.state = "canceled"
else:
raise UserError("Sold properties can't be sold")
return True

@api.constrains("selling_price", "expected_price")
def _check_selling(self):
for record in self:
if (
not float_is_zero(record.selling_price, precision_digits=3)
and float_compare(
record.selling_price,
record.expected_price * 0.9,
precision_digits=3,
)
< 0
):
raise UserError(
"The selling price cannot be lower than 90% of the expected price"
)
60 changes: 60 additions & 0 deletions estate/models/estate_property_offer.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
from odoo import api, fields, models


class EstatePropertyOffer(models.Model):
_name = "estate.property.offer"
_description = "Property Offer"
_order = "price asc"

price = fields.Float()
status = fields.Selection(
string="Type",
selection=[("accepted", "Accepted"), ("refused", "Refused")],
copy=False,
)
partner_id = fields.Many2one("res.partner", required=True)
property_id = fields.Many2one("estate.property", required=True)
validity = fields.Integer(default=7)
date_deadline = fields.Date(
compute="_compute_date_deadline", inverse="_inverse_date_deadline"
)
property_type_id = fields.Many2one(
"estate.property.type",
store=True,
string="Property Type",
related="property_id.property_type_id",
)

_sql_constraints = [
(
"check_positive_price",
"CHECK(price>0)",
"The offer price should be positive.",
),
]

@api.depends("validity", "create_date")
def _compute_date_deadline(self):
for record in self:
if not record.create_date:
record.create_date = fields.Datetime.today()
ataha1 marked this conversation as resolved.
Show resolved Hide resolved
record.date_deadline = fields.Datetime.add(
record.create_date, days=record.validity
)

def _inverse_date_deadline(self):
for record in self:
record.validity = (record.date_deadline - record.create_date.date()).days

def action_accept(self):
self.ensure_one()
self.status = "accepted"
self.property_id.selling_price = self.price
self.property_id.buyer = self.partner_id
self.property_id.state = "offer accepted"
return True

def action_refuse(self):
for record in self:
ataha1 marked this conversation as resolved.
Show resolved Hide resolved
record.status = "refused"
return True
13 changes: 13 additions & 0 deletions estate/models/estate_property_tag.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
from odoo import fields, models


class EstatePropertyTag(models.Model):
_name = "estate.property.tag"
_description = "Property Tags"
_order = "name"

name = fields.Char()
color = fields.Integer()
_sql_constraints = [
("unique_name", "UNIQUE(name)", "The property tag must be unique.")
]
25 changes: 25 additions & 0 deletions estate/models/estate_property_type.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
from odoo import fields, models, api


class EstatePropertyType(models.Model):
_name = "estate.property.type"
_description = "Estate Property Type"
_order = "sequence, name"

name = fields.Char(required=True)
property_ids = fields.One2many(
"estate.property", "property_type_id", string="Property"
)
sequence = fields.Integer("Sequence")
offer_ids = fields.One2many(
"estate.property.offer", inverse_name="property_type_id"
)
offer_count = fields.Integer(compute="_compute_offer_count")
_sql_constraints = [
("unique_name", "UNIQUE(name)", "The property type must be unique.")
]

@api.depends("offer_ids")
def _compute_offer_count(self):
for record in self:
record.offer_count = len(record.offer_ids)
5 changes: 5 additions & 0 deletions estate/security/ir.model.access.csv
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
id,name,model_id/id,group_id/id,perm_read,perm_write,perm_create,perm_unlink
access_estate_property,access_estate_property,model_estate_property,base.group_user,1,1,1,1
access_estate_property_type,access_estate_property_type,model_estate_property_type,base.group_user,1,1,1,1
access_estate_property_tag,access_estate_property_tag,model_estate_property_tag,base.group_user,1,1,1,1
access_estate_property_offer,access_estate_property_offer,model_estate_property_offer,base.group_user,1,1,1,1
12 changes: 12 additions & 0 deletions estate/views/estate_menus.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
<?xml version="1.0"?>
<odoo>
<menuitem id="estate_menu_root" name="Real Estate">
<menuitem id="estate_first_level_menu" name="Advertisement">
<menuitem id="estate_property_menu_action" action="estate_property_action"/>
</menuitem>
<menuitem id="estate_second_level_menu" name="Settings">
<menuitem id="estate_property_type_menu_action" action="estate_property_type_action"/>
<menuitem id="estate_property_tag_menu_action" action="estate_property_tag_action"/>
</menuitem>
</menuitem>
</odoo>
42 changes: 42 additions & 0 deletions estate/views/estate_property_offer_views.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
<?xml version="1.0"?>
<odoo>
<record id="estate_property_offer_action" model="ir.actions.act_window">
<field name="name">Property Offers</field>
<field name="res_model">estate.property.offer</field>
<field name="view_mode">list,form</field>
<field name="domain">[('property_type_id', '=', active_id)]</field>
</record>

<record id="estate_property_offer_list" model="ir.ui.view">
<field name="name">estate.property.offer.list</field>
<field name="model">estate.property.offer</field>
<field name="arch" type="xml">
<list editable="bottom" decoration-success="status=='accepted'" decoration-danger="status=='refused'">
<field name="price" string="Price"/>
<field name="partner_id" string="Partner"/>
<field name="validity" string="Validity"/>
<field name="date_deadline" string="Deadline"/>
<button name="action_accept" type="object" icon="fa-check" invisible="status" title="Accept"/>
<button name="action_refuse" type="object" icon="fa-times" invisible="status" title="Refuse"/>
</list>
</field>
</record>

<record id="estate_property_offer_form" model="ir.ui.view">
<field name="name">estate.property.offer.form</field>
<field name="model">estate.property.offer</field>
<field name="arch" type="xml">
<form>
<sheet>
<group>
<field name="price" width="40px" string="Price"/>
<field name="partner_id" width="40px" string="Partner"/>
<field name="validity" width="40px" string="Validity"/>
<field name="date_deadline" width="40px" string="Deadline"/>
<field name="status" width="40px" string="Status"/>
</group>
</sheet>
</form>
</field>
</record>
</odoo>
32 changes: 32 additions & 0 deletions estate/views/estate_property_tag_views.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
<?xml version="1.0"?>
<odoo>
<record id="estate_property_tag_action" model="ir.actions.act_window">
<field name="name">Tags</field>
<field name="res_model">estate.property.tag</field>
<field name="view_mode">list,form</field>
</record>

<record id="estate_property_tag_list" model="ir.ui.view">
<field name="name">estate.property.tag.list</field>
<field name="model">estate.property.tag</field>
<field name="arch" type="xml">
<list editable="bottom">
<field name="name" string="Title"/>
</list>
</field>
</record>

<record id="estate_property_tag_form" model="ir.ui.view">
<field name="name">estate.property.form</field>
<field name="model">estate.property.tag</field>
<field name="arch" type="xml">
<form>
<sheet>
<group>
<field name="name" placeholder="e.g. cozy" string="Name"/>
</group>
</sheet>
</form>
</field>
</record>
</odoo>
Loading