Commit 5ecef459 by Owo Sugiana

Menu RT

1 parent 2e7f07e1
Catat Warga
===========
Modul Odoo untuk pencatatan warga. Aplikasi untuk Ketua RT.
Modul Odoo untuk pencatatan warga. Aplikasi digunakan oleh pengurus RT.
Berikut rencananya:
Berikut fiturnya:
1. Company adalah RT
2. Setiap user bisa menangani beberapa company (bawaan Odoo)
1. Konsepnya seperti Google Drive yaitu memungkinkan lokasi yang sama (RT, RW,
hingga provinsi yang sama) dibuat oleh user A dan B. Dengan begitu sebuah
server dapat digunakan secara luas se-Indonesia.
2. Setiap user bisa membuat beberapa lokasi.
3. Pendaftaran menggunakan teknik undangan. User A klik menu Undangan dan
masukkan nama serta email calon user. Kemudian sistem akan mengirim tautan
unik untuk pendaftaran.
Rencana berikutnya:
1. User A bisa memberi hak akses ke user B sebagai *pembaca* atau *pengubah*
data warga.
2. Ekspor ke CSV
from . import models
from . import (
models,
wizard,
controllers,
)
......@@ -4,12 +4,17 @@
'author': 'opensipkd.com',
'license': 'LGPL-3',
'application': True,
'summary': 'Aplikasi untuk Ketua RT mencatat warganya',
'summary': 'Aplikasi untuk pengurus RT mencatat warganya',
'depends': [
'wilayah',
],
'base', 'mail', 'wilayah'],
'data': [
'views/res_company.xml',
'views/res_partner.xml'
'views/res_partner.xml',
'views/rt.xml',
'wizard/undangan_views.xml',
'security/ir.model.access.csv',
'security/ir_rule.xml',
'data/mail_template_data.xml',
'data/res_partner_category.xml',
]
}
from . import undangan
from logging import getLogger
import werkzeug
from odoo import http
from odoo.http import request
from odoo.exceptions import UserError
from odoo.addons.auth_signup.models.res_users import SignupError
from odoo.addons.auth_signup.controllers.main import AuthSignupHome
_logger = getLogger(__name__)
class TerimaUndangan(AuthSignupHome):
@http.route(
'/web/undangan', type='http', auth='public', website=True,
sitemap=False)
def web_terima_undangan(self, *args, **kw):
qcontext = self.get_auth_signup_qcontext()
token = qcontext.get('token')
if not token:
raise werkzeug.exceptions.NotFound()
if 'error' not in qcontext and request.httprequest.method == 'POST':
try:
partner = request.env['res.partner'].sudo().\
_signup_retrieve_partner(
token, check_validity=True, raise_exception=True)
d = dict(login=partner.email, partner_id=partner.id)
user = request.env['res.users'].sudo().create(d)
group = request.env.ref('warga.group_warga')
group.sudo().write(dict(users=[(4, user.id)]))
self.do_signup(qcontext)
return self.web_login(*args, **kw)
except UserError as e:
qcontext['error'] = e.args[0]
except SignupError:
qcontext['error'] = _("Could not reset your password")
_logger.exception('error when resetting password')
except Exception as e:
qcontext['error'] = str(e)
response = request.render('auth_signup.signup', qcontext)
response.headers['X-Frame-Options'] = 'DENY'
return response
<?xml version="1.0" encoding="utf-8"?>
<odoo>
<data noupdate="0">
<!-- Email template for reset password -->
<record id="warga_undangan_email" model="mail.template">
<field name="name">Pendaftaran sebagai Pencatat Warga</field>
<field name="model_id" ref="base.model_res_partner"/>
<field name="subject">Undangan Jadi Pencatat Warga</field>
<field name="email_from">{{ object.create_uid.company_id.email_formatted }}</field>
<field name="email_to">{{ object.email_formatted }}</field>
<field name="body_html" type="html">
<p>Anda diundang <t t-out="object.create_uid.partner_id.name or ''">teman</t> untuk jadi pencatat warga di lingkungan Anda sendiri.</p>
<p><a t-att-href="object.signup_url">Lanjutkan pendaftaran</a></p>
</field>
<field name="lang">{{ object.lang }}</field>
<field name="auto_delete" eval="True"/>
</record>
</data>
</odoo>
<?xml version="1.0" encoding="utf-8"?>
<odoo>
<record id="res_partner_category_warga_rt" model="res.partner.category">
<field name="name">RT</field>
<field name="color" eval="2"/>
</record>
<record id="res_partner_category_warga" model="res.partner.category">
<field name="name">Warga</field>
<field name="color" eval="3"/>
</record>
<record id="res_partner_category_warga_anggota" model="res.partner.category">
<field name="name">Anggota Keluarga</field>
<field name="color" eval="4"/>
</record>
</odoo>
from . import (
res_partner,
res_company,
res_partner,
)
from logging import getLogger
from odoo import (
models,
fields,
......@@ -6,17 +7,29 @@ from odoo import (
from odoo.exceptions import ValidationError
def range_validation(val, label, first=1, last=999):
if not val:
return
try:
n = int(val)
if first <= n <= last:
return str(n)
except ValueError:
pass
raise ValidationError(f'{label} harus angka {first} - {last}')
class Company(models.Model):
_inherit = 'res.company'
kabupaten_id = fields.Many2one(
'kabupaten', string='Kabupaten', ondelete='restrict',
'kabupaten', ondelete='restrict',
domain="[('state_id', '=?', state_id)]")
kecamatan_id = fields.Many2one(
'kecamatan', string='Kecamatan', ondelete='restrict',
'kecamatan', ondelete='restrict',
domain="[('kabupaten_id', '=?', kabupaten_id)]")
kelurahan_id = fields.Many2one(
'kelurahan', string='Kelurahan', ondelete='restrict',
'kelurahan', ondelete='restrict',
domain="[('kecamatan_id', '=?', kecamatan_id)]")
rw = fields.Char()
rt = fields.Char()
......@@ -66,24 +79,8 @@ class Company(models.Model):
@api.onchange('rw')
def _onchange_rw(self):
if self.rw:
try:
n = int(self.rw)
if 1 <= n <= 999:
self.rw = str(n)
return
except ValueError:
pass
raise ValidationError('RW harus angka 1 - 999')
self.rw = range_validation(self.rw, 'RW')
@api.onchange('rt')
def _onchange_rt(self):
if self.rt:
try:
n = int(self.rt)
if 1 <= n <= 999:
self.rt = str(n)
return
except ValueError:
pass
raise ValidationError('RT harus angka 1 - 999')
self.rt = range_validation(self.rt, 'RT')
......@@ -4,26 +4,56 @@ from odoo import (
api,
)
from odoo.exceptions import ValidationError
from .res_company import range_validation
ERR_NIK = ValidationError(f'NIK harus 16 angka')
def nik_validation(val):
if not val:
return
val = val.strip()
try:
if int(val) < 0:
raise ERR_NIK
return val
except ValueError:
pass
raise ERR_NIK
class Partner(models.Model):
_inherit = 'res.partner'
@api.model
def _get_default_country(self):
return self.env.ref('base.id')
country_id = fields.Many2one(
'res.country', ondelete='restrict', required=True,
default=_get_default_country)
kabupaten_id = fields.Many2one(
'kabupaten', string='Kabupaten', ondelete='restrict',
'kabupaten', ondelete='restrict',
domain="[('state_id', '=?', state_id)]")
kecamatan_id = fields.Many2one(
'kecamatan', string='Kecamatan', ondelete='restrict',
'kecamatan', ondelete='restrict',
domain="[('kabupaten_id', '=?', kabupaten_id)]")
kelurahan_id = fields.Many2one(
'kelurahan', string='Kelurahan', ondelete='restrict',
'kelurahan', ondelete='restrict',
domain="[('kecamatan_id', '=?', kecamatan_id)]")
rw = fields.Char()
rt = fields.Char()
rw = fields.Char(string='RW')
rt = fields.Char(string='RT')
hubungan_keluarga = fields.Char() # Kepala Keluarga / Istri / Anak
@api.onchange('l10n_id_nik')
def _onchange_nik(self):
self.l10n_id_nik = nik_validation(self.l10n_id_nik)
@api.onchange('country_id')
def _onchange_country_id(self):
if self.country_id:
# self.currency_id = self.country_id.currency_id
if self.country_id != self.state_id.country_id:
self.state_id = False
else:
......@@ -65,24 +95,8 @@ class Partner(models.Model):
@api.onchange('rw')
def _onchange_rw(self):
if self.rw:
try:
n = int(self.rw)
if 1 <= n <= 999:
self.rw = str(n)
return
except ValueError:
pass
raise ValidationError('RW harus angka 1 - 999')
self.rw = range_validation(self.rw, 'RW')
@api.onchange('rt')
def _onchange_rt(self):
if self.rt:
try:
n = int(self.rt)
if 1 <= n <= 999:
self.rt = str(n)
return
except ValueError:
pass
raise ValidationError('RT harus angka 1 - 999')
self.rt = range_validation(self.rt, 'RT')
id,name,model_id:id,group_id:id,perm_read,perm_write,perm_create,perm_unlink
access_undangan_user,access_undangan_dari_user,model_warga_undangan,base.group_user,1,1,1,0
access_undangan_portal,access_undangan_dari_portal,model_warga_undangan,base.group_portal,1,1,1,0
access_pop_message_user,access_pop_message_user,model_custom_pop_message,base.group_user,1,0,0,0
access_pop_message_portal,access_pop_message_portal,model_custom_pop_message,base.group_portal,1,0,0,0
<?xml version="1.0" encoding="utf-8"?>
<odoo>
<record model="res.groups" id="group_warga">
<field name="name">Warga</field>
</record>
<record model="ir.rule" id="hak_akses_warga">
<field name="name">Hak Akses Warga</field>
<field name="model_id" ref="base.model_res_partner"/>
<field name="domain_force">
['|', '|', '|',
('id', '=', 2),
('create_uid', '=', user.id),
('user_id', '=', user.id),
('user_ids','=',user.id)]
</field>
<field name="groups" eval="[Command.link(ref('warga.group_warga'))]"/>
</record>
<record model="ir.rule" id="base.res_partner_rule_private_employee">
<field name="groups" eval="[Command.unlink(ref('base.group_user'))]"/>
<field name="groups" eval="[Command.link(ref('base.group_erp_manager'))]"/>
</record>
<record model="ir.ui.menu" id="mail.menu_root_discuss">
<field name="groups_id" eval="[Command.unlink(ref('base.group_user'))]"/>
<field name="groups_id" eval="[Command.link(ref('base.group_erp_manager'))]"/>
</record>
</odoo>
<?xml version="1.0" encoding="utf-8"?>
<odoo>
<data>
<record model="ir.ui.view" id="view_rt_form">
<field name="name">res.rt.form</field>
<field name="model">res.company</field>
<field name="inherit_id" ref="base.view_company_form"/>
<field name="type">form</field>
<field name="arch" type="xml">
<xpath expr="//field[@name='city']" position="attributes">
<attribute name="invisible">1</attribute>
</xpath>
<field name="state_id" position="before">
<label for="rt" string="RT" attrs="{'invisible': [('rt', '=', False)]}"/>
<field name="rt" placeholder="RT" class="o_address_zip"/>
<label for="rw" string="RW" attrs="{'invisible': [('rw', '=', False)]}"/>
<field name="rw" placeholder="RW" class="o_address_zip"/>
<field name="kelurahan_id" placeholder="Kelurahan"/>
<field name="kecamatan_id" placeholder="Kecamatan"/>
<field name="kabupaten_id" placeholder="Kota / Kabupaten"/>
</field>
<record model="ir.ui.view" id="view_rt_form">
<field name="name">res.rt.form</field>
<field name="model">res.company</field>
<field name="inherit_id" ref="base.view_company_form"/>
<field name="type">form</field>
<field name="arch" type="xml">
<xpath expr="//field[@name='city']" position="attributes">
<attribute name="invisible">1</attribute>
</xpath>
<field name="state_id" position="before">
<label for="rt" string="RT" attrs="{'invisible': [('rt', '=', False)]}"/>
<field name="rt" placeholder="RT" class="o_address_zip"/>
<label for="rw" string="RW" attrs="{'invisible': [('rw', '=', False)]}"/>
<field name="rw" placeholder="RW" class="o_address_zip"/>
<field name="kelurahan_id" placeholder="Kelurahan"/>
<field name="kecamatan_id" placeholder="Kecamatan"/>
<field name="kabupaten_id" placeholder="Kota / Kabupaten"/>
</field>
</record>
</data>
</field>
</record>
</odoo>
<?xml version="1.0" encoding="utf-8"?>
<odoo>
<data>
<record model="ir.ui.view" id="view_warga_form">
<field name="name">res.warga.form</field>
<field name="model">res.partner</field>
<field name="inherit_id" ref="base.view_partner_form"/>
<field name="type">form</field>
<field name="arch" type="xml">
<xpath expr="//field[@name='city']" position="attributes">
<attribute name="invisible">1</attribute>
</xpath>
<field name="state_id" position="before">
<label for="rt" string="RT" attrs="{'invisible': [('rt', '=', False)]}"/>
<field name="rt" placeholder="RT" class="o_address_zip"/>
<label for="rw" string="RW" attrs="{'invisible': [('rw', '=', False)]}"/>
<field name="rw" placeholder="RW" class="o_address_zip"/>
<field name="kelurahan_id" placeholder="Kelurahan"/>
<field name="kecamatan_id" placeholder="Kecamatan"/>
<field name="kabupaten_id" placeholder="Kota / Kabupaten"/>
</field>
<record id="view_warga_form" model="ir.ui.view">
<field name="name">res.warga.form</field>
<field name="model">res.partner</field>
<field name="inherit_id" ref="base.view_partner_form"/>
<field name="type">form</field>
<field name="arch" type="xml">
<xpath expr="//field[@name='city']" position="attributes">
<attribute name="invisible">1</attribute>
</xpath>
<xpath expr="//field[@name='vat']" position="attributes">
<attribute name="invisible">1</attribute>
</xpath>
<xpath expr="//page[@name='sales_purchases']" position="attributes">
<attribute name="invisible">1</attribute>
</xpath>
<field name="state_id" position="before">
<label for="rt" string="RT" attrs="{'invisible': [('rt', '=', False)]}"/>
<field name="rt" placeholder="RT" class="o_address_zip"/>
<label for="rw" string="RW" attrs="{'invisible': [('rw', '=', False)]}"/>
<field name="rw" placeholder="RW" class="o_address_zip"/>
<field name="kelurahan_id" placeholder="Kelurahan"/>
<field name="kecamatan_id" placeholder="Kecamatan"/>
<field name="kabupaten_id" placeholder="Kota / Kabupaten"/>
</field>
</record>
</data>
</field>
</record>
</odoo>
<?xml version="1.0" encoding="utf-8"?>
<odoo>
<record id="action_warga_rt_form" model="ir.actions.act_window">
<field name="name">RT</field>
<field name="type">ir.actions.act_window</field>
<field name="res_model">res.partner</field>
<field name="domain">
[
('category_id.name', '=', 'RT'),
('create_uid', '=', uid),
]
</field>
<field name="context" eval="{'default_category_id': [ref('warga.res_partner_category_warga_rt')]}"/>
<field name="view_mode">kanban,tree,form,activity</field>
<field name="help" type="html">
<p class="o_view_nocontent_smiling_face">
Buat RT, tempat kos, kontrakan, cluster, atau kawasan yang Anda kelola.
</p>
</field>
</record>
<record id="view_warga_rt_form" model="ir.ui.view">
<field name="name">res.partner.rt.form</field>
<field name="model">res.partner</field>
<field name="priority">0</field>
<field name="arch" type="xml">
<form>
<h1>
<field name="name" placeholder="Contoh: RT 7. Bisa juga nama cluster atau apartemen"/>
</h1>
<group>
<group>
<field name="street"/>
<field name="rt"/>
<field name="rw"/>
<field name="kelurahan_id"/>
<field name="kecamatan_id"/>
</group>
<group>
<field name="kabupaten_id"/>
<field name="city" invisible="1"/>
<field name="state_id" string="Provinsi"/>
<field name="country_id" string="Negara" attrs="{'readonly': 1}"/>
<field name="zip"/>
</group>
</group>
<notebook colspan="2">
<page string="Kepala Keluarga">
<field name="child_ids" context="{'default_parent_id': active_id,
'default_category_id': [2],
'default_type': 'other',
'default_hubungan_keluarga': 'Kepala Keluarga',
'default_street': street,
'default_country_id': country_id,
'default_state_id': state_id,
'default_kabupaten_id': kabupaten_id,
'default_kecamatan_id': kecamatan_id,
'default_kelurahan_id': kelurahan_id,
'default_rw': rw, 'default_rt': rt,
'default_zip': zip}">
<tree>
<field name="name"/>
<field name="street" string="Jalan / No."/>
<field name="mobile"/>
</tree>
<form string="Kepala Keluarga">
<field name="parent_id" invisible="1"/>
<field name="type" invisible="1"/>
<field name="city" invisible="1"/>
<field name="category_id" invisible="1"/>
<field name="hubungan_keluarga" invisible="1"/>
<group>
<group>
<field name="name"/>
<field name="street" string="Jalan / No."/>
<field name="mobile" widget="phone"/>
<field name="email" widget="email"/>
<field name="phone" widget="phone"/>
</group>
<group>
<label for="rt" string="Alamat"/>
<div class="o_address_format">
<label for="rt" string="RT"/>
<field name="rt"/>
<label for="rw" string="RW"/>
<field name="rw"/>
<field name="kelurahan_id" placeholder="Kelurahan"/>
<field name="kecamatan_id" placeholder="Kecamatan"/>
<field name="kabupaten_id" placeholder="Kabupaten"/>
<field name="state_id" placeholder="Provinsi"/>
<field name="country_id" placeholder="Negara"/>
<field name="zip"/>
</div>
</group>
</group>
<notebook colspan="2">
<page string="Anggota Keluarga">
<field name="child_ids" context="{'default_parent_id': active_id,
'default_category_id': [3],
'default_type': 'other',
'default_street': street,
'default_country_id': country_id,
'default_state_id': state_id,
'default_kabupaten_id': kabupaten_id,
'default_kecamatan_id': kecamatan_id,
'default_kelurahan_id': kelurahan_id,
'default_rw': rw, 'default_rt': rt,
'default_zip': zip}">
<tree>
<field name="name"/>
<field name="hubungan_keluarga"/>
<field name="mobile"/>
</tree>
<form>
<field name="parent_id" invisible="1"/>
<field name="type" invisible="1"/>
<field name="city" invisible="1"/>
<field name="category_id" invisible="1"/>
<group>
<group>
<field name="name"/>
<field name="hubungan_keluarga" placeholder="Istri / Anak"/>
<field name="mobile" widget="phone"/>
<field name="email" widget="email"/>
<field name="phone" widget="phone"/>
</group>
<group>
<label for="rt" string="Alamat"/>
<div class="o_address_format">
<field name="street" string="Jalan / No."/>
<label for="rt" string="RT"/>
<field name="rt"/>
<label for="rw" string="RW"/>
<field name="rw"/>
<field name="kelurahan_id" placeholder="Kelurahan"/>
<field name="kecamatan_id" placeholder="Kecamatan"/>
<field name="kabupaten_id" placeholder="Kabupaten"/>
<field name="state_id" placeholder="Provinsi"/>
<field name="country_id" placeholder="Negara"/>
<field name="zip"/>
</div>
</group>
</group>
</form>
</field>
</page>
<page string="Catatan">
<field name="comment" placeholder="Tulis hal menarik"/>
</page>
</notebook>
</form>
</field>
</page>
<page string="Catatan">
<field name="comment" placeholder="Tulis hal menarik"/>
</page>
</notebook>
</form>
</field>
</record>
<menuitem name="RT"
id="menu_root_rt"
sequence="20"
web_icon="contacts,static/description/icon.png"
groups="base.group_user,base.group_partner_manager"
action="action_warga_rt_form"/>
</odoo>
from . import undangan
from odoo import (
api,
fields,
models,
_,
)
from odoo.exceptions import (
ValidationError,
UserError,
)
class CustomPopMessage(models.TransientModel):
_name = 'custom.pop.message'
_description = 'Penampil pesan'
name = fields.Char('Message')
class KirimUndangan(models.TransientModel):
_name = 'warga.undangan'
_description = 'Undang jadi pengguna'
name = fields.Char(required=True)
email = fields.Char(required=True)
def kirim(self):
t = self.email.split('@')
if len(t) != 2:
raise ValidationError(f'Email {self.email} tidak benar')
template = self.env.ref('warga.warga_undangan_email')
if not template:
raise UserError(
_('The template "Portal: new user" not found for sending '
'email to the portal user.'))
d = dict(name=self.name, email=self.email)
partner = self.env['res.partner'].create(d)
lang = partner.sudo().lang
portal_url = partner.with_context(
signup_force_type_in_url='', lang=lang).\
_get_signup_url_for_action()[partner.id]
partner.sudo().signup_prepare('undangan')
template.with_context(
dbname=self._cr.dbname, portal_url=portal_url, lang=lang).\
send_mail(partner.id, force_send=True)
return {
'name': 'Undang jadi pengguna',
'type': 'ir.actions.act_window',
'view_type': 'form',
'view_mode': 'form',
'res_model': 'custom.pop.message',
'target': 'new',
'context': {
'default_name': 'Undangan sudah dikirim. '
f'Kabari {partner.name} untuk cek email.'}
}
<?xml version="1.0"?>
<odoo>
<record id="view_warga_undangan" model="ir.ui.view">
<field name="name">undangan.view</field>
<field name="model">warga.undangan</field>
<field name="arch" type="xml">
<form string="Undang pengurus RT">
<group>
<field name="name" placeholder="Nama lengkap"/>
<field name="email"
placeholder="Ajak pengurus RT lainnya untuk menggunakan aplikasi ini."/>
</group>
<footer>
<button string="Undang" name="kirim" type="object" class="btn-primary"/>
<button string="Batal" special="cancel" class="btn-secondary"/>
</footer>
</form>
</field>
</record>
<record id="custom_pop_message_wizard_view_form" model="ir.ui.view">
<field name="name">custom.pop.message.form</field>
<field name="model">custom.pop.message</field>
<field name="arch" type="xml">
<form string="Custom POP Message">
<field name="name" readonly="1"/>
<footer>
<button string="Close" class="btn-default" special="cancel"/>
</footer>
</form>
</field>
</record>
<record id="action_warga_undangan" model="ir.actions.act_window">
<field name="name">Undang Jadi Pengguna</field>
<field name="res_model">warga.undangan</field>
<field name="view_mode">tree,form</field>
<field name="view_id" ref="view_warga_undangan"/>
<field name="target">new</field>
</record>
<menuitem id="menu_warga_undangan"
parent="warga.menu_root_rt"
sequence="99"
name="Undang"
action="action_warga_undangan"/>
</odoo>
id,name,model_id:id,group_id:id,perm_read,perm_write,perm_create,perm_unlink
access_kabupaten,access.kabupaten,model_kabupaten,base.group_user,1,0,0,0
access_kecamatan,access.kecamatan,model_kecamatan,base.group_user,1,0,0,0
access_kelurahan,access.kelurahan,model_kelurahan,base.group_user,1,0,0,0
access_kabupaten,access_kabupaten,model_kabupaten,base.group_user,1,0,0,0
access_kecamatan,access_kecamatan,model_kecamatan,base.group_user,1,0,0,0
access_kelurahan,access_kelurahan,model_kelurahan,base.group_user,1,0,0,0
Markdown is supported
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!