Skip to content

[ADD] estate: implement core real estate property management module #758

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

Draft
wants to merge 24 commits into
base: 18.0
Choose a base branch
from

Conversation

shib-odoo
Copy link

Core Functionality:

  • Created base models for property listings with fields for pricing, descriptions , and status tracking
  • Implemented property type categorization (House, Apartment etc.)
  • Added tagging system for property characteristics (Cozy, Renovated etc.)
  • Developed offer management system to track buyer proposals

Technical Implementation:

  1. Models:

    • estate.property (main listing model)
    • estate.property.type (categorization)
    • estate.property.tag (characteristics)
    • estate.property.offer (purchase offers)
  2. Relationships:

    • Many2one: Property → Type, Buyer, Salesperson
    • Many2many: Property ↔ Tags
    • One2many: Property → Offers
  3. Security:

    • Configured access rights for all models
    • Set appropriate permissions for CRUD operations
  4. UI/UX:

    • Custom list view with key property attributes
    • Detailed form view with tabbed interface
    • Advanced search with filters and grouping
    • Intuitive menu structure
  5. Business Logic:

    • Default values (salesperson = current user)
    • Field constraints (read-only selling price)
    • Non-copyable fields (availability date)

task-001 (Chapter 1–7 Odoo 18 Developer Tutorial)

Core Functionality:
- Created base models for property listings with fields for pricing, descriptions
 , and status tracking
- Implemented property type categorization (House, Apartment etc.)
- Added tagging system for property characteristics (Cozy, Renovated etc.)
- Developed offer management system to track buyer proposals

Technical Implementation:
1. Models:
   - estate.property (main listing model)
   - estate.property.type (categorization)
   - estate.property.tag (characteristics)
   - estate.property.offer (purchase offers)

2. Relationships:
   - Many2one: Property → Type, Buyer, Salesperson
   - Many2many: Property ↔ Tags
   - One2many: Property → Offers

3. Security:
   - Configured access rights for all models
   - Set appropriate permissions for CRUD operations

4. UI/UX:
   - Custom list view with key property attributes
   - Detailed form view with tabbed interface
   - Advanced search with filters and grouping
   - Intuitive menu structure

5. Business Logic:
   - Default values (salesperson = current user)
   - Field constraints (read-only selling price)
   - Non-copyable fields (availability date)

task-001 (Chapter 1–7 Odoo 18 Developer Tutorial)
@robodoo
Copy link

robodoo commented May 7, 2025

Pull request status dashboard

shib-odoo added 2 commits May 8, 2025 18:31
…arameter

SQL Constraints:

- Add CHECK(expected_price > 0) to ensure strictly positive expected prices.

- Add CHECK(selling_price >= 0) for non-negative selling prices.

-  Ensure price > 0 for offers via SQL constraint.

- Enforce unique names for property tags and types with UNIQUE(name).

Python Constraint:

- Add @api.constrains to validate selling price ≥ 90% of expected price.

Fixes:

- Correct typo in selling_price field parameter (readOnly → readonly).

- Cleanup existing invalid data (e.g., non-positive prices) to apply constraints.
- Fix: Replace active_id with id in estate.property.type stat button context to
   resolve access rights error.

- Add: Inline list view for properties linked to types in estate.property.type
   form.

- Add: Stat button on property type form to view linked offers.
Copy link

@adsh-odoo adsh-odoo left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hello @shib-odoo
Some comments/questions
Thanks!!

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Missing blank line EOF needs to check other occurrences also

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Added blank line EOF in all the respective files.

'security/ir.model.access.csv',
'views/estate_property_views.xml',
'views/estate_menus.xml',

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change

Comment on lines 1 to 5
from odoo import api,models, fields
from dateutil.relativedelta import relativedelta
from odoo.exceptions import UserError
from odoo.exceptions import ValidationError
from odoo.tools import float_compare, float_is_zero

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
from odoo import api,models, fields
from dateutil.relativedelta import relativedelta
from odoo.exceptions import UserError
from odoo.exceptions import ValidationError
from odoo.tools import float_compare, float_is_zero
from dateutil.relativedelta import relativedelta
from odoo import api, fields, models
from odoo.exceptions import UserError, ValidationError
from odoo.tools import float_compare, float_is_zero

Generally we follow a convention where we imports the external libraries first and then we make the imports from odoo.
https://www.odoo.com/documentation/18.0/contributing/development/coding_guidelines.html#imports

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fix the imports where all the external libraries are on the top followed by the imports from odoo in all the files.

Comment on lines 33 to 78
name = fields.Char(required=True) # Required field
description = fields.Text()
postcode = fields.Char()
date_availability = fields.Date( default=lambda self: fields.Date.today() + relativedelta(months=3),copy=False)
expected_price = fields.Float(required=True) # Required field
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(
selection=[
('North', 'North'),
('South', 'South'),
('East', 'East'),
('West', 'West'),
],
)
active = fields.Boolean(default=True),
state = fields.Selection(
selection=[
('new', 'New'),
('offer_received', 'Offer Received'),
('offer_accepted', 'Offer Accepted'),
('sold', 'Sold'),
('cancelled', 'Cancelled'),
],
required=True,
default='new',
copy=False
)
property_type_id = fields.Many2one("estate.property.type", string="Property Type")
offer_ids = fields.One2many("estate.property.offer", "property_id", string="Offers")
buyer_id = fields.Many2one("res.partner", string="Buyer", copy=False)
salesperson_id = fields.Many2one(
"res.users",
string="Salesperson",
default=lambda self: self.env.user, # Default to current user
)
tag_ids = fields.Many2many("estate.property.tag", string="Tags")

# Add to EstateProperty class
total_area = fields.Float(compute="_compute_total_area", string="Total Area")
best_price = fields.Float(compute="_compute_best_price", string="Best Offer")

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Field definition should be above followed by the business logic as per our coding guidelines. it may change in certain cases for special methods. Hint: refer codebase

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Change file structure by moving field definitions on top then compute methods and last is the business logic.

from odoo.exceptions import UserError
from odoo.exceptions import ValidationError
from odoo.tools import float_compare, float_is_zero
class EstateProperty(models.Model):

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Need 2 blank lines before defining the class.



def action_cancel(self):
for prop in self:

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Generally, we write `` `for record self``` because here self refers to a recordset.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

image
Change naming convention that improves clarity and readability in codebase by changing "for prop in self" to "for record in self"

_sql_constraints = [
('check_offer_price_positive', 'CHECK(price > 0)', 'The offer price must be strictly positive.'),
]
@api.depends('create_date', 'validity')
Copy link

@adsh-odoo adsh-odoo May 12, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We can remove create_date from depends as it will never change after record creation, and at the time of record default value for validity is there, so it will serve our purpose. Can you give it a try?

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

image
After updating the dependency from 'create_date' to 'validity', the recomputation still works correctly. I’ve verified this through UI testing.

Comment on lines 6 to 8
_sql_constraints = [
("unique_name", "UNIQUE(name)", "Property type name must be unique."),
]

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It should be after the field definition.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Move ' _sql_constraints' after all field definitions in all the model files

Comment on lines 13 to 16
<list string="Estate offer list"
editable="top"
decoration-danger="status == 'refused'"
decoration-success="status == 'accepted'">

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
<list string="Estate offer list"
editable="top"
decoration-danger="status == 'refused'"
decoration-success="status == 'accepted'">
<list string="Estate offer list"
editable="top"
decoration-danger="status == 'refused'"
decoration-success="status == 'accepted'">

Need to check the indenation in other places also.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed indentation issue in all the xml files.

<group>
<field name="name"/>
<field name="tag_ids"
widget="many2many_tags"

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

indentation issue.

shib-odoo added 21 commits May 12, 2025 15:50
…ency

- Replaced `for prop in self` with `for record in self` across methods for
  clarity, since `self` is a recordset.
- Moved `_sql_constraints` definitions below field declarations in all models,
  per community conventions.
- Removed `create_date` from @api.depends in `_compute_date_deadline` since
   it doesn't change after record creation.
- Fixed indentation issues in all the xml files for better readability of codebase.
- Fixed the suggested whitespace and newline style issues in all the files
   suggested by Github checklist
- Fix minor issues related to github style checklist
- Fix minor issues related to github style checklist
…with offers

- Added two demo properties: 'Big Villa' and 'Trailer home', including full
  field population.
- Created associated property offers for these demo properties using existing
    partners.
- Demonstrated inline offer creation using Command.create for a new property.
- Ensured property type is set to 'Residential' for all demo properties.
- Set offer statuses explicitly (accepted/refused) for demo evaluation.
- Created controllers with /properties and /properties/<int:property_id> routes
   to render the properties
-  Added wizard  in my module making it compatible with selecting one or more
   properties at a time and making an offer on it.
- Build report pdf to download the report of the offers for perticular property
\
This commit introduces several improvements across the Card and TodoList
components to enhance reusability, interactivity, and UI consistency in the
`awesome_owl` module.

1. Card Component:
- Replaced static content with support for slots to allow arbitrary child
  components.
- Added a toggle feature to show/hide card content using internal state.
- Enhanced styling for better visual clarity, spacing, and Bootstrap integration.

2. Playground Template:
- Updated the playground template to insert multiple cards with dynamic content,
  including embedded Counter components and summary sections.
- Improved layout using Bootstrap utilities (`gap`, `d-flex`, column grids).
- Replaced placeholder text with meaningful titles and added alert components
  for feedback.

3. TodoList Component:
- Improved the styling of todo items using Bootstrap badges, spacing, muted text,
  and contextual coloring.
- Refactored `removeTodoItem()` to use splice instead of replacing the array
 (which Owl does not track reactively), ensuring UI updates correctly.
- Applied visual polish to headers, spacing, and input fields for a more
  polished experience.

These changes are part of a broader effort to demonstrate advanced Owl component
patterns, including slots, event communication, component state, and styling
integration with Bootstrap.
This commit introduces several improvements across the Card and TodoList
components to enhance reusability, interactivity, and UI consistency in the
`awesome_owl` module.

1. Card Component:
- Replaced static content with support for slots to allow arbitrary child
  components.
- Added a toggle feature to show/hide card content using internal state.
- Enhanced styling for better visual clarity, spacing, and Bootstrap integration.

2. Playground Template:
- Updated the playground template to insert multiple cards with dynamic content,
  including embedded Counter components and summary sections.
- Improved layout using Bootstrap utilities (`gap`, `d-flex`, column grids).
- Replaced placeholder text with meaningful titles and added alert components
  for feedback.

3. TodoList Component:
- Improved the styling of todo items using Bootstrap badges, spacing, muted text,
  and contextual coloring.
- Refactored `removeTodoItem()` to use splice instead of replacing the array
 (which Owl does not track reactively), ensuring UI updates correctly.
- Applied visual polish to headers, spacing, and input fields for a more
  polished experience.

These changes are part of a broader effort to demonstrate advanced Owl component
patterns, including slots, event communication, component state, and styling
integration with Bootstrap.
This commit introduces several improvements across the Card and TodoList
components to enhance reusability, interactivity, and UI consistency in the
`awesome_owl` module.

1. Card Component:
- Replaced static content with support for slots to allow arbitrary child
  components.
- Added a toggle feature to show/hide card content using internal state.
- Enhanced styling for better visual clarity, spacing, and Bootstrap integration.

2. Playground Template:
- Updated the playground template to insert multiple cards with dynamic content,
  including embedded Counter components and summary sections.
- Improved layout using Bootstrap utilities (`gap`, `d-flex`, column grids).
- Replaced placeholder text with meaningful titles and added alert components
  for feedback.

3. TodoList Component:
- Improved the styling of todo items using Bootstrap badges, spacing, muted text,
  and contextual coloring.
- Refactored `removeTodoItem()` to use splice instead of replacing the array
 (which Owl does not track reactively), ensuring UI updates correctly.
- Applied visual polish to headers, spacing, and input fields for a more
  polished experience.

These changes are part of a broader effort to demonstrate advanced Owl component
patterns, including slots, event communication, component state, and styling
integration with Bootstrap.
- Switched from @api.model to @api.model_create_multi to support batch creation.
- Added guard clauses for empty input and missing property_id.
- Improved error handling using UserError and ValidationError where appropriate.
- Validated offer price against current best price using property.best_price.
- Prevented offers on sold, cancelled, or offer-accepted properties.
- Ensured property state is updated to 'offer_received' only when needed.

This improves code readability, consistency with Odoo standards, and prevents
invalid offer creation scenarios.
Added explicit validation in `estate.property.offer.create` to raise
ValidationError when an offer price is zero or negative. This ensures
consistency with the SQL constraint and aligns with unit test
expectations.

Previously, invalid prices could trigger unrelated UserError messages
(e.g., price not exceeding best price), causing tests to fail. Now,
the error is caught early and clearly reported.
Added explicit validation in `estate.property.offer.create` to raise
ValidationError when an offer price is zero or negative. This ensures
consistency with the SQL constraint and aligns with unit test
expectations.

Previously, invalid prices could trigger unrelated UserError messages
(e.g., price not exceeding best price), causing tests to fail. Now,
the error is caught early and clearly reported.
Added explicit validation in `estate.property.offer.create` to raise
ValidationError when an offer price is zero or negative. This ensures
consistency with the SQL constraint and aligns with unit test
expectations.

Previously, invalid prices could trigger unrelated UserError messages
(e.g., price not exceeding best price), causing tests to fail. Now,
the error is caught early and clearly reported.
Added explicit validation in `estate.property.offer.create` to raise
ValidationError when an offer price is zero or negative. This ensures
consistency with the SQL constraint and aligns with unit test
expectations.

Previously, invalid prices could trigger unrelated UserError messages
(e.g., price not exceeding best price), causing tests to fail. Now,
the error is caught early and clearly reported.
This commit introduces a user-configurable settings dialog to the dashboard,
allowing users to selectively show or hide dashboard items. Preferences are
persisted using local storage for improved user experience.

The dialog is built using OWL's Dialog component and includes improved close
handling by passing an explicit `close` function via props, solving previous
issues with `this.env.services.dialog.close`.

Styling of the dashboard and individual items has been significantly enhanced:
- Cards now feature consistent padding, elevation on hover, and responsive layout.
- The `DashboardItem` component has flexible sizing and a modern hover animation.
- `NumberCard` and other widgets have improved font scaling and alignment.

The overall dashboard layout is now more responsive, adapting to smaller screen
sizes with proper wrapping and spacing of cards.

Also includes:
- Button controls to open settings and navigate to leads/customers.
- Proper use of `useState` to track selected items in the dialog.
- Improved use of scoped props and default slot composition for better
   maintainability.

This change improves both the usability and aesthetic quality of the dashboard,
bringing it closer to a production-grade interface.

task-101 (related to improving dashboard customizability and UX)
Fixes odoo#42 (dialog not closing on apply)
This commit introduces a user-configurable settings dialog to the dashboard,
allowing users to selectively show or hide dashboard items. Preferences are
persisted using local storage for improved user experience.

The dialog is built using OWL's Dialog component and includes improved close
handling by passing an explicit `close` function via props, solving previous
issues with `this.env.services.dialog.close`.

Styling of the dashboard and individual items has been significantly enhanced:
- Cards now feature consistent padding, elevation on hover, and responsive layout.
- The `DashboardItem` component has flexible sizing and a modern hover animation.
- `NumberCard` and other widgets have improved font scaling and alignment.

The overall dashboard layout is now more responsive, adapting to smaller screen
sizes with proper wrapping and spacing of cards.

Also includes:
- Button controls to open settings and navigate to leads/customers.
- Proper use of `useState` to track selected items in the dialog.
- Improved use of scoped props and default slot composition for better
   maintainability.

This change improves both the usability and aesthetic quality of the dashboard,
bringing it closer to a production-grade interface.

task-101 (related to improving dashboard customizability and UX)
Fixes odoo#42 (dialog not closing on apply)
…nvoice line

This commit adds a computed 'Book Price' field on both sale order lines
and customer invoice lines, allowing users to compare the price from the
pricelist with any manually adjusted price.

- Added a computed `book_price` field to `sale.order.line`, calculated
  based on the selected pricelist, product, quantity, and UoM.

- Added a computed `book_price` field to `account.move.line` (invoice
  lines), fetched from the related sale order's pricelist.

- Updated the sale order form view to display 'Book Price' on order lines.

- Updated the customer invoice form view to display 'Book Price' on
  invoice lines, using `column_invisible` to restrict visibility to
  customer invoices only (`move_type == 'out_invoice'`).

These changes help users analyze pricing transparency and identify
discounts or overrides from the original pricelist.
-fixed styling issue of adding new line at the end of the file
The goal is to ensure that the book price:
- Is correctly calculated based on the pricelist's discount formula.
- Falls back to the list price if no pricelist is applied.
- Is propagated from sale orders to customer invoice lines.
- Is still computed but hidden in the UI for non-customer invoices.

The tests cover sale order line creation with and without a pricelist,
sale order confirmation and invoice generation, and behavior in refund
scenarios.

All tests are based on `TransactionCase` and are suitable for `post_install`
execution. They help ensure the reliability of the custom `book_price`
logic implemented in sale and account modules.
Fix the styling issue in the model files
Fix the styling issue in the model files
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants