Class: Pump
- Inherits:
-
Object
- Object
- Pump
- Defined in:
- lib/unipump.rb
Defined Under Namespace
Classes: Error
Constant Summary collapse
- DEFAULT_CREDENTIALS_FILE =
".unipump.environment"
- DEFAULT_ENVIRONMENT =
"test"
- TEST_SYSTEM =
Environment-dependent constants
"81470"
- PROD_SYSTEM =
Uniconta test system ID
"86837"
- TEST_TIMEREG_TABLE =
"tidsreg_test"
- PROD_TIMEREG_TABLE =
"tidsreg"
- TEST_FAKTURALINIE_VIEW =
"v_fakturalinie_test"
- PROD_FAKTURALINIE_VIEW =
"v_fakturalinie"
- TRANSACTION_FILE =
Transaction file. This contains IDs of the most recently uploaded TIDSREG records. It is cleared when the corresponding records in the database are updated
"unipump.transaction"
Instance Attribute Summary collapse
-
#conn ⇒ Object
readonly
Database connection.
-
#curr_comma ⇒ Object
readonly
Hack FIXME.
-
#dry_run ⇒ Object
readonly
Standard options.
-
#environment ⇒ Object
readonly
Run-time environment.
-
#jlog ⇒ Object
readonly
Path to JSON log file or nil.
-
#log ⇒ Object
readonly
Path to log file.
-
#log_file ⇒ Object
readonly
Log file object.
-
#password ⇒ Object
readonly
Returns the value of attribute password.
-
#quiet ⇒ Object
readonly
Standard options.
-
#username ⇒ Object
readonly
Returns the value of attribute username.
-
#xlog ⇒ Object
readonly
Path to transaction log file.
Instance Method Summary collapse
- #fakturalinie_view ⇒ Object
-
#initialize(username, password, environment, log: nil, xlog: nil, jlog: nil, dry_run: false, quiet: false) ⇒ Pump
constructor
A new instance of Pump.
- #insert_order_line_list_url ⇒ Object
- #insert_order_list_url ⇒ Object
- #order_list_url ⇒ Object
- #root_url ⇒ Object
-
#system ⇒ Object
Environment-dependent resources.
- #tidsreg_table ⇒ Object
- #upload(end_date, accounts) ⇒ Object
Constructor Details
#initialize(username, password, environment, log: nil, xlog: nil, jlog: nil, dry_run: false, quiet: false) ⇒ Pump
Returns a new instance of Pump.
73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 |
# File 'lib/unipump.rb', line 73 def initialize( username, password, environment, log: nil, xlog: nil, jlog: nil, dry_run: false, quiet: false) @environment = environment @username = username @password = password @log = log || '/dev/null' @xlog = xlog || TRANSACTION_FILE @jlog = jlog @dry_run = dry_run @quiet = quiet @log_file = File.open(@log, "a") @conn = UniPump::Connection.new @curr_comma = "" !File.exist?(@xlog) or ShellOpts::error "Found transaction file: #{@xlog}. Please fix this" FileUtils.rm_f(@jlog) if @jlog end |
Instance Attribute Details
#conn ⇒ Object (readonly)
Database connection
60 61 62 |
# File 'lib/unipump.rb', line 60 def conn @conn end |
#curr_comma ⇒ Object (readonly)
Hack FIXME
62 63 64 |
# File 'lib/unipump.rb', line 62 def curr_comma @curr_comma end |
#dry_run ⇒ Object (readonly)
Standard options
57 58 59 |
# File 'lib/unipump.rb', line 57 def dry_run @dry_run end |
#environment ⇒ Object (readonly)
Run-time environment. “prod” or “test”
53 54 55 |
# File 'lib/unipump.rb', line 53 def environment @environment end |
#jlog ⇒ Object (readonly)
Path to JSON log file or nil
56 57 58 |
# File 'lib/unipump.rb', line 56 def jlog @jlog end |
#log ⇒ Object (readonly)
Path to log file
54 55 56 |
# File 'lib/unipump.rb', line 54 def log @log end |
#log_file ⇒ Object (readonly)
Log file object
59 60 61 |
# File 'lib/unipump.rb', line 59 def log_file @log_file end |
#password ⇒ Object (readonly)
Returns the value of attribute password.
52 53 54 |
# File 'lib/unipump.rb', line 52 def password @password end |
#quiet ⇒ Object (readonly)
Standard options
57 58 59 |
# File 'lib/unipump.rb', line 57 def quiet @quiet end |
#username ⇒ Object (readonly)
Returns the value of attribute username.
51 52 53 |
# File 'lib/unipump.rb', line 51 def username @username end |
#xlog ⇒ Object (readonly)
Path to transaction log file
55 56 57 |
# File 'lib/unipump.rb', line 55 def xlog @xlog end |
Instance Method Details
#fakturalinie_view ⇒ Object
67 |
# File 'lib/unipump.rb', line 67 def fakturalinie_view = (environment == "prod" ? PROD_FAKTURALINIE_VIEW : TEST_FAKTURALINIE_VIEW) |
#insert_order_line_list_url ⇒ Object
71 |
# File 'lib/unipump.rb', line 71 def insert_order_line_list_url = "#{root_url}/InsertList/DebtorOrderLineClientUser" |
#insert_order_list_url ⇒ Object
70 |
# File 'lib/unipump.rb', line 70 def insert_order_list_url = "#{root_url}/InsertList/DebtorOrderClientUser" |
#order_list_url ⇒ Object
69 |
# File 'lib/unipump.rb', line 69 def order_list_url = "#{root_url}/DebtorOrderClientUser" |
#root_url ⇒ Object
68 |
# File 'lib/unipump.rb', line 68 def root_url = "https://odata.uniconta.com/api/Entities/#{system}" |
#system ⇒ Object
Environment-dependent resources
65 |
# File 'lib/unipump.rb', line 65 def system = (environment == "prod" ? PROD_SYSTEM : TEST_SYSTEM) |
#tidsreg_table ⇒ Object
66 |
# File 'lib/unipump.rb', line 66 def tidsreg_table = (environment == "prod" ? PROD_TIMEREG_TABLE : TEST_TIMEREG_TABLE) |
#upload(end_date, accounts) ⇒ Object
92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 |
# File 'lib/unipump.rb', line 92 def upload(end_date, accounts) t0 = Time.now emit("#{ShellOpts.instance.name} @ #{Time.now.strftime("%F %T")}") # Select specific accounts. Mostly for debug konto_constraint = (accounts.empty? ? "and 1 = 1" : "and kontonummer in (#{accounts.join(', ')})") # Fetch existing Uniconta orders uniconta_order_numbers = emit_exec(" Fetch orders") { get_uniconta_order_numbers(order_list_url) } emit " Found #{uniconta_order_numbers.size} orders in Uniconta" # Compute orders that are does not exist in Uniconta uniconta_order_numbers.reject! { |order_number| if order_number.to_s.size < 8 emit " Illegal order number: '#{order_number}', ignored" elsif order_number < 24000000 emit " Outdated order number: '#{order_number}', ignored" end } # Find order with lines that have not been pumped yet (lines with # fakturadato equal to null) orders = conn.structs %( select distinct sagsnummer, ansvarlig, kontonummer, ordrenummer from #{fakturalinie_view} where dato < '#{end_date}' and fakturadato is null #{konto_constraint} order by ansvarlig, sagsnummer ) emit " Found #{orders.size} orders in Sagsys" # Exclude orders with kontonummer equal to nil excluded = [] orders.delete_if { |order| if order.kontonummer.nil? excluded << order end } emit " #{excluded.size} with absent kontonummer - ignored" if !excluded.empty? # Compute order numbers that should be created. This is computed as a Set # for fast lookup missing_order_numbers = Set.new(orders.map(&:ordrenummer).map(&:to_i) - uniconta_order_numbers) # create order in Uniconta emit " Create #{missing_order_numbers.size} orders" for order in orders if missing_order_numbers.include?(order.ordrenummer.to_i) json = format_order(order) emit_exec(" #{order.ordrenummer}") { upload_json(insert_order_list_url, json) } end end # Process each order emit " Upload orders" for order in orders # We want to save a list of uploaded tidsreg records in case the # database update fails after the invoice lines have been uploaded to # Uniconta, but we also want the database to aggregate the tidsreg # records for us so we end up with the two queries below. It is # important the the two queries selects the same records so we use the # common method #line_constraint to supply the where constraint # # The list is written to a FILE that is removed after the records have # been updated sucessfully. It is an error if it exists already # line_constraint = %( dato <= '#{end_date}' and ordrenummer = '#{order.ordrenummer}' and fakturadato is null #{konto_constraint} ) # Begin transaction. We hope that the default isolation level is # resonable in MsSql conn.exec("begin transaction") # Find keys for affected TIMEREG records. These are saved to disk so # that we may recover from a database failure after the order lines # have been uploaded tidsreg_keys = conn.records %( select distinct meid, said, acid, dato from #{fakturalinie_view} where #{line_constraint} ) # Save tidsreg keys to disk File.open(xlog, "w") { |xfile| xfile.puts "# Ordrenummer: #{order.ordrenummer} (#{order.sagsnummer})" xfile.puts tidsreg_keys.to_a } # Find the order lines. These are aggregated tidsreg records and could # be coalesced into the previous select but then we would have to do # grouping and ordering in ruby. Note that the select clause in the pre order_lines = conn.structs %( select ordrenummer, medarbejderogrolle as "rolle", maaned, tekst + ': ' + md + ' ' + cast(aar as nvarchar) as "tekst", ydelse, sum(timer) as antal, pris from #{fakturalinie_view} f where #{line_constraint} group by ordrenummer, medarbejderogrolle, acid, tekst, md, maaned, aar, ydelse, pris order by ordrenummer, maaned, medarbejderogrolle, acid ) emit " #{order.ordrenummer} (#{order.sagsnummer}) #{order_lines.size} items" # Create json object for HTTP json = format_order_line(order, order_lines, end_date) # Transfer JSON document. Note that HTTP redirects are not supported # (yet) emit_exec(" Upload") { upload_json(insert_order_line_list_url, json) } # Update Sagsys database emit_exec(" Update") { update_tidsreg(tidsreg_keys, end_date) } # The whole operation completed successfully so we remove the transaction file FileUtils.rm_f(xlog) end emit "Done @ #{Time.now.strftime("%F %T")}" emit "" end |