commit 8432ffe4491c5bc20da3183e9f814f57f2ce8603 Author: Mustafa Yontar Date: Sun Jan 31 00:29:48 2021 +0300 inital diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..35b0d26 --- /dev/null +++ b/.gitignore @@ -0,0 +1,25 @@ +__pycache__/ +*.py[cod] +*$py.class +*.so +.Python +env/ +.venv/ +build/ +develop-eggs/ +dist/ +downloads/ +eggs/ +.eggs/ +lib/ +lib64/ +parts/ +sdist/ +var/ +*.egg-info/ +.installed.cfg +*.egg +.idea/ +*.pem +*test.py +trash diff --git a/adunatio.md b/adunatio.md new file mode 100644 index 0000000..af9d1f1 --- /dev/null +++ b/adunatio.md @@ -0,0 +1,147 @@ +# Dernek yazılımı + +Türkiye'de faal derneklerin, mevcut mevzuata dahil olarak derneğin yasal süreçlerini, üye ilişkilerini ve muhasebelerini takip edebilmeleri için bir yazılım geliştirilmesi gerekliliği ortaya atılmıştır. + +## Temel gereksinimler + +Yazılımın temel gereksinimlerini n ayrı başlıkta inceleyebiliriz: + +* Defterlerin tutulması +* Gelir/gider takibi +* Üye takibi +* Evrak/dosya takibi +* Aidat takibi +* Bilgi e-postası / formu takibi +* Genel kurul süreç takibi +* E-posta ve SMS ile toplu duyurular +* Aidat ödeme (iyzico) + +### Defterlerin tutulması + +Dernekler 4 adet defter tutmak zorundadır: + +* Karar defteri +* İşletme defteri +* Üye kayıt defteri +* Evrak kayıt defteri + +5253 sayılı Dernekler Kanunu uyarınca bu defterler elektronik ortamda tutulabilir, ancak önceden Sivil Toplumla İlişkiler Müdürlüğü tarafından mühürlenmiş numaralı kağıtlara yazdırılarak saklanmalıdır. Bu defterlerin şekli standarda bağlanmış olup siviltoplum.gov.tr adresinde mevcuttur. + +#### Karar defteri + +Karar defteri, dernek yönetim kurulunun aldığı kararların yazıldığı temel defterdir. Bu defterde aşağıdaki alanlar mevcuttur: + +* Karar numarası: Her karara sırayla numara veriliyor (unsigned int), bu numaralar 1'den başlıyor. Tarih sırası ile numara sırası sabit gitmek zorunda, yani 5 numaralı kararın tarihi 4 numaralı karardan eski olamaz. +* Tarih: Karar tarihi, GG/AA/YYYY +* Konu: String field, kararın ne hakkında olduğu yazılıyor. +* Toplantıya katılan üyeler: Kararın alındığı toplantıya katılan kişilerin adı soyadı, bunları yönetim kurulu listesinden çekebiliriz. Başkan ve üyeler diye iki ayrı field var. +* Kararın metni: Karar metni, standart. +* İmzalar: Katılan her üye için metnin altında imza alanı oluyor, kişiler isimlerinin üzerine imza atıyor. + +#### Üye kayıt defteri + +Üye kayıt defteri, derneğe üye olan her üyenin bilgilerinin işlendiği bir defterdir. Bu defteri yazılım içinde üyeler diye bir başlık altında defter formatından bağımsız olarak tutmak daha kullanışlı olabilir. Bu defterde normalde aşağıdaki alanlar mevcut: + +* Fotoğraf: üyenin fotoğrafı, 25x35 mm +* Adı soyadı +* TCKN +* Tabiiyeti +* Ana adı +* Baba adı +* Cinsiyeti +* Mesleği +* E-posta adresi +* İkametgah adresi +* Üyeliğe kabul tarihi +* Üyelikten çıkma tarihi +* Açıklamalar +* + +Bunlara ek olarak, üye aidatı tahsilatı diye bir alanı daha var, bu alanda da her bir giriş için yılı, alındı belgesinin tarihi+sıra no, tutarı (lira+kr) alanları mevcut. + +#### İşletme defteri + +Derneğin muhasebe kayıtlarının tutulduğu defterdir. Sol sayfada giderler, sağ sayfada gelirler yazılıdır. Her bir sayfa önceki sayfadan nakil toplam ile başlar, sonra her satır için: + +* Sıra no +* Alındı belgesinin tarihi+no +* Açıklama +* Tutar (Lira+Kr) + +En son satırda da yazıyla ve sayıyla toplam tutar yazılır. + +#### Evrak kayıt defteri + +Evrak kayıt defteri, derneğe gelen ve giden her nevi evrakın yazıldığı defterdir. Üye başvuruları, verilen cevaplar, gelen bildirimler vs. hepsi yazılır, denetimlerde genelde çok bakılmaz. Sol sayfada gelen evrak, sağ sayfada giden evrak yazılır. Her bir satırda: + +* Kayıt sıra no +* Geldiği kişi veya kurum +* Tarihi +* Sayısı +* Eki +* Konusu +* Muhafaza edildiği dosya no + +Belgeler diye bir sayfa yapıp, her bir girdi için PDF eklenebilse çok güzel olur. + +### Gelir-gider takibi + +Derneğin gelirleri ve giderlerinin kategori bazlı olarak takibini ve borçlar/alacakların kurulması önemli bir konu, bunun için yazılımda en olmazsa olmaz kısımlardan bir tanesi. + +Temel olarak gider ve gelir kategorileri atanacak ve bu kategorilere harcama/gelir yazılacak. Her bir harcama/gelir için: + +* Tarih +* Hesap +* Alıcı/bağışçı ismi +* TCKN/VKN +* Açıklama +* Referans no +* Makbuz/fatura (PDF) +* Düzenli mi? (Haftalık-Aylık-Yıllık) + +Düzenli olarak işaretlenen giderler, her ayın o günü yaklaşırken e-posta uyarısı vermeli. + +### Üye takibi + +Dernek üyelerinin profilleri buraya girilecek, üye kayıt defteri buna göre oluşturulacak. Her üye için: + +* Üye no (1'den başlayarak sırayla) +* Kullanıcı adı/parola (kendileri servise girip aidat vs ödeme yapabilsinler diye) +* Ad soyad +* TCKN +* Ana adı +* Baba adı +* Doğum tarihi +* Cinsiyet +* Meslek +* E-posta +* Telefon +* Adres +* Üyeliğe giriş tarihi +* Üyelikten çıkış tarihi +* Üyelik tipi (asıl-destekçi-onursal) +* Aidat tipi (Tam, indirimli) +* Yorumlar/Açıklamalar +* GnuPG Fingerprint + +### Evrak/dosya takibi + +Belgeler diye bir alanda, PDF vb. belgelerin yüklenip kategorize edilebileceği bir bölüm iyi olacaktır. Evrak kayıt defteri de burada gelen evrak/giden evrak kategorilerinden oluşturulabilir. + +### Aidat takibi + +En önemli işlerden birisi bu, her üyenin aidat tipine göre ve yıllık aidat miktarına göre üyelere borç yazılması ve hangi üyenin ne kadar borcunun olduğunun takip edilmesi lazım. Ayrıca üyelerin yaptıkları genel bağışların da tutulması lazım. Örneğin aylık aidat 40 lira ise, aidatlarım diye bir sekmede üyelerin ay ay aidatlarını ödeyip ödemediklerinin görülebilmesi, eğer toplu ödedilerse (örneğin 6 aylık peşin 240 lira) altı ayın üstünün çizilmesi lazım. Üyelere borç hatırlatmasının yapılması, aidat tahsilatının hangi yolla yapıldığının girilebilmesi lazım. + +### Bilgi e-postası / formu takibi + +Orta öncelikli bir konu olarak, bilgi formuna veya e-postasına düşen e-postaların görülebileceği ve mümkünse cevaplanabileceği bir alan faydalı olabilir. + +### E-posta ve SMS ile toplu duyurular + +Üyelerin tamamına veya bir kısmına e-posta ve SMS ile duyuru atılabilmesi ihtiyacı var, e-posta için standart SMTP iş görür, SMS için Verimor API entegrasyonu lazım. + + + +### Aidat ödeme (iyzico) + +Üyelerin kendi aidat borçlarını görüp, bunları iyzico ile ödeyebilmeleri ve otomatik ödemeye bağlayabilmeleri lazım. Kart değişikliği veya otomatik ödeme iptali, isteğe bağlı ödeme de lazım. diff --git a/docker-compose.yaml b/docker-compose.yaml new file mode 100644 index 0000000..e69de29 diff --git a/internal_lib/EncryptedField.py b/internal_lib/EncryptedField.py new file mode 100644 index 0000000..5adf1c4 --- /dev/null +++ b/internal_lib/EncryptedField.py @@ -0,0 +1,69 @@ +import re + +from mongoengine.base import BaseField +from mongoengine.queryset import STRING_OPERATORS + + +class EncryptedStringField(BaseField): + """A unicode string field.""" + + def __init__(self, regex=None, max_length=None, min_length=None, **kwargs): + """ + :param regex: (optional) A string pattern that will be applied during validation + :param max_length: (optional) A max length that will be applied during validation + :param min_length: (optional) A min length that will be applied during validation + :param kwargs: Keyword arguments passed into the parent :class:`~mongoengine.BaseField` + """ + self.regex = re.compile(regex) if regex else None + self.max_length = max_length + self.min_length = min_length + super().__init__(**kwargs) + + def to_python(self, value): + + if isinstance(value, str): + return value + try: + value = value.decode("utf-8") + except Exception: + pass + return value + + def validate(self, value): + if not isinstance(value, str): + self.error("StringField only accepts string values") + + if self.max_length is not None and len(value) > self.max_length: + self.error("String value is too long") + + if self.min_length is not None and len(value) < self.min_length: + self.error("String value is too short") + + if self.regex is not None and self.regex.match(value) is None: + self.error("String value did not match validation regex") + + def lookup_member(self, member_name): + return None + + def prepare_query_value(self, op, value): + if not isinstance(op, str): + return value + + if op in STRING_OPERATORS: + case_insensitive = op.startswith("i") + op = op.lstrip("i") + + flags = re.IGNORECASE if case_insensitive else 0 + + regex = r"%s" + if op == "startswith": + regex = r"^%s" + elif op == "endswith": + regex = r"%s$" + elif op == "exact": + regex = r"^%s$" + + # escape unsafe characters which could lead to a re.error + value = re.escape(value) + value = re.compile(regex % value, flags) + return super().prepare_query_value(op, value) \ No newline at end of file diff --git a/internal_lib/__init__.py b/internal_lib/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/main.py b/main.py new file mode 100644 index 0000000..68aa93d --- /dev/null +++ b/main.py @@ -0,0 +1,16 @@ +# This is a sample Python script. + +# Press Shift+F10 to execute it or replace it with your code. +# Press Double Shift to search everywhere for classes, files, tool windows, actions, and settings. + + +def print_hi(name): + # Use a breakpoint in the code line below to debug your script. + print(f'Hi, {name}') # Press Ctrl+8 to toggle the breakpoint. + + +# Press the green button in the gutter to run the script. +if __name__ == '__main__': + print_hi('PyCharm') + +# See PyCharm help at https://www.jetbrains.com/help/pycharm/ diff --git a/models/Account.py b/models/Account.py new file mode 100644 index 0000000..675a4bd --- /dev/null +++ b/models/Account.py @@ -0,0 +1,10 @@ +from mongoengine import * + +from models.Union import Union + + +class Account(Document): + union = ReferenceField(Union) + bank = StringField() + number = StringField() + quick_number = StringField() diff --git a/models/EmbededDocuments.py b/models/EmbededDocuments.py new file mode 100644 index 0000000..f665ff4 --- /dev/null +++ b/models/EmbededDocuments.py @@ -0,0 +1,8 @@ +from mongoengine import * + + +class Descriptions(EmbeddedDocument): + from models.User import User + text = StringField() + user = ReferenceField(User) + date = DateTimeField() diff --git a/models/File.py b/models/File.py new file mode 100644 index 0000000..0c9342d --- /dev/null +++ b/models/File.py @@ -0,0 +1,15 @@ +from mongoengine import * + +from models.Group import Group + + +class Directory(Document): + path = StringField() + can_see_user_group = ReferenceField(Group) + is_system = BooleanField(default=False) + + +class File(Document): + path = ReferenceField(Directory) + file = FileField() + description = StringField() diff --git a/models/Group.py b/models/Group.py new file mode 100644 index 0000000..44ca034 --- /dev/null +++ b/models/Group.py @@ -0,0 +1,15 @@ +from mongoengine import * + +from models.Union import Union + + +class Group(Document): + union = ReferenceField(Union) + name = StringField() + rights = StringField() + + +class PaymentGroup(Document): + union = ReferenceField(Union) + name = StringField() + discount_percent = IntField() diff --git a/models/Payment.py b/models/Payment.py new file mode 100644 index 0000000..1993bcb --- /dev/null +++ b/models/Payment.py @@ -0,0 +1,28 @@ +from mongoengine import * + +from models.Account import Account +from models.File import File +from models.Union import Union +from models.User import User + + +class Payments(Document): + """ + all payments income and outcome together! + """ + union = ReferenceField(Union) + user = ReferenceField(User) + income = BooleanField(default=False) + date = DateTimeField() + regular = BooleanField(default=False) + regular_type = StringField(choices=('Weekly','Monthly','Yearly')) + price = DecimalField() + description = StringField() + reference_no = StringField() + file = ReferenceField(File) + account = ReferenceField(Account) + due_date = DateTimeField() + is_paid = BooleanField(default=False) + is_donate = BooleanField(default=False) + + diff --git a/models/Union.py b/models/Union.py new file mode 100644 index 0000000..3e91e1f --- /dev/null +++ b/models/Union.py @@ -0,0 +1,9 @@ +from mongoengine import * + + +class Union(Document): + name = StringField() + logo = ImageField(thumbnail_size=(120,120)) + description = StringField() + legal_registration_number = StringField() + headquarter = StringField() \ No newline at end of file diff --git a/models/User.py b/models/User.py new file mode 100644 index 0000000..15e10f7 --- /dev/null +++ b/models/User.py @@ -0,0 +1,96 @@ +from mongoengine import * + +from internal_lib.EncryptedField import EncryptedStringField +from models.EmbededDocuments import Descriptions +from models.Group import Group +from models.Union import Union + + +class User(Document): + union = ReferenceField(Union) + member_no = LongField() + username = StringField() + photo = ImageField(thumbnail_size=(85,120)) + password = StringField() + name = StringField() + middle_name = StringField() + last_name = StringField() + gov_id = EncryptedStringField() + mother_name = EncryptedStringField() + father_name = EncryptedStringField() + email = EncryptedStringField() + place_of_birth = EncryptedStringField() + date_of_birth = EncryptedStringField() + telephone = EncryptedStringField() + job = StringField() + address = EncryptedStringField() + accept_date = DateTimeField() + dismissal_date = DateTimeField() + descriptions = ListField(EmbeddedDocumentField(Descriptions)) + gender = StringField(choices=( + 'Man', + 'Women', + 'Agender', + 'Androgyne', + 'Androgynous', + 'Bigender', + 'Cis', + 'Cisgender', + 'Cis Female', + 'Cis Male', + 'Cis Man', + 'Cis Woman', + 'Cisgender Female', + 'Cisgender Male', + 'Cisgender Man', + 'Cisgender Woman', + 'Female to Male', + 'FTM', + 'Gender Fluid', + 'Gender Nonconforming', + 'Gender Questioning', + 'Gender Variant', + 'Genderqueer', + 'Intersex', + 'Male to Female', + 'MTF', + 'Neither', + 'Neutrois', + 'Non-binary', + 'Other', + 'Pangender', + 'Trans', + 'Trans*', + 'Trans Female', + 'Trans* Female', + 'Trans Male', + 'Trans* Male', + 'Trans Man', + 'Trans* Man', + 'Trans Person', + 'Trans* Person', + 'Trans Woman', + 'Trans* Woman', + 'Transfeminine', + 'Transgender', + 'Transgender Female', + 'Transgender Male', + 'Transgender Man', + 'Transgender Person', + 'Transgender Woman', + 'Transmasculine', + 'Transsexual', + 'Transsexual Female', + 'Transsexual Male', + 'Transsexual Man', + 'Transsexual Person', + 'Transsexual Woman', + 'Two-Spirit' + )) + gnupg_fingerprint = StringField() + user_group = ReferenceField(Group) + payment_group = ReferenceField(Group) + + + + diff --git a/models/__init__.py b/models/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/requirements.txt b/requirements.txt new file mode 100644 index 0000000..6f8d06d --- /dev/null +++ b/requirements.txt @@ -0,0 +1,4 @@ +mongoengine +flask-mongoengine +flask +pycryptodome \ No newline at end of file