jewerly_system 1.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
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