xeroizer 0.1.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 (292) hide show
  1. data/.bundle/config +2 -0
  2. data/Gemfile +12 -0
  3. data/Gemfile.lock +22 -0
  4. data/LICENSE.txt +20 -0
  5. data/README.md +416 -0
  6. data/Rakefile +39 -0
  7. data/VERSION +1 -0
  8. data/lib/.DS_Store +0 -0
  9. data/lib/big_decimal_to_s.rb +9 -0
  10. data/lib/class_level_inheritable_attributes.rb +25 -0
  11. data/lib/nokogiri_utils.rb +58 -0
  12. data/lib/xeroizer.rb +55 -0
  13. data/lib/xeroizer/ca-certificates.crt +2642 -0
  14. data/lib/xeroizer/exceptions.rb +94 -0
  15. data/lib/xeroizer/generic_application.rb +40 -0
  16. data/lib/xeroizer/http.rb +148 -0
  17. data/lib/xeroizer/http_encoding_helper.rb +49 -0
  18. data/lib/xeroizer/models/account.rb +60 -0
  19. data/lib/xeroizer/models/address.rb +31 -0
  20. data/lib/xeroizer/models/branding_theme.rb +22 -0
  21. data/lib/xeroizer/models/contact.rb +46 -0
  22. data/lib/xeroizer/models/credit_note.rb +113 -0
  23. data/lib/xeroizer/models/currency.rb +18 -0
  24. data/lib/xeroizer/models/invoice.rb +138 -0
  25. data/lib/xeroizer/models/item.rb +27 -0
  26. data/lib/xeroizer/models/item_purchase_sale_details.rb +17 -0
  27. data/lib/xeroizer/models/journal.rb +25 -0
  28. data/lib/xeroizer/models/journal_line.rb +28 -0
  29. data/lib/xeroizer/models/line_item.rb +43 -0
  30. data/lib/xeroizer/models/manual_journal.rb +42 -0
  31. data/lib/xeroizer/models/manual_journal_line.rb +24 -0
  32. data/lib/xeroizer/models/option.rb +15 -0
  33. data/lib/xeroizer/models/organisation.rb +23 -0
  34. data/lib/xeroizer/models/payment.rb +30 -0
  35. data/lib/xeroizer/models/phone.rb +25 -0
  36. data/lib/xeroizer/models/tax_rate.rb +25 -0
  37. data/lib/xeroizer/models/tracking_category.rb +23 -0
  38. data/lib/xeroizer/models/tracking_category_child.rb +17 -0
  39. data/lib/xeroizer/oauth.rb +130 -0
  40. data/lib/xeroizer/partner_application.rb +44 -0
  41. data/lib/xeroizer/private_application.rb +27 -0
  42. data/lib/xeroizer/public_application.rb +21 -0
  43. data/lib/xeroizer/record/application_helper.rb +22 -0
  44. data/lib/xeroizer/record/base.rb +116 -0
  45. data/lib/xeroizer/record/base_model.rb +138 -0
  46. data/lib/xeroizer/record/base_model_http_proxy.rb +134 -0
  47. data/lib/xeroizer/record/model_definition_helper.rb +94 -0
  48. data/lib/xeroizer/record/record_association_helper.rb +94 -0
  49. data/lib/xeroizer/record/validation_helper.rb +59 -0
  50. data/lib/xeroizer/record/validators/associated_validator.rb +33 -0
  51. data/lib/xeroizer/record/validators/inclusion_of_validator.rb +22 -0
  52. data/lib/xeroizer/record/validators/presence_of_validator.rb +19 -0
  53. data/lib/xeroizer/record/validators/validator.rb +24 -0
  54. data/lib/xeroizer/record/xml_helper.rb +113 -0
  55. data/lib/xeroizer/response.rb +17 -0
  56. data/test/stub_responses/accounts.xml +589 -0
  57. data/test/stub_responses/api_exception.xml +153 -0
  58. data/test/stub_responses/bogus_oauth_error +1 -0
  59. data/test/stub_responses/branding_themes.xml +20 -0
  60. data/test/stub_responses/contact.xml +40 -0
  61. data/test/stub_responses/contacts.xml +3349 -0
  62. data/test/stub_responses/create_credit_note.xml +104 -0
  63. data/test/stub_responses/create_invoice.xml +64 -0
  64. data/test/stub_responses/credit_note.xml +69 -0
  65. data/test/stub_responses/credit_note_not_found_error.xml +1 -0
  66. data/test/stub_responses/credit_notes.xml +149 -0
  67. data/test/stub_responses/currencies.xml +12 -0
  68. data/test/stub_responses/invalid_api_key_error.xml +1 -0
  69. data/test/stub_responses/invalid_consumer_key +1 -0
  70. data/test/stub_responses/invalid_request_token +1 -0
  71. data/test/stub_responses/invoice.xml +91 -0
  72. data/test/stub_responses/invoice_not_found_error.xml +1 -0
  73. data/test/stub_responses/invoices.xml +1728 -0
  74. data/test/stub_responses/items.xml +112 -0
  75. data/test/stub_responses/manual_journal.xml +45 -0
  76. data/test/stub_responses/manual_journals.xml +40 -0
  77. data/test/stub_responses/organisation.xml +16 -0
  78. data/test/stub_responses/organisations.xml +16 -0
  79. data/test/stub_responses/rate_limit_exceeded +1 -0
  80. data/test/stub_responses/records/contact-043892a1-aef1-4c18-88d8-b8ccb6d31466.xml +38 -0
  81. data/test/stub_responses/records/contact-09664078-efe2-4a88-89a5-67eac9b0047b.xml +40 -0
  82. data/test/stub_responses/records/contact-0a4cf37b-a1a8-4753-9ee2-f9207f63a8ff.xml +48 -0
  83. data/test/stub_responses/records/contact-0e74f929-11b9-4255-a035-1fdfe573e676.xml +40 -0
  84. data/test/stub_responses/records/contact-0f471ca5-15c9-405e-a1b9-7cc35194b673.xml +38 -0
  85. data/test/stub_responses/records/contact-13cd4c47-baa6-4f07-93f6-6442310df4bf.xml +47 -0
  86. data/test/stub_responses/records/contact-158a2667-82ee-43bf-8f33-a6cc9524092d.xml +38 -0
  87. data/test/stub_responses/records/contact-17465072-6fa3-40bf-bc42-97765d9e1bea.xml +38 -0
  88. data/test/stub_responses/records/contact-1975b0ed-b7ba-4c61-bae8-2aa6d78b0dee.xml +39 -0
  89. data/test/stub_responses/records/contact-1b2be6e9-8d58-4da9-aaf8-4fe5471b653c.xml +53 -0
  90. data/test/stub_responses/records/contact-1c40da58-fe1d-4e97-b729-b2abdae94d9e.xml +38 -0
  91. data/test/stub_responses/records/contact-258176a5-c622-4394-9c94-6f88c3ea12e5.xml +40 -0
  92. data/test/stub_responses/records/contact-299dd3a0-a417-4a37-8a04-2f55e91963e5.xml +40 -0
  93. data/test/stub_responses/records/contact-2be39278-5154-4ed1-8eb0-676f25acfc66.xml +40 -0
  94. data/test/stub_responses/records/contact-2e58cff6-488c-4a32-884b-baf848010229.xml +40 -0
  95. data/test/stub_responses/records/contact-2faccd41-935e-40aa-b74e-e2fc28ac34c3.xml +38 -0
  96. data/test/stub_responses/records/contact-31af01e7-2ca7-45b9-a500-b02db996568e.xml +38 -0
  97. data/test/stub_responses/records/contact-344f1113-a25b-4344-b82e-bedeacc17c8e.xml +40 -0
  98. data/test/stub_responses/records/contact-3e776c4b-ea9e-4bb1-96be-6b0c7a71a37f.xml +39 -0
  99. data/test/stub_responses/records/contact-3fc1fc6c-e5ff-4e40-b6f3-7eb535637d87.xml +38 -0
  100. data/test/stub_responses/records/contact-416ab20c-5357-4beb-a740-e8d175d71efb.xml +38 -0
  101. data/test/stub_responses/records/contact-41a42865-f15a-4fa1-b643-47877608f557.xml +54 -0
  102. data/test/stub_responses/records/contact-42771b60-19a7-4692-af81-dd9f9b9362d4.xml +43 -0
  103. data/test/stub_responses/records/contact-451ceb28-9610-44c9-8f35-3225482f2413.xml +40 -0
  104. data/test/stub_responses/records/contact-4ab343ad-1ebb-4afe-9d48-1814a93c2081.xml +38 -0
  105. data/test/stub_responses/records/contact-4bb77692-42d4-4565-85a0-8849eb85e039.xml +39 -0
  106. data/test/stub_responses/records/contact-4dec292f-3ab7-46a8-83e4-5fb5eac42c7f.xml +40 -0
  107. data/test/stub_responses/records/contact-4e2f192e-8397-4d4d-97ca-a4fc5ac531bf.xml +38 -0
  108. data/test/stub_responses/records/contact-5188c17c-7786-4436-ad6e-9da2997386d0.xml +40 -0
  109. data/test/stub_responses/records/contact-52442753-b1c4-40b7-9b79-c33997de5837.xml +40 -0
  110. data/test/stub_responses/records/contact-565acaa9-e7f3-4fbf-80c3-16b081ddae10.xml +38 -0
  111. data/test/stub_responses/records/contact-571a2414-81ff-4f8f-8498-d91d83793131.xml +44 -0
  112. data/test/stub_responses/records/contact-58697449-85ef-46ae-83fc-6a9446f037fb.xml +40 -0
  113. data/test/stub_responses/records/contact-58bf2ae3-5144-4628-8de2-e165ac2bcdc6.xml +40 -0
  114. data/test/stub_responses/records/contact-5d41dafd-eb7e-42c1-bd5a-ba3be1da0960.xml +38 -0
  115. data/test/stub_responses/records/contact-5f005a09-5ce4-4fb4-8096-e69c18be636e.xml +38 -0
  116. data/test/stub_responses/records/contact-60d578d9-3e10-4aef-b5dc-9d9fd60a3633.xml +47 -0
  117. data/test/stub_responses/records/contact-62392126-dba4-4a75-b907-5875ebf75259.xml +40 -0
  118. data/test/stub_responses/records/contact-642c7fb5-e8e5-48e1-a710-39a18c6c3217.xml +40 -0
  119. data/test/stub_responses/records/contact-64aebf9c-bb89-4b38-b99b-405bd1ece6fd.xml +40 -0
  120. data/test/stub_responses/records/contact-64eedbc9-1fa0-485a-837f-705f23188161.xml +38 -0
  121. data/test/stub_responses/records/contact-65e96c9f-1595-4653-9a8a-2a36d49223c2.xml +40 -0
  122. data/test/stub_responses/records/contact-67d26b93-ccb4-4890-9bf1-284b70ea755d.xml +38 -0
  123. data/test/stub_responses/records/contact-69d3e538-44b3-4e00-a5f6-7dddcb6e0656.xml +40 -0
  124. data/test/stub_responses/records/contact-6a8450bc-f81a-4bb0-a8f6-aa4afe9497c7.xml +40 -0
  125. data/test/stub_responses/records/contact-6c70e424-41d6-4b9b-af3e-b3a9f3589106.xml +40 -0
  126. data/test/stub_responses/records/contact-6de0b0cf-560c-4503-aab3-e1543c329deb.xml +54 -0
  127. data/test/stub_responses/records/contact-72dd6a02-396e-42a2-a4d6-cc3fa75dfece.xml +41 -0
  128. data/test/stub_responses/records/contact-755f1475-d255-43a8-bedc-5ea7fd26c71f.xml +51 -0
  129. data/test/stub_responses/records/contact-78a9d0a0-3d8c-4f84-af3e-f260bf4a9dc0.xml +38 -0
  130. data/test/stub_responses/records/contact-79aa39ca-22b0-42c2-9026-78757a29d665.xml +42 -0
  131. data/test/stub_responses/records/contact-804f4140-5978-48fe-ba20-b56e5b834b18.xml +40 -0
  132. data/test/stub_responses/records/contact-812d4f28-1681-4241-8e34-d15c5520ba35.xml +38 -0
  133. data/test/stub_responses/records/contact-860b99a9-0958-4c8d-a98f-bb1f092b16bb.xml +60 -0
  134. data/test/stub_responses/records/contact-87c8da45-97cc-46be-b170-398da0eacfb8.xml +40 -0
  135. data/test/stub_responses/records/contact-8a154a19-6c6c-404b-bbc9-6deae2d18251.xml +38 -0
  136. data/test/stub_responses/records/contact-8bb6931d-2865-44e9-9a23-ed1fb9c7a46c.xml +40 -0
  137. data/test/stub_responses/records/contact-936c9759-01da-4063-b472-424ab9f48212.xml +38 -0
  138. data/test/stub_responses/records/contact-9d12a994-9640-4b75-95cc-3de1e9d0ef09.xml +38 -0
  139. data/test/stub_responses/records/contact-9fe59245-1fbb-4157-93c3-dc97388f3746.xml +40 -0
  140. data/test/stub_responses/records/contact-a06a7225-6f8a-4522-8400-c534dd43a16e.xml +40 -0
  141. data/test/stub_responses/records/contact-a76a85fe-73a2-46fa-aba7-791f36103cdb.xml +40 -0
  142. data/test/stub_responses/records/contact-a93b5f40-0346-4d21-9181-431e129911c0.xml +40 -0
  143. data/test/stub_responses/records/contact-abf272dd-6b1d-4829-af88-c57bf55855e3.xml +38 -0
  144. data/test/stub_responses/records/contact-ad24c33b-256b-4157-ad56-cbcf0e8db7b1.xml +47 -0
  145. data/test/stub_responses/records/contact-b107129d-f4c9-438e-9573-64b778527f4a.xml +40 -0
  146. data/test/stub_responses/records/contact-b233288a-aa26-4b26-9fc7-779d797dd56f.xml +40 -0
  147. data/test/stub_responses/records/contact-b2b5333a-2546-4975-891f-d71a8a640d23.xml +38 -0
  148. data/test/stub_responses/records/contact-b4d149bf-1823-4bd2-96da-9032388c9686.xml +40 -0
  149. data/test/stub_responses/records/contact-b78d4fd1-4306-4d83-a0b9-61458d1c53a2.xml +40 -0
  150. data/test/stub_responses/records/contact-b7d108a8-d5f7-4f16-a7c9-26eaed98e8de.xml +40 -0
  151. data/test/stub_responses/records/contact-baeed0f3-7989-4874-99b3-59f23032cb73.xml +38 -0
  152. data/test/stub_responses/records/contact-bc51a3a1-b7f6-46ca-ac9e-19b87e6ca100.xml +40 -0
  153. data/test/stub_responses/records/contact-be9f3aab-52f5-4d9c-94b4-87f7d9e5ee8b.xml +38 -0
  154. data/test/stub_responses/records/contact-c135f994-01e4-427b-9e15-acfe8a477c16.xml +49 -0
  155. data/test/stub_responses/records/contact-c14edf75-15e4-4a9c-86e4-f52e2fe7cfa4.xml +40 -0
  156. data/test/stub_responses/records/contact-ca9b9abc-c2dc-4221-8101-31f464d314cc.xml +44 -0
  157. data/test/stub_responses/records/contact-cc4db604-9ed8-4eef-8a29-51b5b70496a0.xml +38 -0
  158. data/test/stub_responses/records/contact-cce9b044-be4a-43b3-9dc7-c027d8dd35b2.xml +38 -0
  159. data/test/stub_responses/records/contact-d0cd2c4f-18a0-4f7c-a32a-2db00f29d298.xml +43 -0
  160. data/test/stub_responses/records/contact-d6851dc2-9ed9-4515-bc0b-810b09c06a6a.xml +38 -0
  161. data/test/stub_responses/records/contact-d6a384fb-f46f-41a3-8ac7-b7bc9e0b5efa.xml +46 -0
  162. data/test/stub_responses/records/contact-d74e61cf-2ad0-4f0d-b9d1-6a808e3f70cf.xml +40 -0
  163. data/test/stub_responses/records/contact-d9ab0f61-3b56-4e2b-be39-f33c11bd99e3.xml +40 -0
  164. data/test/stub_responses/records/contact-dbb1f0b5-a71b-4458-8462-104acd0fec6b.xml +38 -0
  165. data/test/stub_responses/records/contact-dd981bd6-40dd-496d-a282-bf7d3391b8b9.xml +40 -0
  166. data/test/stub_responses/records/contact-e1826204-cc0a-42a5-a6d0-4b352d9d5953.xml +40 -0
  167. data/test/stub_responses/records/contact-e2d955db-f366-42dd-87f7-fbdb4da2306f.xml +40 -0
  168. data/test/stub_responses/records/contact-e32e2130-3d27-443a-8313-48fffa03cf53.xml +40 -0
  169. data/test/stub_responses/records/contact-e3a68332-d322-4816-8678-73a537c8cd33.xml +38 -0
  170. data/test/stub_responses/records/contact-e6ac76a3-ca32-4fa1-8ef9-6a4bf8b0ec2a.xml +40 -0
  171. data/test/stub_responses/records/contact-e6ca965d-7c48-480e-be39-e847307f474a.xml +38 -0
  172. data/test/stub_responses/records/contact-e77d1f20-2e8e-46ec-9a10-50335a216724.xml +40 -0
  173. data/test/stub_responses/records/contact-e8b98c13-a424-41d2-ba0e-7b7621411e7a.xml +38 -0
  174. data/test/stub_responses/records/contact-e8e9a2c2-3e7e-48ed-8528-c3d61b28f276.xml +39 -0
  175. data/test/stub_responses/records/contact-eb43fcc6-87ec-4a0a-b243-d718bee4e2cb.xml +38 -0
  176. data/test/stub_responses/records/contact-ef6f54c1-eb45-4956-b8cd-1be82ad665f2.xml +43 -0
  177. data/test/stub_responses/records/contact-efdb3600-f233-42e2-8f18-ce7e2a95e4b1.xml +38 -0
  178. data/test/stub_responses/records/contact-f7eca431-5c97-4d24-93fd-004bb8a6c644.xml +40 -0
  179. data/test/stub_responses/records/contact-fb078879-5d6d-474f-825f-61dc90689349.xml +38 -0
  180. data/test/stub_responses/records/contact-fc39b273-4aa2-4785-99ca-24672f6c0000.xml +38 -0
  181. data/test/stub_responses/records/contact-fc9ec3a6-a2fe-4300-a8cb-ca8a0b3662e0.xml +40 -0
  182. data/test/stub_responses/records/contact-fdf96102-7491-44b6-bf4d-7a77ff25f890.xml +40 -0
  183. data/test/stub_responses/records/contact-fe61ead1-8afc-4f0b-beda-066620227aad.xml +38 -0
  184. data/test/stub_responses/records/credit_note-371cd138-1e5c-4ec1-a8c6-a1c10e8bdab1.xml +69 -0
  185. data/test/stub_responses/records/credit_note-3bffc09b-79f2-490d-b91b-c59b700b43a4.xml +91 -0
  186. data/test/stub_responses/records/credit_note-43c678ee-f357-48e2-b192-b6e3634762f9.xml +90 -0
  187. data/test/stub_responses/records/credit_note-482c018b-d329-4e05-9b4f-7a4cfc695aa0.xml +73 -0
  188. data/test/stub_responses/records/credit_note-4f67130a-749a-4ee6-98b2-743adbc11245.xml +60 -0
  189. data/test/stub_responses/records/credit_note-50e98404-2fba-4031-af67-8ba4bb227c44.xml +74 -0
  190. data/test/stub_responses/records/credit_note-7df8949c-b71f-40c0-bbcf-39f2f450f286.xml +73 -0
  191. data/test/stub_responses/records/credit_note-b356e488-2678-4be4-ad4b-d294df2d48d6.xml +76 -0
  192. data/test/stub_responses/records/invoice-0032f627-3156-4d30-9b1c-4d3b994dc921.xml +82 -0
  193. data/test/stub_responses/records/invoice-00c9511b-24b9-4190-a90a-8abf2fe9f4a0.xml +74 -0
  194. data/test/stub_responses/records/invoice-024d7994-a26c-4c20-9894-13934840fc31.xml +73 -0
  195. data/test/stub_responses/records/invoice-0e64a623-c2a1-446a-93ed-eb897f118cbc.xml +96 -0
  196. data/test/stub_responses/records/invoice-15e88e57-2554-4496-a18e-eb3f5c622345.xml +73 -0
  197. data/test/stub_responses/records/invoice-166f0588-d0ba-458c-b28a-8edd4c8fc463.xml +73 -0
  198. data/test/stub_responses/records/invoice-1d1ba340-afa2-4f4c-8ff7-a147bda9a47b.xml +91 -0
  199. data/test/stub_responses/records/invoice-290ef4c4-baec-492b-b4dd-c102826470ae.xml +85 -0
  200. data/test/stub_responses/records/invoice-30a87092-31b5-4a2c-831e-327486533dd2.xml +77 -0
  201. data/test/stub_responses/records/invoice-30dbd181-72a8-43df-b392-4241bf43d5fc.xml +68 -0
  202. data/test/stub_responses/records/invoice-33e4123e-7cdd-4f05-9a0a-eb8adeb2b868.xml +92 -0
  203. data/test/stub_responses/records/invoice-387db692-26ac-47e6-b6cc-015343809bda.xml +73 -0
  204. data/test/stub_responses/records/invoice-3b28bf11-ed2f-4cf4-8e9e-fcae730cc292.xml +89 -0
  205. data/test/stub_responses/records/invoice-3fcb9847-b350-412e-ab90-7d9d774ad881.xml +89 -0
  206. data/test/stub_responses/records/invoice-4602eda6-abe9-448e-b65f-ae6bea21f0eb.xml +96 -0
  207. data/test/stub_responses/records/invoice-46441f63-873f-4cdc-a278-b8fe516f3abb.xml +91 -0
  208. data/test/stub_responses/records/invoice-4ad1ec01-f4a3-41d7-bbb4-d2ab2fec8e65.xml +73 -0
  209. data/test/stub_responses/records/invoice-4b9afceb-f7c7-4e64-8aac-7b009971fd52.xml +84 -0
  210. data/test/stub_responses/records/invoice-4edbf6d5-4e92-43af-bedd-7effc0b86833.xml +68 -0
  211. data/test/stub_responses/records/invoice-4fad1af2-b871-4ac5-a15a-3c5e32d2e2c4.xml +83 -0
  212. data/test/stub_responses/records/invoice-52ee4d67-cae4-462c-adb2-182c39017f3d.xml +81 -0
  213. data/test/stub_responses/records/invoice-54585f46-c1a0-4432-bd4f-c1fae2fba59b.xml +66 -0
  214. data/test/stub_responses/records/invoice-5613938b-9e27-472e-92ae-3b038b669d10.xml +85 -0
  215. data/test/stub_responses/records/invoice-5aa9451d-95d1-4f95-a966-bbab2573f71c.xml +73 -0
  216. data/test/stub_responses/records/invoice-5aadcd34-01a9-4b8d-a2bb-d7cc1de9fa45.xml +87 -0
  217. data/test/stub_responses/records/invoice-5f6deadf-36a2-495a-9980-ceb11e8af9a9.xml +83 -0
  218. data/test/stub_responses/records/invoice-625ffe1b-f5d8-438e-a376-981de5f5a733.xml +75 -0
  219. data/test/stub_responses/records/invoice-64cd559e-8e03-46af-b461-8555285cee71.xml +84 -0
  220. data/test/stub_responses/records/invoice-666f8dbb-bc9a-476c-8ec4-4665d7f83190.xml +63 -0
  221. data/test/stub_responses/records/invoice-66fbe37f-49b1-43fd-97ed-85114022cd2f.xml +77 -0
  222. data/test/stub_responses/records/invoice-673dd7cc-beb7-4697-83d4-0c47cb400cc2.xml +85 -0
  223. data/test/stub_responses/records/invoice-69fc971e-9b37-41c5-9c87-174330f22343.xml +65 -0
  224. data/test/stub_responses/records/invoice-70e6db69-e5a4-42c7-a397-aa3212c2945f.xml +73 -0
  225. data/test/stub_responses/records/invoice-766d1289-b440-4675-a656-1a0612ecac77.xml +74 -0
  226. data/test/stub_responses/records/invoice-76bcb361-f93b-4513-b312-5a4af306d276.xml +66 -0
  227. data/test/stub_responses/records/invoice-76e3f056-479f-417c-a72b-f3d767899b87.xml +89 -0
  228. data/test/stub_responses/records/invoice-77b338ef-ecc0-4b95-a0d7-2617b0054611.xml +103 -0
  229. data/test/stub_responses/records/invoice-7be9956d-5316-4f6b-a66a-d355b3f159b2.xml +82 -0
  230. data/test/stub_responses/records/invoice-7dae876a-b424-436b-a4e6-17b3fdeec80c.xml +82 -0
  231. data/test/stub_responses/records/invoice-7e862d93-8dab-4856-8b0c-d844e09d750f.xml +66 -0
  232. data/test/stub_responses/records/invoice-803f70b0-56d9-4157-9787-41df271777a0.xml +82 -0
  233. data/test/stub_responses/records/invoice-86102312-aa3f-438c-9938-6840f4d8dda6.xml +74 -0
  234. data/test/stub_responses/records/invoice-8694c9c5-7097-4449-a708-b8c1982921a4.xml +68 -0
  235. data/test/stub_responses/records/invoice-86d6e00f-ef56-49f7-9a54-796ccd5ca057.xml +83 -0
  236. data/test/stub_responses/records/invoice-88e77f0f-54a5-4efc-a979-7e22223cc4d7.xml +65 -0
  237. data/test/stub_responses/records/invoice-8b0ccb6a-d9b7-4da5-8360-ef7fb157b5aa.xml +65 -0
  238. data/test/stub_responses/records/invoice-935fc854-8037-4111-8d91-993010c331cc.xml +73 -0
  239. data/test/stub_responses/records/invoice-95ef3000-c764-4ba9-a66a-b6e2d161f839.xml +62 -0
  240. data/test/stub_responses/records/invoice-962ef33f-c9d2-4602-9b9f-93a02bea23b3.xml +90 -0
  241. data/test/stub_responses/records/invoice-9868b472-1983-48e9-8edf-7e81ddf2c03a.xml +83 -0
  242. data/test/stub_responses/records/invoice-9a448e9b-a9fa-4a8b-98f5-6dc892a37374.xml +62 -0
  243. data/test/stub_responses/records/invoice-a1d04a14-96a8-4067-a0ff-8136990a354f.xml +91 -0
  244. data/test/stub_responses/records/invoice-a3bc62ef-f11b-4a9c-a4f9-a342bda371b5.xml +65 -0
  245. data/test/stub_responses/records/invoice-a6894ca0-60ee-4d45-9dd4-b44fcba46ec5.xml +88 -0
  246. data/test/stub_responses/records/invoice-a9f765e6-b9bc-4505-a47b-fb3ecb327e7b.xml +85 -0
  247. data/test/stub_responses/records/invoice-aa0173af-8707-4e7f-8dde-4c7a357bd312.xml +92 -0
  248. data/test/stub_responses/records/invoice-ab63738a-370a-43a5-bfa3-620d684e66d0.xml +81 -0
  249. data/test/stub_responses/records/invoice-b0344791-5a8a-40dd-a208-d99a461a6c10.xml +82 -0
  250. data/test/stub_responses/records/invoice-b1e53910-473c-46a3-b3cb-38ece571220e.xml +66 -0
  251. data/test/stub_responses/records/invoice-b2c02d0b-41a8-4d4d-97d7-014c78b3547d.xml +91 -0
  252. data/test/stub_responses/records/invoice-b75b3928-ab72-4424-8b93-9cdbbde4cd72.xml +80 -0
  253. data/test/stub_responses/records/invoice-bcd8a71f-aa31-4d0f-8a01-13ea26363ddf.xml +92 -0
  254. data/test/stub_responses/records/invoice-bfbb7c45-de02-45e7-b065-d9863ecfb0d8.xml +65 -0
  255. data/test/stub_responses/records/invoice-c12aff7e-12bf-4185-8702-460929f19674.xml +76 -0
  256. data/test/stub_responses/records/invoice-c3380b96-976d-4b3e-8b26-8d01eb6a3742.xml +85 -0
  257. data/test/stub_responses/records/invoice-c963f2b0-cbe1-4abd-9ccc-7e512c942068.xml +66 -0
  258. data/test/stub_responses/records/invoice-d62646b9-d0a9-4fdb-9561-756a8b7eba45.xml +63 -0
  259. data/test/stub_responses/records/invoice-dba2f021-f149-4191-a126-5351d587ab0e.xml +74 -0
  260. data/test/stub_responses/records/invoice-de5d9c29-21b3-4342-958b-ed72c4bd7ab0.xml +106 -0
  261. data/test/stub_responses/records/invoice-e3d96555-2876-4364-a46a-7551a4f52611.xml +119 -0
  262. data/test/stub_responses/records/invoice-e4a0afbd-aea0-450b-ae23-0ce921e84a77.xml +99 -0
  263. data/test/stub_responses/records/invoice-e9cb9ecb-58ef-43a8-bd20-69a85338142d.xml +74 -0
  264. data/test/stub_responses/records/invoice-ec9a6f67-7128-4a63-8ba3-5e516f455f9b.xml +92 -0
  265. data/test/stub_responses/records/invoice-ed0f2587-84fc-4aef-bc4b-b1a262e24484.xml +78 -0
  266. data/test/stub_responses/records/invoice-f362ca53-8ade-4047-865a-bb64bee5863d.xml +73 -0
  267. data/test/stub_responses/records/invoice-f571c38b-5be1-41e1-ad5a-ff6184284beb.xml +104 -0
  268. data/test/stub_responses/records/invoice-f5832195-5cd3-4660-ad3f-b73d9c64f263.xml +83 -0
  269. data/test/stub_responses/records/invoice-f9c857eb-64cd-4235-a078-d04b52c77ea7.xml +74 -0
  270. data/test/stub_responses/records/manual_journal-4765d07b-aa03-4e56-9166-50661958c864.xml +38 -0
  271. data/test/stub_responses/records/manual_journal-53fc5558-5b76-4ecd-ae5c-c4af3ccde87c.xml +31 -0
  272. data/test/stub_responses/records/manual_journal-bb6cfcfc-4500-4475-bd3a-93ee512428e0.xml +31 -0
  273. data/test/stub_responses/records/manual_journal-f00a355b-7374-445c-886b-0437bea4095c.xml +45 -0
  274. data/test/stub_responses/refresh_responses.rb +29 -0
  275. data/test/stub_responses/tax_rates.xml +118 -0
  276. data/test/stub_responses/token_expired +1 -0
  277. data/test/stub_responses/tracking_categories.xml +27 -0
  278. data/test/stub_responses/unknown_error.xml +1 -0
  279. data/test/test_helper.rb +49 -0
  280. data/test/unit/models/contact_test.rb +26 -0
  281. data/test/unit/models/credit_note_test.rb +37 -0
  282. data/test/unit/models/invoice_test.rb +51 -0
  283. data/test/unit/oauth_test.rb +72 -0
  284. data/test/unit/private_application_test.rb +20 -0
  285. data/test/unit/record/base_model_test.rb +50 -0
  286. data/test/unit/record/base_test.rb +57 -0
  287. data/test/unit/record/model_definition_test.rb +159 -0
  288. data/test/unit/record/parse_where_hash_test.rb +63 -0
  289. data/test/unit/record/record_association_test.rb +36 -0
  290. data/test/unit/record/validators_test.rb +180 -0
  291. data/xeroizer.gemspec +375 -0
  292. metadata +533 -0
data/.bundle/config ADDED
@@ -0,0 +1,2 @@
1
+ ---
2
+ BUNDLE_DISABLE_SHARED_GEMS: "1"
data/Gemfile ADDED
@@ -0,0 +1,12 @@
1
+ source "http://rubygems.org"
2
+
3
+ gem 'builder', '>= 2.1.2'
4
+ gem 'oauth', '>= 0.3.6'
5
+ gem 'activesupport'
6
+ gem 'nokogiri'
7
+ gem "jeweler", "~> 1.5.2"
8
+
9
+ group :test do
10
+ gem 'mocha'
11
+ gem 'shoulda'
12
+ end
data/Gemfile.lock ADDED
@@ -0,0 +1,22 @@
1
+ GEM
2
+ remote: http://rubygems.org/
3
+ specs:
4
+ activesupport (3.0.3)
5
+ builder (3.0.0)
6
+ mocha (0.9.10)
7
+ rake
8
+ nokogiri (1.4.4)
9
+ oauth (0.4.4)
10
+ rake (0.8.7)
11
+ shoulda (2.11.3)
12
+
13
+ PLATFORMS
14
+ ruby
15
+
16
+ DEPENDENCIES
17
+ activesupport
18
+ builder (>= 2.1.2)
19
+ mocha
20
+ nokogiri
21
+ oauth (>= 0.3.6)
22
+ shoulda
data/LICENSE.txt ADDED
@@ -0,0 +1,20 @@
1
+ Copyright (c) 2011 Wayne Robinson
2
+
3
+ Permission is hereby granted, free of charge, to any person obtaining
4
+ a copy of this software and associated documentation files (the
5
+ "Software"), to deal in the Software without restriction, including
6
+ without limitation the rights to use, copy, modify, merge, publish,
7
+ distribute, sublicense, and/or sell copies of the Software, and to
8
+ permit persons to whom the Software is furnished to do so, subject to
9
+ the following conditions:
10
+
11
+ The above copyright notice and this permission notice shall be
12
+ included in all copies or substantial portions of the Software.
13
+
14
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
15
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
16
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
17
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
18
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
19
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
20
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
data/README.md ADDED
@@ -0,0 +1,416 @@
1
+ Xeroizer API Library
2
+ ====================
3
+
4
+ **Homepage**: [http://waynerobinson.github.com/xeroizer](http://waynerobinson.github.com/xeroizer)
5
+ **Git**: [git://github.com/waynerobinson/xeroizer.git](git://github.com/waynerobinson/xeroizer.git)
6
+ **Github**: [https://github.com/waynerobinson/xeroizer](https://github.com/waynerobinson/xeroizer)
7
+ **Author**: Wayne Robinson [http://www.wayne-robinson.com](http://www.wayne-robinson.com)
8
+ **Contributors**: See Contributors section below
9
+ **Copyright**: 2007-2010
10
+ **License**: MIT License
11
+
12
+ Introduction
13
+ ------------
14
+
15
+ This library is designed to help ruby/rails based applications communicate with the publicly available API for Xero.
16
+
17
+ If you are unfamiliar with the Xero API, you should first read the documentation located at http://developer.xero.com.
18
+
19
+ Basic Usage
20
+ -----------
21
+
22
+ require 'xeroizer'
23
+
24
+ # Create client (used to communicate with the API).
25
+ client = Xeroizer::PublicApplication.new(YOUR_OAUTH_CONSUMER_KEY, YOUR_OAUTH_CONSUMER_SECRET)
26
+
27
+ # Retrieve list of contacts (note: all communication must be made through the client).
28
+ contacts = client.Contact.all(:order => 'Name')
29
+
30
+ Authentication
31
+ --------------
32
+
33
+ Xero uses OAuth to authenticate API clients. The OAuth gem (with minor modification) by John Nunemaker ([http://github.com/jnunemaker/twitter](http://github.com/jnunemaker/twitter)) is used in this library. If you've used this before, things will all seem very familar.
34
+
35
+ There are three methods of authentication detailed below:
36
+
37
+ ### All: Consumer Key/Secret
38
+
39
+ All methods of authentication require your OAuth consumer key and secret. This can be found for your application
40
+ in the API management console at [http://api.xero.com](http://api.xero.com).
41
+
42
+ ### Public Applications
43
+
44
+ Public applications use a 3-legged authorisation process. A user will need to authorise your
45
+ application against each organisation that you want access to. Your application can have access
46
+ to many organisations at once by going through the authorisation process for each organisation.
47
+
48
+ The access token received will expire after 30 minutes. If you want access for longer you will need
49
+ the user to re-authorise your application.
50
+
51
+ Authentication occcurs in 3 steps:
52
+
53
+ client = Xeroizer::PublicApplication.new(YOUR_OAUTH_CONSUMER_KEY, YOUR_OAUTH_CONSUMER_SECRET)
54
+
55
+ # 1. Get a RequestToken from Xero. The :oauth_url is the URL the user will be redirected to
56
+ # after they have authenticated your application.
57
+ #
58
+ # Note: The callback URL's domain must match that listed for your application in http://api.xero.com
59
+ # otherwise the user will not be redirected and only be shown the authentication code.
60
+ request_token = client.request_token(:oauth_url => 'http://yourapp.com/oauth/callback')
61
+
62
+ # 2. Redirect the user to the URL specified by the RequestToken.
63
+ #
64
+ # Note: example uses redirect_to method defined in Rails controllers.
65
+ redirect_to request_token.authorize_url
66
+
67
+ # 3. Exchange RequestToken for AccessToken.
68
+ # This access token will be used for all subsequent requests but it is stored within the client
69
+ # application so you don't have to record it.
70
+ #
71
+ # Note: This example assumes the callback URL is a Rails action.
72
+ client.authorize_from_request(request_token.token, request_token.secret, :oauth_verifier => params[:oauth_verifier])
73
+
74
+ You can now use the client to access the Xero API methods, e.g.
75
+
76
+ contacts = client.Contact.all
77
+
78
+ #### Example Rails Controller
79
+
80
+ class XeroSessionController < ApplicationController
81
+
82
+ before_filter :get_xero_client
83
+
84
+ public
85
+
86
+ def new
87
+ request_token = @xero_client.request_token(:oauth_url => 'http://yourapp.com/xero_session/create')
88
+ session[:request_token] = request_token.token
89
+ session[:request_secret] = request_token.secret
90
+
91
+ redirect_to request_token.authorize_url
92
+ end
93
+
94
+ def create
95
+ @xero_client.authorize_from_request(
96
+ session[:request_token],
97
+ session[:request_secret],
98
+ :oauth_verifier => params[:oauth_verifier] )
99
+
100
+ session[:xero_auth] = {
101
+ :access_token => @xero_client.access_token.token,
102
+ :access_key => @xero_client.access_token.key }
103
+
104
+ session.data.delete(:request_token)
105
+ session.data.delete(:request_secret)
106
+ end
107
+
108
+ def destroy
109
+ session.data.delete(:xero_auth)
110
+ end
111
+
112
+ private
113
+
114
+ def get_xero_client
115
+ @xero_client = Xeroizer::PublicApplication.new(YOUR_OAUTH_CONSUMER_KEY, YOUR_OAUTH_CONSUMER_SECRET)
116
+
117
+ # Add AccessToken if authorised previously.
118
+ if session[:xero_auth]
119
+ @xero_client.authorize_from_access(
120
+ session[:xero_auth][:access_token],
121
+ session[:xero_auth][:access_key] )
122
+ end
123
+ end
124
+ end
125
+
126
+ #### Storing AccessToken
127
+
128
+ You can store the access token/secret pair so you can access the API again without user intervention. Currently these
129
+ tokens are only valid for 30 minutes and will raise a `Xeroizer::OAuth::TokenExpired` exception if you try to access
130
+ the API beyond the token's expiry time.
131
+
132
+ If you want API access for longer consider creating a PartnerApplication which will allow you to renew tokens.
133
+
134
+ access_key = client.access_token.token
135
+ access_secret = client.access_token.secret
136
+
137
+ ### Private Applications
138
+
139
+ Private applications use a 2-legged authorisation process. When you register your application, you will select
140
+ the organisation that is authorised to your application. This cannot be changed afterwards, although you can
141
+ register another private application if you have multiple organisations.
142
+
143
+ Note: You can only register organisations you are authorised to yourself.
144
+
145
+ Private applications require a private RSA keypair which is used to sign each request to the API. You can
146
+ generate this keypair on Mac OSX or Linux with OpenSSL. For example:
147
+
148
+ openssl genrsa -out privatekey.pem 1024
149
+ openssl req -newkey rsa:1024 -x509 -key privatekey.pem -out publickey.cer -days 365
150
+ openssl pkcs12 -export -out public_privatekey.pfx -inkey privatekey.pem -in publickey.cer
151
+
152
+ You need to upload this `public_privatekey.pfx` file to your private application in [http://api.xero.com](http://api.xero.com).
153
+
154
+ Example usage:
155
+
156
+ client = Xeroizer::PrivateApplication.new(YOUR_OAUTH_CONSUMER_KEY, YOUR_OAUTH_CONSUMER_SECRET, "/path/to/privatekey.pem")
157
+ contacts = client.Contact.all
158
+
159
+ ### Partner Applications
160
+
161
+ Partner applications use a combination of 3-legged authorisation, private key message signing and client-side SSL
162
+ certificate signing.
163
+
164
+ Partner applications are only in beta testing via the Xero API and you will need to contact Xero (network@xero.com) to
165
+ get permission to create a partner application and for them to send you information on obtaining your client-side SSL
166
+ certificate.
167
+
168
+ Ruby's OpenSSL library rqeuires the certificate and private key to be extracted from the `entrust-client.p12` file
169
+ downloaded via Xero's instructions. To extract:
170
+
171
+ openssl pkcs12 -in entrust-client.p12 -clcerts -nokeys -out entrust-cert.pem
172
+ openssl pkcs12 -in entrust-client.p12 -nocerts -out entrust-private.pem
173
+ openssl rsa -in entrust-private.pem -out entrust-private-nopass.pem
174
+
175
+ # This last step removes the password that you added to the private key
176
+ # when it was exported.
177
+
178
+ After you have followed the instructions provided by Xero for partner applications and uploaded your certificate you can
179
+ access the partner application in a similar way to public applications.
180
+
181
+ Authentication occcurs in 3 steps:
182
+
183
+ client = Xeroizer::PartnerApplication.new(
184
+ YOUR_OAUTH_CONSUMER_KEY,
185
+ YOUR_OAUTH_CONSUMER_SECRET,
186
+ "/path/to/privatekey.pem",
187
+ "/path/to/entrust-cert.pem",
188
+ "/path/to/entrust-private-nopass.pem"
189
+ )
190
+
191
+ # 1. Get a RequestToken from Xero. The :oauth_url is the URL the user will be redirected to
192
+ # after they have authenticated your application.
193
+ #
194
+ # Note: The callback URL's domain must match that listed for your application in http://api.xero.com
195
+ # otherwise the user will not be redirected and only be shown the authentication code.
196
+ request_token = client.request_token(:oauth_url => 'http://yourapp.com/oauth/callback')
197
+
198
+ # 2. Redirect the user to the URL specified by the RequestToken.
199
+ #
200
+ # Note: example uses redirect_to method defined in Rails controllers.
201
+ redirect_to request_token.authorize_url
202
+
203
+ # 3. Exchange RequestToken for AccessToken.
204
+ # This access token will be used for all subsequent requests but it is stored within the client
205
+ # application so you don't have to record it.
206
+ #
207
+ # Note: This example assumes the callback URL is a Rails action.
208
+ client.authorize_from_request(request_token.token, request_token.secret, :oauth_verifier => params[:oauth_verifier])
209
+
210
+ This AccessToken will last for 30 minutes however, when using the partner application API you can
211
+ renew this token. To be able to renew this token, you need to save the following data from this organisation's
212
+ AccessToken:
213
+
214
+ session_handle = client.session_handle
215
+ access_key = client.access_token.token
216
+ access_secret = client.access_token.secret
217
+
218
+ Two other interesting attributes of the PartnerApplication client are:
219
+
220
+ > **`#expires_at`**: Time this AccessToken will expire (usually 30 minutes into the future).
221
+ > **`#authorization_expires_at`**: How long this organisation has authorised you to access their data (usually 365 days into the future).
222
+
223
+ #### AccessToken Renewal
224
+
225
+ Renewal of an access token requires knowledge of the previous access token generated for this organisation. To renew:
226
+
227
+ # If you still have a client instance.
228
+ client.renew_access_token
229
+
230
+ # If you are renewing from stored token/session details.
231
+ client.renew_access_token(access_key, access_secret, session_handle)
232
+
233
+ This will invalidate the previous token and refresh the `access_key` and `access_secret` as specified in the
234
+ initial authorisation process. You must always know the previous token's details to renew access to this
235
+ session.
236
+
237
+ If you lose these details at any stage you can always reauthorise by redirecting the user back to the Xero OAuth gateway.
238
+
239
+ Retrieving Data
240
+ ---------------
241
+
242
+ Each of the below record types is implemented within this library. To allow for multiple access tokens to be used at the same
243
+ time in a single application, the model classes are accessed from the instance of PublicApplication, PrivateApplication
244
+ or PartnerApplication. All class-level operations occur on this singleton. For example:
245
+
246
+ xero = Xeroizer::PublicApplication.new(YOUR_OAUTH_CONSUMER_KEY, YOUR_OAUTH_CONSUMER_SECRET)
247
+ xero.authorize_from_access(session[:xero_auth][:access_token], session[:xero_auth][:access_key])
248
+
249
+ contacts = xero.Contact.all(:order => 'Name')
250
+
251
+ new_contact = xero.Contact.build(:name => 'ABC Development')
252
+ saved = new_contact.save
253
+
254
+ ### \#all(options = {})
255
+
256
+ Retrieves list of all records with matching options.
257
+
258
+ **Note:** Some records (Invoice, CreditNote) only return summary information for the contact and no line items
259
+ when returning them this list operation. This library takes care of automatically retrieving the
260
+ contact and line items from Xero on first access however, this first access has a large performance penalty
261
+ and will count as an extra query towards your 1,000/day and 60/minute request per organisation limit.
262
+
263
+ Valid options are:
264
+
265
+ > **:modified\_since**
266
+
267
+ > Records modified after this `Time` (must be specified in UTC).
268
+
269
+ > **:order**
270
+
271
+ > Field to order by. Should be formatted as Xero-based field (e.g. 'Name', 'ContactID', etc)
272
+
273
+ > **:where**
274
+
275
+ > __See *Where Filters* section below.__
276
+
277
+ ### \#first(options = {})
278
+
279
+ This is a shortcut method for `all` and actually runs all however, this method only returns the
280
+ first entry returned by all and never an array.
281
+
282
+ ### \#find(id)
283
+
284
+ Looks up a single record matching `id`. This ID can either be the internal GUID Xero uses for the record
285
+ or, in the case of Invoice, CreditNote and Contact records, your own custom reference number used when
286
+ creating these records.
287
+
288
+ ### Where filters
289
+
290
+ #### Hash
291
+
292
+ You can specify find filters by providing the :where option with a hash. For example:
293
+
294
+ invoices = Xero.Invoice.all(:where => {:type => 'ACCREC', :amount_due_is_not => 0})
295
+
296
+ will automatically create the Xero string:
297
+
298
+ Type=="ACCREC"&&AmountDue<>0
299
+
300
+ The default method for filtering is the equality '==' operator however, these can be overridden
301
+ by modifying the postfix of the attribute name (as you can see for the :amount\_due field above).
302
+
303
+ \{attribute_name}_is_not will use '<>'
304
+ \{attribute_name}_is_greater_than will use '>'
305
+ \{attribute_name}_is_greater_than_or_equal_to will use '>='
306
+ \{attribute_name}_is_less_than will use '<'
307
+ \{attribute_name}_is_less_than_or_equal_to will use '<='
308
+
309
+ The default is '=='
310
+
311
+ **Note:** Currently, the hash-conversion library only allows for AND-based criteria and doesn't
312
+ take into account associations. For these, please use the custom filter method below.
313
+
314
+ #### Custom Xero-formatted string
315
+
316
+ Xero allows advanced custom filters to be added to a request. The where parameter can reference any XML element
317
+ in the resulting response, including all nested XML elements.
318
+
319
+ **Example 1: Retrieve all invoices for a specific contact ID:**
320
+
321
+ invoices = xero.Invoice.all(:where => 'Contact.ContactID.ToString()=="cd09aa49-134d-40fb-a52b-b63c6a91d712"')
322
+
323
+ **Example 2: Retrieve all unpaid ACCREC Invoices against a particular Contact Name:**
324
+
325
+ invoices = xero.Invoice.all(:where => 'Contact.Name=="Basket Case" && Type=="ACCREC" && AmountDue<>0')
326
+
327
+ **Example 3: Retrieve all Invoices PAID between certain dates**
328
+
329
+ invoices = xero.Invoice.all(:where => 'FullyPaidOnDate>=DateTime.Parse("2010-01-01T00:00:00")&&FullyPaidOnDate<=DateTime.Parse("2010-01-08T00:00:00")')
330
+
331
+ **Example 4: Retrieve all Bank Accounts:**
332
+
333
+ accounts = xero.Account.all(:where => 'Type=="BANK"')
334
+
335
+ **Example 5: Retrieve all DELETED or VOIDED Invoices:**
336
+
337
+ invoices = xero.Invoice.all(:where => 'Status=="VOIDED" OR Status=="DELETED"')
338
+
339
+ **Example 6: Retrieve all contacts with specific text in the contact name:**
340
+
341
+ contacts = xero.Contact.all(:where => 'Name.Contains("Peter")')
342
+ contacts = xero.Contact.all(:where => 'Name.StartsWith("Pet")')
343
+ contacts = xero.Contact.all(:where => 'Name.EndsWith("er")')
344
+
345
+ Associations
346
+ ------------
347
+
348
+ Records may be associated with each other via two different methods, `has_many` and `belongs_to`.
349
+
350
+ **has\_many example:**
351
+
352
+ invoice = xero.Invoice.find('cd09aa49-134d-40fb-a52b-b63c6a91d712')
353
+ invoice.line_items.each do | line_item |
354
+ puts "Line Description: #{line_item.description}"
355
+ end
356
+
357
+ **belongs\_to example:**
358
+
359
+ invoice = xero.Invoice.find('cd09aa49-134d-40fb-a52b-b63c6a91d712')
360
+ puts "Invoice Contact Name: #{invoice.contact.name}"
361
+
362
+ Creating/Updating Data
363
+ ----------------------
364
+
365
+ ### Creating
366
+
367
+ New records can be created like:
368
+
369
+ contact = xero.Contact.build(:name => 'Contact Name')
370
+ contact.first_name = 'Joe'
371
+ contact.last_name = 'Bloggs'
372
+ contact.add_address(:type => 'STREET', :line1 => '12 Testing Lane', :city => 'Brisbane')
373
+ contact.add_phone(:type => 'DEFAULT', :area_code => '07', :number => '3033 1234')
374
+ contact.add_phone(:type => 'MOBILE', :number => '0412 123 456')
375
+ contact.save
376
+
377
+ To add to a `has_many` association use the `add_{association}` method. For example:
378
+
379
+ contact.add_address(:type => 'STREET', :line1 => '12 Testing Lane', :city => 'Brisbane')
380
+
381
+ To add to a `belongs_to` association use the `build_{association}` method. For example:
382
+
383
+ invoice.build_contact(:name => 'ABC Company')
384
+
385
+ ### Updating
386
+
387
+ If the primary GUID for the record is present, the library will attempt to update the record instead of
388
+ creating it. It is important that this record is downloaded from the Xero API first before attempting
389
+ an update. For example:
390
+
391
+ contact = xero.Contact.find("cd09aa49-134d-40fb-a52b-b63c6a91d712")
392
+ contact.name = "Another Name Change"
393
+ contact.save
394
+
395
+ Have a look at the models in `lib/xeroizer/models/` to see the valid attributes, associations and
396
+ minimum validation requirements for each of the record types.
397
+
398
+ ### Errors
399
+
400
+ If a record doesn't match it's internal validation requirements the `#save` method will return
401
+ `false` and the `#errors` attribute will be populated with what went wrong.
402
+
403
+ For example:
404
+
405
+ contact = xero.Contact.build
406
+ saved = contact.save
407
+
408
+ # contact.errors will contain [[:name, "can't be blank"]]
409
+
410
+ \#errors\_for(:attribute\_name) is a helper method to return just the errors associated with
411
+ that attribute. For example:
412
+
413
+ contact.errors_for(:name) # will contain ["can't be blank"]
414
+
415
+ If something goes really wrong and the particular validation isn't handled by the internal
416
+ validators then the library may raise a `Xeroizer::ApiException`.