ecom_core 1.2.17 → 1.2.22
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.
- checksums.yaml +4 -4
- data/app/models/ecom/core/attendance_sheet.rb +18 -2
- data/app/models/ecom/core/company.rb +23 -0
- data/app/models/ecom/core/crew_time.rb +45 -17
- data/app/models/ecom/core/overtime_sheet.rb +22 -2
- data/app/models/ecom/core/payment.rb +2 -0
- data/app/models/ecom/core/payroll.rb +10 -0
- data/db/migrate/20190101085530_create_ecom_core_companies.rb +1 -1
- data/db/migrate/20191225140433_create_ecom_core_attendance_sheets.rb +3 -2
- data/db/migrate/20200410090701_create_ecom_core_overtime_sheets.rb +3 -2
- data/lib/ecom/core/version.rb +1 -1
- data/spec/factories/ecom/core/application_modules.rb +3 -1
- data/spec/factories/ecom/core/attendance_sheets.rb +3 -2
- data/spec/factories/ecom/core/companies.rb +9 -1
- data/spec/factories/ecom/core/currencies.rb +6 -2
- data/spec/factories/ecom/core/equipment.rb +3 -1
- data/spec/factories/ecom/core/equipment_categories.rb +3 -1
- data/spec/factories/ecom/core/equipment_locations.rb +3 -1
- data/spec/factories/ecom/core/lookups.rb +3 -1
- data/spec/factories/ecom/core/overtime_sheets.rb +3 -2
- data/spec/factories/ecom/core/overtime_types.rb +3 -1
- data/spec/factories/ecom/core/product_groups.rb +3 -1
- data/spec/factories/ecom/core/product_types.rb +3 -1
- data/spec/factories/ecom/core/resource_types.rb +3 -1
- data/spec/factories/ecom/core/stakeholder_types.rb +3 -1
- data/spec/factories/ecom/core/stakeholders.rb +3 -1
- metadata +2 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 226d69727b3ecb4a8edfb5fc4c4a92fb6c91ff999d928f5509acaa9da26ec19a
|
4
|
+
data.tar.gz: bb35268fa5cda39ace0d06240ad53f06da6845076df4e23041b6bd3ce147a36b
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 6069a79e8aa94eae4e49e9a68d0783a366fdb92c355d3d73ed4fdbd0ab2feaeb3f2c4c3678d8fda11a0cf7d25b783a89677d213752b1c9e5909e84f3491e52c6
|
7
|
+
data.tar.gz: cf82927e16d1809c43760e3238202a87df93c10ae89b06cc482e598758c933d4368ecab45b8fb1d38c4dfec10180ae35e87e8ad785bde60d6329eac9e4c81c2f
|
@@ -61,7 +61,7 @@ module Ecom
|
|
61
61
|
|
62
62
|
raise 'There is no open attendance sheet to submit.' if sheet.nil?
|
63
63
|
|
64
|
-
sheet.
|
64
|
+
sheet.submitted_at = DateTime.now
|
65
65
|
sheet.status = StatusConstants::SUBMITTED
|
66
66
|
sheet.save
|
67
67
|
sheet
|
@@ -75,11 +75,27 @@ module Ecom
|
|
75
75
|
sheet = AttendanceSheet.open_for_date(date, project_id)
|
76
76
|
raise 'There is no open attendance sheet to submit for the selected day.' unless sheet
|
77
77
|
|
78
|
-
sheet.
|
78
|
+
sheet.submitted_at = DateTime.now
|
79
79
|
sheet.status = StatusConstants::SUBMITTED
|
80
80
|
sheet.save
|
81
81
|
sheet
|
82
82
|
end
|
83
|
+
|
84
|
+
def submit
|
85
|
+
raise 'Attendance sheet is not open and cannot be submitted' if status != StatusConstants::OPEN
|
86
|
+
|
87
|
+
self.submitted_at = DateTime.now
|
88
|
+
self.status = StatusConstants::SUBMITTED
|
89
|
+
save
|
90
|
+
end
|
91
|
+
|
92
|
+
def approve
|
93
|
+
raise 'Attendance sheet is not submitted and cannot be approved' unless status == StatusConstants::SUBMITTED
|
94
|
+
|
95
|
+
self.status = StatusConstants::APPROVED
|
96
|
+
self.approved_at = DateTime.now
|
97
|
+
save
|
98
|
+
end
|
83
99
|
end
|
84
100
|
end
|
85
101
|
end
|
@@ -1,10 +1,33 @@
|
|
1
1
|
module Ecom
|
2
2
|
module Core
|
3
3
|
class Company < ApplicationRecord
|
4
|
+
# The settings attribute is a json string containing setting key value
|
5
|
+
# pairs. The valid settings are:
|
6
|
+
# payroll_periods => a list of payroll period values. Each
|
7
|
+
# payroll period has the following entries:
|
8
|
+
# order: A numeric value indicating the order of the period.
|
9
|
+
# date: A numeric value indicating the date of each month which
|
10
|
+
# upto which timesheet data is used for payroll calculation
|
11
|
+
# for the specific period.
|
12
|
+
# comparison: One of [:exact, :days_before_month_end]. :exact will
|
13
|
+
# force calculations to be made upto the exact mentioned date
|
14
|
+
# value, whereas :days_before_month_end is often used for last
|
15
|
+
# period payroll calculation and we want to specify on which date
|
16
|
+
# before the end of the month we have to limit timesheet data to
|
17
|
+
# calculate payroll. E.g. 3 days before month end would be 27th
|
18
|
+
# for september and 28th for october.
|
19
|
+
#
|
20
|
+
# period_count - the total number of periods per month for payroll.
|
21
|
+
# this value should align with the defined payroll periods.
|
22
|
+
#
|
4
23
|
has_many :projects
|
5
24
|
|
6
25
|
validates :code, :name, :address, :telephone, presence: true
|
7
26
|
validates :code, :name, uniqueness: true
|
27
|
+
|
28
|
+
def valid_settings?
|
29
|
+
settings['payroll_periods'].count == settings['period_count']
|
30
|
+
end
|
8
31
|
end
|
9
32
|
end
|
10
33
|
end
|
@@ -2,15 +2,15 @@ module Ecom
|
|
2
2
|
module Core
|
3
3
|
class CrewTime < ApplicationRecord
|
4
4
|
# Time Ranges
|
5
|
-
MORNING =
|
6
|
-
AFTERNOON =
|
7
|
-
|
5
|
+
MORNING = :morning
|
6
|
+
AFTERNOON = :afternoon
|
7
|
+
FULL_DAY = :full_day
|
8
8
|
|
9
|
-
TIME_RANGE = {
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
}.freeze
|
9
|
+
# TIME_RANGE = {
|
10
|
+
# MORNING => { start: Time.zone.parse('5:00 AM'), end: Time.zone.parse('9:00 AM') },
|
11
|
+
# AFTERNOON => { start: Time.zone.parse('10:00 AM'), end: Time.zone.parse('2:00 PM') },
|
12
|
+
# BOTH => { start: Time.zone.parse('5:00 AM'), end: Time.zone.parse('2:00 PM') }
|
13
|
+
# }.freeze
|
14
14
|
|
15
15
|
belongs_to :attendance_sheet_entry
|
16
16
|
belongs_to :revision_to, class_name: 'Ecom::Core::CrewTime', optional: true
|
@@ -47,15 +47,42 @@ module Ecom
|
|
47
47
|
attendance_sheet_entry.save
|
48
48
|
end
|
49
49
|
|
50
|
+
# A method to get the available time ranges at a given point.
|
51
|
+
# We cannot define the range variables as a constant because
|
52
|
+
# the parsing should be done at the exact moment we are about
|
53
|
+
# to do time range calculations to avoid errors caused by date
|
54
|
+
# mismatches
|
55
|
+
def define_range
|
56
|
+
morning = {
|
57
|
+
start: Time.zone.parse('5:00 AM'),
|
58
|
+
finish: Time.zone.parse('9:00 AM')
|
59
|
+
}
|
60
|
+
afternoon = {
|
61
|
+
start: Time.zone.parse('10:00 AM'),
|
62
|
+
finish: Time.zone.parse('2:00 PM')
|
63
|
+
}
|
64
|
+
full_day = {
|
65
|
+
start: Time.zone.parse('5:00 AM'),
|
66
|
+
finish: Time.zone.parse('2:00 PM')
|
67
|
+
}
|
68
|
+
|
69
|
+
{
|
70
|
+
morning: morning,
|
71
|
+
afternoon: afternoon,
|
72
|
+
full_day: full_day
|
73
|
+
}
|
74
|
+
end
|
75
|
+
|
50
76
|
# A method to check if checkin and checkout range falls in the morning,
|
51
77
|
# afternoon, or both.
|
52
78
|
def find_range(start, finish)
|
53
|
-
|
54
|
-
|
55
|
-
|
56
|
-
|
79
|
+
range = define_range
|
80
|
+
if start.before?(range[:morning][:finish]) && finish.before?(range[:afternoon][:start])
|
81
|
+
:morning
|
82
|
+
elsif start.after?(range[:morning][:finish]) && finish.after?(range[:afternoon][:start])
|
83
|
+
:afternoon
|
57
84
|
else
|
58
|
-
|
85
|
+
:full_day
|
59
86
|
end
|
60
87
|
end
|
61
88
|
|
@@ -63,13 +90,14 @@ module Ecom
|
|
63
90
|
# of the defined morning and afternoon ranges
|
64
91
|
def compute_hours
|
65
92
|
# Reparse time to avoid errors caused by date differences
|
93
|
+
range = define_range
|
66
94
|
start = Time.zone.parse(checkin_time.strftime('%I:%M%p'))
|
67
95
|
finish = Time.zone.parse(checkout_time.strftime('%I:%M%p'))
|
68
|
-
|
69
|
-
left = start.before?(
|
70
|
-
right = finish.after?(
|
96
|
+
day_part = find_range(start, finish)
|
97
|
+
left = start.before?(range[day_part][:start]) ? range[day_part][:start] : start
|
98
|
+
right = finish.after?(range[day_part][:finish]) ? range[day_part][:finish] : finish
|
71
99
|
time = (right - left) / 1.hour
|
72
|
-
time -= 1 if
|
100
|
+
time -= 1 if day_part == FULL_DAY
|
73
101
|
time
|
74
102
|
end
|
75
103
|
end
|
@@ -24,14 +24,18 @@ module Ecom
|
|
24
24
|
OvertimeSheet.open(project_id).where(date: date).exists?
|
25
25
|
end
|
26
26
|
|
27
|
+
def self.exists_for_date?(date, project_id)
|
28
|
+
OvertimeSheet.by_project(project_id).by_date(date).exists?
|
29
|
+
end
|
30
|
+
|
27
31
|
# Overtime sheet should be created using the
|
28
32
|
# method below only. This is because we need to
|
29
33
|
# check if there is an open overtime already,
|
30
34
|
# and also that we have only one open overtime
|
31
35
|
# sheet at a time.
|
32
36
|
def self.create_new(date, project_id)
|
33
|
-
if OvertimeSheet.
|
34
|
-
raise 'There is already an
|
37
|
+
if OvertimeSheet.exists_for_date?(date, project_id)
|
38
|
+
raise 'There is already an overtime sheet for the selected date.'
|
35
39
|
end
|
36
40
|
|
37
41
|
if OvertimeSheet.open_exists?(project_id)
|
@@ -40,6 +44,22 @@ module Ecom
|
|
40
44
|
|
41
45
|
OvertimeSheet.create(date: date, opened_at: Time.now, status: StatusConstants::OPEN, project_id: project_id)
|
42
46
|
end
|
47
|
+
|
48
|
+
def submit
|
49
|
+
raise 'Overtime sheet is not open and cannot be submitted' if status != StatusConstants::OPEN
|
50
|
+
|
51
|
+
self.submitted_at = DateTime.now
|
52
|
+
self.status = StatusConstants::SUBMITTED
|
53
|
+
save
|
54
|
+
end
|
55
|
+
|
56
|
+
def approve
|
57
|
+
raise 'Overtime sheet is not submitted and cannot be approved' unless status == StatusConstants::SUBMITTED
|
58
|
+
|
59
|
+
self.status = StatusConstants::APPROVED
|
60
|
+
self.approved_at = DateTime.now
|
61
|
+
save
|
62
|
+
end
|
43
63
|
end
|
44
64
|
end
|
45
65
|
end
|
@@ -9,6 +9,16 @@ module Ecom
|
|
9
9
|
scope :by_project, ->(id) { where(project_id: id) }
|
10
10
|
scope :by_month, ->(month) { where(month: month) }
|
11
11
|
scope :by_year, ->(year) { where(year: year) }
|
12
|
+
|
13
|
+
def create_next
|
14
|
+
m = month + 1
|
15
|
+
y = year
|
16
|
+
if m > 12
|
17
|
+
m = 1
|
18
|
+
y += 1
|
19
|
+
end
|
20
|
+
Payroll.create(month: m, year: y)
|
21
|
+
end
|
12
22
|
end
|
13
23
|
end
|
14
24
|
end
|
@@ -2,8 +2,9 @@ class CreateEcomCoreAttendanceSheets < ActiveRecord::Migration[6.0]
|
|
2
2
|
def change
|
3
3
|
create_table :ecom_core_attendance_sheets do |t|
|
4
4
|
t.date :date, null: false
|
5
|
-
t.
|
6
|
-
t.
|
5
|
+
t.datetime :opened_at, null: false
|
6
|
+
t.datetime :submitted_at
|
7
|
+
t.datetime :approved_at
|
7
8
|
t.string :remark
|
8
9
|
t.string :status, null: false, default: 'Open'
|
9
10
|
t.references :project,
|
@@ -2,8 +2,9 @@ class CreateEcomCoreOvertimeSheets < ActiveRecord::Migration[6.0]
|
|
2
2
|
def change
|
3
3
|
create_table :ecom_core_overtime_sheets do |t|
|
4
4
|
t.date :date, null: false
|
5
|
-
t.
|
6
|
-
t.
|
5
|
+
t.datetime :opened_at, null: false
|
6
|
+
t.datetime :submitted_at
|
7
|
+
t.datetime :approved_at
|
7
8
|
t.string :remark
|
8
9
|
t.string :status, null: false, default: 'Open'
|
9
10
|
t.references :project,
|
data/lib/ecom/core/version.rb
CHANGED
@@ -1,8 +1,9 @@
|
|
1
1
|
FactoryBot.define do
|
2
2
|
factory :attendance_sheet, class: Ecom::Core::AttendanceSheet do
|
3
3
|
date { Date.today }
|
4
|
-
opened_at {
|
5
|
-
|
4
|
+
opened_at { DateTime.now }
|
5
|
+
submitted_at { nil }
|
6
|
+
approved_at { nil }
|
6
7
|
remark { FFaker::Name.name }
|
7
8
|
status { Ecom::Core::StatusConstants::OPEN }
|
8
9
|
association :project
|
@@ -3,9 +3,17 @@ FactoryBot.define do
|
|
3
3
|
sequence :code do |n|
|
4
4
|
"CCode#{n}"
|
5
5
|
end
|
6
|
-
name
|
6
|
+
sequence :name do |n|
|
7
|
+
"CName#{n}"
|
8
|
+
end
|
7
9
|
address { FFaker::Address.street_address }
|
8
10
|
telephone { FFaker::PhoneNumber.phone_number }
|
9
11
|
email { FFaker::Internet.email }
|
12
|
+
settings do
|
13
|
+
{
|
14
|
+
payroll_periods: [{ order: 1, date: 25, comparison: :exact }],
|
15
|
+
period_count: 1
|
16
|
+
}
|
17
|
+
end
|
10
18
|
end
|
11
19
|
end
|
@@ -1,6 +1,8 @@
|
|
1
1
|
FactoryBot.define do
|
2
2
|
factory :equipment, class: Ecom::Core::Equipment do
|
3
|
-
name
|
3
|
+
sequence :name do |n|
|
4
|
+
"EQName#{n}"
|
5
|
+
end
|
4
6
|
description { FFaker::Name.name }
|
5
7
|
minimum_acquisition_time { 10 }
|
6
8
|
brands { ['Brand I', 'Brand II', 'Brand III'] }
|
@@ -1,8 +1,9 @@
|
|
1
1
|
FactoryBot.define do
|
2
2
|
factory :overtime_sheet, class: Ecom::Core::OvertimeSheet do
|
3
3
|
date { Date.today }
|
4
|
-
opened_at {
|
5
|
-
submitted_at {
|
4
|
+
opened_at { DateTime.now }
|
5
|
+
submitted_at { nil }
|
6
|
+
approved_at { nil }
|
6
7
|
status { Ecom::Core::StatusConstants::OPEN }
|
7
8
|
association :project
|
8
9
|
end
|
@@ -1,6 +1,8 @@
|
|
1
1
|
FactoryBot.define do
|
2
2
|
factory :product_type, class: Ecom::Core::ProductType do
|
3
|
-
name
|
3
|
+
sequence :name do |n|
|
4
|
+
"PTname#{n}"
|
5
|
+
end
|
4
6
|
association :work_product_template
|
5
7
|
dimension { [{ name: 'length', label: 'length' }, { name: 'width', label: 'width' }] }
|
6
8
|
end
|
@@ -1,6 +1,8 @@
|
|
1
1
|
FactoryBot.define do
|
2
2
|
factory :stakeholder, class: Ecom::Core::Stakeholder do
|
3
|
-
name
|
3
|
+
sequence :name do |n|
|
4
|
+
"Stakeholder #{n}"
|
5
|
+
end
|
4
6
|
type_of_business { FFaker::Name.name }
|
5
7
|
address { FFaker::Name.name }
|
6
8
|
license_no { FFaker::Name.name }
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: ecom_core
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 1.2.
|
4
|
+
version: 1.2.22
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Henock L.
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2020-
|
11
|
+
date: 2020-08-01 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: aasm
|