jewerly_system 1.0.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (31) hide show
  1. checksums.yaml +7 -0
  2. data/CODE_OF_CONDUCT.md +84 -0
  3. data/Gemfile +13 -0
  4. data/LICENSE.txt +21 -0
  5. data/README.md +1 -0
  6. data/jewelry_system.gemspec +17 -0
  7. data/lib/jewerly_system/version.rb +5 -0
  8. data/lib/jewerly_system.rb +10 -0
  9. data/lib/source/customer/controllers/customer_input_form_controller_create.rb +44 -0
  10. data/lib/source/customer/controllers/customer_input_form_controller_edit.rb +52 -0
  11. data/lib/source/customer/controllers/customer_list_controller.rb +104 -0
  12. data/lib/source/customer/customer_db_data_source.rb +63 -0
  13. data/lib/source/customer/ui/customer_input_form.rb +70 -0
  14. data/lib/source/customer/ui/customer_list_view.rb +159 -0
  15. data/lib/source/master/controllers/master_controller.rb +57 -0
  16. data/lib/source/master/controllers/master_input_form_controller_create.rb +44 -0
  17. data/lib/source/master/controllers/master_input_form_controller_edit.rb +53 -0
  18. data/lib/source/master/controllers/master_list_controller.rb +101 -0
  19. data/lib/source/master/master_db_data_source.rb +65 -0
  20. data/lib/source/master/ui/master_input_form.rb +72 -0
  21. data/lib/source/master/ui/master_list_view.rb +163 -0
  22. data/lib/source/models/customer.rb +37 -0
  23. data/lib/source/models/master.rb +32 -0
  24. data/lib/source/models/product.rb +31 -0
  25. data/lib/source/models/student.rb +102 -0
  26. data/lib/source/models/student_base.rb +100 -0
  27. data/lib/source/models/student_short.rb +50 -0
  28. data/lib/source/state_holders/list_state_notifier.rb +47 -0
  29. data/lib/source/util/logger_holder.rb +24 -0
  30. data/sig/jewerly_system.rbs +4 -0
  31. metadata +86 -0
@@ -0,0 +1,57 @@
1
+ # # frozen_string_literal: true
2
+ #
3
+ # require './jew/views/main_window'
4
+ # require './jew/repositories/student_repository'
5
+ # require './jew/repositories/adapters/db_source_adapter'
6
+ # require './jew/repositories/containers/data_list_student_short'
7
+ # require './jew/state_holders/list_state_notifier'
8
+ # require_relative '../author_db_data_source'
9
+ # require 'win32api'
10
+ #
11
+ # class AuthorController
12
+ #
13
+ #
14
+ # def initialize(view)
15
+ # @view = view
16
+ # @state_notifier = ListStateNotifier.new
17
+ # @state_notifier.add_listener(@view)
18
+ # @author_rep = AuthorDBDataSource.new
19
+ # end
20
+ #
21
+ # def edit (number)
22
+ # master = @state_notifier.get(number)
23
+ # puts master.id
24
+ # # @view.show_edit_dialog(master)
25
+ # end
26
+ #
27
+ # def add (number)
28
+ # master = @state_notifier.get(number)
29
+ # puts master
30
+ # # @view.show_add_dialog(master)
31
+ # end
32
+ #
33
+ #
34
+ # def remove (number)
35
+ # master = @state_notifier.get(number)
36
+ # puts master
37
+ # # @view.show_remove_dialog(master)
38
+ # end
39
+ #
40
+ # def refresh_data(page, per_page)
41
+ # items = @author_rep.get_list(per_page, page, 'FirstName', 'ASC' )
42
+ # @state_notifier.set_all(items)
43
+ # @view.update_student_count(@author_rep.count)
44
+ # end
45
+ #
46
+ # def refresh()
47
+ # items = @author_rep.get_list(per_page, page, 'FirstName', 'ASC' )
48
+ # @state_notifier.set_all(items)
49
+ # @view.update_student_count(@author_rep.count)
50
+ # end
51
+ #
52
+ # def on_db_conn_error
53
+ # api = Win32API.new('user32', 'MessageBox', ['L', 'P', 'P', 'L'], 'I')
54
+ # api.call(0, "No connection to DB", "Error", 0)
55
+ # exit(false)
56
+ # end
57
+ # end
@@ -0,0 +1,44 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'win32api'
4
+
5
+ class MasterInputFormControllerCreate
6
+ def initialize(parent_controller)
7
+ @parent_controller = parent_controller
8
+ @author_rep = MasterDBDataSource.new
9
+ end
10
+
11
+ def set_view(view)
12
+ @view = view
13
+ end
14
+
15
+ def on_view_created
16
+ # begin
17
+ # @student_rep = StudentRepository.new(DBSourceAdapter.new)
18
+ # rescue Mysql2::Error::ConnectionError
19
+ # on_db_conn_error
20
+ # end
21
+ end
22
+
23
+ def process_fields(fields)
24
+ begin
25
+ puts fields
26
+ item = Master.new(-1, *fields.values)
27
+ puts item
28
+ item = @author_rep.add(item)
29
+ @parent_controller.state_notifier.add(item)
30
+ @view.close
31
+ rescue ArgumentError => e
32
+ api = Win32API.new('user32', 'MessageBox', ['L', 'P', 'P', 'L'], 'I')
33
+ api.call(0, e.message, 'Error', 0)
34
+ end
35
+ end
36
+
37
+ private
38
+
39
+ def on_db_conn_error
40
+ api = Win32API.new('user32', 'MessageBox', ['L', 'P', 'P', 'L'], 'I')
41
+ api.call(0, "No connection to DB", "Error", 0)
42
+ @view.close
43
+ end
44
+ end
@@ -0,0 +1,53 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'win32api'
4
+
5
+ class MasterInputFormControllerEdit
6
+ def initialize(parent_controller, item)
7
+ @parent_controller = parent_controller
8
+ @item = item
9
+ @author_rep = MasterDBDataSource.new
10
+ end
11
+
12
+ def set_view(view)
13
+ @view = view
14
+ end
15
+
16
+ def on_view_created
17
+ # begin
18
+ # @student_rep = StudentRepository.new(DBSourceAdapter.new)
19
+ # rescue Mysql2::Error::ConnectionError
20
+ # on_db_conn_error
21
+ # end
22
+
23
+ # @item = @author_rep.get(@item_id)
24
+ # @view.make_readonly(:git, :telegram, :email, :phone)
25
+ populate_fields(@item)
26
+ end
27
+
28
+ def populate_fields(item)
29
+ @view.set_value(:first_name, item.first_name)
30
+ @view.set_value(:last_name, item.last_name)
31
+ @view.set_value(:father_name, item.father_name)
32
+ end
33
+
34
+ def process_fields(fields)
35
+ begin
36
+ item = Master.new(@item.master_id, *fields.values)
37
+ item = @author_rep.change(item)
38
+ @parent_controller.state_notifier.replace(@item, item)
39
+ @view.close
40
+ rescue ArgumentError => e
41
+ api = Win32API.new('user32', 'MessageBox', ['L', 'P', 'P', 'L'], 'I')
42
+ api.call(0, e.message, 'Error', 0)
43
+ end
44
+ end
45
+
46
+ private
47
+
48
+ def on_db_conn_error
49
+ api = Win32API.new('user32', 'MessageBox', ['L', 'P', 'P', 'L'], 'I')
50
+ api.call(0, "No connection to DB", "Error", 0)
51
+ @view.close
52
+ end
53
+ end
@@ -0,0 +1,101 @@
1
+ # frozen_string_literal: true
2
+
3
+ require './jew/state_holders/list_state_notifier'
4
+ require_relative '../ui/master_input_form'
5
+ require_relative 'master_input_form_controller_create'
6
+ require_relative 'master_input_form_controller_edit'
7
+ require_relative '../master_db_data_source'
8
+ require 'win32api'
9
+
10
+ class MasterListController
11
+
12
+ attr_reader :state_notifier;
13
+ def initialize(view)
14
+ LoggerHolder.instance.debug('MasterListController: init start')
15
+ @view = view
16
+ @state_notifier = ListStateNotifier.new
17
+ @state_notifier.add_listener(@view)
18
+ @author_rep = MasterDBDataSource.new
19
+
20
+ @sort_columns = %w[MasterID FirstName LastName FatherName]
21
+ @sort_by = @sort_columns.first
22
+
23
+ @father_name_filter_columns = [nil, true, false]
24
+ @father_name_filter = @father_name_filter_columns.first
25
+ LoggerHolder.instance.debug('MasterListController: init done')
26
+ end
27
+
28
+ def on_view_created
29
+ # begin
30
+ # @student_rep = StudentRepository.new(DBSourceAdapter.new)
31
+ # rescue Mysql2::Error::ConnectionError
32
+ # on_db_conn_error
33
+ # end
34
+ end
35
+
36
+ def show_view
37
+ @view.create.show
38
+ end
39
+
40
+ def show_modal_add
41
+ LoggerHolder.instance.debug('MasterListController: showing modal (add)')
42
+ controller = MasterInputFormControllerCreate.new(self)
43
+ view = MasterInputForm.new(controller)
44
+ controller.set_view(view)
45
+ view.create.show
46
+ end
47
+
48
+ def show_modal_edit(current_page, per_page, selected_row)
49
+ # item_num = (current_page - 1) * per_page + selected_row
50
+
51
+ item = @state_notifier.get(selected_row)
52
+
53
+ controller = MasterInputFormControllerEdit.new(self, item)
54
+ view = MasterInputForm.new(controller)
55
+ controller.set_view(view)
56
+ view.create.show
57
+ end
58
+
59
+ def delete_selected(current_page, per_page, selected_row)
60
+ begin
61
+ LoggerHolder.instance.debug('MasterListController: deleting selected master')
62
+ item = @state_notifier.get(selected_row)
63
+ @author_rep.delete(item.master_id)
64
+ @state_notifier.delete(item)
65
+ rescue
66
+ api = Win32API.new('user32', 'MessageBox', ['L', 'P', 'P', 'L'], 'I')
67
+ api.call(0, "You cannot delete the master because it is associated with some product", "Error", 0)
68
+ end
69
+ end
70
+
71
+ def refresh_data(page, per_page)
72
+ LoggerHolder.instance.debug('MasterListController: refreshing data...')
73
+ items = @author_rep.get_list(per_page, page, @sort_by, 'ASC', @father_name_filter)
74
+ @state_notifier.set_all(items)
75
+ @view.update_student_count(@author_rep.count)
76
+ end
77
+
78
+ def sort(page, per_page, sort_index)
79
+ LoggerHolder.instance.debug('MasterListController: filter start...')
80
+ @sort_by = @sort_columns[sort_index]
81
+ refresh_data(page, per_page)
82
+ LoggerHolder.instance.debug('MasterListController: filter end.')
83
+ end
84
+
85
+ def filter_father_name(page, per_page, filter_index)
86
+ LoggerHolder.instance.debug('MasterListController: filter start...')
87
+ @father_name_filter = @father_name_filter_columns[filter_index]
88
+ LoggerHolder.instance.debug('MasterListController: filter end.')
89
+ refresh_data(page, per_page)
90
+ end
91
+
92
+ private
93
+
94
+ def on_db_conn_error(error)
95
+ LoggerHolder.instance.error('MasterListController: DB connection error:')
96
+ LoggerHolder.instance.error(error.message)
97
+ api = Win32API.new('user32', 'MessageBox', ['L', 'P', 'P', 'L'], 'I')
98
+ api.call(0, "No connection to DB", "Error", 0)
99
+ exit(false)
100
+ end
101
+ end
@@ -0,0 +1,65 @@
1
+ require 'mysql2'
2
+ require_relative '../data_sources/db_client'
3
+
4
+ class MasterDBDataSource
5
+ def initialize
6
+ @client = DBClient.instance
7
+ end
8
+
9
+ def add(master)
10
+ query = "INSERT INTO Master (FirstName, LastName, FatherName) VALUES ('#{master.first_name}', '#{master.last_name}', #{master.father_name.nil? ? 'NULL' : "'#{master.father_name}'"})"
11
+ @client.query(query)
12
+ master_id = @client.last_id
13
+ get(master_id)
14
+ end
15
+
16
+ def change(master)
17
+ query = "UPDATE Master SET FirstName='#{master.first_name}', LastName='#{master.last_name}', FatherName=#{master.father_name.nil? ? 'NULL' : "'#{master.father_name}'"} WHERE MasterID=#{master.master_id}"
18
+ @client.query(query)
19
+ get(master.master_id)
20
+ end
21
+
22
+ def delete(id)
23
+ query = "DELETE FROM Master WHERE MasterID=#{id}"
24
+ @client.query(query)
25
+ end
26
+
27
+ def get(id)
28
+ query = "SELECT * FROM Master WHERE MasterID=#{id}"
29
+ result = @client.query(query).first
30
+ if result
31
+ Master.new(result[:'MasterID'], result[:'FirstName'], result[:'LastName'], result[:'FatherName'])
32
+ else
33
+ nil
34
+ end
35
+ end
36
+
37
+ def get_list(page_size, page_num, sort_field, sort_direction, has_father_name = nil)
38
+ offset = (page_num - 1) * page_size
39
+ query = "SELECT * FROM Master"
40
+
41
+ if has_father_name == true
42
+ query += " WHERE FatherName IS NOT NULL"
43
+ elsif has_father_name == false
44
+ query += " WHERE FatherName IS NULL"
45
+ end
46
+
47
+ query += " ORDER BY #{sort_field} #{sort_direction} LIMIT #{page_size} OFFSET #{offset}"
48
+ results = @client.query(query)
49
+
50
+ masters = []
51
+ results.each do |result|
52
+ masters << Master.new(result[:'MasterID'], result[:'FirstName'], result[:'LastName'], result[:'FatherName'])
53
+ end
54
+
55
+ masters
56
+ end
57
+
58
+
59
+ def count
60
+ query = "SELECT COUNT(*) FROM Master"
61
+ result = @client.query(query).first
62
+
63
+ result[:'COUNT(*)']
64
+ end
65
+ end
@@ -0,0 +1,72 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'glimmer-dsl-libui'
4
+ require_relative '../controllers/master_input_form_controller_create'
5
+ require './jew/models/master'
6
+ require './jew/util/logger_holder'
7
+ require 'win32api'
8
+
9
+ class MasterInputForm
10
+ include Glimmer
11
+
12
+ def initialize(controller, existing_student = nil)
13
+ @item = existing_student.to_hash unless existing_student.nil?
14
+ @controller = controller
15
+ @entries = {}
16
+ LoggerHolder.instance.debug('MasterInputForm: initialized')
17
+ end
18
+
19
+ def on_create
20
+ @controller.on_view_created
21
+ end
22
+
23
+ def create
24
+ @root_container = window('Мастер', 300, 70) {
25
+ resizable false
26
+
27
+ vertical_box {
28
+ @student_form = form {
29
+ stretchy false
30
+
31
+ fields = [[:first_name, 'Имя мастера'], [:last_name, 'Фамилия мастера'], [:father_name, 'Отчество мастера']]
32
+
33
+ fields.each do |field|
34
+ @entries[field[0]] = entry {
35
+ label field[1]
36
+ }
37
+ end
38
+ }
39
+
40
+ button('Сохранить') {
41
+ stretchy false
42
+
43
+ on_clicked {
44
+ values = @entries.transform_values { |v| v.text.force_encoding("utf-8").strip }
45
+ values.transform_values! { |v| v.empty? ? nil : v}
46
+
47
+ @controller.process_fields(values)
48
+ LoggerHolder.instance.debug('MasterInputForm: adding/edit master to DB')
49
+ }
50
+ }
51
+ }
52
+ }
53
+ on_create
54
+ @root_container
55
+ end
56
+
57
+ def set_value(field, value)
58
+ return unless @entries.include?(field)
59
+
60
+ @entries[field].text = value
61
+ end
62
+
63
+ def make_readonly(*fields)
64
+ fields.each do |field|
65
+ @entries[field].read_only = true
66
+ end
67
+ end
68
+
69
+ def close
70
+ @root_container.destroy
71
+ end
72
+ end
@@ -0,0 +1,163 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'glimmer-dsl-libui'
4
+ require_relative '../controllers/master_list_controller'
5
+ require_relative '../controllers/master_controller'
6
+ require_relative 'master_input_form'
7
+ require './jew/util/logger_holder'
8
+
9
+ class MasterListView
10
+ include Glimmer
11
+
12
+ PAGE_SIZE = 20
13
+
14
+ def initialize
15
+ @controller = MasterListController.new(self)
16
+ @current_page = 1
17
+ @total_count = 0
18
+ LoggerHolder.instance.debug('MasterListView: initialize')
19
+ end
20
+
21
+ def on_create
22
+ @controller.on_view_created
23
+ @controller.refresh_data(@current_page, PAGE_SIZE)
24
+ end
25
+
26
+ def update(masters)
27
+ @items = []
28
+
29
+ i = 0
30
+ item_num = 0
31
+ masters.each do |author|
32
+ i += 1
33
+ item_num = ((@current_page - 1) * PAGE_SIZE) + i
34
+ @items << Struct.new(:№, :id, :имя_мастера, :фамилия_мастера, :отчество_мастера).new(item_num, author.master_id, author.first_name, author.last_name, author.father_name)
35
+ end
36
+
37
+ @table.model_array = @items
38
+ @page_label.text = "#{@current_page} / #{(@total_count / PAGE_SIZE.to_f).ceil}"
39
+ end
40
+
41
+ def update_student_count(new_cnt)
42
+ @total_count = new_cnt
43
+ @page_label.text = "#{@current_page} / #{(@total_count / PAGE_SIZE.to_f).ceil}"
44
+ end
45
+
46
+ def create
47
+
48
+ root_container = horizontal_box {
49
+ # Секция 1
50
+ vertical_box {
51
+ stretchy false
52
+
53
+ vertical_box {
54
+ stretchy false
55
+
56
+ label {
57
+ text 'Отчество'
58
+ }
59
+ combobox { |c|
60
+ items ['Не важно','Есть','Нет']
61
+ selected 0
62
+ on_selected do
63
+ @controller.filter_father_name(@current_page, PAGE_SIZE, c.selected)
64
+ end
65
+ }
66
+
67
+ label {
68
+ text 'Сортировка'
69
+ }
70
+ combobox { |c|
71
+ items ['ID','Имя мастера','Фамилия мастера', 'Отчество мастера']
72
+ selected 0
73
+ on_selected do
74
+ @controller.sort(@current_page, PAGE_SIZE, c.selected)
75
+ end
76
+ }
77
+ }
78
+
79
+
80
+ }
81
+
82
+ # Секция 2
83
+ vertical_box {
84
+ @table = refined_table(
85
+ table_editable: false,
86
+ filter: lambda do |row_hash, query|
87
+ utf8_query = query.force_encoding("utf-8")
88
+ row_hash['Имя мастера'].include?(utf8_query)
89
+ end,
90
+ table_columns: {
91
+ '№' => :text,
92
+ 'ID' => :text,
93
+ 'Имя мастера' => :text,
94
+ 'Фамилия мастера' => :text,
95
+ 'Отчество мастера' => :text,
96
+ },
97
+ per_page: PAGE_SIZE,
98
+
99
+ )
100
+
101
+ @pages = horizontal_box {
102
+ stretchy false
103
+
104
+ button("<") {
105
+ stretchy true
106
+
107
+ on_clicked do
108
+ @current_page = [@current_page - 1, 1].max
109
+ @controller.refresh_data(@current_page, PAGE_SIZE)
110
+ end
111
+
112
+ }
113
+ @page_label = label("...") { stretchy false }
114
+ button(">") {
115
+ stretchy true
116
+
117
+ on_clicked do
118
+ @current_page = [@current_page + 1, (@total_count / PAGE_SIZE.to_f).ceil].min
119
+ @controller.refresh_data(@current_page, PAGE_SIZE)
120
+ end
121
+ }
122
+ }
123
+ }
124
+
125
+ # Секция 3
126
+ vertical_box {
127
+ stretchy false
128
+
129
+ button('Добавить') {
130
+ stretchy false
131
+
132
+ on_clicked {
133
+ @controller.show_modal_add
134
+ }
135
+ }
136
+ button('Изменить') {
137
+ stretchy false
138
+
139
+ on_clicked {
140
+ @controller.show_modal_edit(@current_page, PAGE_SIZE, @table.selection) unless @table.selection.nil?
141
+ }
142
+ }
143
+ button('Удалить') {
144
+ stretchy false
145
+
146
+ on_clicked {
147
+ @controller.delete_selected(@current_page, PAGE_SIZE, @table.selection) unless @table.selection.nil?
148
+ @controller.refresh_data(@current_page, PAGE_SIZE)
149
+ }
150
+ }
151
+ button('Обновить') {
152
+ stretchy false
153
+
154
+ on_clicked {
155
+ @controller.refresh_data(@current_page, PAGE_SIZE)
156
+ }
157
+ }
158
+ }
159
+ }
160
+ on_create
161
+ root_container
162
+ end
163
+ end
@@ -0,0 +1,37 @@
1
+ class Customer
2
+ EMAIL_REGEX = /\A[\w+\-.]+@[a-z\d\-]+(\.[a-z\d\-]+)*\.[a-z]+\z/i
3
+
4
+ attr_reader :customer_id, :name, :email
5
+
6
+ def initialize(customer_id, name, email = nil)
7
+ validate_null('customer_id', customer_id)
8
+ validate_null('name', name)
9
+
10
+ validate_name_length(name)
11
+ validate_email(email)
12
+
13
+ @customer_id = customer_id
14
+ @name = name
15
+ @email = email
16
+ end
17
+
18
+ private
19
+
20
+ def validate_null(name, value)
21
+ if value.nil?
22
+ raise ArgumentError, "The argument '#{name}' cannot be null"
23
+ end
24
+ end
25
+
26
+ def validate_name_length(name)
27
+ if name.length > 100
28
+ raise ArgumentError, "The number of characters in the name exceeds 100: #{name}"
29
+ end
30
+ end
31
+
32
+ def validate_email(email)
33
+ if email && !email.match?(EMAIL_REGEX)
34
+ raise ArgumentError, "Invalid email format: #{email}"
35
+ end
36
+ end
37
+ end
@@ -0,0 +1,32 @@
1
+ class Master
2
+ attr_reader :master_id, :first_name, :last_name, :father_name
3
+
4
+ def initialize(master_id, first_name, last_name, father_name = nil)
5
+ validate_null('master_id', master_id)
6
+ validate_null('first_name', first_name)
7
+ validate_null('last_name', last_name)
8
+
9
+ validate_name_length(first_name, last_name, father_name)
10
+
11
+ @master_id = master_id
12
+ @first_name = first_name
13
+ @last_name = last_name
14
+ @father_name = father_name
15
+ end
16
+
17
+ private
18
+
19
+ def validate_null(name, value)
20
+ if value.nil?
21
+ raise ArgumentError, "The argument '#{name}' cannot be null"
22
+ end
23
+ end
24
+
25
+ def validate_name_length(first_name, last_name, father_name)
26
+ [first_name, last_name, father_name].each do |name|
27
+ if name && name.length > 50
28
+ raise ArgumentError, "The number of characters in the name exceeds 50: #{name}"
29
+ end
30
+ end
31
+ end
32
+ end
@@ -0,0 +1,31 @@
1
+ class Product
2
+ attr_reader :product_id, :title, :master_id, :customer_id
3
+
4
+ def initialize(product_id, title, master_id, customer_id)
5
+ validate_null('product_id', product_id)
6
+ validate_null('title', title)
7
+ validate_null('master_id', master_id)
8
+ validate_null('customer_id', customer_id)
9
+
10
+ validate_title_length(title)
11
+
12
+ @product_id = product_id
13
+ @title = title
14
+ @master_id = master_id
15
+ @customer_id = customer_id
16
+ end
17
+
18
+ private
19
+
20
+ def validate_null(name, value)
21
+ if value.nil?
22
+ raise ArgumentError, "Argument '#{name}' cannot be null"
23
+ end
24
+ end
25
+
26
+ def validate_title_length(title)
27
+ if title.length > 255
28
+ raise ArgumentError, "The title contains more than 255 characters: #{title}"
29
+ end
30
+ end
31
+ end