moco-ruby 1.1.0 → 1.3.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 (70) hide show
  1. checksums.yaml +4 -4
  2. data/.rubocop.yml +1 -0
  3. data/CHANGELOG.md +56 -1
  4. data/Gemfile.lock +45 -40
  5. data/README.md +98 -25
  6. data/lib/moco/client.rb +65 -0
  7. data/lib/moco/connection.rb +45 -22
  8. data/lib/moco/entities/activity.rb +31 -1
  9. data/lib/moco/entities/catalog_service.rb +54 -0
  10. data/lib/moco/entities/comment.rb +61 -0
  11. data/lib/moco/entities/company.rb +57 -2
  12. data/lib/moco/entities/contact.rb +56 -0
  13. data/lib/moco/entities/custom_property.rb +49 -0
  14. data/lib/moco/entities/deal.rb +38 -2
  15. data/lib/moco/entities/deal_category.rb +27 -0
  16. data/lib/moco/entities/employment.rb +55 -0
  17. data/lib/moco/entities/expense.rb +37 -2
  18. data/lib/moco/entities/expense_template.rb +39 -0
  19. data/lib/moco/entities/fixed_cost.rb +30 -0
  20. data/lib/moco/entities/holiday.rb +33 -2
  21. data/lib/moco/entities/hourly_rate.rb +33 -0
  22. data/lib/moco/entities/internal_hourly_rate.rb +32 -0
  23. data/lib/moco/entities/invoice.rb +70 -1
  24. data/lib/moco/entities/invoice_attachment.rb +34 -0
  25. data/lib/moco/entities/invoice_bookkeeping_export.rb +33 -0
  26. data/lib/moco/entities/invoice_payment.rb +51 -0
  27. data/lib/moco/entities/invoice_reminder.rb +51 -0
  28. data/lib/moco/entities/letter_paper.rb +23 -0
  29. data/lib/moco/entities/offer.rb +111 -0
  30. data/lib/moco/entities/offer_approval.rb +42 -0
  31. data/lib/moco/entities/offer_attachment.rb +34 -0
  32. data/lib/moco/entities/payment_schedule.rb +48 -0
  33. data/lib/moco/entities/planning_entry.rb +43 -2
  34. data/lib/moco/entities/presence.rb +34 -2
  35. data/lib/moco/entities/profile.rb +24 -0
  36. data/lib/moco/entities/project.rb +85 -10
  37. data/lib/moco/entities/project_contract.rb +50 -0
  38. data/lib/moco/entities/project_group.rb +38 -0
  39. data/lib/moco/entities/purchase.rb +90 -0
  40. data/lib/moco/entities/purchase_bookkeeping_export.rb +34 -0
  41. data/lib/moco/entities/purchase_budget.rb +47 -0
  42. data/lib/moco/entities/purchase_category.rb +38 -0
  43. data/lib/moco/entities/purchase_draft.rb +25 -0
  44. data/lib/moco/entities/purchase_payment.rb +51 -0
  45. data/lib/moco/entities/receipt.rb +55 -0
  46. data/lib/moco/entities/recurring_expense.rb +55 -0
  47. data/lib/moco/entities/reports/absences.rb +16 -0
  48. data/lib/moco/entities/reports/cashflow.rb +16 -0
  49. data/lib/moco/entities/reports/finance.rb +16 -0
  50. data/lib/moco/entities/reports/utilization.rb +16 -0
  51. data/lib/moco/entities/schedule.rb +39 -2
  52. data/lib/moco/entities/session.rb +58 -0
  53. data/lib/moco/entities/tag.rb +30 -0
  54. data/lib/moco/entities/tagging.rb +27 -0
  55. data/lib/moco/entities/task.rb +25 -2
  56. data/lib/moco/entities/task_template.rb +38 -0
  57. data/lib/moco/entities/unit.rb +36 -0
  58. data/lib/moco/entities/user.rb +50 -2
  59. data/lib/moco/entities/user_role.rb +29 -0
  60. data/lib/moco/entities/vat_code_purchase.rb +29 -0
  61. data/lib/moco/entities/vat_code_sale.rb +29 -0
  62. data/lib/moco/entities/web_hook.rb +32 -2
  63. data/lib/moco/entities/work_time_adjustment.rb +51 -0
  64. data/lib/moco/entities.rb +5 -5
  65. data/lib/moco/sync.rb +7 -7
  66. data/lib/moco/version.rb +1 -1
  67. data/lib/moco.rb +55 -1
  68. data/moco.gemspec +38 -0
  69. data/sync_activity.rb +1 -1
  70. metadata +51 -8
@@ -0,0 +1,30 @@
1
+ # frozen_string_literal: true
2
+
3
+ module MOCO
4
+ # Represents a MOCO tag/label
5
+ #
6
+ # == Required attributes for create:
7
+ # name - String, tag name (e.g., "Important")
8
+ # context - String, entity type this tag applies to:
9
+ # "Project", "Contact", "Company", "Deal", "Offer",
10
+ # "Invoice", "Purchase", "User"
11
+ #
12
+ # == Read-only attributes:
13
+ # id, created_at, updated_at
14
+ #
15
+ # == Example:
16
+ # moco.tags.create(
17
+ # name: "Priority",
18
+ # context: "Project"
19
+ # )
20
+ #
21
+ # == Note:
22
+ # To apply tags to entities, use the `tags` attribute when
23
+ # creating/updating the entity: { tags: ["Tag1", "Tag2"] }
24
+ #
25
+ class Tag < BaseEntity
26
+ def to_s
27
+ name.to_s
28
+ end
29
+ end
30
+ end
@@ -0,0 +1,27 @@
1
+ # frozen_string_literal: true
2
+
3
+ module MOCO
4
+ # Represents a MOCO tagging (association between a tag and an entity)
5
+ # This is a read-only entity representing tag assignments.
6
+ #
7
+ # == Read-only attributes:
8
+ # id, entity_id, entity_type, tag (Hash), created_at, updated_at
9
+ #
10
+ # == Note:
11
+ # To apply tags to entities, use the `tags` attribute when
12
+ # creating/updating the entity directly:
13
+ # moco.projects.update(123, tags: ["Priority", "Important"])
14
+ #
15
+ # See also: Tag entity for managing available tags.
16
+ #
17
+ class Tagging < BaseEntity
18
+ # Associations
19
+ def tag
20
+ association(:tag)
21
+ end
22
+
23
+ def to_s
24
+ "Tagging ##{id}"
25
+ end
26
+ end
27
+ end
@@ -1,8 +1,31 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module MOCO
4
- # Represents a MOCO task
5
- # Provides methods for task-specific associations
4
+ # Represents a MOCO task (project service/activity type)
5
+ # Tasks are nested under projects: project.tasks.create(...)
6
+ #
7
+ # == Required attributes for create:
8
+ # name - String, task name (e.g., "Development", "Design / UX")
9
+ #
10
+ # == Optional attributes:
11
+ # billable - Boolean, whether time on this task is billable
12
+ # active - Boolean, whether task is active
13
+ # budget - Float/Integer, budget in hours or currency
14
+ # hourly_rate - Float/Integer, rate for this task (used if project billing_variant is "task")
15
+ # description - String, task description
16
+ #
17
+ # == Read-only attributes (returned by API):
18
+ # id, created_at, updated_at
19
+ #
20
+ # == Example:
21
+ # project = moco.projects.find(123)
22
+ # project.tasks.create(
23
+ # name: "Development",
24
+ # billable: true,
25
+ # hourly_rate: 150,
26
+ # budget: 100
27
+ # )
28
+ #
6
29
  class Task < BaseEntity
7
30
  # Associations
8
31
  def project
@@ -0,0 +1,38 @@
1
+ # frozen_string_literal: true
2
+
3
+ module MOCO
4
+ # Represents a MOCO task template (Standardleistungen)
5
+ # Pre-defined task types for projects
6
+ #
7
+ # == Required attributes for create:
8
+ # name - String, task name (e.g., "Development", "Design")
9
+ #
10
+ # == Optional attributes:
11
+ # description - String, task description
12
+ # revenue_category_id - Integer, revenue category for invoicing
13
+ # billable - Boolean, whether tasks are billable by default
14
+ # project_default - Boolean, auto-add to new projects
15
+ # index - Integer, display order (e.g., 10, 20, 30)
16
+ #
17
+ # == Read-only attributes:
18
+ # id, revenue_category (Hash), created_at, updated_at
19
+ #
20
+ # == Example:
21
+ # moco.task_templates.create(
22
+ # name: "Backend Development",
23
+ # description: "Server-side programming",
24
+ # billable: true,
25
+ # project_default: true,
26
+ # index: 10
27
+ # )
28
+ #
29
+ class TaskTemplate < BaseEntity
30
+ def self.entity_path
31
+ "account/task_templates"
32
+ end
33
+
34
+ def to_s
35
+ name.to_s
36
+ end
37
+ end
38
+ end
@@ -0,0 +1,36 @@
1
+ # frozen_string_literal: true
2
+
3
+ module MOCO
4
+ # Represents a MOCO unit/team (Teams)
5
+ #
6
+ # == Required attributes for create:
7
+ # name - String, team name (e.g., "Development Team")
8
+ #
9
+ # == Read-only attributes:
10
+ # id, users (Array of user hashes), created_at, updated_at
11
+ #
12
+ # == Example:
13
+ # # Create a new team
14
+ # moco.units.create(name: "Marketing Team")
15
+ #
16
+ # # Get users in a team
17
+ # team = moco.units.find(123)
18
+ # team.users # => Array of User objects
19
+ #
20
+ # == Note:
21
+ # To assign users to a team, update the user with unit_id:
22
+ # moco.users.update(user_id, unit_id: team.id)
23
+ #
24
+ # Deleting a unit is only possible if no users are assigned to it.
25
+ #
26
+ class Unit < BaseEntity
27
+ # Get users belonging to this unit
28
+ def users
29
+ has_many(:users, :unit_id)
30
+ end
31
+
32
+ def to_s
33
+ name.to_s
34
+ end
35
+ end
36
+ end
@@ -1,8 +1,44 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module MOCO
4
- # Represents a MOCO user
5
- # Provides methods for user-specific operations and associations
4
+ # Represents a MOCO user (staff member)
5
+ #
6
+ # == Required attributes for create:
7
+ # firstname - String, first name
8
+ # lastname - String, last name
9
+ # email - String, email address (used for login)
10
+ # unit_id - Integer, team/unit ID
11
+ #
12
+ # == Optional attributes:
13
+ # password - String, initial password (if not set, user gets welcome email)
14
+ # role_id - Integer, permission role ID
15
+ # active - Boolean, whether user is active
16
+ # external - Boolean, true for contractors/external staff
17
+ # language - String, one of: "de", "de-AT", "de-CH", "en", "it", "fr"
18
+ # mobile_phone - String, mobile phone number
19
+ # work_phone - String, work phone number
20
+ # home_address - String, home address (use \n for line breaks)
21
+ # bday - String, birthday "YYYY-MM-DD"
22
+ # iban - String, bank account IBAN
23
+ # tags - Array of Strings, e.g., ["Developer", "Remote"]
24
+ # custom_properties - Hash, e.g., {"Start Date": "2024-01-01"}
25
+ # info - String, additional notes
26
+ # welcome_email - Boolean, send welcome email (default: true if no password)
27
+ # avatar - Hash, { filename: "photo.jpg", base64: "..." }
28
+ #
29
+ # == Read-only attributes:
30
+ # id, avatar_url, unit (Hash), role (Hash), created_at, updated_at
31
+ #
32
+ # == Example:
33
+ # moco.users.create(
34
+ # firstname: "John",
35
+ # lastname: "Doe",
36
+ # email: "john.doe@company.com",
37
+ # unit_id: 123,
38
+ # language: "en",
39
+ # tags: ["Developer"]
40
+ # )
41
+ #
6
42
  class User < BaseEntity
7
43
  # Instance methods for user-specific operations
8
44
  def performance_report
@@ -22,6 +58,18 @@ module MOCO
22
58
  has_many(:holidays)
23
59
  end
24
60
 
61
+ def employments
62
+ has_many(:employments)
63
+ end
64
+
65
+ def work_time_adjustments
66
+ has_many(:work_time_adjustments)
67
+ end
68
+
69
+ def unit
70
+ association(:unit)
71
+ end
72
+
25
73
  def full_name
26
74
  "#{firstname} #{lastname}"
27
75
  end
@@ -0,0 +1,29 @@
1
+ # frozen_string_literal: true
2
+
3
+ module MOCO
4
+ # Represents a MOCO user permission role
5
+ # Read-only list of available permission roles
6
+ #
7
+ # == Read-only attributes:
8
+ # id, name, created_at, updated_at
9
+ #
10
+ # == Common roles:
11
+ # - Admin
12
+ # - Manager
13
+ # - Coworker
14
+ # - etc.
15
+ #
16
+ # == Note:
17
+ # Permission roles are configured in MOCO's admin interface.
18
+ # Use role_id when creating/updating users to assign a role.
19
+ #
20
+ class UserRole < BaseEntity
21
+ def self.entity_path
22
+ "users/roles"
23
+ end
24
+
25
+ def to_s
26
+ name.to_s
27
+ end
28
+ end
29
+ end
@@ -0,0 +1,29 @@
1
+ # frozen_string_literal: true
2
+
3
+ module MOCO
4
+ # Represents a MOCO purchase VAT code (Steuerschlüssel Einkauf)
5
+ # Read-only VAT rates for purchases/receipts
6
+ #
7
+ # == Read-only attributes:
8
+ # id, code, tax, reverse_charge, intra_eu, active
9
+ #
10
+ # == Filtering:
11
+ # moco.vat_code_purchases.where(active: true)
12
+ # moco.vat_code_purchases.where(reverse_charge: true)
13
+ # moco.vat_code_purchases.where(intra_eu: true)
14
+ # moco.vat_code_purchases.where(ids: "123,456")
15
+ #
16
+ # == Note:
17
+ # VAT codes are configured in MOCO's admin interface.
18
+ # Use vat_code_id when creating purchases/receipts.
19
+ #
20
+ class VatCodePurchase < BaseEntity
21
+ def self.entity_path
22
+ "vat_code_purchases"
23
+ end
24
+
25
+ def to_s
26
+ "#{code} - #{name}"
27
+ end
28
+ end
29
+ end
@@ -0,0 +1,29 @@
1
+ # frozen_string_literal: true
2
+
3
+ module MOCO
4
+ # Represents a MOCO sales VAT code (Steuerschlüssel Verkauf)
5
+ # Read-only VAT rates for invoices/offers
6
+ #
7
+ # == Read-only attributes:
8
+ # id, code, tax, reverse_charge, intra_eu, active,
9
+ # print_gross_total, notice_tax_exemption, credit_account
10
+ #
11
+ # == Filtering:
12
+ # moco.vat_code_sales.where(active: true)
13
+ # moco.vat_code_sales.where(reverse_charge: true)
14
+ # moco.vat_code_sales.where(intra_eu: true) # EU intra-community
15
+ #
16
+ # == Note:
17
+ # VAT codes are configured in MOCO's admin interface.
18
+ # Use vat_code_id when creating invoices/offers.
19
+ #
20
+ class VatCodeSale < BaseEntity
21
+ def self.entity_path
22
+ "vat_code_sales"
23
+ end
24
+
25
+ def to_s
26
+ "#{code} - #{name}"
27
+ end
28
+ end
29
+ end
@@ -1,8 +1,38 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module MOCO
4
- # Represents a MOCO webhook
5
- # Provides methods for webhook-specific operations
4
+ # Represents a MOCO webhook for event notifications
5
+ #
6
+ # == Required attributes for create:
7
+ # target - String, entity type to watch:
8
+ # "Activity", "Company", "Contact", "Project",
9
+ # "Invoice", "Offer", "Deal", "Expense"
10
+ # event - String, event type: "create", "update", "delete"
11
+ # hook - String, URL to receive webhook payloads (e.g., "https://example.org/callback")
12
+ #
13
+ # == Read-only attributes:
14
+ # id, disabled, disabled_at, created_at, updated_at
15
+ #
16
+ # == Instance methods:
17
+ # enable - Enable the webhook
18
+ # disable - Disable the webhook
19
+ #
20
+ # == Example:
21
+ # # Create webhook for new activities
22
+ # moco.web_hooks.create(
23
+ # target: "Activity",
24
+ # event: "create",
25
+ # hook: "https://example.org/moco-activity-webhook"
26
+ # )
27
+ #
28
+ # # Disable a webhook
29
+ # webhook = moco.web_hooks.find(123)
30
+ # webhook.disable
31
+ #
32
+ # == Note:
33
+ # Only the `hook` URL can be updated after creation.
34
+ # To change target/event, delete and recreate the webhook.
35
+ #
6
36
  class WebHook < BaseEntity
7
37
  # Override entity_path to match API path
8
38
  def self.entity_path
@@ -0,0 +1,51 @@
1
+ # frozen_string_literal: true
2
+
3
+ module MOCO
4
+ # Represents a MOCO user work time adjustment record
5
+ # (Korrekturen Zeiterfassung) for overtime/undertime corrections
6
+ #
7
+ # == Required attributes for create:
8
+ # user_id - Integer, user to adjust
9
+ # date - String, "YYYY-MM-DD" effective date
10
+ # description - String, reason for adjustment
11
+ # hours - Float, hours to add (positive) or subtract (negative)
12
+ #
13
+ # == Read-only attributes:
14
+ # id, user (Hash), creator (Hash), created_at, updated_at
15
+ #
16
+ # == Example:
17
+ # # Add overtime from previous year
18
+ # moco.work_time_adjustments.create(
19
+ # user_id: 123,
20
+ # date: "2024-01-01",
21
+ # description: "Overtime carryover from 2023",
22
+ # hours: 42.0
23
+ # )
24
+ #
25
+ # # Correct time balance
26
+ # moco.work_time_adjustments.create(
27
+ # user_id: 123,
28
+ # date: "2024-06-15",
29
+ # description: "Correction for unpaid leave",
30
+ # hours: -16.0
31
+ # )
32
+ #
33
+ # == Filtering:
34
+ # moco.work_time_adjustments.where(user_id: 123)
35
+ # moco.work_time_adjustments.where(from: "2024-01-01", to: "2024-12-31")
36
+ #
37
+ class WorkTimeAdjustment < BaseEntity
38
+ def self.entity_path
39
+ "users/work_time_adjustments"
40
+ end
41
+
42
+ # Associations
43
+ def user
44
+ association(:user)
45
+ end
46
+
47
+ def to_s
48
+ "WorkTimeAdjustment ##{id} (#{date})"
49
+ end
50
+ end
51
+ end
data/lib/moco/entities.rb CHANGED
@@ -44,7 +44,7 @@ module MOCO
44
44
  end
45
45
  end
46
46
 
47
- # https://hundertzehn.github.io/mocoapp-api-docs/sections/projects.html
47
+ # https://docs.mocoapp.com/api/docs/v1#tag/projects.html
48
48
  # @deprecated Use MOCO::Project from entities/project.rb instead
49
49
  class Project < BaseEntity
50
50
  attr_accessor :id, :active, :name, :customer, :tasks
@@ -54,7 +54,7 @@ module MOCO
54
54
  end
55
55
  end
56
56
 
57
- # https://hundertzehn.github.io/mocoapp-api-docs/sections/project_tasks.html
57
+ # https://docs.mocoapp.com/api/docs/v1#tag/project_tasks.html
58
58
  # @deprecated Use MOCO::Task from entities/task.rb instead
59
59
  class Task < BaseEntity
60
60
  attr_accessor :id, :active, :name, :project_id, :billable
@@ -64,7 +64,7 @@ module MOCO
64
64
  end
65
65
  end
66
66
 
67
- # https://hundertzehn.github.io/mocoapp-api-docs/sections/activities.html
67
+ # https://docs.mocoapp.com/api/docs/v1#tag/activities.html
68
68
  # @deprecated Use MOCO::Activity from entities/activity.rb instead
69
69
  class Activity < BaseEntity
70
70
  attr_accessor :id, :active, :date, :description, :project, :task, :seconds, :hours, :billable, :billed, :user,
@@ -79,13 +79,13 @@ module MOCO
79
79
  end
80
80
  end
81
81
 
82
- # https://hundertzehn.github.io/mocoapp-api-docs/sections/companies.html
82
+ # https://docs.mocoapp.com/api/docs/v1#tag/companies.html
83
83
  # @deprecated Use MOCO::Company from entities/company.rb instead
84
84
  class Customer < BaseEntity
85
85
  attr_accessor :id, :name
86
86
  end
87
87
 
88
- # https://hundertzehn.github.io/mocoapp-api-docs/sections/users.html
88
+ # https://docs.mocoapp.com/api/docs/v1#tag/users.html
89
89
  # @deprecated Use MOCO::User from entities/user.rb instead
90
90
  class User < BaseEntity
91
91
  attr_accessor :id, :firstname, :lastname
data/lib/moco/sync.rb CHANGED
@@ -364,7 +364,7 @@ module MOCO
364
364
  # rubocop:enable Metrics/AbcSize, Metrics/CyclomaticComplexity, Metrics/MethodLength
365
365
 
366
366
  def fetch_assigned_projects
367
- # Use .projects.assigned for the source, standard .projects for the target
367
+ # Use .projects.assigned for both source and target to get embedded tasks
368
368
  source_filters = @filters.fetch(:source, {}).merge(active: "true")
369
369
  # Get the proxy, then fetch all results into the instance variable
370
370
  @source_projects = @source.projects.assigned.where(source_filters).all
@@ -372,19 +372,19 @@ module MOCO
372
372
  @source_projects.each do |project|
373
373
  debug_log " Source Project: #{project.id} - #{project.name} (#{project.identifier})"
374
374
  debug_log " Tasks:"
375
- project.tasks.each do |task|
375
+ project.embedded_tasks.each do |task|
376
376
  debug_log " Task: #{task.id} - #{task.name}"
377
377
  end
378
378
  end
379
379
 
380
380
  target_filters = @filters.fetch(:target, {}).merge(active: "true")
381
381
  # Get the proxy, then fetch all results into the instance variable
382
- @target_projects = @target.projects.where(target_filters).all
382
+ @target_projects = @target.projects.assigned.where(target_filters).all
383
383
  debug_log "Found #{@target_projects.size} target projects:"
384
384
  @target_projects.each do |project|
385
385
  debug_log " Target Project: #{project.id} - #{project.name} (#{project.identifier})"
386
386
  debug_log " Tasks:"
387
- project.tasks.each do |task|
387
+ project.embedded_tasks.each do |task|
388
388
  debug_log " Task: #{task.id} - #{task.name}"
389
389
  end
390
390
  end
@@ -401,7 +401,7 @@ module MOCO
401
401
  @project_mapping[source_project.id] = target_project
402
402
  debug_log "Mapped source project #{source_project.id} (#{source_project.name}) to target project #{target_project.id} (#{target_project.name})"
403
403
 
404
- target_project.tasks.each do |target_task|
404
+ target_project.embedded_tasks.each do |target_task|
405
405
  source_task = match_task(target_task, source_project)
406
406
  if source_task
407
407
  @task_mapping[source_task.id] = target_task
@@ -520,7 +520,7 @@ module MOCO
520
520
  return @default_task_cache[target_project.id] if @default_task_cache.key?(target_project.id)
521
521
 
522
522
  # Search for the default task in the target project
523
- default_task = target_project.tasks.find { |task| task.name == @default_task_name }
523
+ default_task = target_project.embedded_tasks.find { |task| task.name == @default_task_name }
524
524
 
525
525
  # Cache the result (even if nil)
526
526
  @default_task_cache[target_project.id] = default_task
@@ -545,7 +545,7 @@ module MOCO
545
545
 
546
546
  def match_task(target_task, source_project)
547
547
  # Get tasks from the source project (embedded in projects.assigned response)
548
- tasks = source_project.tasks
548
+ tasks = source_project.embedded_tasks
549
549
 
550
550
  # Only proceed if we have tasks to match against
551
551
  return nil if tasks.empty?
data/lib/moco/version.rb CHANGED
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module MOCO
4
- VERSION = "1.1.0"
4
+ VERSION = "1.3.0"
5
5
  end
data/lib/moco.rb CHANGED
@@ -11,8 +11,10 @@ require_relative "moco/connection"
11
11
  require_relative "moco/collection_proxy"
12
12
  require_relative "moco/nested_collection_proxy"
13
13
 
14
- # New API (v2)
14
+ # Entities
15
15
  require_relative "moco/entities/base_entity"
16
+
17
+ # Core entities (original)
16
18
  require_relative "moco/entities/project"
17
19
  require_relative "moco/entities/activity"
18
20
  require_relative "moco/entities/user"
@@ -26,6 +28,58 @@ require_relative "moco/entities/schedule"
26
28
  require_relative "moco/entities/presence"
27
29
  require_relative "moco/entities/holiday"
28
30
  require_relative "moco/entities/planning_entry"
31
+
32
+ # New standalone entities
33
+ require_relative "moco/entities/contact"
34
+ require_relative "moco/entities/offer"
35
+ require_relative "moco/entities/purchase"
36
+ require_relative "moco/entities/receipt"
37
+ require_relative "moco/entities/unit"
38
+ require_relative "moco/entities/comment"
39
+ require_relative "moco/entities/tag"
40
+ require_relative "moco/entities/tagging"
41
+ require_relative "moco/entities/deal_category"
42
+ require_relative "moco/entities/project_group"
43
+ require_relative "moco/entities/profile"
44
+
45
+ # Account-level entities
46
+ require_relative "moco/entities/catalog_service"
47
+ require_relative "moco/entities/custom_property"
48
+ require_relative "moco/entities/expense_template"
49
+ require_relative "moco/entities/fixed_cost"
50
+ require_relative "moco/entities/hourly_rate"
51
+ require_relative "moco/entities/internal_hourly_rate"
52
+ require_relative "moco/entities/task_template"
53
+ require_relative "moco/entities/user_role"
54
+ require_relative "moco/entities/vat_code_sale"
55
+ require_relative "moco/entities/vat_code_purchase"
56
+
57
+ # Nested/sub-resource entities
58
+ require_relative "moco/entities/employment"
59
+ require_relative "moco/entities/work_time_adjustment"
60
+ require_relative "moco/entities/project_contract"
61
+ require_relative "moco/entities/payment_schedule"
62
+ require_relative "moco/entities/recurring_expense"
63
+ require_relative "moco/entities/invoice_payment"
64
+ require_relative "moco/entities/invoice_reminder"
65
+ require_relative "moco/entities/invoice_bookkeeping_export"
66
+ require_relative "moco/entities/invoice_attachment"
67
+ require_relative "moco/entities/offer_approval"
68
+ require_relative "moco/entities/offer_attachment"
69
+ require_relative "moco/entities/letter_paper"
70
+ require_relative "moco/entities/session"
71
+ require_relative "moco/entities/purchase_category"
72
+ require_relative "moco/entities/purchase_draft"
73
+ require_relative "moco/entities/purchase_payment"
74
+ require_relative "moco/entities/purchase_budget"
75
+ require_relative "moco/entities/purchase_bookkeeping_export"
76
+
77
+ # Reports
78
+ require_relative "moco/entities/reports/absences"
79
+ require_relative "moco/entities/reports/cashflow"
80
+ require_relative "moco/entities/reports/finance"
81
+ require_relative "moco/entities/reports/utilization"
82
+
29
83
  require_relative "moco/client"
30
84
  require_relative "moco/entity_collection"
31
85
 
data/moco.gemspec ADDED
@@ -0,0 +1,38 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative "lib/moco/version"
4
+
5
+ Gem::Specification.new do |spec|
6
+ spec.name = "moco-ruby"
7
+ spec.version = MOCO::VERSION
8
+ spec.authors = ["Teal Bauer"]
9
+ spec.email = ["rubygems@teal.is"]
10
+
11
+ spec.summary = "A Ruby Gem to interact with the MOCO API."
12
+ spec.description = "A modern, Ruby-esque interface for the MOCO API (mocoapp.com). " \
13
+ "Provides an ActiveRecord-style interface for projects, activities, users, companies, and more."
14
+ spec.homepage = "https://github.com/starsong-consulting/moco-ruby"
15
+ spec.required_ruby_version = ">= 3.2.0"
16
+ spec.license = "Apache-2.0"
17
+
18
+ spec.metadata["homepage_uri"] = spec.homepage
19
+ spec.metadata["source_code_uri"] = "https://github.com/starsong-consulting/moco-ruby"
20
+ spec.metadata["changelog_uri"] = "https://github.com/starsong-consulting/moco-ruby/blob/main/CHANGELOG.md"
21
+
22
+ # Specify which files should be added to the gem when it is released.
23
+ # The `git ls-files -z` loads the files in the RubyGem that have been added into git.
24
+ spec.files = Dir.chdir(__dir__) do
25
+ `git ls-files -z`.split("\x0").reject do |f|
26
+ (f == __FILE__) || f.match(%r{\A(?:(?:bin|test|spec|features)/|\.(?:git|circleci)|appveyor)})
27
+ end
28
+ end
29
+ spec.bindir = "exe"
30
+ spec.executables = spec.files.grep(%r{\Aexe/}) { |f| File.basename(f) }
31
+ spec.require_paths = ["lib"]
32
+
33
+ spec.add_dependency "activesupport", ">= 7.0"
34
+ spec.add_dependency "faraday", ">= 2.0"
35
+ spec.add_dependency "fuzzy_match", "~> 2.1.0"
36
+
37
+ spec.metadata["rubygems_mfa_required"] = "true"
38
+ end
data/sync_activity.rb CHANGED
@@ -94,7 +94,7 @@ syncer = MOCO::Sync.new(
94
94
  syncer.source_projects.each do |project|
95
95
  if syncer.project_mapping[project.id]
96
96
  puts "✅ Project #{project} --> #{syncer.project_mapping[project.id]}"
97
- project.tasks.each do |task|
97
+ project.embedded_tasks.each do |task|
98
98
  if syncer.task_mapping[task.id]
99
99
  puts " ✅ Task #{task} --> #{syncer.task_mapping[task.id]}"
100
100
  else