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

[ADD] estate: Create an estate app #332

Draft
wants to merge 31 commits into
base: 18.0
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
31 commits
Select commit Hold shift + click to select a range
cf94eef
[ADD] estate: Create an estate app
maap-odoo Feb 6, 2025
ce1fc85
[IMP] estate: Implement basic views and filter for property management
maap-odoo Feb 9, 2025
51039c0
[IMP] estate: added property types, tags, offers, and relations in es…
maap-odoo Feb 10, 2025
5e071b7
[IMP] estate: added property types, tags, offers, and relations in es…
maap-odoo Feb 10, 2025
49e123c
[IMP] estate: Implement action buttons for property and offer manage…
maap-odoo Feb 10, 2025
2de60b5
[FIX] estate: UI Changes and enforce sale conditions
maap-odoo Feb 11, 2025
32be757
[IMP] estate: Enforce SQL constraints and Python constraint
maap-odoo Feb 11, 2025
ab5c9ca
[IMP] estate: Improve UI, ordering, filters, and constraints
maap-odoo Feb 12, 2025
e22b923
[FIX] estate: Change the sequence of XML file in the manifest file
maap-odoo Feb 12, 2025
bb1056f
[IMP] estate: Improve CRUD operations & UI via Inheritance
maap-odoo Feb 13, 2025
b5c78a0
[ADD] estate_account: Automatic Invoice Creation on Property Sale
maap-odoo Feb 13, 2025
3015561
[IMP] estate: Added Kanban View for Properties
maap-odoo Feb 13, 2025
b5614a5
[IMP] estate: Refactored module for better consistency and best prac…
maap-odoo Feb 13, 2025
c4cb6c8
[IMP] estate: implement access control rules, add demo data
maap-odoo Feb 16, 2025
71281b1
[IMP] estate: add chatter to the Property and refactored the import …
maap-odoo Feb 17, 2025
f793251
[FIX] estate: change the main template of Kanban view
maap-odoo Feb 17, 2025
3743fab
[FIX] estate: Ensure AccessError is raised before creating an invoice
maap-odoo Feb 17, 2025
75020d0
[IMP] estate: Implement estate property website
maap-odoo Feb 17, 2025
2753d33
[IMP] estate: implement PDF reports with templates and inheritance
maap-odoo Feb 18, 2025
9959bf5
[FIX] estate: Restrict salesperson field edit & improve error transl…
maap-odoo Feb 18, 2025
06d7ce7
[IMP] estate: implement property offer validations & test coverage
maap-odoo Feb 18, 2025
d22ef93
[IMP] estate: implement multi-property offer wizard
maap-odoo Feb 19, 2025
7a3c500
[IMP] awesome_owl: Implement Counter and reusable Card component
maap-odoo Feb 19, 2025
1ad07f9
[IMP] awesome_owl: Support HTML content in Card component
maap-odoo Feb 19, 2025
483b3d9
[FIX] estate: Add placeholder images and fix property wizard access …
maap-odoo Feb 20, 2025
645b2bc
[IMP] awesome_owl: Add props validation & parent-child communication
maap-odoo Feb 20, 2025
a2cefbf
[IMP] awesome_owl: Implement basic Todo List component
maap-odoo Feb 20, 2025
a2366ae
[IMP] awesome_owl: Add dynamic attributes for completed todos in Tod…
maap-odoo Feb 21, 2025
8f896f5
[IMP] awesome_owl: Enable adding new todos dynamically in TodoList
maap-odoo Feb 21, 2025
64134fc
[IMP] awesome_owl: Add input auto-focus using t-ref and useRef
maap-odoo Feb 21, 2025
f69b92c
[IMP] awesome_owl: Add input auto-focus and todo toggle functionality
maap-odoo Feb 21, 2025
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
9 changes: 9 additions & 0 deletions awesome_owl/static/src/card/card.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
import { Component } from "@odoo/owl";

export class Card extends Component {
static template = "awesome_owl.Card";
static props = {
title: { type: String },
content: { type: String },
};
}
15 changes: 15 additions & 0 deletions awesome_owl/static/src/card/card.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
<?xml version="1.0" encoding="UTF-8" ?>
<templates xml:space="preserve">
<t t-name="awesome_owl.Card">
<div class="card d-inline-block shadow-sm m-2 border-1" style="width: 18rem;">
<div class="card-body">
<h5 class="card-title">
<t t-out="props.title"/>
</h5>
<p class="card-text text-muted">
<t t-out="props.content"/>
</p>
</div>
</div>
</t>
</templates>
21 changes: 21 additions & 0 deletions awesome_owl/static/src/counter/counter.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
import { Component, useState } from "@odoo/owl";

export class Counter extends Component {
static template = "awesome_owl.Counter";

static props = {
onChange: { type: Function, optional: true }
};

setup() {
this.state = useState({ value: 0 });
}

increment() {
this.state.value++;

if (this.props.onChange) {
this.props.onChange();
}
}
}
15 changes: 15 additions & 0 deletions awesome_owl/static/src/counter/counter.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
<?xml version="1.0" encoding="UTF-8" ?>
<templates xml:space="preserve">
<t t-name="awesome_owl.Counter">
<div class="card d-inline-block m-2 shadow-sm border-1" style="width: 18rem;">
<div class="card-body text-center">
<p class="card-text">Counter: <t t-esc="state.value"/></p>
<button class="btn btn-dark mt-2"
style="background-color: rgb(30, 248, 255);"
t-on-click="increment">
Increment
</button>
</div>
</div>
</t>
</templates>
21 changes: 19 additions & 2 deletions awesome_owl/static/src/playground.js
Original file line number Diff line number Diff line change
@@ -1,7 +1,24 @@
/** @odoo-module **/

import { Component } from "@odoo/owl";
import { Component, markup, useState } from "@odoo/owl";
import { Counter } from "./counter/counter";
import { Card } from "./card/card";
import { TodoList } from "./todo_list/todo_list";

export class Playground extends Component {
static template = "awesome_owl.playground";
static template = "awesome_owl.Playground";
static components = { Counter, Card, TodoList };

setup() {
this.cards = [
{ title: "Safe String", content: "This is a normal string" },
{ title: "Styled HTML", content: markup("<b style='color: red;'>This is bold and red</b>") },
{ title: "List Example", content: markup("<ul><li>Item 1</li><li>Item 2</li></ul>") },
];
this.sum = useState({ value: 0 });
}

incrementSum() {
this.sum.value++;
}
}
24 changes: 20 additions & 4 deletions awesome_owl/static/src/playground.xml
Original file line number Diff line number Diff line change
@@ -1,10 +1,26 @@
<?xml version="1.0" encoding="UTF-8" ?>
<templates xml:space="preserve">
<t t-name="awesome_owl.Playground">
<div class="container my-4">
<h1 class="text-center text-primary">Welcome to the Playground</h1>
<div class="m-2 d-flex flex-row gap-3">
<div class="card p-3 shadow-sm">
<Counter onChange.bind="incrementSum"/>
<Counter onChange.bind="incrementSum"/>
<div class="text-center mt-2">
<strong>The sum is:</strong> <t t-esc="sum.value"/>
</div>
</div>

<t t-name="awesome_owl.playground">
<div class="p-3">
hello world
<div class="d-flex flex-wrap gap-2">
<t t-foreach="cards" t-as="card" t-key="card.title">
<Card title="card.title" content="card.content"/>
</t>
</div>
</div>
<div class="mt-4">
<TodoList/>
</div>
</div>
</t>

</templates>
26 changes: 26 additions & 0 deletions awesome_owl/static/src/todo_list/todo_item.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
import { Component } from "@odoo/owl";

export class TodoItem extends Component {
static template = "awesome_owl.TodoItem";

static props = {
todo: {
type: Object,
shape: {
id: Number,
description: String,
isCompleted: Boolean,
},
},
toggleState: Function,
removeTodo: Function,
};

onChange() {
this.props.toggleState(this.props.todo.id);
}

onRemove() {
this.props.removeTodo(this.props.todo.id);
}
}
15 changes: 15 additions & 0 deletions awesome_owl/static/src/todo_list/todo_item.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
<?xml version="1.0" encoding="UTF-8" ?>
<templates xml:space="preserve">
<t t-name="awesome_owl.TodoItem">
<div class="d-flex align-items-center p-2 border rounded bg-light shadow-sm">
<div class="form-check">
<input class="form-check-input" type="checkbox" t-att-id="props.todo.id" t-att-checked="props.todo.isCompleted" t-on-change="onChange"/>
<label t-att-for="props.todo.id" t-att-class="props.todo.isCompleted ? 'text-decoration-line-through text-muted' : '' ">
<t t-esc="props.todo.id"/>.
<t t-esc="props.todo.description"/>
</label>
<span role="button" class="fa fa-trash ms-3 text-danger" t-on-click="onRemove"/>
</div>
</div>
</t>
</templates>
39 changes: 39 additions & 0 deletions awesome_owl/static/src/todo_list/todo_list.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
import { Component, useState } from "@odoo/owl";
import { TodoItem } from "./todo_item";
import { useAutofocus } from "../utils";

export class TodoList extends Component {
static template = "awesome_owl.TodoList";
static components = { TodoItem };

setup() {
this.nextId = 1;
this.todos = useState([]);
useAutofocus("input")
}

addTodo(ev) {
if (ev.keyCode === 13 && ev.target.value != "") {
this.todos.push({
id: this.nextId++,
description: ev.target.value,
isCompleted: false
});
ev.target.value = "";
}
}

toggleTodo(todoId) {
const todo = this.todos.find((todo) => todo.id === todoId);
if (todo) {
todo.isCompleted = !todo.isCompleted;
}
}

removeTodo(todoId) {
const todoIndex = this.todos.findIndex((todo) => todo.id === todoId);
if (todoIndex >= 0) {
this.todos.splice(todoIndex, 1);
}
}
}
18 changes: 18 additions & 0 deletions awesome_owl/static/src/todo_list/todo_list.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
<?xml version="1.0" encoding="UTF-8" ?>
<templates xml:space="preserve">
<t t-name="awesome_owl.TodoList">
<div class="card shadow-sm border-1 p-3 m-2 bg-light">
<div class="card-body">
<h5 class="card-title text-primary">Todo List</h5>
<div class="list-group">
<input class="form-control mb-3" type="text" placeholder="Add a todo" t-on-keyup="addTodo" t-ref="input"/>
<t t-foreach="todos" t-as="todo" t-key="todo.id">
<div class="list-group-item border-0">
<TodoItem todo="todo" toggleState.bind="toggleTodo" removeTodo.bind="removeTodo"/>
</div>
</t>
</div>
</div>
</div>
</t>
</templates>
8 changes: 8 additions & 0 deletions awesome_owl/static/src/utils.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
import { useRef, onMounted } from "@odoo/owl";

export function useAutofocus(refName) {
const ref = useRef(refName);
onMounted(() => {
ref.el.focus();
});
}
2 changes: 2 additions & 0 deletions estate/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
from . import controllers
from . import models
39 changes: 39 additions & 0 deletions estate/__manifest__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
{
'name': 'Real Estate',
'version': '1.0',
'category': 'Real Estate',
'summary': 'A real estate management app for listing, selling, and tracking properties.',
'author': 'Maan Patel',
'depends': ['base', 'mail', 'website'],
'data' : [
'security/estate_security.xml',
'security/ir.model.access.csv',
'views/estate_property_wizard_views.xml',
'views/estate_property_views.xml',
'views/estate_property_offer_views.xml',
'views/estate_property_type_views.xml',
'views/estate_property_tag_views.xml',
'views/res_users_views.xml',
'views/estate_menus.xml',
'views/estate_properties_template.xml',
'views/estate_properties_details.xml',
'report/estate_property_templates.xml',
'report/estate_property_reports.xml',
'report/estate_property_salesman_report.xml',
],
"demo": [
"demo/estate_property_type_demo.xml",
"demo/estate_property_tag_demo.xml",
"demo/estate_property_demo.xml",
"demo/estate_property_offer_demo.xml",
],
'assets': {
'web.assets_backend': [
'estate/static/description/icon.png',
],
},
'images': ['static/description/icon.png'],
'installable': True,
'application': True,
'license': 'LGPL-3'
}
1 change: 1 addition & 0 deletions estate/controllers/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
from . import property
29 changes: 29 additions & 0 deletions estate/controllers/property.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
from odoo import http
from odoo.http import request


class PropertyWebsite(http.Controller):

@http.route(['/properties', '/properties/page/<int:page>'], auth='public', website=True)
def list_properties(self, page=1, **kwargs):
Property = request.env['estate.property'].sudo()
per_page= 6
domain = [('status', 'not in', ['sold', 'cancelled'])]
total_properties = Property.search_count(domain)
properties = Property.search(domain, offset=(page-1) * per_page, limit=per_page)
pager = request.website.pager(
url='/properties',
total=total_properties,
page=page,
step=per_page
)
return request.render('estate.property_listing_template', {
'properties': properties,
'pager': pager
})

@http.route(['/property/<model("estate.property"):property>'], type='http', auth="public", website=True)
def property_details(self, property, **kwargs):
return request.render('estate.property_details_template', {
'property': property
})
60 changes: 60 additions & 0 deletions estate/demo/estate_property_demo.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
<?xml version="1.0" encoding="UTF-8"?>
<odoo>
<record id="demo_estate_property_1" model="estate.property">
<field name="name">Big Villa</field>
<field name="property_type_id" ref="estate.property_type_residential" />
<field name="status">new</field>
<field name="description">A nice and big villa</field>
<field name="postcode">12345</field>
<field name="date_availability">2020-02-02</field>
<field name="expected_price">1600000</field>
<field name="bedrooms">6</field>
<field name="living_area">100</field>
<field name="facades">4</field>
<field name="garage">True</field>
<field name="garden">True</field>
<field name="garden_area">100000</field>
<field name="garden_orientation">south</field>
<field name="tag_ids" eval="[(6, 0, [ref('property_tag_luxury'), ref('property_tag_ocean_view')])]"/>
</record>

<record id="demo_estate_property_2" model="estate.property">
<field name="name">Modern Penthouse</field>
<field name="property_type_id" ref="estate.property_type_residential" />
<field name="status">cancelled</field>
<field name="description">A luxurious penthouse with a rooftop pool</field>
<field name="postcode">67890</field>
<field name="date_availability">2023-05-10</field>
<field name="expected_price">2200000</field>
<field name="bedrooms">4</field>
<field name="living_area">180</field>
<field name="facades">2</field>
<field name="garage">True</field>
<field name="garden">False</field>
<field name="tag_ids" eval="[(6, 0, [ref('property_tag_luxury'), ref('property_tag_city_view')])]"/>
</record>

<record id="demo_estate_property_3" model="estate.property">
<field name="name">Cozy Suburban House</field>
<field name="property_type_id" ref="estate.property_type_land" />
<field name="status">new</field>
<field name="description">A comfortable home in a quiet neighborhood</field>
<field name="postcode">11223</field>
<field name="date_availability">2024-08-15</field>
<field name="expected_price">350000</field>
<field name="bedrooms">3</field>
<field name="living_area">120</field>
<field name="facades">3</field>
<field name="garage">True</field>
<field name="garden">True</field>
<field name="garden_area">500</field>
<field name="garden_orientation">west</field>
<field name="tag_ids" eval="[(6, 0, [ref('property_tag_cozy'), ref('property_tag_wooden'), ref('property_tag_ocean_view')])]"/>
<field name="offer_ids"
eval="[
Command.create({'price': 350000,'validity': 12,'partner_id': ref('base.res_partner_12')}),
Command.create({'price': 300000,'validity': 11,'partner_id': ref('base.res_partner_10')}),
Command.create({'price': 400000,'validity': 1,'partner_id': ref('base.res_partner_2') }),
]" />
</record>
</odoo>
30 changes: 30 additions & 0 deletions estate/demo/estate_property_offer_demo.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
<?xml version="1.0" encoding="UTF-8"?>
<odoo>
<record id="estate_property_offer_1" model="estate.property.offer">
<field name="property_id" ref="estate.demo_estate_property_1" />
<field name="partner_id" ref="base.res_partner_12" />
<field name="price">1450000</field>
<field name="validity">14</field>
</record>

<record id="estate_property_offer_2" model="estate.property.offer">
<field name="property_id" ref="estate.demo_estate_property_1" />
<field name="partner_id" ref="base.res_partner_12" />
<field name="price">1500000</field>
<field name="validity">14</field>
</record>

<record id="estate_property_offer_3" model="estate.property.offer">
<field name="property_id" ref="estate.demo_estate_property_1" />
<field name="partner_id" ref="base.res_partner_2" />
<field name="price">1500001</field>
<field name="validity">14</field>
</record>

<function name="action_accept_offer" model="estate.property.offer"
eval="[ref('estate_property_offer_1')]" />
<function name="action_refuse_offer" model="estate.property.offer"
eval="[ref('estate_property_offer_2')]" />
<function name="action_refuse_offer" model="estate.property.offer"
eval="[ref('estate_property_offer_3')]" />
</odoo>
Loading