Browse Source

Introduce design chapter with explanations and UML diagrams.

Markus Lorenz 3 years ago
parent
commit
ec14107f4c
10 changed files with 944 additions and 283 deletions
  1. 1
    0
      .gitignore
  2. 15
    10
      COPYRIGHT.rst
  3. 5
    3
      README.rst
  4. 55
    0
      docs/bibliography/index.rst
  5. 18
    4
      docs/conf.py
  6. 766
    0
      docs/design/index.rst
  7. 53
    0
      docs/glossary/index.rst
  8. 11
    16
      docs/index.rst
  9. 2
    2
      docs/overview/routes.rst
  10. 18
    248
      docs/requirements/index.rst

+ 1
- 0
.gitignore View File

@@ -13,6 +13,7 @@ cover/
13 13
 dist/
14 14
 docs/_build/
15 15
 env/
16
+utils/
16 17
 off/
17 18
 pyramid.pid
18 19
 python-eggs/

+ 15
- 10
COPYRIGHT.rst View File

@@ -14,9 +14,11 @@ Copyright (c) 2015 Cultural Commons Collecting Society SCE mit
14 14
 beschränkter Haftung (C3S SCE) and contributors.
15 15
 
16 16
 
17
+
17 18
 Code
18 19
 ----
19 20
 
21
+
20 22
 This program is free software: you can redistribute it and/or modify
21 23
 it under the terms of the GNU General Public License as published by
22 24
 the Free Software Foundation, either version 3 of the License, or
@@ -31,9 +33,11 @@ You should have received a copy of the GNU General Public License
31 33
 along with this program.  If not, see <http://www.gnu.org/licenses/>.
32 34
 
33 35
 
36
+
34 37
 Documentation
35 38
 -------------
36 39
 
40
+
37 41
 The documentation of this program is licensed under a Creative Commons
38 42
 Attribution 4.0 license (CC-BY 4.0).
39 43
 
@@ -51,23 +55,24 @@ this program.
51 55
 
52 56
 Redistributes works are copyright and licensed as follows:
53 57
 
58
+- Bootstrap -- Copyright (c) 2011-2014 Twitter, Inc.  Licensed under MIT
59
+  (https://github.com/twbs/bootstrap/blob/master/LICENSE).
60
+
54 61
 - jQuery -- Copyright (c) 2005, 2014 jQuery Foundation, Inc.  Licensed under
55 62
   MIT (https://jquery.org/license/).
56 63
 
57
-- jQuery UI -- Copyright (c) 2013 jQuery Foundation and other contributors.
58
-  Licensed under MIT (https://jquery.org/license/).
59
-
60
-- Bootstrap -- Copyright (c) 2011-2014 Twitter, Inc.  Licensed under MIT
61
-  (https://github.com/twbs/bootstrap/blob/master/LICENSE).
64
+- jQuery timepicker addon -- Copyright (c) 2011 Trent Richardson. Dual
65
+  licensed under the MIT and GPL
66
+  (http://trentrichardson.com/Impromptu/GPL-LICENSE.txt,
67
+  http://trentrichardson.com/Impromptu/MIT-LICENSE.txt).
62 68
 
63 69
 - jQuery Form Plugin -- Copyright (c) 2006-2013 M. Alsup.  Dual licensed
64 70
   under MIT and GPL (https://github.com/malsup/form/).
65 71
 
72
+- jQuery UI -- Copyright (c) 2013 jQuery Foundation and other contributors.
73
+  Licensed under MIT (https://jquery.org/license/).
74
+
66 75
 - Masked Input plugin for jQuery -- Copyright (c) 2007-2009 Josh Bush.
67
-  Licensed under the MIT
76
+  Licensed under MIT
68 77
   (http://digitalbush.com/projects/masked-input-plugin/#license)
69 78
 
70
-- jQuery timepicker addon -- Copyright (c) 2011 Trent Richardson. Dual
71
-  licensed under the MIT and GPL
72
-  (http://trentrichardson.com/Impromptu/GPL-LICENSE.txt,
73
-  http://trentrichardson.com/Impromptu/MIT-LICENSE.txt).

+ 5
- 3
README.rst View File

@@ -36,7 +36,6 @@ https://yes.c3s.cc/docs/
36 36
 Setup
37 37
 -----
38 38
 
39
-
40 39
 Install dependencies:
41 40
 
42 41
 Development:
@@ -56,10 +55,13 @@ LaTeX pdf compilation:
56 55
 
57 56
    $ sudo apt-get install texlive-latex-base texlive-latex-recommended texlive-latex-extra texlive-fonts-recommended texlive-fonts-extra pgf texlive-lang-german
58 57
 
59
-- Documentation diagram generation:
58
+Documentation:
60 59
 ::
61 60
 
62
-   $ sudo apt-get install graphviz
61
+   $ sudo apt-get install graphviz openjdk-8-jre
62
+   $ mkdir utils
63
+   $ wget 'http://downloads.sourceforge.net/project/plantuml/plantuml.jar' -O utils/plantuml.jar
64
+   $ env/bin/pip install sphinx sphinxcontrib-plantuml
63 65
 
64 66
 Setup:
65 67
 ::

+ 55
- 0
docs/bibliography/index.rst View File

@@ -0,0 +1,55 @@
1
+============
2
+Bibliography
3
+============
4
+
5
+
6
+.. [Bootstrap] Bootstrap CSS framework, https://getbootstrap.com/
7
+
8
+.. [C3S_Statute] C3S: Articles of Association of the Cultural Commons
9
+   Collecting Society SCE (C3S). http://archive.c3s.cc/legal/C3S_en_v1.0.pdf,
10
+   https://archive.c3s.cc/aktuell/legal/C3S_SCE_de.pdf.
11
+
12
+.. [Chameleon] Chameleon template engine for Python,
13
+   https://chameleon.readthedocs.org/en/latest/
14
+
15
+.. [EU_CR_1435_2003_SCE] The council of the European Union, Council Regulation
16
+   (EC) No 1435/2003 of 22 July 2003 on the Statute for a European Cooperative
17
+   Society (SCE),
18
+   http://eur-lex.europa.eu/legal-content/EN/TXT/?uri=CELEX:32003R1435
19
+
20
+.. [EU_SCE_Statute] Statute for a European Cooperative Society, EUR-Lex,
21
+   http://eur-lex.europa.eu/legal-content/EN/TXT/?uri=uriserv%3Al26018
22
+
23
+.. [GenG] http://www.gesetze-im-internet.de/geng/
24
+
25
+.. [GnuPG] Gnu Privacy Guard, https://gnupg.org/
26
+
27
+.. [Graphviz] Graphviz graph visualization software, https://www.graphviz.org/
28
+
29
+.. [jQuery] jQuery JavaScript framework, https://jquery.com/
30
+
31
+.. [jQueryUI] jQuery user interface library, https://jqueryui.com/
32
+
33
+.. [PDFtk] PDF Labs, PDFtk,
34
+   https://www.pdflabs.com/tools/pdftk-the-pdf-toolkit/
35
+
36
+.. [PlantUML] PlantUML, http://plantuml.com/
37
+
38
+.. [Pyramid] Pyramid web framework,
39
+   https://docs.pylonsproject.org/projects/pyramid/en/latest/
40
+
41
+.. [Sphinx] Sphinx documentation generator, https://sphinx-doc.org/
42
+
43
+.. [Sphinx-Graphviz] https://build-me-the-docs-please.readthedocs.org/en/latest/Using_Sphinx/UsingGraphicsAndDiagramsInSphinx.html
44
+
45
+.. [SQLAlchemy] SQLAlchemy Object Relational Mapper,
46
+   https://www.sqlalchemy.org/
47
+
48
+.. [SQLAlchemy-Continuum] SQLAlchemy-Continuum, SQLAlchemy versioning
49
+   extension, https://sqlalchemy-continuum.readthedocs.org/en/latest/
50
+
51
+.. [TeX_Live] The TeX User Group, TeX Live, https://www.tug.org/texlive/
52
+
53
+.. [Wiki_Kano] Wikipedia: Kano model.
54
+   https://en.wikipedia.org/w/index.php?title=Kano_model&oldid=678655771
55
+

+ 18
- 4
docs/conf.py View File

@@ -16,10 +16,11 @@ import sys
16 16
 import os
17 17
 import alabaster
18 18
 
19
-# If extensions (or modules to document with autodoc) are in another directory,
20
-# add these directories to sys.path here. If the directory is relative to the
21
-# documentation root, use os.path.abspath to make it absolute, like shown here.
22
-#sys.path.insert(0, os.path.abspath('.'))
19
+# If extensions (or modules to document with autodoc) are in another
20
+# directory, add these directories to sys.path here. If the directory is
21
+# relative to the documentation root, use os.path.abspath to make it absolute,
22
+# like shown here.
23
+# sys.path.insert(0, os.path.abspath('.'))
23 24
 sys.path.insert(0, os.path.abspath('..'))
24 25
 
25 26
 # -- General configuration ------------------------------------------------
@@ -36,14 +37,27 @@ extensions = [
36 37
     'sphinx.ext.autosummary',
37 38
     'sphinx.ext.coverage',
38 39
     'sphinx.ext.graphviz',
40
+    'sphinxcontrib.plantuml',
39 41
 ]
40 42
 
43
+# command for generating PlantUML diagrams
44
+plantuml = 'java -jar ../utils/plantuml.jar'
45
+
41 46
 # Add any paths that contain templates here, relative to this directory.
42 47
 templates_path = ['_templates']
43 48
 
44 49
 # The suffix of source filenames.
45 50
 source_suffix = '.rst'
46 51
 
52
+# Enable automatic number display for figures.
53
+numfig = True
54
+numfig_format = {
55
+  'figure': 'Figure %s:',
56
+  'table': 'Table %s:',
57
+  'code-block': 'Listing %s:',
58
+}
59
+numfig_secnum_depth = 1
60
+
47 61
 # The encoding of source files.
48 62
 #source_encoding = 'utf-8-sig'
49 63
 

+ 766
- 0
docs/design/index.rst View File

@@ -0,0 +1,766 @@
1
+######
2
+Design
3
+######
4
+
5
+
6
+This chapter focuses on the design of the C3S Membership Administration
7
+(c3sMembership).
8
+
9
+
10
+
11
+==========
12
+Data Model
13
+==========
14
+
15
+
16
+The data model is the basis of the application. It describes all data
17
+entities, their properties and relations. Abstraction and reusability are two
18
+of the main requirements the design aims to fufill.
19
+
20
+The package diagram of the data model:
21
+
22
+.. uml::
23
+   :caption: The UML package diagram of the data model.
24
+
25
+   @startuml
26
+   package Data {
27
+       package Users
28
+       package Membership
29
+       Users <.. Membership
30
+       package MembershipProcesses
31
+       Membership <.. MembershipProcesses
32
+       package Accounting
33
+       package Shares
34
+       Membership <.. Shares
35
+       package Invoicing
36
+       package Dues
37
+       Invoicing <.. Dues
38
+       Accounting <.. Dues
39
+       Membership <.. Dues
40
+   }
41
+   @enduml
42
+
43
+
44
+
45
+-----
46
+Users
47
+-----
48
+
49
+
50
+The Users package defines the basic data entities for the application's user
51
+management. The class User stores only the necessary information for providing
52
+access to the application. User groups are used for granting permissions on
53
+certain functions of the application.
54
+
55
+.. uml::
56
+   :caption: UML class diagram of the Users package.
57
+
58
+   @startuml
59
+   package Users {
60
+
61
+       class UserGroup {
62
+           id
63
+           name
64
+       }
65
+
66
+       class User {
67
+           id
68
+           email_address
69
+           password_hash
70
+           last_password_change
71
+       }
72
+
73
+       UserGroup "*" -- "*" User
74
+       class Staff
75
+       User <|-- Staff
76
+   }
77
+   @enduml
78
+
79
+
80
+
81
+----------
82
+Membership
83
+----------
84
+
85
+
86
+The Membership package contains data entities for storing member specific
87
+information. The two types of members are natural persons and legal bodies
88
+which require different attributes.
89
+
90
+Member attributes:
91
+
92
+================= ========= ==================================================
93
+Attribute name    Data type Description
94
+================= ========= ==================================================
95
+id                Integer   (Primary key) Technical id of the data record
96
+address_line_1    String    First address line of the postal address
97
+address_line_2    String    Second address line of the postal address
98
+postal_code       String    Postal code of the postal address
99
+city              String    City of the postal address
100
+country           String    Country of the postal address
101
+locale            String    Language which the member prefers to communicate
102
+                            in
103
+status            String    Possible values:
104
+                            
105
+                            - "applied": the member applied for membership
106
+                            - "admitted": the membership was admitted
107
+                            - "given_notice": the member gave notice to resign
108
+                              from membership
109
+                            - "resigned": the membership status turns to
110
+                              resigned after the notice period ends
111
+                            - "excluded": The member was excluded
112
+                            - "died": The natural person member died
113
+                            - "liquidated": The legal body member was
114
+                              liquidated
115
+                            - "rejected": The member applied for membership
116
+                              but was rejected
117
+                            - "withdrawn": The member applied for membership
118
+                              but withdrew the application before it was
119
+                              admitted
120
+                            
121
+type              String    Possible values:
122
+                            
123
+                            - "normal": The member is a normal member  with
124
+                              full right.
125
+                            - "investing": The member is an investor (non-
126
+                              user) member.
127
+                            
128
+membership_number String    (Business key) Membership number of the member.
129
+================= ========= ==================================================
130
+
131
+NaturalPersonMember attributes:
132
+
133
+=================== ========= ================================================
134
+Attribute name      Data type Description
135
+=================== ========= ================================================
136
+first_name          String    The given name of the member
137
+last_name           String    The family name of the member
138
+title               String    The title of the member
139
+date_of_birth       Date      The date of birth of the  member
140
+is_member_of_colsoc Boolean   Indicates whether the member is member of at
141
+                              least one other collecting society
142
+name_of_colsoc      String    The names of the other collecting societies the
143
+                              member is a member of
144
+=================== ========= ================================================
145
+
146
+LegalBodyMember attributes:
147
+
148
+=================== ========= ================================================
149
+Attribute name      Data type Description
150
+=================== ========= ================================================
151
+name                String    The name of the legal body
152
+court_of_law        String    The court of law which registered the legal body
153
+registration_number String    The registration number of the legal body at the
154
+                              court of law
155
+=================== ========= ================================================
156
+
157
+The following figure shows the UML class diagram of the Membership package:
158
+
159
+.. uml::
160
+   :caption: UML class diagram of the Membership package.
161
+
162
+   @startuml
163
+   package Users {
164
+       class User
165
+   }
166
+
167
+   package Membership {
168
+
169
+       class Member {
170
+           id
171
+           address_line_1
172
+           address_line_2
173
+           postal_code
174
+           city
175
+           country
176
+           locale
177
+           status
178
+           ' applied, admitted, given_notice, resigned, excluded, died_or_liquidated, rejected, withdrawn
179
+           type
180
+           ' normal, investing
181
+           number
182
+       }
183
+
184
+       User <|-- Member
185
+
186
+       class NaturalPersonMember {
187
+           first_name
188
+           last_name
189
+           title
190
+           date_of_birth
191
+           is_member_of_colsoc
192
+           name_of_colsoc
193
+       }
194
+
195
+       Member <|-- NaturalPersonMember
196
+
197
+       class LegalBodyMember {
198
+           name
199
+           court_of_law
200
+           registration_number
201
+       }
202
+
203
+       Member <|-- LegalBodyMember
204
+   }
205
+   @enduml
206
+
207
+
208
+
209
+------
210
+Shares
211
+------
212
+
213
+
214
+Each member must buy at least one share which the C3S issues. Members can also
215
+transfer shares between each other and they can restitute them. Shares
216
+therefore only exist in terms of transfers. They come to existence when the
217
+C3S issues them and cease to exist when they are restituted.
218
+
219
+Thus, shares can be viewed from a bookkeeping perspective as something which
220
+is moved form one cooperative entity to another, i.e. from the cooperative to
221
+a member, between members of from a member back to the cooperative.
222
+
223
+The share transaction is the data entity representing such a transfer of
224
+shares. I consists of (for now) exactly two splits, each for each cooperative
225
+entity which either gives or receives the shares. A share is received when the
226
+quantity is positive and given when it is negative. The quantity sum of each
227
+share transaction must always be zero. The current quantity a cooperative
228
+entity possesses is the sum of the quantity of all its splits.
229
+
230
+ShareTransaction:
231
+
232
+================= ========= ==================================================
233
+Attribute name    Data type Description
234
+================= ========= ==================================================
235
+id                Integer   (Primary key)
236
+requested         Timestamp The time when the share transfer was requested,
237
+                            e.g. for issuing share the time of the
238
+                            membership application.
239
+valued            Timestamp The time when the share transfer was valued, i.e.
240
+                            when it became effective.
241
+booked            Timestamp The time when the share transfer was booked into
242
+                            the system.
243
+type              String    The type of the shares transfer. Possible values:
244
+
245
+                            - "acquisition": The member acquires shares from
246
+                              the C3S which issues them.
247
+                            - "transfer": Shares are transferred between
248
+                              members.
249
+                            - "restitution": The member returns shares to the
250
+                              C3S.
251
+================= ========= ==================================================
252
+
253
+ShareTransactionSplit:
254
+
255
+==================== ========= ===============================================
256
+Attribute name       Data type Description
257
+==================== ========= ===============================================
258
+id                   Integer   (Primary key) Technical id of the data record
259
+share_transaction_id Integer   (Foreign Key, ShareTransaction.id) The
260
+                               technical id of the share transaction to which
261
+                               the split belongs.
262
+member_id            Integer   (Foreign key, Member.id) The technical id of
263
+                               the member which is affected by the shares
264
+                               transfer
265
+quantity             Decimal   The quantity of shares which are transferred.
266
+                               A positive quantity implies a gain and a
267
+                               negative quantity the loss of shares. The
268
+                               quantity sum of all splits must always be zero.
269
+==================== ========= ===============================================
270
+
271
+
272
+.. uml::
273
+   :caption: UML class diagram of the Shares package.
274
+   
275
+   @startuml
276
+   package Membership {
277
+       class Member
278
+   }
279
+   package Shares {
280
+
281
+       class ShareTransaction {
282
+           id
283
+           request_timestamp
284
+           value_timestamp
285
+           booking_timestamp
286
+           type
287
+       }
288
+
289
+       class ShareTransactionSplit {
290
+           id
291
+           share_transaction_id
292
+           member_id
293
+           quantity
294
+       }
295
+
296
+       ShareTransaction "1" <-- "2" ShareTransactionSplit
297
+       Member "1" <-- "*" ShareTransactionSplit
298
+   }
299
+   @enduml
300
+
301
+Example:
302
+
303
+ShareTransaction:
304
+
305
+== ========== =========== ========== ===========
306
+id requested  valued      booked     type
307
+== ========== =========== ========== ===========
308
+1  2015-09-20 2015-09-26  2015-09-21 acquisition
309
+2  2015-09-21 2015-09-26  2015-09-21 acquisition
310
+3  2015-09-25 2015-09-26  2015-09-30 transfer   
311
+4  2015-09-27 2015-09-27  2015-09-30 restitution
312
+== ========== =========== ========== ===========
313
+
314
+ShareTransactionSplit:
315
+
316
+== ==================== ======= ========
317
+id share_transaction_id member  quantity
318
+== ==================== ======= ========
319
+1  1                    Member1 +10.0
320
+2  1                    C3S     -10.0
321
+3  2                    Member2 +20.0
322
+4  2                    C3S     -20.0
323
+5  3                    Member1 -10.0
324
+6  3                    Member2 +10.0
325
+7  4                    Member2 -30.0
326
+8  4                    C3S     +30.0
327
+== ==================== ======= ========
328
+
329
+For simplification the member_id attribute is replaced by a member attribute
330
+in this example.
331
+
332
+With share_transaction_id 1 10 shares are issued from the C3S and acquired by
333
+Member1. In the following share_transaction_id 2 C3S issues 20 shares to
334
+Member2. The transfer of 10 shares of Member1 to Member2 is booked with
335
+share_transaction_id 3 and finally in share_transaction_id 4 Member2
336
+restitutes all by then 30 shares in its possession to the C3S.
337
+
338
+
339
+--------------------
340
+Membership Processes
341
+--------------------
342
+
343
+
344
+.. uml::
345
+   :caption: UML class diagram of the Membership Processes package.
346
+
347
+   @startuml
348
+   package Membership {
349
+       class Member
350
+   }
351
+   package MembershipProcesses {
352
+       class MembershipStatusChange {
353
+           id
354
+           member_id
355
+       }
356
+
357
+       Member "1" <-- "*" MembershipStatusChange
358
+
359
+       class MembershipApplication {
360
+           id
361
+           phase
362
+           ' TODO: applied, admitted, rejected, withdrawn
363
+           ' Wie können Datumswerte für rejected und withdrawn konsistent dargestellt werden?
364
+           ' Normalisierung nötig?
365
+           signature_received_date
366
+           signature_confirmed_date,
367
+           payment_received_date
368
+           payment_confirmed_date
369
+
370
+           ' TODO: Eigentlich müsste für MembershipApplication eine Rechnung
371
+           ' ausgestellt und zu dieser ein Zahlungseingang verbucht werden.
372
+       }
373
+
374
+       MembershipStatusChange <|-- MembershipApplication
375
+
376
+       class MembershipResignation {
377
+           id
378
+           notice_date
379
+           notice_period_end_date
380
+           effective_date
381
+           withdrawn_date
382
+
383
+           ' TODO: Wird für die Rückerstattung der Anteilsgebühr ein Beleg
384
+           ' ausgestellt, ähnlich einer Storno-Rechnung? Dieser könnte mit der
385
+           ' Kündigung verknüpft werden und es könnte einen Zahlungsvorgang
386
+           ' dazu im Accounting geben.
387
+
388
+       }
389
+
390
+       MembershipStatusChange <|-- MembershipResignation
391
+
392
+       class MembershipExclusion {
393
+           id
394
+           decision_date
395
+           ' TODO: Eigenschaften mit rechtlichen Voraussetzungen abgleichen.
396
+       }
397
+
398
+       MembershipStatusChange <|-- MembershipExclusion
399
+
400
+       ' TODO: death, liquidation
401
+   }
402
+   @enduml
403
+
404
+
405
+
406
+-----------------------
407
+Membership Certificates
408
+-----------------------
409
+
410
+
411
+.. uml::
412
+   :caption: UML class diagram of the Membership Certificates package.
413
+
414
+   @startuml
415
+   package Membership {
416
+       class Member
417
+   }
418
+   package MembershipCertificates {
419
+       class MemberCertificateAccessToken {
420
+           member_id
421
+       }
422
+       AccessToken <|-- MemberCertificateAccessToken
423
+       Member "1" <-- "*" MemberCertificateAccessToken
424
+   }
425
+   @enduml
426
+
427
+
428
+
429
+----------
430
+Accounting
431
+----------
432
+
433
+
434
+.. uml::
435
+   :caption: UML class diagram of the Accounting package.
436
+
437
+   @startuml
438
+   package Accounting {
439
+       class Account {
440
+           id
441
+           name
442
+       }
443
+
444
+       class AccountTransaction {
445
+           id
446
+           description
447
+       }
448
+
449
+       class AccountTransactionSplit {
450
+           id
451
+           transaction_id
452
+           account_id
453
+       }
454
+
455
+       Account "1" <-- "*" AccountTransactionSplit
456
+       AccountTransaction "1" <-- "*" AccountTransactionSplit
457
+   }
458
+   @enduml
459
+
460
+
461
+
462
+---------
463
+Invoicing
464
+---------
465
+
466
+
467
+.. uml::
468
+   :caption: UML class diagram of the Invoicing package.
469
+
470
+   @startuml
471
+   package Accounting {
472
+       class AccountTransaction
473
+   }
474
+   package Invoicing {
475
+       class Invoice {
476
+           id
477
+           number
478
+           date
479
+           type
480
+       }
481
+
482
+       class InvoicePosition {
483
+           id
484
+           invoice_id
485
+           number
486
+           name
487
+           unit_price
488
+           currency
489
+           quantity
490
+           type
491
+           description
492
+
493
+           ' TODO: Eigentlich reicht es nicht, wenn hier Accounts referenziert
494
+           ' werden, es müssen Buchungen auf einen Account referenziert werden.
495
+           ' Pro Posten muss automatisch eine Buchung auf ein Konto erfolgen.
496
+       }
497
+
498
+       Invoice "1" <--"1..*" InvoicePosition
499
+       AccountTransaction "1" <-- "*" InvoicePosition
500
+
501
+       class InvoiceCancellation {
502
+           id
503
+           invoice_id
504
+           cancellation_invoice_id
505
+       }
506
+
507
+       Invoice "1" <-- "1" InvoiceCancellation : invoice
508
+       Invoice "1" <-- "1" InvoiceCancellation : cancellation_invoice
509
+   }
510
+   @enduml
511
+
512
+
513
+
514
+----
515
+Dues
516
+----
517
+
518
+
519
+.. uml::
520
+   :caption: UML class diagram of the Dues package.
521
+
522
+   @startuml
523
+   package Invoicing {
524
+       class Invoice
525
+   }
526
+   package Accounting {
527
+       class Account
528
+   }
529
+   package Membership {
530
+       class Member
531
+   }
532
+   package Dues {
533
+       class Dues {
534
+           id
535
+           name
536
+           description
537
+       }
538
+       class DuesInvoice {
539
+           id
540
+           member_id
541
+           dues_id
542
+       }
543
+       Invoice <|-- DuesInvoice
544
+       Member "1" <-- "*" DuesInvoice
545
+       Dues "1" <-- "*" DuesInvoice
546
+
547
+       class DuesAccount {
548
+           id
549
+           member_id
550
+           dues_id
551
+       }
552
+
553
+       Account <|-- DuesAccount
554
+       Dues "1" <-- "*" DuesAccount
555
+       Member "1" <-- "*" DuesAccount
556
+   }
557
+   @enduml
558
+
559
+
560
+
561
+Discount:
562
+
563
+- ID
564
+- Begin date
565
+- End date
566
+- Discount type
567
+- Discount amount
568
+- Member ID (FK)
569
+
570
+Invoice:
571
+
572
+- ID
573
+- Invoice number (business key)
574
+- Creation date
575
+- Invoice date
576
+- Due date
577
+- Total amount (cancellation: negative amount)
578
+- Member ID (FK)
579
+
580
+Invoice position:
581
+
582
+- ID
583
+- Description
584
+- Amount
585
+- Invoice ID (FK)
586
+
587
+Payment:
588
+
589
+- ID
590
+- Value (in EUR)
591
+- Booking date (date when the data was entered into the system)
592
+- Value date (date when the payment arrived, i.e. the cash was handed over or
593
+  the payment was received on the bank account)
594
+- Type: cash/transfer
595
+- Reference/comment (e.g. transfer purpose)
596
+- Invoice ID (FK)
597
+
598
+Membership application:
599
+
600
+- ID
601
+- Application date
602
+- Decision date
603
+- Share ID
604
+- Application incoming date
605
+- Payment incoming date
606
+- Member ID (FK)
607
+
608
+**TODO:** *Redundancy of payment incoming date if the payments are tracked in
609
+a seperate table. Resolve.*
610
+
611
+Membership resignation:
612
+
613
+- ID
614
+- Application date
615
+- Decision date
616
+- Member ID (FK)
617
+
618
+Shares should be stored in a double-entry bookkeeping style. This means that
619
+shares are always transferred. If acquired by a new member, the C3S "looses"
620
+the amount of shares and at the same time the new member "gains" them. When
621
+shares are sold between members, the selling member "looses" them and the
622
+buying member "gains" them. This leads to shares being transactions between
623
+two entities.
624
+
625
+
626
+**Todo:**
627
+
628
+- *Members: Should member inherit from user or could multiple users be
629
+  associated with a user?*
630
+
631
+- *Payments*
632
+
633
+  - *Can be assigned to:*
634
+
635
+    - *Invoices for shares: acquisition, restitution*
636
+
637
+    - *Invoices for membership fees: fee payable, discount*
638
+
639
+- *Shares*
640
+
641
+  - *Can be acquired, transferred/sold and restituted.*
642
+  
643
+  - *For transfer/sale two members are involved which must be reflected in the
644
+    data model.*
645
+  
646
+  - *Have different states: applied for and not paid yet, paid for but not
647
+    approved yet, approved, denied but not refunded, refunded*
648
+
649
+- *Invoices should be sent for the acquisition and restitution. This is not
650
+  necessarily the case at the moment.*
651
+
652
+- *Email addresses might need to be abstracted. It is necessary to store
653
+  whether an email address was confirmed. Confirmation works through the
654
+  generation of a token which is sent to the email address. If the link
655
+  including the token is clicked, the email address is verified. Therefore,
656
+  the token as well as a flag about the successful verification need to be
657
+  stored. This can happen more than once in case a password reset is
658
+  requested.*
659
+  
660
+- *Use SQLAlchemy-Continuum for keeping history where necessary.*
661
+
662
+
663
+========================
664
+Environment Architecture
665
+========================
666
+
667
+
668
+.. uml::
669
+   :caption: UML component diagram of the environment architecture.
670
+   
671
+   @startuml
672
+   [Apache] --> [c3sMembership]
673
+   [c3sMembership] --> [SQLite]
674
+   [c3sMembership] --> [Python]
675
+   @enduml
676
+
677
+
678
+========================
679
+Application Architecture
680
+========================
681
+
682
+
683
+.. uml::
684
+   :caption: UML package diagram of the application architecture.
685
+
686
+   @startuml
687
+   package Data {
688
+       package SQLAlchemy
689
+       package SQLAlchemyContinuum
690
+       SQLAlchemyContinuum ..> SQLAlchemy
691
+   }
692
+   package ExternalServices {
693
+       package GnuPG
694
+       package Email
695
+       package PdfTk
696
+       package LaTeX
697
+   }
698
+   package Logic
699
+   Logic ..> Data
700
+   Logic ..> ExternalServices
701
+   package Presentation {
702
+       package PyramidViews
703
+       package Pyramid
704
+       PyramidViews ..> Pyramid
705
+       package Deform
706
+       PyramidViews ..> Deform
707
+       package Colander
708
+       PyramidViews ..> Colander
709
+       package ChameleonTemplates
710
+       ChameleonTemplates ..> PyramidViews
711
+       package Bootstrap
712
+       ChameleonTemplates ..> Bootstrap
713
+       package jQuery
714
+       ChameleonTemplates ..> jQuery
715
+       package jQueryUI
716
+       jQueryUI ..> jQuery
717
+       ChameleonTemplates ..> jQueryUI
718
+   }
719
+   Presentation ..> Logic
720
+   @enduml
721
+
722
+.. uml::
723
+   :caption: UML package diagram of the documentation.
724
+
725
+   @startuml
726
+   package Documentation {
727
+      package Sphinx
728
+      package Graphviz
729
+      Sphinx ..> Graphviz
730
+      package PlantUML
731
+      PlantUML ..> Graphviz
732
+      Sphinx ..> PlantUML
733
+   }
734
+   @enduml
735
+
736
+
737
+- External services
738
+
739
+  - GnuPG [GnuPG]_
740
+  - Email
741
+  - PDFtk [PDFtk]_
742
+  - TeX Live [TeX_Live]_
743
+
744
+- Data layer
745
+
746
+  - SQLAlchemy ORM model [SQLAlchemy]_
747
+  - SQLAlchemy-Continuum [SQLAlchemy-Continuum]_
748
+
749
+- Logic layer
750
+- Presentation layer
751
+
752
+  - Pyramid Views [Pyramid]_
753
+  - Chameleon Templates [Chameleon]_
754
+  - Bootstrap [Bootstrap]_
755
+  - jQuery [jQuery]_
756
+  - jQueryUI [jQueryUI]_
757
+
758
+- Documentation
759
+
760
+  - Sphinx [Sphinx]_
761
+  - Graphviz [Graphviz]_, [Sphinx-Graphviz]_ 
762
+  - PlantUML [PlantUML]_
763
+
764
+
765
+**TODO**: *Elaborate on the architecture of the membership application.*
766
+

+ 53
- 0
docs/glossary/index.rst View File

@@ -0,0 +1,53 @@
1
+========
2
+Glossary
3
+========
4
+
5
+
6
+- Acquisition of membership (German "Erwerb der Mitgliedschaft")
7
+  [EU_CR_1435_2003_SCE]_ art. 14
8
+
9
+- Administrative board (German "Verwaltungsrat", "board of directors" was used
10
+  in an old version of the [C3S_Statute]_): see [EU_SCE_Statute]_ section
11
+  "Structure of the SCE", [C3S_Statute]_ § 12 II b, § 17.
12
+
13
+- Advisory board (German "Beirat"): see [C3S_Statute]_ § 12 II e.
14
+
15
+- Annual financial statement (German "Jahresabschluss"): see [C3S_Statute]_
16
+  § 22.
17
+
18
+- Arbitration court (German "Schiedsgericht", the [C3S_Statute]_ used "court
19
+  of arbitration" before): see [C3S_Statute]_ § 12 II d.
20
+
21
+- Bankrupsy (German "Konkurs"), see [EU_CR_1435_2003_SCE]_ art. 15(1)
22
+
23
+- Expulsion (German "Ausschluss"), see [EU_CR_1435_2003_SCE]_ art. 15(1)
24
+
25
+- Founding member (German "Gründungsmitglied"), see [EU_CR_1435_2003_SCE]_ art. 5(2)
26
+
27
+- Full membership (German "Ordentliche Mitgliedschaft"): see [C3S_Statute]_
28
+  § 4 I.
29
+
30
+- General assembly (German "Generalversammlung"): see [C3S_Statute]_ § 12 II
31
+  a, § 13.
32
+
33
+- Investor (non-user) member (German "investierendes (nicht nutzendes)
34
+  Mitglied"): see [EU_CR_1435_2003_SCE]_ art. 14(1)
35
+
36
+- Legal body (German "juristische Person"): [EU_CR_1435_2003_SCE]_ art. 14(1)
37
+
38
+- Managing directors (German "Geschäftsführende Direktoren", "executive
39
+  directors" was used in an old version of the [C3S_Statute]_): see
40
+  [EU_CR_1435_2003_SCE]_ Article 42 No. 1, [C3S_Statute]_ § 12 II c, § 16.
41
+
42
+- Natural person (German "natürliche Person"): see [EU_CR_1435_2003_SCE]_ art.
43
+  14(1)
44
+
45
+- Resignation (German "Austritt"): see [EU_CR_1435_2003_SCE]_ art. 15(1), [C3S_Statute]_ § 8.
46
+
47
+- Share (German "Geschäftsanteil"): see [EU_CR_1435_2003_SCE]_ art. 1(2), [C3S_Statute]_ § 9.
48
+
49
+- Statute (articles of association, German "Satzung") [C3S_Statute]_
50
+
51
+- Winding-up (German "Auflösung"): see [EU_CR_1435_2003_SCE]_ art. 15(1)
52
+
53
+

+ 11
- 16
docs/index.rst View File

@@ -1,18 +1,10 @@
1
-.. c3sMembership documentation master file, created by
2
-   sphinx-quickstart on Fri Jan 16 23:30:02 2015.
3
-   You can adapt this file completely to your liking, but it should at least
4
-   contain the root `toctree` directive.
5
-
6
-
7
-Welcome to c3sMembership's documentation!
8
-=========================================
9
-
10
-This app handles membership for C3S SCE
11
-(Cultural Commons Collecting Society SCE mit beschränkter Haftung.
12
-
13
-It used to be the form to aquire members now has functionality
14
-to cater for the whole life cycle of memberships.
1
+C3S Membership Administration (c3sMembership) documentation
2
+===========================================================
15 3
 
4
+This app handles membership administration for C3S SCE (Cultural Commons
5
+Collecting Society SCE mit beschränkter Haftung). It used to be the form to
6
+aquire members now has functionality to cater for the whole life cycle of
7
+memberships.
16 8
 
17 9
 Contents:
18 10
 
@@ -21,13 +13,16 @@ Contents:
21 13
    :numbered:
22 14
 
23 15
    overview/index
24
-   code/index
25 16
    requirements/index
17
+   design/index
26 18
    development/index
19
+   hacking/index
20
+   code/index
27 21
    models/index
28 22
    tests/index
29
-   hacking/index
30 23
    deployment/index
24
+   glossary/index
25
+   bibliography/index
31 26
    license_texts/index
32 27
 
33 28
 

+ 2
- 2
docs/overview/routes.rst View File

@@ -8,7 +8,7 @@ The routes can be configured in several ways. In c3sMembership
8 8
 the routes are declared in the main method (in __init__.py),
9 9
 so this configuration is applied during app instanatiation.
10 10
 
11
-.. list-table:: **List of Routes and Patterns**
11
+.. list-table:: List of Routes and Patterns
12 12
    :widths: 30 30 30
13 13
    :header-rows: 1
14 14
 
@@ -23,7 +23,7 @@ so this configuration is applied during app instanatiation.
23 23
      - :ref:`code_docs_accountants`
24 24
    * - static
25 25
      - /static/
26
-     - :ref:`code_docs_init`
26
+     - :ref:`code_docs_init_main`
27 27
 
28 28
 
29 29
 

+ 18
- 248
docs/requirements/index.rst View File

@@ -2,6 +2,7 @@
2 2
 Requirements Specification
3 3
 ##########################
4 4
 
5
+
5 6
 The requirements specification is used to track the requirements of the
6 7
 existing c3sMembership application as well as new requirements for future
7 8
 development.
@@ -52,9 +53,10 @@ Requirements Sources
52 53
 ====================
53 54
 
54 55
 
55
-The requirements are gathered from stakeholders and existing documentation.
56
+The requirements are gathered from stakeholders, legal regulations and
57
+existing documentation.
56 58
 
57
-Stakeholders are the uses of the application:
59
+Stakeholders are the users of the application:
58 60
 
59 61
 - Financial accounting: for working with membership applications, invoices,
60 62
   payments, analyses, working with certain membership data (e.g. adresses,
@@ -97,8 +99,8 @@ Business Processes
97 99
 ==================
98 100
 
99 101
 
100
-Business processes are the basis of the application. They would even function
101
-with pen and paper but shall be technically supported by the application.
102
+Business processes are the basis of the application. They would function with
103
+pen and paper but shall be technically supported by the application.
102 104
 Therefore, this chapter describes and explains the business processes from
103 105
 which use cases and the majority of business and technical requirements can be
104 106
 derived. The business processes themselves are independent from the technology
@@ -114,8 +116,6 @@ Acquisition of Membership
114 116
 The acquisition of membership must be approved by the administrative board
115 117
 ([EU_CR_1435_2003_SCE]_ art. 14(1)).
116 118
 
117
-
118
-
119 119
 **TODO:**
120 120
 
121 121
 - *Clarification of legal rules and regulations including the C3S statue which
@@ -183,12 +183,10 @@ Loss of Membership Upon Death
183 183
 -----------------------------
184 184
 
185 185
 
186
-Membership shall be lost upon death ([EU_CR_1435_2003_SCE]_ art. 15(1)).
186
+Membership shall be lost upon death ([EU_CR_1435_2003_SCE]_ art. 15(1), § 77
187
+[GenG]_, § 4 IV d [C3S_Statute]_).
187 188
 
188
-**TODO:**
189
-
190
-- *Death or liquidation of a legal entity or private company (§§ 77 [GenG]_,
191
-  § 4 IV d [C3S_Statute]_)*
189
+**TODO:** *Elaborate.*
192 190
 
193 191
 
194 192
 
@@ -197,12 +195,10 @@ Loss of Membership Upon Bankrupsy
197 195
 ---------------------------------
198 196
 
199 197
 
200
-Membership shall be lost upon bankrupsy ([EU_CR_1435_2003_SCE]_ art. 15(1)).
201
-
202
-**TODO:**
198
+Membership shall be lost upon bankrupsy ([EU_CR_1435_2003_SCE]_ art. 15(1), §
199
+77a [GenG]_, § 4 IV d [C3S_Statute]_).
203 200
 
204
-- *Death or liquidation of a legal entity or private company (§§ 77a [GenG]_,
205
-  § 4 IV d [C3S_Statute]_)*
201
+**TODO:** *Elaborate.*
206 202
 
207 203
 
208 204
 
@@ -460,199 +456,6 @@ Financial Accounting
460 456
 **TODO:** *Elaborate.*
461 457
 
462 458
 
463
-----------
464
-Data model
465
-----------
466
-
467
-
468
-User:
469
-
470
-- ID
471
-- Email address
472
-- Password hash
473
-
474
-User-member association:
475
-
476
-- ID
477
-- User ID (FK)
478
-- Member ID (FK)
479
-
480
-Member:
481
-
482
-- ID
483
-- Membership number (business key)
484
-- Family name
485
-- Given name
486
-- Date of birth
487
-- Email address
488
-- First address line
489
-- Second address line
490
-- Postal code
491
-- City
492
-- Country
493
-- Language
494
-- Membership type: full/investing
495
-- Is legal entity
496
-- Court of law
497
-- Registration number
498
-- Is member of other collecting society
499
-- Collecting societies of additional membership
500
-- Accouting comment
501
-
502
-Membership status:
503
-
504
-- ID
505
-- Type: acquired/resigned/exclusion
506
-- Date
507
-- Member ID (FK)
508
-
509
-Discount:
510
-
511
-- ID
512
-- Begin date
513
-- End date
514
-- Discount type
515
-- Discount amount
516
-- Member ID (FK)
517
-
518
-Invoice:
519
-
520
-- ID
521
-- Invoice number (business key)
522
-- Creation date
523
-- Invoice date
524
-- Due date
525
-- Total amount (cancellation: negative amount)
526
-- Member ID (FK)
527
-
528
-Invoice line item:
529
-
530
-- ID
531
-- Description
532
-- Amount
533
-- Invoice ID (FK)
534
-
535
-Payment:
536
-
537
-- ID
538
-- Value (in EUR)
539
-- Booking date (date when the data was entered into the system)
540
-- Value date (date when the payment arrived, i.e. the cash was handed over or
541
-  the payment was received on the bank account)
542
-- Type: cash/transfer
543
-- Reference/comment (e.g. transfer purpose)
544
-- Invoice ID (FK)
545
-
546
-Membership application:
547
-
548
-- ID
549
-- Application date
550
-- Decision date
551
-- Share ID
552
-- Application incoming date
553
-- Payment incoming date
554
-- Member ID (FK)
555
-
556
-**TODO:** *Redundancy of payment incoming date if the payments are tracked in
557
-a seperate table. Resolve.*
558
-
559
-Membership resignation:
560
-
561
-- ID
562
-- Application date
563
-- Decision date
564
-- Member ID (FK)
565
-
566
-Share:
567
-
568
-- ID
569
-- Member ID (FK)
570
-- Application date
571
-- Decision date
572
-- Status: applied, paid, approved, denied, refunded
573
-- Type: acquisition/emission, transfer, restitution/redemption
574
-- Share count (negative for restitution in case of resignation and exclusion
575
-  as well as transfer)
576
-
577
-
578
-**Todo:**
579
-
580
-- *Payments*
581
-
582
-  - *Can be assigned to:*
583
-
584
-    - *Invoices for shares: acquisition, restitution*
585
-
586
-    - *Invoices for membership fees: fee payable, discount*
587
-
588
-- *Shares*
589
-
590
-  - *Can be acquired, transferred/sold and restituted.*
591
-  
592
-  - *For transfer/sale two members are involved which must be reflected in the
593
-    data model.*
594
-  
595
-  - *Have different states: applied for and not paid yet, paid for but not
596
-    approved yet, approved, denied but not refunded, refunded*
597
-
598
-  - *Shares should be stored in a double-entry bookkeeping style. This means
599
-    that shares are always transferred. If acquired by a new member, the C3S
600
-    "looses" the amount of shares and at the same time the new member "gains"
601
-    them. When shares are sold between members, the selling member "looses"
602
-    them and the buying member "gains" them. This leads to shares being
603
-    transactions between two entities.*
604
-
605
-    *ShareTransaction:*
606
-
607
-    == ========== =========== ===========
608
-    ID ValueDate  BookingDate Type       
609
-    == ========== =========== ===========
610
-    1  2015-09-20 2015-09-26  Acquisition
611
-    2  2015-09-21 2015-09-26  Acquisition
612
-    3  2015-09-25 2015-09-26  Transfer   
613
-    4  2015-09-27 2015-09-27  Restitution
614
-    == ========== =========== ===========
615
-
616
-    *ShareTransactionSplit:*
617
-
618
-    == ============= ======= =====
619
-    ID TransactionID Account Value
620
-    == ============= ======= =====
621
-    1  1             Member1 +10
622
-    2  1             C3S     -10
623
-    3  2             Member2 +20
624
-    4  2             C3S     -20
625
-    5  3             Member1 -10
626
-    6  3             Member2 +10
627
-    7  4             Member2 -30
628
-    8  4             C3S     +30
629
-    == ============= ======= =====
630
-
631
-- *Invoices should be sent for the acquisition and restitution. This is not
632
-  necessarily the case at the moment.*
633
-
634
-- *Email addresses might need to be abstracted. It is necessary to store
635
-  whether an email address was confirmed. Confirmation works through the
636
-  generation of a token which is sent to the email address. If the link
637
-  including the token is clicked, the email address is verified. Therefore,
638
-  the token as well as a flag about the successful verification need to be
639
-  stored. This can happen more than once in case a password reset is
640
-  requested.*
641
-  
642
-- *Check whether the changes to a member dataset must be stored in an
643
-  audit-proof way. It could also lead to privacy issues and needs to be
644
-  legally clarified.*
645
-
646
-- *Legal entities can also become members. Therefore, given name, family name
647
-  and name of the company or association need to be stored somehow.*
648
-
649
-  - *One solution would be to store all fields in the same data entity and
650
-    fill the appropriate ones.*
651
-  
652
-  - *Another solution is to put these fields into two additional data entities
653
-    and join them when necessary.*
654
-
655
-
656 459
 
657 460
 ======================
658 461
 Technical Requirements
@@ -707,14 +510,6 @@ Quality Requiremements
707 510
 
708 511
 
709 512
 
710
-===========
711
-Open Topics
712
-===========
713
-
714
-TODO...
715
-
716
-
717
-
718 513
 ========
719 514
 Glossary
720 515
 ========
@@ -732,8 +527,8 @@ Glossary
732 527
 - Annual financial statement (German "Jahresabschluss"): see [C3S_Statute]_
733 528
   § 22.
734 529
 
735
-- Arbitration court (German "Schiedsgericht", the [C3S_Statue]_ used "court of
736
-  arbitration" before): see [C3S_Statute]_ § 12 II d.
530
+- Arbitration court (German "Schiedsgericht", the [C3S_Statute]_ used "court
531
+  of arbitration" before): see [C3S_Statute]_ § 12 II d.
737 532
 
738 533
 - Bankrupsy (German "Konkurs"), see [EU_CR_1435_2003_SCE]_ art. 15(1)
739 534
 
@@ -741,10 +536,6 @@ Glossary
741 536
 
742 537
 - Founding member (German "Gründungsmitglied"), see [EU_CR_1435_2003_SCE]_ art. 5(2)
743 538
 
744
-- Managing directors (German "Geschäftsführende Direktoren", "executive
745
-  directors" was used in an old version of the [C3S_Statute]_): see
746
-  [EU_CR_1435_2003_SCE]_ Article 42 No. 1, [C3S_Statute]_ § 12 II c, § 16.
747
-
748 539
 - Full membership (German "Ordentliche Mitgliedschaft"): see [C3S_Statute]_
749 540
   § 4 I.
750 541
 
@@ -756,6 +547,10 @@ Glossary
756 547
 
757 548
 - Legal body (German "juristische Person"): [EU_CR_1435_2003_SCE]_ art. 14(1)
758 549
 
550
+- Managing directors (German "Geschäftsführende Direktoren", "executive
551
+  directors" was used in an old version of the [C3S_Statute]_): see
552
+  [EU_CR_1435_2003_SCE]_ Article 42 No. 1, [C3S_Statute]_ § 12 II c, § 16.
553
+
759 554
 - Natural person (German "natürliche Person"): see [EU_CR_1435_2003_SCE]_ art.
760 555
   14(1)
761 556
 
@@ -768,28 +563,3 @@ Glossary
768 563
 - Winding-up (German "Auflösung"): see [EU_CR_1435_2003_SCE]_ art. 15(1)
769 564
 
770 565
 
771
-============
772
-Bibliography
773
-============
774
-
775
-
776
-.. [C3S_Statute] C3S: Articles of Association of the Cultural Commons
777
-   Collecting Society SCE (C3S). http://archive.c3s.cc/legal/C3S_en_v1.0.pdf,
778
-   https://archive.c3s.cc/aktuell/legal/C3S_SCE_de.pdf.
779
-
780
-.. [EU_CR_1435_2003_SCE] The council of the European Union, Council Regulation
781
-   (EC) No 1435/2003 of 22 July 2003 on the Statute for a European Cooperative
782
-   Society (SCE),
783
-   http://eur-lex.europa.eu/legal-content/EN/TXT/?uri=CELEX:32003R1435
784
-
785
-.. [EU_SCE_Statute] Statute for a European Cooperative Society, EUR-Lex,
786
-   http://eur-lex.europa.eu/legal-content/EN/TXT/?uri=uriserv%3Al26018
787
-
788
-.. [GenG] http://www.gesetze-im-internet.de/geng/
789
-
790
-.. [Pyramid]
791
-   http://docs.pylonsproject.org/projects/pyramid/en/latest/narr/introduction.html#pyramid-and-other-web-frameworks
792
-
793
-.. [Wiki_Kano] Wikipedia: Kano model.
794
-   https://en.wikipedia.org/w/index.php?title=Kano_model&oldid=678655771
795
-

Loading…
Cancel
Save