diff --git a/.codeclimate.yml b/.codeclimate.yml new file mode 100644 index 0000000000..d737c0b3c9 --- /dev/null +++ b/.codeclimate.yml @@ -0,0 +1,13 @@ +engines: + rubocop: + enabled: true + scss-lint: + enabled: true +ratings: + paths: + - app/** + - lib/** + - "**.rb" +exclude_paths: +- spec/**/* +- vendor/**/* diff --git a/.gitignore b/.gitignore index f465ad7306..42107a8e2e 100644 --- a/.gitignore +++ b/.gitignore @@ -34,6 +34,7 @@ config/abr.yml config/heroku_env.rb config/newrelic.yml config/initializers/feature_toggle.rb +config/initializers/db2fog.rb NERD_tree* coverage libpeerconnection.log diff --git a/.rspec_parallel b/.rspec_parallel new file mode 100644 index 0000000000..cee31855e2 --- /dev/null +++ b/.rspec_parallel @@ -0,0 +1,4 @@ +--format Fuubar +--format ParallelTests::RSpec::SummaryLogger --out tmp/spec_summary.log +--format ParallelTests::RSpec::RuntimeLogger --out tmp/parallel_runtime_rspec.log +--tag ~performance diff --git a/.rubocop.yml b/.rubocop.yml new file mode 100644 index 0000000000..2e70cbc53b --- /dev/null +++ b/.rubocop.yml @@ -0,0 +1,29 @@ +AllCops: + Include: + - '**/Rakefile' + - '**/config.ru' + Exclude: + - 'db/**/*' + - 'config/**/*' + - 'script/**/*' + - 'spec/**/*' + - !ruby/regexp /old_and_unused\.rb$/ + +Documentation: + Enabled: false + +Style/EmptyLinesAroundClassBody: + Enabled: false + +Style/BracesAroundHashParameters: + Enabled: false + +Metrics/LineLength: + Enabled: false + Max: 120 + +MethodLength: + Enabled: false + +StringLiterals: + Enabled: false diff --git a/.ruby-version b/.ruby-version index ae6d5b9cbe..cd57a8b95d 100644 --- a/.ruby-version +++ b/.ruby-version @@ -1 +1 @@ -1.9.3-p392 +2.1.5 diff --git a/.scss-lint.yml b/.scss-lint.yml new file mode 100644 index 0000000000..d90daf1407 --- /dev/null +++ b/.scss-lint.yml @@ -0,0 +1,3 @@ +scss_files: 'app/assets/stylesheets/**/*.css.scss' + +exclude: 'app/assets/stylesheets/shared/**' diff --git a/.travis.yml b/.travis.yml index 0a0a55517f..0ba99dc9e1 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,15 +1,58 @@ language: ruby +sudo: false +cache: bundler bundler_args: --without development rvm: - - "1.9.3" -services: postgresql -before_install: + - "2.1.5" + +# Set the timezone for phantomjs with TZ +# Set the timezone for karma with TIMEZONE +# +# The test cases are roughly split according to their test times. +# It would be better to use https://github.com/ArturT/knapsack. +env: + global: + - TZ="Australia/Melbourne" + - TIMEZONE="Australia/Melbourne" + - CI_NODE_TOTAL=5 + matrix: + - CI_NODE_INDEX=0 + - CI_NODE_INDEX=1 + - CI_NODE_INDEX=2 + - CI_NODE_INDEX=3 + - CI_NODE_INDEX=4 KARMA="true" GITHUB_DEPLOY="true" + before_script: - cp config/database.travis.yml config/database.yml - - psql -c 'create database open_food_network_test;' -U postgres - cp config/application.yml.example config/application.yml + - RAILS_ENV=test bundle exec rake db:create db:schema:load + - > + if [ "$KARMA" = "true" ]; then + npm install karma@0.12.31 + npm install karma-jasmine@0.1.5 + npm install karma-phantomjs-launcher@0.1.4 + npm install karma-coffee-preprocessor@0.2.1 + npm install -g karma-cli@0.0.4 + fi + script: - - RAILS_ENV=test bundle exec rake db:migrate --trace - - bundle exec rake spec + - 'if [ "$KARMA" = "true" ]; then bundle exec rake karma:run; else echo "Skipping karma run"; fi' + #- "KNAPSACK_GENERATE_REPORT=true bundle exec rspec spec" + - "bundle exec rake knapsack:rspec" + +after_success: + - > + if [ "$GITHUB_DEPLOY" = "true" -a "$TRAVIS_PULL_REQUEST" = "false" -a -n "$TRAVIS_BRANCH" -a -n "$GITHUB_API_SECRET" ]; then + description="`git show "$TRAVIS_BRANCH" -s --oneline --no-color`" + data="{ + \"ref\":\"$TRAVIS_BRANCH\", + \"description\":\"$description\", + \"environment\":\"staging\", + \"required_contexts\":[]}" + curl -u "$GITHUB_API_SECRET" -d "$data" "https://api.github.com/repos/$TRAVIS_REPO_SLUG/deployments" + else + echo "Not deploying on this build." + fi + notifications: email: false diff --git a/.tx/config b/.tx/config new file mode 100644 index 0000000000..6ba637cb7e --- /dev/null +++ b/.tx/config @@ -0,0 +1,8 @@ +[main] +host = https://www.transifex.com + +[open-food-network.enyml] +file_filter = config/locales/.yml +source_lang = en +type = YML + diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md new file mode 100644 index 0000000000..6a43ed5f48 --- /dev/null +++ b/CONTRIBUTING.md @@ -0,0 +1,33 @@ +# Contributing + +We love pull requests from everyone. Here are some instructions for +contributing code to Open Food Network. See the [developer wiki](https://github.com/openfoodfoundation/openfoodnetwork/wiki) for more information. + +Fork, then clone the repo: + + git clone git@github.com:your-username/openfoodnetwork.git + +Follow the instructions in README.markdown to set up your machine. + +Make sure the tests pass: + + rspec spec + +Make your change. Add tests for your change. Make the tests pass: + + rspec spec + +Push to your fork and [submit a pull request][pr]. + +[pr]: https://github.com/openfoodfoundation/openfoodnetwork/compare/ + +At this point you're waiting on us. We may suggest some changes or +improvements or alternatives. + +To increase the chance that your pull request is swiftly accepted: + +* Write tests +* Use a style consistent with the rest of the codebase +* Before submitting, [rebase your work][rebase] on the current master branch + +[rebase]: https://www.atlassian.com/git/tutorials/merging-vs-rebasing/workflow-walkthrough diff --git a/Gemfile b/Gemfile index b63961a3ef..300b17ffa3 100644 --- a/Gemfile +++ b/Gemfile @@ -1,14 +1,16 @@ source 'https://rubygems.org' -ruby "1.9.3" +ruby "2.1.5" gem 'rails', '3.2.21' gem 'rails-i18n', '~> 3.0.0' gem 'i18n', '~> 0.6.11' +gem 'nokogiri' + gem 'pg' -gem 'spree', :github => 'openfoodfoundation/spree', :branch => '1-3-stable' -gem 'spree_i18n', :github => 'spree/spree_i18n', :branch => '1-3-stable' -gem 'spree_auth_devise', :github => 'spree/spree_auth_devise', :branch => '1-3-stable' +gem 'spree', github: 'openfoodfoundation/spree', branch: '1-3-stable' +gem 'spree_i18n', github: 'spree/spree_i18n', branch: '1-3-stable' +gem 'spree_auth_devise', github: 'spree/spree_auth_devise', branch: '1-3-stable' # Waiting on merge of PR #117 # https://github.com/spree-contrib/better_spree_paypal_express/pull/117 @@ -30,6 +32,7 @@ gem 'newrelic_rpm' gem 'haml' gem 'sass', "~> 3.3" gem 'sass-rails', '~> 3.2.3', groups: [:default, :assets] +gem 'redcarpet' gem 'aws-sdk' gem 'db2fog' gem 'andand' @@ -49,6 +52,12 @@ gem 'custom_error_message', :github => 'jeremydurham/custom-err-msg' gem 'angularjs-file-upload-rails', '~> 1.1.0' gem 'roadie-rails', '~> 1.0.3' gem 'figaro' +gem 'blockenspiel' +gem 'acts-as-taggable-on', '~> 3.4' +gem 'paper_trail', '~> 3.0.8' + +gem 'wicked_pdf' +gem 'wkhtmltopdf-binary' gem 'foreigner' gem 'immigrant' @@ -69,13 +78,14 @@ group :assets do gem 'turbo-sprockets-rails3' gem 'foundation-icons-sass-rails' gem 'momentjs-rails' - gem 'angular-rails-templates' + gem 'angular-rails-templates', '~> 0.2.0' end + gem "foundation-rails" gem 'foundation_rails_helper', github: 'willrjmarshall/foundation_rails_helper', branch: "rails3" gem 'jquery-rails' - +gem 'css_splitter' group :test, :development do @@ -87,13 +97,15 @@ group :test, :development do gem 'factory_girl_rails', :require => false gem 'capybara' gem 'database_cleaner', '0.7.1', :require => false - gem 'simplecov', :require => false gem 'awesome_print' gem 'letter_opener' gem 'timecop' gem 'poltergeist' + gem 'rspec-retry' gem 'json_spec' gem 'unicorn-rails' + gem 'atomic' + gem 'knapsack' end group :test do @@ -104,7 +116,7 @@ group :test do end group :development do - gem 'pry-debugger' + gem 'pry-byebug' gem 'debugger-linecache' gem 'guard' gem 'guard-livereload' @@ -112,4 +124,5 @@ group :development do gem 'guard-rails' gem 'guard-zeus' gem 'guard-rspec' + gem 'parallel_tests' end diff --git a/Gemfile.lock b/Gemfile.lock index 1b62201120..75763fb4a0 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -23,7 +23,7 @@ GIT GIT remote: git://github.com/openfoodfoundation/spree.git - revision: afcc23e489eb604a3e2651598a7c8364e2acc7b3 + revision: 6e3edfe40a5de8eba0095b2c5f3db9ea54c3afda branch: 1-3-stable specs: spree (1.3.6.beta) @@ -54,7 +54,7 @@ GIT rabl (= 0.7.2) rails (~> 3.2.16) ransack (= 0.7.2) - select2-rails (= 3.2.1) + select2-rails (= 3.5.9.3) state_machine (= 1.1.2) stringex (~> 1.3.2) truncate_html (~> 0.5.5) @@ -107,6 +107,7 @@ GIT GEM remote: https://rubygems.org/ specs: + CFPropertyList (2.3.2) actionmailer (3.2.21) actionpack (= 3.2.21) mail (~> 2.5.4) @@ -121,10 +122,10 @@ GEM rack-test (~> 0.6.1) sprockets (~> 2.2.1) active_link_to (1.0.0) - active_model_serializers (0.8.1) + active_model_serializers (0.8.3) activemodel (>= 3.0) - activemerchant (1.48.0) - activesupport (>= 3.2.14, < 5.0.0) + activemerchant (1.57.0) + activesupport (>= 3.2.14, < 5.1) builder (>= 2.1.2, < 4.0.0) i18n (>= 0.6.9) nokogiri (~> 1.4) @@ -142,17 +143,20 @@ GEM activesupport (3.2.21) i18n (~> 0.6, >= 0.6.4) multi_json (~> 1.0) + acts-as-taggable-on (3.5.0) + activerecord (>= 3.2, < 5) acts_as_list (0.1.4) addressable (2.3.3) andand (1.3.3) - angular-rails-templates (0.1.1) + angular-rails-templates (0.2.0) railties (>= 3.1) - sprockets + sprockets (~> 2) tilt angularjs-file-upload-rails (1.1.0) angularjs-rails (1.2.13) ansi (1.4.2) arel (3.0.3) + atomic (1.1.99) awesome_nested_set (2.1.5) activerecord (>= 3.0.0) awesome_print (1.0.2) @@ -163,12 +167,16 @@ GEM bcrypt (3.1.7) bcrypt-ruby (3.1.5) bcrypt (>= 3.1.3) + blockenspiel (0.4.5) bugsnag (1.5.2) httparty (>= 0.6, < 1.0) multi_json (~> 1.0) builder (3.0.4) + byebug (2.7.0) + columnize (~> 0.3) + debugger-linecache (~> 1.2) cancan (1.6.8) - capybara (2.2.1) + capybara (2.5.0) mime-types (>= 1.16) nokogiri (>= 1.3.3) rack (>= 1.0.0) @@ -187,12 +195,12 @@ GEM coffee-rails (3.2.2) coffee-script (>= 2.2.0) railties (~> 3.2.0) - coffee-script (2.2.0) + coffee-script (2.4.1) coffee-script-source execjs - coffee-script-source (1.3.3) + coffee-script-source (1.10.0) colorize (0.7.7) - columnize (0.3.6) + columnize (0.9.0) comfortable_mexican_sofa (1.6.24) active_link_to (~> 1.0.0) paperclip (>= 2.3.0) @@ -217,6 +225,8 @@ GEM safe_yaml (~> 0.9.0) css_parser (1.3.5) addressable + css_splitter (0.4.1) + sprockets (>= 2.0.0) daemons (1.2.2) dalli (2.7.2) database_cleaner (0.7.1) @@ -224,12 +234,7 @@ GEM activerecord (~> 3.0) fog (~> 1.0) rails (~> 3.0) - debugger (1.6.1) - columnize (>= 0.3.1) - debugger-linecache (~> 1.2.0) - debugger-ruby_core_source (~> 1.2.3) debugger-linecache (1.2.0) - debugger-ruby_core_source (1.2.3) delayed_job (4.0.4) activesupport (>= 3.0, < 4.2) delayed_job_active_record (4.0.2) @@ -247,10 +252,9 @@ GEM eventmachine (>= 0.12.9) http_parser.rb (~> 0.5.3) erubis (2.7.0) - eventmachine (1.0.3) - excon (0.25.3) - execjs (1.4.0) - multi_json (~> 1.0) + eventmachine (1.0.8) + excon (0.45.4) + execjs (2.6.0) factory_girl (3.3.0) activesupport (>= 3.0.0) factory_girl_rails (3.3.0) @@ -261,19 +265,117 @@ GEM figaro (0.7.0) bundler (~> 1.0) rails (>= 3, < 5) - fog (1.14.0) + fission (0.5.0) + CFPropertyList (~> 2.2) + fog (1.36.0) + fog-aliyun (>= 0.1.0) + fog-atmos + fog-aws (>= 0.6.0) + fog-brightbox (~> 0.4) + fog-core (~> 1.32) + fog-dynect (~> 0.0.2) + fog-ecloud (~> 0.1) + fog-google (<= 0.1.0) + fog-json + fog-local + fog-powerdns (>= 0.1.1) + fog-profitbricks + fog-radosgw (>= 0.0.2) + fog-riakcs + fog-sakuracloud (>= 0.0.4) + fog-serverlove + fog-softlayer + fog-storm_on_demand + fog-terremark + fog-vmfusion + fog-voxel + fog-xenserver + fog-xml (~> 0.1.1) + ipaddress (~> 0.5) + nokogiri (~> 1.5, >= 1.5.11) + fog-aliyun (0.1.0) + fog-core (~> 1.27) + fog-json (~> 1.0) + ipaddress (~> 0.8) + xml-simple (~> 1.1) + fog-atmos (0.1.0) + fog-core + fog-xml + fog-aws (0.7.6) + fog-core (~> 1.27) + fog-json (~> 1.0) + fog-xml (~> 0.1) + ipaddress (~> 0.8) + fog-brightbox (0.9.0) + fog-core (~> 1.22) + fog-json + inflecto (~> 0.0.2) + fog-core (1.35.0) builder - excon (~> 0.25.0) - formatador (~> 0.2.0) - mime-types - multi_json (~> 1.0) - net-scp (~> 1.1) - net-ssh (>= 2.1.3) - nokogiri (~> 1.5) - ruby-hmac + excon (~> 0.45) + formatador (~> 0.2) + fog-dynect (0.0.2) + fog-core + fog-json + fog-xml + fog-ecloud (0.3.0) + fog-core + fog-xml + fog-google (0.1.0) + fog-core + fog-json + fog-xml + fog-json (1.0.2) + fog-core (~> 1.0) + multi_json (~> 1.10) + fog-local (0.2.1) + fog-core (~> 1.27) + fog-powerdns (0.1.1) + fog-core (~> 1.27) + fog-json (~> 1.0) + fog-xml (~> 0.1) + fog-profitbricks (0.0.5) + fog-core + fog-xml + nokogiri + fog-radosgw (0.0.4) + fog-core (>= 1.21.0) + fog-json + fog-xml (>= 0.0.1) + fog-riakcs (0.1.0) + fog-core + fog-json + fog-xml + fog-sakuracloud (1.4.0) + fog-core + fog-json + fog-serverlove (0.1.2) + fog-core + fog-json + fog-softlayer (1.0.2) + fog-core + fog-json + fog-storm_on_demand (0.1.1) + fog-core + fog-json + fog-terremark (0.1.0) + fog-core + fog-xml + fog-vmfusion (0.1.0) + fission + fog-core + fog-voxel (0.1.0) + fog-core + fog-xml + fog-xenserver (0.2.2) + fog-core + fog-xml + fog-xml (0.1.2) + fog-core + nokogiri (~> 1.5, >= 1.5.11) foreigner (1.6.1) activerecord (>= 3.0.0) - formatador (0.2.4) + formatador (0.2.5) foundation-icons-sass-rails (3.0.0) railties (>= 3.1.1) sass-rails (>= 3.1.1) @@ -315,23 +417,28 @@ GEM immigrant (0.1.6) activerecord (>= 3.0) foreigner (>= 1.2.1) + inflecto (0.0.2) + ipaddress (0.8.0) journey (1.0.4) jquery-rails (2.2.2) railties (>= 3.0, < 5.0) thor (>= 0.14, < 2.0) - json (1.8.2) + json (1.8.3) json_spec (1.1.1) multi_json (~> 1.0) rspec (~> 2.0) kaminari (0.14.1) actionpack (>= 3.0.0) activesupport (>= 3.0.0) - kgio (2.7.4) + kgio (2.9.3) + knapsack (1.5.1) + rake + timecop (>= 0.1.0) launchy (2.1.2) addressable (~> 2.3) letter_opener (1.0.0) launchy (>= 2.0.4) - libv8 (3.16.14.3) + libv8 (3.16.14.11) listen (2.2.0) celluloid (>= 0.15.2) rb-fsevent (>= 0.9.3) @@ -347,28 +454,31 @@ GEM railties (>= 3.1) money (5.1.1) i18n (~> 0.6.0) - multi_json (1.11.0) + multi_json (1.11.2) multi_xml (0.5.5) - net-scp (1.1.2) - net-ssh (>= 2.6.5) - net-ssh (2.6.8) - newrelic_rpm (3.6.7.152) - nokogiri (1.6.6.2) + newrelic_rpm (3.12.0.288) + nokogiri (1.6.6.4) mini_portile (~> 0.6.0) oj (2.1.2) orm_adapter (0.5.0) + paper_trail (3.0.8) + activerecord (>= 3.0, < 5.0) + activesupport (>= 3.0, < 5.0) paperclip (3.5.4) activemodel (>= 3.0.0) activesupport (>= 3.0.0) cocaine (~> 0.5.3) mime-types + parallel (1.4.1) + parallel_tests (1.3.7) + parallel paypal-sdk-core (0.2.10) multi_json (~> 1.0) xml-simple paypal-sdk-merchant (1.106.1) paypal-sdk-core (~> 0.2.3) pg (0.13.2) - poltergeist (1.5.0) + poltergeist (1.7.0) capybara (~> 2.1) cliver (~> 0.3.1) multi_json (~> 1.0) @@ -380,13 +490,13 @@ GEM coderay (~> 1.0.5) method_source (~> 0.8) slop (~> 3.4) - pry-debugger (0.2.2) - debugger (~> 1.3) - pry (~> 0.9.10) + pry-byebug (1.3.2) + byebug (~> 2.7) + pry (~> 0.9.12) rabl (0.7.2) activesupport (>= 2.3.14) multi_json (~> 1.0) - rack (1.4.5) + rack (1.4.7) rack-cache (1.2) rack (>= 0.4) rack-livereload (0.3.15) @@ -413,7 +523,7 @@ GEM rake (>= 0.8.7) rdoc (~> 3.4) thor (>= 0.14.6, < 2.0) - raindrops (0.9.0) + raindrops (0.13.0) rake (10.4.2) ransack (0.7.2) actionpack (~> 3.0) @@ -424,6 +534,7 @@ GEM ffi (>= 0.5.0) rdoc (3.12.2) json (~> 1.4) + redcarpet (3.2.3) ref (1.0.5) representative (1.0.5) activesupport (>= 2.2.2) @@ -454,7 +565,8 @@ GEM rspec-core (~> 2.14.0) rspec-expectations (~> 2.14.0) rspec-mocks (~> 2.14.0) - ruby-hmac (0.4.0) + rspec-retry (0.4.2) + rspec-core ruby-progressbar (1.7.1) safe_yaml (0.9.5) sass (3.3.14) @@ -462,14 +574,10 @@ GEM railties (~> 3.2.0) sass (>= 3.1.10) tilt (~> 1.3) - select2-rails (3.2.1) + select2-rails (3.5.9.3) thor (~> 0.14) shoulda-matchers (1.1.0) activesupport (>= 3.0.0) - simplecov (0.7.1) - multi_json (~> 1.0) - simplecov-html (~> 0.7.1) - simplecov-html (0.7.1) slop (3.4.5) spinjs-rails (1.3) rails (>= 3.1) @@ -497,10 +605,10 @@ GEM turn (0.8.3) ansi tzinfo (0.3.44) - uglifier (1.2.4) + uglifier (2.7.1) execjs (>= 0.3.0) - multi_json (>= 1.0.2) - unicorn (4.3.1) + json (>= 1.8.0) + unicorn (4.9.0) kgio (~> 2.6) rack raindrops (~> 0.7) @@ -517,10 +625,15 @@ GEM webmock (1.13.0) addressable (>= 2.2.7) crack (>= 0.3.2) - websocket-driver (0.3.2) + websocket-driver (0.6.2) + websocket-extensions (>= 0.1.0) + websocket-extensions (0.1.2) whenever (0.9.2) activesupport (>= 2.3.4) chronic (>= 0.6.3) + wicked_pdf (0.11.0) + rails + wkhtmltopdf-binary (0.9.9.3) xml-simple (1.1.4) xpath (2.0.0) nokogiri (~> 1.3) @@ -532,17 +645,21 @@ PLATFORMS DEPENDENCIES active_model_serializers + acts-as-taggable-on (~> 3.4) andand - angular-rails-templates + angular-rails-templates (~> 0.2.0) angularjs-file-upload-rails (~> 1.1.0) angularjs-rails (= 1.2.13) + atomic awesome_print aws-sdk + blockenspiel bugsnag capybara coffee-rails (~> 3.2.1) comfortable_mexican_sofa compass-rails + css_splitter custom_error_message! daemons dalli @@ -570,27 +687,32 @@ DEPENDENCIES immigrant jquery-rails json_spec + knapsack letter_opener momentjs-rails newrelic_rpm + nokogiri oj + paper_trail (~> 3.0.8) paperclip + parallel_tests pg poltergeist - pry-debugger + pry-byebug rabl rack-livereload rack-ssl rails (= 3.2.21) rails-i18n (~> 3.0.0) + redcarpet representative_view roadie-rails (~> 1.0.3) rspec-rails + rspec-retry sass (~> 3.3) sass-rails (~> 3.2.3) shoulda-matchers simple_form! - simplecov spinjs-rails spree! spree_auth_devise! @@ -606,3 +728,8 @@ DEPENDENCIES unicorn-rails webmock whenever + wicked_pdf + wkhtmltopdf-binary + +BUNDLED WITH + 1.10.6 diff --git a/README.markdown b/README.md similarity index 72% rename from README.markdown rename to README.md index b9aaf8193c..c572aae193 100644 --- a/README.markdown +++ b/README.md @@ -1,3 +1,4 @@ +[![Build Status](https://travis-ci.org/openfoodfoundation/openfoodnetwork.svg?branch=master)](https://travis-ci.org/openfoodfoundation/openfoodnetwork) [![Code Climate](https://codeclimate.com/github/openfoodfoundation/openfoodnetwork.png)](https://codeclimate.com/github/openfoodfoundation/openfoodnetwork) # Open Food Network @@ -12,16 +13,23 @@ We're part of global movement - get involved! * Find out more and join in the conversation - http://openfoodnetwork.org -## Dependencies +## Getting started + +Below are instructions for setting up a development environment for Open Food Network. More information is in the [developer wiki](https://github.com/openfoodfoundation/openfoodnetwork/wiki). + +If you're interested in provisioning a server, see [the project's Ansible playbooks](https://github.com/openfoodfoundation/ofn_deployment). + + +### Dependencies * Rails 3.2.x -* Ruby >= 1.9.3 +* Ruby 2.1.5 * PostgreSQL database * PhantomJS (for testing) * See Gemfile for a list of gems required -## Get it +### Get it The source code is managed with Git (a version control system) and hosted at GitHub. @@ -32,19 +40,20 @@ You can view the code at: You can download the source with the command: - git clone git@github.com:openfoodfoundation/openfoodnetwork + git clone https://github.com/openfoodfoundation/openfoodnetwork.git -## Get it running +### Get it running For those new to Rails, the following tutorial will help get you up to speed with configuring a Rails environment: http://guides.rubyonrails.org/getting_started.html . -First, check your dependencies: Ensure that you have Ruby 1.9.x installed: +First, check your dependencies: Ensure that you have Ruby 2.1.5 installed: ruby --version Install the project's gem dependencies: + cd openfoodnetwork bundle install Configure the site: @@ -52,6 +61,15 @@ Configure the site: cp config/application.yml.example config/application.yml edit config/application.yml +Create a PostgreSQL user: + +* Login as your system postrgresql priviledged user: `sudo -i -u postgres` (this may vary on your OS). Now your prompt looks like: `[postgres@your_host ~]$` +* Create the `ofn` database superuser and give it the password `f00d`: + +``` +createuser -s -P ofn +``` + Create the development and test databases, using the settings specified in `config/database.yml`, and populate them with a schema and seed data: rake db:setup @@ -65,7 +83,7 @@ At long last, your dreams of spinning up a development server can be realised: rails server -## Testing +### Testing Tests, both unit and integration, are based on RSpec. To run the test suite, first prepare the test database: @@ -91,6 +109,10 @@ usage instructions. * Will Marshall (http://soundcloud.com/willmarshall) * Laura Summers (https://github.com/summerscope) * Maikel Linke (https://github.com/mkllnk) +* Lynne Davis (https://github.com/lin-d-hop) +* Paul Mackay (https://github.com/pmackay) +* Steve Petitt (https://github.com/stveep) + ## Licence diff --git a/Rakefile b/Rakefile index 699faf6e9d..9ed55e022e 100644 --- a/Rakefile +++ b/Rakefile @@ -5,3 +5,5 @@ require File.expand_path('../config/application', __FILE__) Openfoodnetwork::Application.load_tasks + +Knapsack.load_tasks if defined?(Knapsack) diff --git a/app/assets/images/case-studies/South_East_Food_Hub.png b/app/assets/images/case-studies/South_East_Food_Hub.png new file mode 100644 index 0000000000..1760a68d72 Binary files /dev/null and b/app/assets/images/case-studies/South_East_Food_Hub.png differ diff --git a/app/assets/images/case-studies/baw-baw.png b/app/assets/images/case-studies/baw-baw.png new file mode 100644 index 0000000000..56dd2c94f1 Binary files /dev/null and b/app/assets/images/case-studies/baw-baw.png differ diff --git a/app/assets/images/case-studies/bfc_logo_square.png b/app/assets/images/case-studies/bfc_logo_square.png new file mode 100644 index 0000000000..90f54df507 Binary files /dev/null and b/app/assets/images/case-studies/bfc_logo_square.png differ diff --git a/app/assets/images/case-studies/bonnie-beef-growers.png b/app/assets/images/case-studies/bonnie-beef-growers.png new file mode 100644 index 0000000000..ad9f5b14ad Binary files /dev/null and b/app/assets/images/case-studies/bonnie-beef-growers.png differ diff --git a/app/assets/images/case-studies/jindivick.jpg b/app/assets/images/case-studies/jindivick.jpg new file mode 100644 index 0000000000..b94ab00166 Binary files /dev/null and b/app/assets/images/case-studies/jindivick.jpg differ diff --git a/app/assets/images/case-studies/jonai.png b/app/assets/images/case-studies/jonai.png new file mode 100644 index 0000000000..6cdf7dd20b Binary files /dev/null and b/app/assets/images/case-studies/jonai.png differ diff --git a/app/assets/images/case-studies/longley.png b/app/assets/images/case-studies/longley.png new file mode 100644 index 0000000000..0d20ae93df Binary files /dev/null and b/app/assets/images/case-studies/longley.png differ diff --git a/app/assets/images/case-studies/mt-alexander.png b/app/assets/images/case-studies/mt-alexander.png new file mode 100644 index 0000000000..efb86d2463 Binary files /dev/null and b/app/assets/images/case-studies/mt-alexander.png differ diff --git a/app/assets/images/case-studies/wandiful.png b/app/assets/images/case-studies/wandiful.png new file mode 100644 index 0000000000..f7c4469022 Binary files /dev/null and b/app/assets/images/case-studies/wandiful.png differ diff --git a/app/assets/images/enterprise-type.png b/app/assets/images/enterprise-type.png new file mode 100644 index 0000000000..0c5741a82f Binary files /dev/null and b/app/assets/images/enterprise-type.png differ diff --git a/app/assets/images/favicon.ico b/app/assets/images/favicon.ico deleted file mode 100644 index 4011e1ba8b..0000000000 Binary files a/app/assets/images/favicon.ico and /dev/null differ diff --git a/app/assets/images/favicon.png b/app/assets/images/favicon.png deleted file mode 100644 index 758c60d7e2..0000000000 Binary files a/app/assets/images/favicon.png and /dev/null differ diff --git a/app/assets/images/groups.svg b/app/assets/images/groups.svg index f4ca32ec27..a2e353d47f 100644 --- a/app/assets/images/groups.svg +++ b/app/assets/images/groups.svg @@ -1,1565 +1 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + \ No newline at end of file diff --git a/app/assets/images/home/background-blurred-oranges.jpg b/app/assets/images/home/background-blurred-oranges.jpg new file mode 100644 index 0000000000..61019198e3 Binary files /dev/null and b/app/assets/images/home/background-blurred-oranges.jpg differ diff --git a/app/assets/images/home/home-apples.jpg b/app/assets/images/home/home-apples.jpg new file mode 100644 index 0000000000..5cf22d4a50 Binary files /dev/null and b/app/assets/images/home/home-apples.jpg differ diff --git a/app/assets/images/home/home-oranges.jpg b/app/assets/images/home/home-oranges.jpg new file mode 100644 index 0000000000..6cd547c97f Binary files /dev/null and b/app/assets/images/home/home-oranges.jpg differ diff --git a/app/assets/images/home/home-strawberries.jpg b/app/assets/images/home/home-strawberries.jpg new file mode 100644 index 0000000000..e837c69065 Binary files /dev/null and b/app/assets/images/home/home-strawberries.jpg differ diff --git a/app/assets/images/home/home.jpg b/app/assets/images/home/home.jpg new file mode 100644 index 0000000000..cb59fbef31 Binary files /dev/null and b/app/assets/images/home/home.jpg differ diff --git a/app/assets/images/home/home1.jpg b/app/assets/images/home/home1.jpg new file mode 100644 index 0000000000..0e37397127 Binary files /dev/null and b/app/assets/images/home/home1.jpg differ diff --git a/app/assets/images/home/home2.jpg b/app/assets/images/home/home2.jpg new file mode 100644 index 0000000000..fa548cb519 Binary files /dev/null and b/app/assets/images/home/home2.jpg differ diff --git a/app/assets/images/home/home3.jpg b/app/assets/images/home/home3.jpg new file mode 100644 index 0000000000..26fdf1037c Binary files /dev/null and b/app/assets/images/home/home3.jpg differ diff --git a/app/assets/images/home/ofn_bg_1.jpg b/app/assets/images/home/ofn_bg_1.jpg deleted file mode 100644 index 416ebf35fb..0000000000 Binary files a/app/assets/images/home/ofn_bg_1.jpg and /dev/null differ diff --git a/app/assets/images/home/shopping-bg.jpg b/app/assets/images/home/shopping-bg.jpg deleted file mode 100644 index 0a02b0f6f4..0000000000 Binary files a/app/assets/images/home/shopping-bg.jpg and /dev/null differ diff --git a/app/assets/images/home/tagline-bg.jpg b/app/assets/images/home/tagline-bg.jpg deleted file mode 100644 index 68366a9569..0000000000 Binary files a/app/assets/images/home/tagline-bg.jpg and /dev/null differ diff --git a/app/assets/images/hubs-bg.jpg b/app/assets/images/hubs-bg.jpg new file mode 100644 index 0000000000..2d1c49e230 Binary files /dev/null and b/app/assets/images/hubs-bg.jpg differ diff --git a/app/assets/images/icon-mask-apple.png b/app/assets/images/icon-mask-apple.png new file mode 100644 index 0000000000..87bfa91bb8 Binary files /dev/null and b/app/assets/images/icon-mask-apple.png differ diff --git a/app/assets/images/icon-mask-bread.png b/app/assets/images/icon-mask-bread.png new file mode 100644 index 0000000000..d5c29d96ff Binary files /dev/null and b/app/assets/images/icon-mask-bread.png differ diff --git a/app/assets/images/icon-mask-magnifier.png b/app/assets/images/icon-mask-magnifier.png new file mode 100644 index 0000000000..7f53b7db9d Binary files /dev/null and b/app/assets/images/icon-mask-magnifier.png differ diff --git a/app/assets/images/icon-mask-truck.png b/app/assets/images/icon-mask-truck.png new file mode 100644 index 0000000000..40550f98c2 Binary files /dev/null and b/app/assets/images/icon-mask-truck.png differ diff --git a/app/assets/images/logo-australia.png b/app/assets/images/logo-australia.png new file mode 100644 index 0000000000..46b8ef071a Binary files /dev/null and b/app/assets/images/logo-australia.png differ diff --git a/app/assets/images/logo-black.png b/app/assets/images/logo-black.png new file mode 100644 index 0000000000..31ec1422f1 Binary files /dev/null and b/app/assets/images/logo-black.png differ diff --git a/app/assets/images/logo-black.svg b/app/assets/images/logo-black.svg new file mode 100644 index 0000000000..5f69f364c4 --- /dev/null +++ b/app/assets/images/logo-black.svg @@ -0,0 +1,82 @@ + + + + + + image/svg+xml + + + + + + + + + + + + + + + + + diff --git a/app/assets/images/logo-color.png b/app/assets/images/logo-color.png new file mode 100644 index 0000000000..e464781a54 Binary files /dev/null and b/app/assets/images/logo-color.png differ diff --git a/app/assets/images/logo-color.svg b/app/assets/images/logo-color.svg new file mode 100644 index 0000000000..84de1b4ceb --- /dev/null +++ b/app/assets/images/logo-color.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/app/assets/images/logo-global-white.png b/app/assets/images/logo-global-white.png new file mode 100644 index 0000000000..8761bafe9d Binary files /dev/null and b/app/assets/images/logo-global-white.png differ diff --git a/app/assets/images/logo-white-notext.png b/app/assets/images/logo-white-notext.png new file mode 100644 index 0000000000..bfd590c621 Binary files /dev/null and b/app/assets/images/logo-white-notext.png differ diff --git a/app/assets/images/logo-white.png b/app/assets/images/logo-white.png new file mode 100644 index 0000000000..ffd1c6f73a Binary files /dev/null and b/app/assets/images/logo-white.png differ diff --git a/app/assets/images/logo-white.svg b/app/assets/images/logo-white.svg new file mode 100644 index 0000000000..c2c22bab40 --- /dev/null +++ b/app/assets/images/logo-white.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/app/assets/images/logo.jpg b/app/assets/images/logo.jpg deleted file mode 100644 index 49e232eb55..0000000000 Binary files a/app/assets/images/logo.jpg and /dev/null differ diff --git a/app/assets/images/logo.png b/app/assets/images/logo.png deleted file mode 100644 index aca7aac1dc..0000000000 Binary files a/app/assets/images/logo.png and /dev/null differ diff --git a/app/assets/images/noimage/large.png b/app/assets/images/noimage/large.png index 29dcff5ea9..166a74488a 100644 Binary files a/app/assets/images/noimage/large.png and b/app/assets/images/noimage/large.png differ diff --git a/app/assets/images/noimage/mini.png b/app/assets/images/noimage/mini.png index 5094c92a18..db94c3ce7f 100644 Binary files a/app/assets/images/noimage/mini.png and b/app/assets/images/noimage/mini.png differ diff --git a/app/assets/images/noimage/product.png b/app/assets/images/noimage/product.png index ca06da639b..6ca94eadb2 100644 Binary files a/app/assets/images/noimage/product.png and b/app/assets/images/noimage/product.png differ diff --git a/app/assets/images/noimage/small.png b/app/assets/images/noimage/small.png index ca06da639b..02088d8da0 100644 Binary files a/app/assets/images/noimage/small.png and b/app/assets/images/noimage/small.png differ diff --git a/app/assets/images/ofn-o.png b/app/assets/images/ofn-o.png new file mode 100644 index 0000000000..d6498ddb4d Binary files /dev/null and b/app/assets/images/ofn-o.png differ diff --git a/app/assets/images/ofn-o.svg b/app/assets/images/ofn-o.svg new file mode 100644 index 0000000000..2082ae6176 --- /dev/null +++ b/app/assets/images/ofn-o.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/app/assets/images/ofn_logo.png b/app/assets/images/ofn_logo.png deleted file mode 100644 index cdf690d997..0000000000 Binary files a/app/assets/images/ofn_logo.png and /dev/null differ diff --git a/app/assets/images/ofn_logo_beta.png b/app/assets/images/ofn_logo_beta.png deleted file mode 100644 index 420a51768d..0000000000 Binary files a/app/assets/images/ofn_logo_beta.png and /dev/null differ diff --git a/app/assets/images/ofn_logo_black.png b/app/assets/images/ofn_logo_black.png deleted file mode 100644 index d5f250daba..0000000000 Binary files a/app/assets/images/ofn_logo_black.png and /dev/null differ diff --git a/app/assets/images/ofn_logo_small.png b/app/assets/images/ofn_logo_small.png deleted file mode 100644 index 90277ab384..0000000000 Binary files a/app/assets/images/ofn_logo_small.png and /dev/null differ diff --git a/app/assets/images/ofw.png b/app/assets/images/ofw.png deleted file mode 100644 index ac571bacd0..0000000000 Binary files a/app/assets/images/ofw.png and /dev/null differ diff --git a/app/assets/images/open-food-network-beta-black.png b/app/assets/images/open-food-network-beta-black.png deleted file mode 100644 index 8e5f810023..0000000000 Binary files a/app/assets/images/open-food-network-beta-black.png and /dev/null differ diff --git a/app/assets/images/open-food-network-beta.png b/app/assets/images/open-food-network-beta.png deleted file mode 100644 index 965a248162..0000000000 Binary files a/app/assets/images/open-food-network-beta.png and /dev/null differ diff --git a/app/assets/images/open-food-network-beta.svg b/app/assets/images/open-food-network-beta.svg deleted file mode 100644 index eb882701d4..0000000000 --- a/app/assets/images/open-food-network-beta.svg +++ /dev/null @@ -1,840 +0,0 @@ - - - - - - - - BETA - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - BETA - - > - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/app/assets/images/pickup.png b/app/assets/images/pickup.png deleted file mode 100644 index aabceb508e..0000000000 Binary files a/app/assets/images/pickup.png and /dev/null differ diff --git a/app/assets/images/pin_bg.png b/app/assets/images/pin_bg.png deleted file mode 100644 index cfdd66162d..0000000000 Binary files a/app/assets/images/pin_bg.png and /dev/null differ diff --git a/app/assets/images/producers.svg b/app/assets/images/producers.svg new file mode 100644 index 0000000000..9804557f4b --- /dev/null +++ b/app/assets/images/producers.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/app/assets/images/select2.png b/app/assets/images/select2.png new file mode 100755 index 0000000000..9790e029f0 Binary files /dev/null and b/app/assets/images/select2.png differ diff --git a/app/assets/images/select2x2.png b/app/assets/images/select2x2.png new file mode 100755 index 0000000000..7e737c98cf Binary files /dev/null and b/app/assets/images/select2x2.png differ diff --git a/app/assets/images/store/cart.png b/app/assets/images/store/cart.png deleted file mode 100755 index 8650c39936..0000000000 Binary files a/app/assets/images/store/cart.png and /dev/null differ diff --git a/app/assets/images/subtle_white_feathers.png b/app/assets/images/subtle_white_feathers.png deleted file mode 100644 index dd699f66ba..0000000000 Binary files a/app/assets/images/subtle_white_feathers.png and /dev/null differ diff --git a/app/assets/images/tile-wide.png b/app/assets/images/tile-wide.png new file mode 100644 index 0000000000..f4e348b73c Binary files /dev/null and b/app/assets/images/tile-wide.png differ diff --git a/app/assets/javascripts/admin/accounts_and_billing_settings/accounts_and_billing_settings.js.coffee b/app/assets/javascripts/admin/accounts_and_billing_settings/accounts_and_billing_settings.js.coffee new file mode 100644 index 0000000000..06ee4fa4ef --- /dev/null +++ b/app/assets/javascripts/admin/accounts_and_billing_settings/accounts_and_billing_settings.js.coffee @@ -0,0 +1 @@ +angular.module("admin.accounts_and_billing_settings", ["admin.utils"]) diff --git a/app/assets/javascripts/admin/accounts_and_billing_settings/directives/method_settings.js.coffee b/app/assets/javascripts/admin/accounts_and_billing_settings/directives/method_settings.js.coffee new file mode 100644 index 0000000000..32ef50bb64 --- /dev/null +++ b/app/assets/javascripts/admin/accounts_and_billing_settings/directives/method_settings.js.coffee @@ -0,0 +1,14 @@ +angular.module("admin.accounts_and_billing_settings").directive "methodSettingsFor", -> + template: "
" + restrict: 'A' + scope: { + enterprise_id: '=methodSettingsFor' + } + link: (scope, element, attrs) -> + scope.include_html = "" + + scope.$watch "enterprise_id", (newVal, oldVal)-> + if !newVal? || newVal == "" + scope.include_html = "" + else + scope.include_html = "/admin/accounts_and_billing_settings/show_methods?enterprise_id=#{newVal};" diff --git a/app/assets/javascripts/admin/admin.js.coffee b/app/assets/javascripts/admin/admin_ofn.js.coffee similarity index 50% rename from app/assets/javascripts/admin/admin.js.coffee rename to app/assets/javascripts/admin/admin_ofn.js.coffee index 1c9f65f91a..d617722175 100644 --- a/app/assets/javascripts/admin/admin.js.coffee +++ b/app/assets/javascripts/admin/admin_ofn.js.coffee @@ -1,3 +1,3 @@ -angular.module("ofn.admin", ["ngResource", "ngAnimate", "ofn.dropdown", "admin.products", "admin.taxons", "infinite-scroll"]).config ($httpProvider) -> +angular.module("ofn.admin", ["ngResource", "ngAnimate", "admin.utils", "admin.indexUtils", "admin.dropdown", "admin.products", "admin.taxons", "infinite-scroll"]).config ($httpProvider) -> $httpProvider.defaults.headers.common["X-CSRF-Token"] = $("meta[name=csrf-token]").attr("content") $httpProvider.defaults.headers.common["Accept"] = "application/json, text/javascript, */*" diff --git a/app/assets/javascripts/admin/all.js b/app/assets/javascripts/admin/all.js index 9a07309352..60ca6f330a 100644 --- a/app/assets/javascripts/admin/all.js +++ b/app/assets/javascripts/admin/all.js @@ -12,14 +12,26 @@ //= require angular //= require angular-resource //= require angular-animate +//= require angular-sanitize //= require admin/spree_core //= require admin/spree_auth //= require admin/spree_promo //= require admin/spree_paypal_express //= require ../shared/ng-infinite-scroll.min.js -//= require ./admin +//= require ../shared/ng-tags-input.min.js +//= require angular-rails-templates +//= require_tree ../templates/admin +//= require ./admin_ofn +//= require ./accounts_and_billing_settings/accounts_and_billing_settings +//= require ./business_model_configuration/business_model_configuration +//= require ./customers/customers +//= require ./dropdown/dropdown //= require ./enterprises/enterprises //= require ./enterprise_groups/enterprise_groups +//= require ./index_utils/index_utils +//= require ./line_items/line_items +//= require ./orders/orders +//= require ./order_cycles/order_cycles //= require ./payment_methods/payment_methods //= require ./products/products //= require ./shipping_methods/shipping_methods @@ -27,7 +39,9 @@ //= require ./taxons/taxons //= require ./utils/utils //= require ./users/users +//= require ./variant_overrides/variant_overrides //= require textAngular.min.js //= require textAngular-sanitize.min.js +//= require ../shared/bindonce.min.js //= require_tree . diff --git a/app/assets/javascripts/admin/bulk_order_management.js.coffee b/app/assets/javascripts/admin/bulk_order_management.js.coffee index eb99f360af..7d0572635d 100644 --- a/app/assets/javascripts/admin/bulk_order_management.js.coffee +++ b/app/assets/javascripts/admin/bulk_order_management.js.coffee @@ -1,6 +1,6 @@ angular.module("ofn.admin").controller "AdminOrderMgmtCtrl", [ - "$scope", "$http", "$filter", "dataFetcher", "blankOption", "pendingChanges", "VariantUnitManager", "OptionValueNamer", "SpreeApiKey" - ($scope, $http, $filter, dataFetcher, blankOption, pendingChanges, VariantUnitManager, OptionValueNamer, SpreeApiKey) -> + "$scope", "$http", "$filter", "dataFetcher", "blankOption", "pendingChanges", "VariantUnitManager", "OptionValueNamer", "SpreeApiKey", "Columns" + ($scope, $http, $filter, dataFetcher, blankOption, pendingChanges, VariantUnitManager, OptionValueNamer, SpreeApiKey, Columns) -> $scope.loading = true $scope.initialiseVariables = -> @@ -11,16 +11,13 @@ angular.module("ofn.admin").controller "AdminOrderMgmtCtrl", [ $scope.confirmDelete = true $scope.startDate = formatDate start $scope.endDate = formatDate end - $scope.pendingChanges = pendingChanges $scope.quickSearch = "" $scope.bulkActions = [ { name: "Delete Selected", callback: $scope.deleteLineItems } ] $scope.selectedBulkAction = $scope.bulkActions[0] $scope.selectedUnitsProduct = {}; $scope.selectedUnitsVariant = {}; $scope.sharedResource = false - $scope.predicate = "" - $scope.reverse = false - $scope.columns = + $scope.columns = Columns.setColumns order_no: { name: "Order No.", visible: false } full_name: { name: "Name", visible: true } email: { name: "Email", visible: false } @@ -32,7 +29,7 @@ angular.module("ofn.admin").controller "AdminOrderMgmtCtrl", [ variant: { name: "Variant", visible: true } quantity: { name: "Quantity", visible: true } max: { name: "Max", visible: true } - unit_value: { name: "Weight/Volume", visible: false } + final_weight_volume: { name: "Weight/Volume", visible: false } price: { name: "Price", visible: false } $scope.initialise = -> $scope.initialiseVariables() @@ -79,6 +76,10 @@ angular.module("ofn.admin").controller "AdminOrderMgmtCtrl", [ line_item.checked = false line_item.supplier = $scope.matchObject $scope.suppliers, line_item.supplier, null line_item.order = orderWithoutLineItems + line_item.original_final_weight_volume = line_item.final_weight_volume + line_item.original_quantity = line_item.quantity + line_item.original_price = line_item.price + lineItems.concat order.line_items , [] @@ -109,6 +110,12 @@ angular.module("ofn.admin").controller "AdminOrderMgmtCtrl", [ $scope.deleteLineItem lineItem for lineItem in lineItems when lineItem.checked $scope.confirmDelete = existingState + $scope.submit = -> + if $scope.bulk_order_form.$valid + pendingChanges.submitAll() + else + alert "Some errors must be resolved be before you can update orders.\nAny fields with red borders contain errors." + $scope.allBoxesChecked = -> checkedCount = $scope.filteredLineItems.reduce (count,lineItem) -> count + (if lineItem.checked then 1 else 0 ) @@ -125,17 +132,17 @@ angular.module("ofn.admin").controller "AdminOrderMgmtCtrl", [ $scope.sumUnitValues = -> sum = $scope.filteredLineItems.reduce (sum,lineItem) -> - sum = sum + lineItem.quantity * lineItem.units_variant.unit_value + sum = sum + lineItem.final_weight_volume , 0 $scope.sumMaxUnitValues = -> sum = $scope.filteredLineItems.reduce (sum,lineItem) -> - sum = sum + Math.max(lineItem.max_quantity,lineItem.quantity) * lineItem.units_variant.unit_value + sum = sum + Math.max(lineItem.max_quantity,lineItem.original_quantity) * lineItem.units_variant.unit_value , 0 - $scope.allUnitValuesPresent = -> + $scope.allFinalWeightVolumesPresent = -> for i,lineItem of $scope.filteredLineItems - return false if !lineItem.units_variant.hasOwnProperty('unit_value') || !(lineItem.units_variant.unit_value > 0) + return false if !lineItem.hasOwnProperty('final_weight_volume') || !(lineItem.final_weight_volume > 0) true # How is this different to OptionValueNamer#name? @@ -166,13 +173,11 @@ angular.module("ofn.admin").controller "AdminOrderMgmtCtrl", [ $scope.orderCycleFilter = $scope.orderCycles[0].id $scope.quickSearch = "" - $scope.weightAdjustedPrice = (lineItem, oldValue) -> - if oldValue <= 0 - oldValue = lineItem.units_variant.unit_value - if lineItem.unit_value <= 0 - lineItem.unit_value = lineItem.units_variant.unit_value - lineItem.price = lineItem.price * lineItem.unit_value / oldValue - #$scope.bulk_order_form.line_item.price.$setViewValue($scope.bulk_order_form.line_item.price.$viewValue) + $scope.weightAdjustedPrice = (lineItem) -> + if lineItem.final_weight_volume > 0 + unit_value = lineItem.final_weight_volume / lineItem.quantity + original_unit_value = lineItem.original_final_weight_volume / lineItem.original_quantity + lineItem.price = lineItem.original_price * (unit_value / original_unit_value) $scope.unitValueLessThanZero = (lineItem) -> if lineItem.units_variant.unit_value <= 0 @@ -180,6 +185,11 @@ angular.module("ofn.admin").controller "AdminOrderMgmtCtrl", [ else false + $scope.updateOnQuantity = (lineItem) -> + if lineItem.quantity > 0 + lineItem.final_weight_volume = lineItem.original_final_weight_volume * lineItem.quantity / lineItem.original_quantity + $scope.weightAdjustedPrice(lineItem) + $scope.$watch "orderCycleFilter", (newVal, oldVal) -> unless $scope.orderCycleFilter == "0" || angular.equals(newVal, oldVal) $scope.startDate = $scope.orderCyclesByID[$scope.orderCycleFilter].first_order diff --git a/app/assets/javascripts/admin/bulk_product_update.js.coffee b/app/assets/javascripts/admin/bulk_product_update.js.coffee index 177859235b..6c85a4fd54 100644 --- a/app/assets/javascripts/admin/bulk_product_update.js.coffee +++ b/app/assets/javascripts/admin/bulk_product_update.js.coffee @@ -1,9 +1,9 @@ -angular.module("ofn.admin").controller "AdminProductEditCtrl", ($scope, $timeout, $http, BulkProducts, DisplayProperties, dataFetcher, DirtyProducts, VariantUnitManager, StatusMessage, producers, Taxons, SpreeApiAuth, tax_categories) -> +angular.module("ofn.admin").controller "AdminProductEditCtrl", ($scope, $timeout, $http, BulkProducts, DisplayProperties, dataFetcher, DirtyProducts, VariantUnitManager, StatusMessage, producers, Taxons, SpreeApiAuth, Columns, tax_categories) -> $scope.loading = true $scope.StatusMessage = StatusMessage - $scope.columns = + $scope.columns = Columns.setColumns producer: {name: "Producer", visible: true} sku: {name: "SKU", visible: false} name: {name: "Name", visible: true} @@ -109,6 +109,12 @@ angular.module("ofn.admin").controller "AdminProductEditCtrl", ($scope, $timeout window.location = "/admin/products/" + product.permalink_live + ((if variant then "/variants/" + variant.id else "")) + "/edit" + $scope.toggleShowAllVariants = -> + showVariants = !DisplayProperties.showVariants 0 + $scope.filteredProducts.forEach (product) -> + DisplayProperties.setShowVariants product.id, showVariants + DisplayProperties.setShowVariants 0, showVariants + $scope.addVariant = (product) -> product.variants.push id: $scope.nextVariantId() @@ -346,6 +352,9 @@ filterSubmitVariant = (variant) -> filteredVariant = {} if not variant.deleted_at? and variant.hasOwnProperty("id") filteredVariant.id = variant.id unless variant.id <= 0 + if variant.hasOwnProperty("sku") + filteredVariant.sku = variant.sku + hasUpdatableProperty = true if variant.hasOwnProperty("on_hand") filteredVariant.on_hand = variant.on_hand hasUpdatableProperty = true diff --git a/app/assets/javascripts/admin/business_model_configuration/business_model_configuration.js.coffee b/app/assets/javascripts/admin/business_model_configuration/business_model_configuration.js.coffee new file mode 100644 index 0000000000..cecb7c397e --- /dev/null +++ b/app/assets/javascripts/admin/business_model_configuration/business_model_configuration.js.coffee @@ -0,0 +1 @@ +angular.module("admin.businessModelConfiguration", ["admin.utils"]) diff --git a/app/assets/javascripts/admin/business_model_configuration/controllers/business_model_configuration_controller.js.coffee b/app/assets/javascripts/admin/business_model_configuration/controllers/business_model_configuration_controller.js.coffee new file mode 100644 index 0000000000..ca757c673d --- /dev/null +++ b/app/assets/javascripts/admin/business_model_configuration/controllers/business_model_configuration_controller.js.coffee @@ -0,0 +1,21 @@ +angular.module("admin.businessModelConfiguration").controller "BusinessModelConfigCtrl", ($scope, $filter) -> + $scope.turnover = 1000 + + $scope.bill = -> + return $filter('currency')(0) unless $scope.fixed || $scope.rate + Number($scope.fixed) + Number($scope.turnover) * Number($scope.rate) + + $scope.cappedBill = -> + return $scope.bill() if !$scope.cap? || Number($scope.cap) == 0 + Math.min($scope.bill(), Number($scope.cap)) + + $scope.capReached = -> + return "No" if !$scope.cap? || Number($scope.cap) == 0 + if $scope.bill() >= Number($scope.cap) then "Yes" else "No" + + $scope.includedTax = -> + return 0 if !$scope.taxRate? || Number($scope.taxRate) == 0 + ($scope.cappedBill() * Number($scope.taxRate)) + + $scope.total = -> + $scope.cappedBill() + $scope.includedTax() diff --git a/app/assets/javascripts/admin/controllers/enterprise_relationships_controller.js.coffee b/app/assets/javascripts/admin/controllers/enterprise_relationships_controller.js.coffee index 88524fb330..47083d443a 100644 --- a/app/assets/javascripts/admin/controllers/enterprise_relationships_controller.js.coffee +++ b/app/assets/javascripts/admin/controllers/enterprise_relationships_controller.js.coffee @@ -9,3 +9,26 @@ angular.module("ofn.admin").controller "AdminEnterpriseRelationshipsCtrl", ($sco $scope.delete = (enterprise_relationship) -> if confirm("Are you sure?") $scope.EnterpriseRelationships.delete enterprise_relationship + + $scope.toggleKeyword = (string, key) -> + string = '' unless string + words = string.split ' ' + words = words.filter (s) -> + s + index = words.indexOf key + if index > -1 + words.splice index, 1 + else + words.push key + words.join ' ' + + $scope.allPermissionsChecked = -> + for i in EnterpriseRelationships.all_permissions + if !$scope.permissions[i] + return false + return true + + $scope.checkAllPermissions = -> + newValue = !$scope.allPermissionsChecked() + EnterpriseRelationships.all_permissions.forEach (p) -> + $scope.permissions[p] = newValue diff --git a/app/assets/javascripts/admin/controllers/enterprises_dashboard_controller.js.coffee b/app/assets/javascripts/admin/controllers/enterprises_dashboard_controller.js.coffee index ad72ff3529..60315d30c1 100644 --- a/app/assets/javascripts/admin/controllers/enterprises_dashboard_controller.js.coffee +++ b/app/assets/javascripts/admin/controllers/enterprises_dashboard_controller.js.coffee @@ -1,5 +1,2 @@ -angular.module("ofn.admin").controller "enterprisesDashboardCtrl", [ - "$scope" - ($scope) -> - $scope.activeTab = "hubs" -] \ No newline at end of file +angular.module("ofn.admin").controller "enterprisesDashboardCtrl", ($scope) -> + $scope.activeTab = "hubs" diff --git a/app/assets/javascripts/admin/controllers/variant_overrides_controller.js.coffee b/app/assets/javascripts/admin/controllers/variant_overrides_controller.js.coffee deleted file mode 100644 index bcc633805f..0000000000 --- a/app/assets/javascripts/admin/controllers/variant_overrides_controller.js.coffee +++ /dev/null @@ -1,67 +0,0 @@ -angular.module("ofn.admin").controller "AdminVariantOverridesCtrl", ($scope, $timeout, Indexer, SpreeApiAuth, PagedFetcher, StatusMessage, hubs, producers, hubPermissions, VariantOverrides, DirtyVariantOverrides) -> - $scope.hubs = hubs - $scope.hub = null - $scope.products = [] - $scope.producers = Indexer.index producers - $scope.hubPermissions = hubPermissions - $scope.variantOverrides = VariantOverrides.variantOverrides - $scope.StatusMessage = StatusMessage - - $scope.initialise = -> - SpreeApiAuth.authorise() - .then -> - $scope.spree_api_key_ok = true - $scope.fetchProducts() - .catch (message) -> - $scope.api_error_msg = message - - - $scope.fetchProducts = -> - url = "/api/products/overridable?page=::page::;per_page=100" - PagedFetcher.fetch url, (data) => $scope.addProducts data.products - - - $scope.addProducts = (products) -> - $scope.products = $scope.products.concat products - VariantOverrides.ensureDataFor hubs, products - - - $scope.selectHub = -> - $scope.hub = (hub for hub in hubs when hub.id == $scope.hub_id)[0] - - - $scope.displayDirty = -> - if DirtyVariantOverrides.count() > 0 - num = if DirtyVariantOverrides.count() == 1 then "one override" else "#{DirtyVariantOverrides.count()} overrides" - StatusMessage.display 'notice', "Changes to #{num} remain unsaved." - else - StatusMessage.clear() - - - $scope.update = -> - if DirtyVariantOverrides.count() == 0 - StatusMessage.display 'alert', 'No changes to save.' - else - StatusMessage.display 'progress', 'Saving...' - DirtyVariantOverrides.save() - .success (updatedVos) -> - DirtyVariantOverrides.clear() - VariantOverrides.updateIds updatedVos - $timeout -> StatusMessage.display 'success', 'Changes saved.' - .error (data, status) -> - $timeout -> StatusMessage.display 'failure', $scope.updateError(data, status) - - - $scope.updateError = (data, status) -> - if status == 401 - "I couldn't get authorisation to save those changes, so they remain unsaved." - - else if status == 400 && data.errors? - errors = [] - for field, field_errors of data.errors - errors = errors.concat field_errors - errors = errors.join ', ' - "I had some trouble saving: #{errors}" - - else - "Oh no! I was unable to save your changes." diff --git a/app/assets/javascripts/admin/customers/controllers/customers_controller.js.coffee b/app/assets/javascripts/admin/customers/controllers/customers_controller.js.coffee new file mode 100644 index 0000000000..c475f1e4df --- /dev/null +++ b/app/assets/javascripts/admin/customers/controllers/customers_controller.js.coffee @@ -0,0 +1,17 @@ +angular.module("admin.customers").controller "customersCtrl", ($scope, Customers, Columns, pendingChanges, shops) -> + $scope.shop = null + $scope.shops = shops + $scope.submitAll = pendingChanges.submitAll + + $scope.columns = Columns.setColumns + email: { name: "Email", visible: true } + code: { name: "Code", visible: true } + tags: { name: "Tags", visible: true } + + $scope.$watch "shop", -> + if $scope.shop? + Customers.loaded = false + $scope.customers = Customers.index(enterprise_id: $scope.shop.id) + + $scope.loaded = -> + Customers.loaded diff --git a/app/assets/javascripts/admin/customers/customers.js.coffee b/app/assets/javascripts/admin/customers/customers.js.coffee new file mode 100644 index 0000000000..3733fe2eea --- /dev/null +++ b/app/assets/javascripts/admin/customers/customers.js.coffee @@ -0,0 +1 @@ +angular.module("admin.customers", ['ngResource', 'ngTagsInput', 'admin.indexUtils', 'admin.dropdown']) \ No newline at end of file diff --git a/app/assets/javascripts/admin/customers/directives/tags_with_translation.js.coffee b/app/assets/javascripts/admin/customers/directives/tags_with_translation.js.coffee new file mode 100644 index 0000000000..e15ec10342 --- /dev/null +++ b/app/assets/javascripts/admin/customers/directives/tags_with_translation.js.coffee @@ -0,0 +1,8 @@ +angular.module("admin.customers").directive "tagsWithTranslation", -> + restrict: "E" + template: "" + scope: + object: "=" + link: (scope, element, attrs) -> + scope.$watchCollection "object.tags", -> + scope.object.tag_list = (tag.text for tag in scope.object.tags).join(",") diff --git a/app/assets/javascripts/admin/customers/services/customer_resource.js.coffee b/app/assets/javascripts/admin/customers/services/customer_resource.js.coffee new file mode 100644 index 0000000000..523e0c1495 --- /dev/null +++ b/app/assets/javascripts/admin/customers/services/customer_resource.js.coffee @@ -0,0 +1,8 @@ +angular.module("admin.customers").factory 'CustomerResource', ($resource) -> + $resource('/admin/customers.json', {}, { + 'index': + method: 'GET' + isArray: true + params: + enterprise_id: '@enterprise_id' + }) diff --git a/app/assets/javascripts/admin/customers/services/customers.js.coffee b/app/assets/javascripts/admin/customers/services/customers.js.coffee new file mode 100644 index 0000000000..9acfa317d2 --- /dev/null +++ b/app/assets/javascripts/admin/customers/services/customers.js.coffee @@ -0,0 +1,16 @@ +angular.module("admin.customers").factory 'Customers', (CustomerResource) -> + new class Customers + customers: [] + customers_by_id: {} + loaded: false + + index: (params={}, callback=null) -> + CustomerResource.index params, (data) => + for customer in data + @customers.push customer + @customers_by_id[customer.id] = customer + + @loaded = true + (callback || angular.noop)(@customers) + + @customers diff --git a/app/assets/javascripts/admin/directives/confirm_link_path.js.coffee b/app/assets/javascripts/admin/directives/confirm_link_path.js.coffee deleted file mode 100644 index 2c8dba4baf..0000000000 --- a/app/assets/javascripts/admin/directives/confirm_link_path.js.coffee +++ /dev/null @@ -1,7 +0,0 @@ -angular.module("ofn.admin").directive "ofnConfirmLinkPath", (ofnConfirmHandler) -> - restrict: "A" - scope: - path: "@ofnConfirmLinkPath" - link: (scope, element, attrs) -> - element.click ofnConfirmHandler scope, -> - window.location = scope.path \ No newline at end of file diff --git a/app/assets/javascripts/admin/directives/confirm_model_change.js.coffee b/app/assets/javascripts/admin/directives/confirm_model_change.js.coffee deleted file mode 100644 index a0b5272981..0000000000 --- a/app/assets/javascripts/admin/directives/confirm_model_change.js.coffee +++ /dev/null @@ -1,6 +0,0 @@ -angular.module("ofn.admin").directive "ofnConfirmModelChange", (ofnConfirmHandler,$timeout) -> - restrict: "A" - link: (scope, element, attrs) -> - handler = ofnConfirmHandler scope, -> scope.fetchOrders() - scope.$watch attrs.ngModel, (oldValue,newValue) -> - handler() unless oldValue == undefined || newValue == oldValue \ No newline at end of file diff --git a/app/assets/javascripts/admin/directives/line_item_upd_attr.js.coffee b/app/assets/javascripts/admin/directives/line_item_upd_attr.js.coffee deleted file mode 100644 index c5afce07a5..0000000000 --- a/app/assets/javascripts/admin/directives/line_item_upd_attr.js.coffee +++ /dev/null @@ -1,25 +0,0 @@ -angular.module("ofn.admin").directive "ofnLineItemUpdAttr", [ - "switchClass", "pendingChanges" - (switchClass, pendingChanges) -> - require: "ngModel" - link: (scope, element, attrs, ngModel) -> - attrName = attrs.ofnLineItemUpdAttr - element.dbValue = scope.$eval(attrs.ngModel) - scope.$watch -> - scope.$eval(attrs.ngModel) - , (value) -> - #if ngModel.$dirty - # i think i can take this out, this directive is still only called - # on a change and only an updated value will create a db call. - if value == element.dbValue - pendingChanges.remove(scope.line_item.id, attrName) - switchClass( element, "", ["update-pending", "update-error", "update-success"], false ) - else - changeObj = - lineItem: scope.line_item - element: element - attrName: attrName - url: "/api/orders/#{scope.line_item.order.number}/line_items/#{scope.line_item.id}?line_item[#{attrName}]=#{value}" - pendingChanges.add(scope.line_item.id, attrName, changeObj) - switchClass( element, "update-pending", ["update-error", "update-success"], false ) -] diff --git a/app/assets/javascripts/admin/directives/toggle_column.js.coffee b/app/assets/javascripts/admin/directives/toggle_column.js.coffee deleted file mode 100644 index 1b8487eeb1..0000000000 --- a/app/assets/javascripts/admin/directives/toggle_column.js.coffee +++ /dev/null @@ -1,11 +0,0 @@ -angular.module("ofn.admin").directive "ofnToggleColumn", -> - link: (scope, element, attrs) -> - element.addClass "selected" if scope.column.visible - element.click "click", -> - scope.$apply -> - if scope.column.visible - scope.column.visible = false - element.removeClass "selected" - else - scope.column.visible = true - element.addClass "selected" \ No newline at end of file diff --git a/app/assets/javascripts/admin/directives/toggle_variants.js.coffee b/app/assets/javascripts/admin/directives/toggle_variants.js.coffee index 410df8d7e9..f5b18ae5cb 100644 --- a/app/assets/javascripts/admin/directives/toggle_variants.js.coffee +++ b/app/assets/javascripts/admin/directives/toggle_variants.js.coffee @@ -1,10 +1,8 @@ angular.module("ofn.admin").directive "ofnToggleVariants", (DisplayProperties) -> link: (scope, element, attrs) -> if DisplayProperties.showVariants scope.product.id - element.removeClass "icon-chevron-right" element.addClass "icon-chevron-down" else - element.removeClass "icon-chevron-down" element.addClass "icon-chevron-right" element.on "click", -> @@ -16,4 +14,4 @@ angular.module("ofn.admin").directive "ofnToggleVariants", (DisplayProperties) - else DisplayProperties.setShowVariants scope.product.id, true element.removeClass "icon-chevron-right" - element.addClass "icon-chevron-down" \ No newline at end of file + element.addClass "icon-chevron-down" diff --git a/app/assets/javascripts/admin/dropdown/controllers/dropdown_controller.js.coffee b/app/assets/javascripts/admin/dropdown/controllers/dropdown_controller.js.coffee new file mode 100644 index 0000000000..02e47ff9f7 --- /dev/null +++ b/app/assets/javascripts/admin/dropdown/controllers/dropdown_controller.js.coffee @@ -0,0 +1,2 @@ +angular.module("admin.dropdown").controller "DropDownCtrl", ($scope) -> + $scope.expanded = false diff --git a/app/assets/javascripts/admin/dropdown/directives/close_on_click.js.coffee b/app/assets/javascripts/admin/dropdown/directives/close_on_click.js.coffee new file mode 100644 index 0000000000..9b506cb8fb --- /dev/null +++ b/app/assets/javascripts/admin/dropdown/directives/close_on_click.js.coffee @@ -0,0 +1,5 @@ + angular.module("admin.dropdown").directive "ofnCloseOnClick", ($document) -> + link: (scope, element, attrs) -> + element.click (event) -> + event.stopPropagation() + scope.$emit "offClick" diff --git a/app/assets/javascripts/admin/dropdown.js.coffee b/app/assets/javascripts/admin/dropdown/directives/dropdown.js.coffee similarity index 56% rename from app/assets/javascripts/admin/dropdown.js.coffee rename to app/assets/javascripts/admin/dropdown/directives/dropdown.js.coffee index e18407abcc..b4ca2869d7 100644 --- a/app/assets/javascripts/admin/dropdown.js.coffee +++ b/app/assets/javascripts/admin/dropdown/directives/dropdown.js.coffee @@ -1,10 +1,9 @@ -dropDownModule = angular.module("ofn.dropdown", []) - -dropDownModule.directive "ofnDropDown", ($document) -> + angular.module("admin.dropdown").directive "ofnDropDown", ($document) -> + restrict: 'C' link: (scope, element, attrs) -> outsideClickListener = (event) -> - unless $(event.target).is("div.ofn_drop_down##{attrs.id} div.menu") || - $(event.target).parents("div.ofn_drop_down##{attrs.id} div.menu").length > 0 + unless $(event.target).is("div.ofn-drop-down##{attrs.id} div.menu") || + $(event.target).parents("div.ofn-drop-down##{attrs.id} div.menu").length > 0 scope.$emit "offClick" element.click (event) -> @@ -20,12 +19,3 @@ dropDownModule.directive "ofnDropDown", ($document) -> scope.$apply -> scope.expanded = true element.addClass "expanded" - -dropDownModule.directive "ofnCloseOnClick", ($document) -> - link: (scope, element, attrs) -> - element.click (event) -> - event.stopPropagation() - scope.$emit "offClick" - -dropDownModule.controller "DropDownCtrl", ($scope) -> - $scope.expanded = false \ No newline at end of file diff --git a/app/assets/javascripts/admin/dropdown/directives/links_dropdown.js.coffee b/app/assets/javascripts/admin/dropdown/directives/links_dropdown.js.coffee new file mode 100644 index 0000000000..a58688a542 --- /dev/null +++ b/app/assets/javascripts/admin/dropdown/directives/links_dropdown.js.coffee @@ -0,0 +1,5 @@ + angular.module("admin.dropdown").directive "linksDropdown", ($window)-> + restrict: "C" + scope: + links: "=" + templateUrl: "admin/links_dropdown.html" diff --git a/app/assets/javascripts/admin/dropdown/dropdown.js.coffee b/app/assets/javascripts/admin/dropdown/dropdown.js.coffee new file mode 100644 index 0000000000..b9c28652f0 --- /dev/null +++ b/app/assets/javascripts/admin/dropdown/dropdown.js.coffee @@ -0,0 +1 @@ +angular.module("admin.dropdown", ['templates']) diff --git a/app/assets/javascripts/admin/enterprise_fees.js b/app/assets/javascripts/admin/enterprise_fees.js index b815cd4266..79b1876263 100644 --- a/app/assets/javascripts/admin/enterprise_fees.js +++ b/app/assets/javascripts/admin/enterprise_fees.js @@ -34,7 +34,7 @@ angular.module('enterprise_fees', []) return function(scope, element, attrs) { if(scope.enterprise_fee.id) { var url = "/admin/enterprise_fees/" + scope.enterprise_fee.id - var html = ''; + var html = ''; //var html = 'Delete Delete'; element.append(html); } diff --git a/app/assets/javascripts/admin/enterprises/controllers/enterprise_controller.js.coffee b/app/assets/javascripts/admin/enterprises/controllers/enterprise_controller.js.coffee index 03cd7d4943..d48f2a42f8 100644 --- a/app/assets/javascripts/admin/enterprises/controllers/enterprise_controller.js.coffee +++ b/app/assets/javascripts/admin/enterprises/controllers/enterprise_controller.js.coffee @@ -1,6 +1,6 @@ angular.module("admin.enterprises") - .controller "enterpriseCtrl", ($scope, NavigationCheck, Enterprise, EnterprisePaymentMethods, EnterpriseShippingMethods, SideMenu) -> - $scope.Enterprise = Enterprise.enterprise + .controller "enterpriseCtrl", ($scope, NavigationCheck, enterprise, EnterprisePaymentMethods, EnterpriseShippingMethods, SideMenu) -> + $scope.Enterprise = enterprise $scope.PaymentMethods = EnterprisePaymentMethods.paymentMethods $scope.ShippingMethods = EnterpriseShippingMethods.shippingMethods $scope.navClear = NavigationCheck.clear diff --git a/app/assets/javascripts/admin/enterprises/controllers/enterprise_index_row_controller.js.coffee b/app/assets/javascripts/admin/enterprises/controllers/enterprise_index_row_controller.js.coffee new file mode 100644 index 0000000000..63b8daf07c --- /dev/null +++ b/app/assets/javascripts/admin/enterprises/controllers/enterprise_index_row_controller.js.coffee @@ -0,0 +1,49 @@ +angular.module("admin.enterprises").controller "EnterpriseIndexRowCtrl", ($scope) -> + $scope.status = -> + if $scope.enterprise.issues.length > 0 + "issue" + else if $scope.enterprise.warnings.length > 0 + "warning" + else + "ok" + + + $scope.producerText = -> + switch $scope.enterprise.is_primary_producer + when true + "Producer" + else + "Non-Producer" + + $scope.packageText = -> + switch $scope.enterprise.is_primary_producer + when true + switch $scope.enterprise.sells + when "none" + "Profile" + when "own" + "Shop" + when "any" + "Hub" + else + "Choose" + else + switch $scope.enterprise.sells + when "none" + "Profile" + when "any" + "Hub" + else + "Choose" + + $scope.updateRowText = -> + $scope.producer = $scope.producerText() + $scope.package = $scope.packageText() + $scope.producerError = ($scope.producer == "Choose") + $scope.packageError = ($scope.package == "Choose") + + + $scope.updateRowText() + + $scope.$on "enterprise:updated", -> + $scope.updateRowText() diff --git a/app/assets/javascripts/admin/enterprises/controllers/enterprises_controller.js.coffee b/app/assets/javascripts/admin/enterprises/controllers/enterprises_controller.js.coffee new file mode 100644 index 0000000000..3d8bfa6446 --- /dev/null +++ b/app/assets/javascripts/admin/enterprises/controllers/enterprises_controller.js.coffee @@ -0,0 +1,13 @@ +angular.module("admin.enterprises").controller 'enterprisesCtrl', ($scope, $q, Enterprises, Columns) -> + requests = [] + requests.push ($scope.allEnterprises = Enterprises.index(ams_prefix: "index")).$promise + + $q.all(requests).then -> + $scope.loaded = true + + $scope.columns = Columns.setColumns + name: { name: "Name", visible: true } + producer: { name: "Producer", visible: true } + package: { name: "Package", visible: true } + status: { name: "Status", visible: true } + manage: { name: "Manage", visible: true } diff --git a/app/assets/javascripts/admin/enterprises/controllers/index_package_panel_controller.js.coffee b/app/assets/javascripts/admin/enterprises/controllers/index_package_panel_controller.js.coffee new file mode 100644 index 0000000000..f191dc9e7a --- /dev/null +++ b/app/assets/javascripts/admin/enterprises/controllers/index_package_panel_controller.js.coffee @@ -0,0 +1,2 @@ +angular.module("admin.enterprises").controller 'indexPackagePanelCtrl', ($scope, $controller) -> + angular.extend this, $controller('indexPanelCtrl', {$scope: $scope}) diff --git a/app/assets/javascripts/admin/enterprises/controllers/index_panel_controller.js.coffee b/app/assets/javascripts/admin/enterprises/controllers/index_panel_controller.js.coffee new file mode 100644 index 0000000000..6f568ca5ea --- /dev/null +++ b/app/assets/javascripts/admin/enterprises/controllers/index_panel_controller.js.coffee @@ -0,0 +1,23 @@ +angular.module("admin.enterprises").controller 'indexPanelCtrl', ($scope, Enterprises) -> + $scope.enterprise = $scope.object + $scope.saving = false + + $scope.saved = -> + Enterprises.saved($scope.enterprise) + + $scope.save = -> + unless $scope.saved() + $scope.saving = true + Enterprises.save($scope.enterprise).then (data) -> + $scope.$emit("enterprise:updated") + $scope.saving = false + , (response) -> + $scope.saving = false + if response.status == 422 && response.data.errors? + message = 'Please resolve the following errors:\n' + for attr, msg of response.data.errors + message += "#{attr} #{msg}\n" + alert(message) + + $scope.resetAttribute = (attribute) -> + Enterprises.resetAttribute($scope.enterprise, attribute) diff --git a/app/assets/javascripts/admin/enterprises/controllers/index_producer_panel_controller.js.coffee b/app/assets/javascripts/admin/enterprises/controllers/index_producer_panel_controller.js.coffee new file mode 100644 index 0000000000..75fd9f1ec5 --- /dev/null +++ b/app/assets/javascripts/admin/enterprises/controllers/index_producer_panel_controller.js.coffee @@ -0,0 +1,14 @@ +angular.module("admin.enterprises").controller 'indexProducerPanelCtrl', ($scope, $controller) -> + angular.extend this, $controller('indexPanelCtrl', {$scope: $scope}) + + $scope.changeToProducer = -> + $scope.resetAttribute('sells') + $scope.resetAttribute('producer_profile_only') + $scope.enterprise.is_primary_producer = true + + $scope.changeToNonProducer = -> + if $scope.enterprise.sells == 'own' + $scope.enterprise.sells = 'any' + if $scope.enterprise.producer_profile_only = true + $scope.enterprise.producer_profile_only = false + $scope.enterprise.is_primary_producer = false diff --git a/app/assets/javascripts/admin/enterprises/controllers/index_status_panel_controller.js.coffee b/app/assets/javascripts/admin/enterprises/controllers/index_status_panel_controller.js.coffee new file mode 100644 index 0000000000..987a206bd0 --- /dev/null +++ b/app/assets/javascripts/admin/enterprises/controllers/index_status_panel_controller.js.coffee @@ -0,0 +1,3 @@ +angular.module("admin.enterprises").controller 'indexStatusPanelCtrl', ($scope, $filter) -> + $scope.issues = $scope.object.issues + $scope.warnings = $scope.object.warnings diff --git a/app/assets/javascripts/admin/enterprises/controllers/side_menu_controller.js.coffee b/app/assets/javascripts/admin/enterprises/controllers/side_menu_controller.js.coffee index d5d0e1681a..45a5d068a4 100644 --- a/app/assets/javascripts/admin/enterprises/controllers/side_menu_controller.js.coffee +++ b/app/assets/javascripts/admin/enterprises/controllers/side_menu_controller.js.coffee @@ -1,6 +1,6 @@ angular.module("admin.enterprises") - .controller "sideMenuCtrl", ($scope, $parse, Enterprise, SideMenu, enterprisePermissions) -> - $scope.Enterprise = Enterprise.enterprise + .controller "sideMenuCtrl", ($scope, $parse, enterprise, SideMenu, enterprisePermissions) -> + $scope.Enterprise = enterprise $scope.menu = SideMenu $scope.select = SideMenu.select diff --git a/app/assets/javascripts/admin/enterprises/directives/monthly_pricing_description.js.coffee b/app/assets/javascripts/admin/enterprises/directives/monthly_pricing_description.js.coffee new file mode 100644 index 0000000000..6331fa2ca5 --- /dev/null +++ b/app/assets/javascripts/admin/enterprises/directives/monthly_pricing_description.js.coffee @@ -0,0 +1,8 @@ +angular.module("admin.enterprises").directive "monthlyPricingDescription", (monthlyBillDescription) -> + restrict: 'E' + scope: + joiner: "@" + template: "" + link: (scope, element, attrs) -> + joiners = { comma: ", ", newline: "
" } + scope.billDescription = monthlyBillDescription.replace("{joiner}", joiners[scope.joiner]) diff --git a/app/assets/javascripts/admin/enterprises/enterprises.js.coffee b/app/assets/javascripts/admin/enterprises/enterprises.js.coffee index 5a7942d639..6be7e00ffa 100644 --- a/app/assets/javascripts/admin/enterprises/enterprises.js.coffee +++ b/app/assets/javascripts/admin/enterprises/enterprises.js.coffee @@ -1 +1 @@ -angular.module("admin.enterprises", [ "admin.payment_methods", "admin.utils", "admin.shipping_methods", "admin.users", "textAngular", "admin.side_menu", "admin.taxons"] ) \ No newline at end of file +angular.module("admin.enterprises", [ "admin.payment_methods", "admin.utils", "admin.shipping_methods", "admin.users", "textAngular", "admin.side_menu", "admin.taxons", 'admin.indexUtils', 'admin.dropdown', 'pasvaz.bindonce', 'ngSanitize'] ) \ No newline at end of file diff --git a/app/assets/javascripts/admin/enterprises/services/enterprise_payment_methods.js.coffee b/app/assets/javascripts/admin/enterprises/services/enterprise_payment_methods.js.coffee index b1a88f82fb..d1808e5001 100644 --- a/app/assets/javascripts/admin/enterprises/services/enterprise_payment_methods.js.coffee +++ b/app/assets/javascripts/admin/enterprises/services/enterprise_payment_methods.js.coffee @@ -1,11 +1,11 @@ angular.module("admin.enterprises") - .factory "EnterprisePaymentMethods", (Enterprise, PaymentMethods) -> + .factory "EnterprisePaymentMethods", (enterprise, PaymentMethods) -> new class EnterprisePaymentMethods paymentMethods: PaymentMethods.paymentMethods constructor: -> for payment_method in @paymentMethods - payment_method.selected = payment_method.id in Enterprise.enterprise.payment_method_ids + payment_method.selected = payment_method.id in enterprise.payment_method_ids displayColor: -> if @paymentMethods.length > 0 && @selectedCount() > 0 diff --git a/app/assets/javascripts/admin/enterprises/services/enterprise_resource.js.coffee b/app/assets/javascripts/admin/enterprises/services/enterprise_resource.js.coffee new file mode 100644 index 0000000000..023f33d86c --- /dev/null +++ b/app/assets/javascripts/admin/enterprises/services/enterprise_resource.js.coffee @@ -0,0 +1,8 @@ +angular.module("admin.enterprises").factory 'EnterpriseResource', ($resource) -> + $resource('/admin/enterprises/:id/:action.json', {}, { + 'index': + method: 'GET' + isArray: true + 'update': + method: 'PUT' + }) diff --git a/app/assets/javascripts/admin/enterprises/services/enterprise_shipping_methods.js.coffee b/app/assets/javascripts/admin/enterprises/services/enterprise_shipping_methods.js.coffee index 3f64c07442..50d15ff002 100644 --- a/app/assets/javascripts/admin/enterprises/services/enterprise_shipping_methods.js.coffee +++ b/app/assets/javascripts/admin/enterprises/services/enterprise_shipping_methods.js.coffee @@ -1,11 +1,11 @@ angular.module("admin.enterprises") - .factory "EnterpriseShippingMethods", (Enterprise, ShippingMethods) -> + .factory "EnterpriseShippingMethods", (enterprise, ShippingMethods) -> new class EnterpriseShippingMethods shippingMethods: ShippingMethods.shippingMethods constructor: -> for shipping_method in @shippingMethods - shipping_method.selected = shipping_method.id in Enterprise.enterprise.shipping_method_ids + shipping_method.selected = shipping_method.id in enterprise.shipping_method_ids displayColor: -> if @shippingMethods.length > 0 && @selectedCount() > 0 diff --git a/app/assets/javascripts/admin/enterprises/services/enterprises.js.coffee b/app/assets/javascripts/admin/enterprises/services/enterprises.js.coffee new file mode 100644 index 0000000000..b159816709 --- /dev/null +++ b/app/assets/javascripts/admin/enterprises/services/enterprises.js.coffee @@ -0,0 +1,40 @@ +angular.module("admin.enterprises").factory 'Enterprises', ($q, EnterpriseResource, blankOption) -> + new class Enterprises + enterprisesByID: {} + pristineByID: {} + + index: (params={}, callback=null) -> + includeBlank = !!params['includeBlank'] + delete params['includeBlank'] + EnterpriseResource.index(params, (data) => + for enterprise in data + @enterprisesByID[enterprise.id] = enterprise + @pristineByID[enterprise.id] = angular.copy(enterprise) + + (callback || angular.noop)(data) + + data.unshift(blankOption()) if includeBlank + data + ) + + save: (enterprise) -> + deferred = $q.defer() + enterprise.$update({id: enterprise.permalink}) + .then( (data) => + @pristineByID[enterprise.id] = angular.copy(enterprise) + deferred.resolve(data) + ).catch (response) -> + deferred.reject(response) + deferred.promise + + saved: (enterprise) -> + @diff(enterprise).length == 0 + + diff: (enterprise) -> + changed = [] + for attr, value of enterprise when not angular.equals(value, @pristineByID[enterprise.id][attr]) + changed.push attr unless attr is "$$hashKey" + changed + + resetAttribute: (enterprise, attribute) -> + enterprise[attribute] = @pristineByID[enterprise.id][attribute] diff --git a/app/assets/javascripts/admin/enterprises/services/permalink_checker.js.coffee b/app/assets/javascripts/admin/enterprises/services/permalink_checker.js.coffee index cb3fde9324..e1b62e6f6e 100644 --- a/app/assets/javascripts/admin/enterprises/services/permalink_checker.js.coffee +++ b/app/assets/javascripts/admin/enterprises/services/permalink_checker.js.coffee @@ -2,6 +2,7 @@ angular.module("admin.enterprises").factory 'PermalinkChecker', ($q, $http) -> new class PermalinkChecker deferredRequest: null deferredAbort: null + MAX_PERMALINK_LENGTH: 255 check: (permalink) => @abort(@deferredAbort) if @deferredRequest && @deferredRequest.promise @@ -15,9 +16,14 @@ angular.module("admin.enterprises").factory 'PermalinkChecker', ($q, $http) -> timeout: deferredAbort.promise ) .success( (data) => - deferredRequest.resolve - permalink: data - available: "Available" + if data.length > @MAX_PERMALINK_LENGTH || !data.match(/^[\w-]+$/) + deferredRequest.resolve + permalink: permalink + available: "Error" + else + deferredRequest.resolve + permalink: data + available: "Available" ).error (data,status) => if status == 409 deferredRequest.resolve diff --git a/app/assets/javascripts/admin/filters/keywords_filter.js.coffee b/app/assets/javascripts/admin/filters/keywords_filter.js.coffee new file mode 100644 index 0000000000..7d207518f0 --- /dev/null +++ b/app/assets/javascripts/admin/filters/keywords_filter.js.coffee @@ -0,0 +1,7 @@ +angular.module("ofn.admin").filter "keywords", ($filter) -> + return (array, query) -> + return array unless query + keywords = query.split ' ' + keywords.forEach (key) -> + array = $filter('filter')(array, key) + array diff --git a/app/assets/javascripts/admin/filters/select_filter.js.coffee b/app/assets/javascripts/admin/filters/select_filter.js.coffee deleted file mode 100644 index 2b03abd613..0000000000 --- a/app/assets/javascripts/admin/filters/select_filter.js.coffee +++ /dev/null @@ -1,7 +0,0 @@ -angular.module("ofn.admin").filter "selectFilter", (blankOption) -> - return (lineItems,selectedSupplier,selectedDistributor,selectedOrderCycle) -> - filtered = [] - filtered.push lineItem for lineItem in lineItems when (angular.equals(selectedSupplier,"0") || lineItem.supplier.id == selectedSupplier) && - (angular.equals(selectedDistributor,"0") || lineItem.order.distributor.id == selectedDistributor) && - (angular.equals(selectedOrderCycle,"0") || lineItem.order.order_cycle.id == selectedOrderCycle) - filtered \ No newline at end of file diff --git a/app/assets/javascripts/admin/index_utils/controllers/columns_controller.js.coffee b/app/assets/javascripts/admin/index_utils/controllers/columns_controller.js.coffee new file mode 100644 index 0000000000..39556983b3 --- /dev/null +++ b/app/assets/javascripts/admin/index_utils/controllers/columns_controller.js.coffee @@ -0,0 +1,4 @@ +angular.module("admin.indexUtils").controller "ColumnsCtrl", ($scope, Columns) -> + $scope.columns = Columns.columns + $scope.predicate = "" + $scope.reverse = false diff --git a/app/assets/javascripts/admin/index_utils/directives/datepicker.js.coffee b/app/assets/javascripts/admin/index_utils/directives/datepicker.js.coffee new file mode 100644 index 0000000000..442d9dc68b --- /dev/null +++ b/app/assets/javascripts/admin/index_utils/directives/datepicker.js.coffee @@ -0,0 +1,9 @@ +angular.module("admin.indexUtils").directive "datepicker", -> + require: "ngModel" + link: (scope, element, attrs, ngModel) -> + element.datepicker + dateFormat: "yy-mm-dd" + onSelect: (dateText, inst) -> + scope.$apply (scope) -> + # Fires ngModel.$parsers + ngModel.$setViewValue dateText diff --git a/app/assets/javascripts/admin/index_utils/directives/ignore_dirty.js.coffee b/app/assets/javascripts/admin/index_utils/directives/ignore_dirty.js.coffee new file mode 100644 index 0000000000..b2afca91cb --- /dev/null +++ b/app/assets/javascripts/admin/index_utils/directives/ignore_dirty.js.coffee @@ -0,0 +1,6 @@ +angular.module("admin.indexUtils").directive "ignoreDirty", -> + restrict: 'A' + require: 'ngModel' + link: (scope, element, attrs, ngModel) -> + #TODO: This is broken, requires AngularJS > 1.3 + ngModel.$setDirty = angular.noop diff --git a/app/assets/javascripts/admin/index_utils/directives/obj_for_update.js.coffee b/app/assets/javascripts/admin/index_utils/directives/obj_for_update.js.coffee new file mode 100644 index 0000000000..254fa5c438 --- /dev/null +++ b/app/assets/javascripts/admin/index_utils/directives/obj_for_update.js.coffee @@ -0,0 +1,36 @@ +angular.module("admin.indexUtils").directive "objForUpdate", (switchClass, pendingChanges) -> + scope: + object: "&objForUpdate" + type: "@objForUpdate" + attr: "@attrForUpdate" + link: (scope, element, attrs) -> + scope.savedValue = scope.object()[scope.attr] + + scope.$watch "object().#{scope.attr}", (value) -> + if value == scope.savedValue + pendingChanges.remove(scope.object().id, scope.attr) + scope.clear() + else + change = + object: scope.object() + type: scope.type + attr: scope.attr + value: value + scope: scope + scope.pending() + pendingChanges.add(scope.object().id, scope.attr, change) + + scope.reset = (value) -> + scope.savedValue = value + + scope.success = -> + switchClass( element, "update-success", ["update-pending", "update-error"], 3000 ) + + scope.pending = -> + switchClass( element, "update-pending", ["update-error", "update-success"], false ) + + scope.error = -> + switchClass( element, "update-error", ["update-pending", "update-success"], false ) + + scope.clear = -> + switchClass( element, "", ["update-pending", "update-error", "update-success"], false ) diff --git a/app/assets/javascripts/admin/index_utils/directives/ofn-select2.js.coffee b/app/assets/javascripts/admin/index_utils/directives/ofn-select2.js.coffee new file mode 100644 index 0000000000..ec454e9216 --- /dev/null +++ b/app/assets/javascripts/admin/index_utils/directives/ofn-select2.js.coffee @@ -0,0 +1,30 @@ +angular.module("admin.indexUtils").directive "ofnSelect2", ($sanitize, $timeout) -> + require: 'ngModel' + restrict: 'C' + scope: + data: "=" + minSearch: "@?" + text: "@?" + blank: "=?" + link: (scope, element, attrs, ngModel) -> + $timeout -> + scope.text ||= 'name' + scope.data.unshift(scope.blank) if scope.blank? && typeof scope.blank is "object" + + item.name = $sanitize(item.name) for item in scope.data + element.select2 + minimumResultsForSearch: scope.minSearch || 0 + data: { results: scope.data, text: scope.text } + initSelection: (element, callback) -> + callback scope.data[0] + formatSelection: (item) -> + item[scope.text] + formatResult: (item) -> + item[scope.text] + + attrs.$observe 'disabled', (value) -> + element.select2('enable', !value) + + ngModel.$formatters.push (value) -> + element.select2('val', value) + value diff --git a/app/assets/javascripts/admin/index_utils/directives/panel_row.js.coffee b/app/assets/javascripts/admin/index_utils/directives/panel_row.js.coffee new file mode 100644 index 0000000000..eb5a4171f4 --- /dev/null +++ b/app/assets/javascripts/admin/index_utils/directives/panel_row.js.coffee @@ -0,0 +1,37 @@ +angular.module("admin.indexUtils").directive "panelRow", (Panels, Columns) -> + restrict: "C" + templateUrl: "admin/panel.html" + scope: + object: "=" + panels: "=" + link: (scope, element, attrs) -> + scope.template = "" + selected = null + scope.columnCount = Columns.visibleCount + + scope.$on "columnCount:changed", (event, count) -> + scope.columnCount = count + + setTemplate = -> + if selected? + scope.template = 'admin/panels/' + scope.panels[selected] + '.html' + else + scope.template = "" + + scope.getSelected = -> + selected + + scope.setSelected = (name) -> + scope.$apply -> + selected = name + setTemplate() + + scope.open = (name) -> + element.show 0, -> + scope.setSelected name + + scope.close = -> + element.hide 0, -> + scope.setSelected null + + Panels.register(scope.object.id, scope) diff --git a/app/assets/javascripts/admin/index_utils/directives/panel_toggle.js.coffee b/app/assets/javascripts/admin/index_utils/directives/panel_toggle.js.coffee new file mode 100644 index 0000000000..df81328905 --- /dev/null +++ b/app/assets/javascripts/admin/index_utils/directives/panel_toggle.js.coffee @@ -0,0 +1,12 @@ +angular.module("admin.indexUtils").directive "panelToggle", -> + restrict: "C" + transclude: true + template: '
' + require: "^panelToggleRow" + scope: + name: "@" + link: (scope, element, attrs, ctrl) -> + scope.selected = ctrl.register(scope.name, element) + + element.on "click", -> + scope.selected = ctrl.select(scope.name) diff --git a/app/assets/javascripts/admin/index_utils/directives/panel_toggle_row.js.coffee b/app/assets/javascripts/admin/index_utils/directives/panel_toggle_row.js.coffee new file mode 100644 index 0000000000..d2d9c90ff8 --- /dev/null +++ b/app/assets/javascripts/admin/index_utils/directives/panel_toggle_row.js.coffee @@ -0,0 +1,29 @@ +angular.module("admin.indexUtils").directive "panelToggleRow", (Panels) -> + restrict: "C" + scope: + object: "=" + selected: "@?" + controller: ($scope) -> + panelToggles = {} + + this.register = (name, element) -> + panelToggles[name] = element + panelToggles[name].addClass("selected") if $scope.selected == name + $scope.selected == name + + this.select = (name) -> + panelToggle.removeClass("selected") for panelName, panelToggle of panelToggles + + switch $scope.selected = Panels.toggle($scope.object.id, name) + when null + panelToggles[name].parent(".panel-toggle-row").removeClass("expanded") + else + panelToggles[$scope.selected].addClass("selected") + panelToggles[$scope.selected].parent(".panel-toggle-row").addClass("expanded") + + $scope.selected == name + + this + # + # link: (scope, element, attrs) -> + # Panels.registerInitialSelection(scope.object.id, scope.selected) diff --git a/app/assets/javascripts/admin/index_utils/directives/select2_min_search.js.coffee b/app/assets/javascripts/admin/index_utils/directives/select2_min_search.js.coffee new file mode 100644 index 0000000000..3caebe8eda --- /dev/null +++ b/app/assets/javascripts/admin/index_utils/directives/select2_min_search.js.coffee @@ -0,0 +1,9 @@ +angular.module("admin.indexUtils").directive "select2MinSearch", ($timeout) -> + require: 'ngModel' + link: (scope, element, attrs, ngModel) -> + element.select2 + minimumResultsForSearch: attrs.select2MinSearch + + ngModel.$formatters.push (value) -> + element.select2('val', value) + value diff --git a/app/assets/javascripts/admin/index_utils/directives/toggle_column.js.coffee b/app/assets/javascripts/admin/index_utils/directives/toggle_column.js.coffee new file mode 100644 index 0000000000..2910e9a7a1 --- /dev/null +++ b/app/assets/javascripts/admin/index_utils/directives/toggle_column.js.coffee @@ -0,0 +1,8 @@ +angular.module("admin.indexUtils").directive "ofnToggleColumn", (Columns) -> + link: (scope, element, attrs) -> + element.addClass "selected" if scope.column.visible + + element.click "click", -> + scope.$apply -> + Columns.toggleColumn(scope.column) + element.toggleClass "selected" diff --git a/app/assets/javascripts/admin/index_utils/filters/attr_filter.js.coffee b/app/assets/javascripts/admin/index_utils/filters/attr_filter.js.coffee new file mode 100644 index 0000000000..c645b507f1 --- /dev/null +++ b/app/assets/javascripts/admin/index_utils/filters/attr_filter.js.coffee @@ -0,0 +1,12 @@ +# Used like a regular angular filter where an object is passed +# Adds the additional special case that a value of 0 for the filter +# acts as a bypass for that particular attribute +angular.module("admin.indexUtils").filter "attrFilter", ($filter) -> + return (objects, filters) -> + Object.keys(filters).reduce (filtered, attr) -> + filter = filters[attr] + return filtered if !filter? || filter == 0 + return $filter('filter')(filtered, (object) -> + object[attr] == filter + ) + , objects diff --git a/app/assets/javascripts/admin/index_utils/index_utils.js.coffee b/app/assets/javascripts/admin/index_utils/index_utils.js.coffee new file mode 100644 index 0000000000..5e5b5cadf2 --- /dev/null +++ b/app/assets/javascripts/admin/index_utils/index_utils.js.coffee @@ -0,0 +1 @@ +angular.module("admin.indexUtils", ['ngResource', 'ngSanitize', 'templates']).config ($httpProvider) -> $httpProvider.defaults.headers.common["X-CSRF-Token"] = $("meta[name=csrf-token]").attr("content"); $httpProvider.defaults.headers.common["Accept"] = "application/json, text/javascript, */*"; \ No newline at end of file diff --git a/app/assets/javascripts/admin/index_utils/services/columns.js.coffee b/app/assets/javascripts/admin/index_utils/services/columns.js.coffee new file mode 100644 index 0000000000..8bd99bf2f2 --- /dev/null +++ b/app/assets/javascripts/admin/index_utils/services/columns.js.coffee @@ -0,0 +1,18 @@ +angular.module("admin.indexUtils").factory 'Columns', ($rootScope) -> + new class Columns + columns: {} + visibleCount: 0 + + setColumns: (columns) => + @columns = {} + @columns[name] = column for name, column of columns + @calculateVisibleCount() + @columns + + toggleColumn: (column) => + column.visible = !column.visible + @calculateVisibleCount() + + calculateVisibleCount: => + @visibleCount = (column for name, column of @columns when column.visible).length + $rootScope.$broadcast "columnCount:changed", @visibleCount diff --git a/app/assets/javascripts/admin/services/data_fetcher.js.coffee b/app/assets/javascripts/admin/index_utils/services/data_fetcher.js.coffee similarity index 79% rename from app/assets/javascripts/admin/services/data_fetcher.js.coffee rename to app/assets/javascripts/admin/index_utils/services/data_fetcher.js.coffee index 735e4cc6bb..bf5580a3b2 100644 --- a/app/assets/javascripts/admin/services/data_fetcher.js.coffee +++ b/app/assets/javascripts/admin/index_utils/services/data_fetcher.js.coffee @@ -1,4 +1,4 @@ -angular.module("ofn.admin").factory "dataFetcher", [ +angular.module("admin.indexUtils").factory "dataFetcher", [ "$http", "$q" ($http, $q) -> return (dataLocation) -> @@ -9,4 +9,4 @@ angular.module("ofn.admin").factory "dataFetcher", [ deferred.reject() deferred.promise -] \ No newline at end of file +] diff --git a/app/assets/javascripts/admin/index_utils/services/dereferencer.js.coffee b/app/assets/javascripts/admin/index_utils/services/dereferencer.js.coffee new file mode 100644 index 0000000000..4793c63034 --- /dev/null +++ b/app/assets/javascripts/admin/index_utils/services/dereferencer.js.coffee @@ -0,0 +1,11 @@ +angular.module("admin.indexUtils").factory 'Dereferencer', -> + new class Dereferencer + dereference: (array, data)-> + if array + for object, i in array + array[i] = data[object.id] + + dereferenceAttr: (array, attr, data)-> + if array + for object in array + object[attr] = data[object[attr].id] unless object[attr] == null diff --git a/app/assets/javascripts/admin/services/indexer.js.coffee b/app/assets/javascripts/admin/index_utils/services/indexer.js.coffee similarity index 85% rename from app/assets/javascripts/admin/services/indexer.js.coffee rename to app/assets/javascripts/admin/index_utils/services/indexer.js.coffee index f9a9688a2f..295df46be3 100644 --- a/app/assets/javascripts/admin/services/indexer.js.coffee +++ b/app/assets/javascripts/admin/index_utils/services/indexer.js.coffee @@ -4,7 +4,7 @@ # Indexer.index producers # -> {1: {id: 1, name: 'one'}, 2: {id: 2, name: 'two'}} -angular.module("ofn.admin").factory 'Indexer', -> +angular.module("admin.indexUtils").factory 'Indexer', -> new class Indexer index: (data, key='id') -> index = {} diff --git a/app/assets/javascripts/admin/services/paged_fetcher.js.coffee b/app/assets/javascripts/admin/index_utils/services/paged_fetcher.js.coffee similarity index 83% rename from app/assets/javascripts/admin/services/paged_fetcher.js.coffee rename to app/assets/javascripts/admin/index_utils/services/paged_fetcher.js.coffee index 9281ed6a42..d65887bb2c 100644 --- a/app/assets/javascripts/admin/services/paged_fetcher.js.coffee +++ b/app/assets/javascripts/admin/index_utils/services/paged_fetcher.js.coffee @@ -1,4 +1,4 @@ -angular.module("ofn.admin").factory "PagedFetcher", (dataFetcher) -> +angular.module("admin.indexUtils").factory "PagedFetcher", (dataFetcher) -> new class PagedFetcher # Given a URL like http://example.com/foo?page=::page::&per_page=20 # And the response includes an attribute pages with the number of pages to fetch @@ -13,4 +13,4 @@ angular.module("ofn.admin").factory "PagedFetcher", (dataFetcher) -> processData data urlForPage: (url, page) -> - url.replace("::page::", page) \ No newline at end of file + url.replace("::page::", page) diff --git a/app/assets/javascripts/admin/index_utils/services/panels.js.coffee b/app/assets/javascripts/admin/index_utils/services/panels.js.coffee new file mode 100644 index 0000000000..27852bed12 --- /dev/null +++ b/app/assets/javascripts/admin/index_utils/services/panels.js.coffee @@ -0,0 +1,19 @@ +angular.module("admin.indexUtils").factory 'Panels', -> + new class Panels + panels: {} + + register: (id, scope) -> + if id? && scope? + @panels[id] = scope + + toggle: (id, name) -> + scope = @panels[id] + selected = scope.getSelected() + switch selected + when name + scope.close() + when null + scope.open(name) + else + scope.setSelected(name) + scope.getSelected() diff --git a/app/assets/javascripts/admin/index_utils/services/pending_changes.js.coffee b/app/assets/javascripts/admin/index_utils/services/pending_changes.js.coffee new file mode 100644 index 0000000000..2f40a7faef --- /dev/null +++ b/app/assets/javascripts/admin/index_utils/services/pending_changes.js.coffee @@ -0,0 +1,33 @@ +angular.module("admin.indexUtils").factory "pendingChanges", (resources) -> + new class pendingChanges + pendingChanges: {} + + add: (id, attr, change) => + @pendingChanges["#{id}"] = {} unless @pendingChanges.hasOwnProperty("#{id}") + @pendingChanges["#{id}"]["#{attr}"] = change + + removeAll: => + @pendingChanges = {} + + remove: (id, attr) => + if @pendingChanges.hasOwnProperty("#{id}") + delete @pendingChanges["#{id}"]["#{attr}"] + delete @pendingChanges["#{id}"] if @changeCount( @pendingChanges["#{id}"] ) < 1 + + submitAll: => + all = [] + for id, objectChanges of @pendingChanges + for attrName, change of objectChanges + all.push @submit(change) + all + + submit: (change) -> + resources.update(change).$promise.then (data) => + @remove change.object.id, change.attr + change.scope.reset( data["#{change.attr}"] ) + change.scope.success() + , (error) -> + change.scope.error() + + changeCount: (objectChanges) -> + Object.keys(objectChanges).length diff --git a/app/assets/javascripts/admin/index_utils/services/request_monitor.js.coffee b/app/assets/javascripts/admin/index_utils/services/request_monitor.js.coffee new file mode 100644 index 0000000000..2fc5ef9829 --- /dev/null +++ b/app/assets/javascripts/admin/index_utils/services/request_monitor.js.coffee @@ -0,0 +1,11 @@ +angular.module("admin.indexUtils").factory 'RequestMonitor', ($q) -> + new class RequestMonitor + loadQueue: $q.when([]) + loadId: 0 + loading: false + + load: (promise) -> + loadId = (@loadId += 1) + @loading = true + @loadQueue = $q.all([@loadQueue, promise]).then => + @loading = false if @loadId == loadId diff --git a/app/assets/javascripts/admin/index_utils/services/resources.js.coffee b/app/assets/javascripts/admin/index_utils/services/resources.js.coffee new file mode 100644 index 0000000000..65dad204d4 --- /dev/null +++ b/app/assets/javascripts/admin/index_utils/services/resources.js.coffee @@ -0,0 +1,30 @@ +angular.module("admin.indexUtils").factory "resources", ($resource) -> + LineItem = $resource '/api/orders/:order_number/line_items/:line_item_id.json', + { order_number: '@order_number', line_item_id: '@line_item_id'}, + 'update': { method: 'PUT' } + Customer = $resource '/admin/customers/:customer_id.json', + { customer_id: '@customer_id'}, + 'update': { method: 'PUT' } + + return { + update: (change) -> + params = {} + data = {} + resource = null + + switch change.type + when "line_item" + resource = LineItem + params.order_number = change.object.order.number + params.line_item_id = change.object.id + data.line_item = {} + data.line_item[change.attr] = change.value + when "customer" + resource = Customer + params.customer_id = change.object.id + data.customer = {} + data.customer[change.attr] = change.value + else "" + + resource.update(params, data) + } diff --git a/app/assets/javascripts/admin/services/spree_api_auth.js.coffee b/app/assets/javascripts/admin/index_utils/services/spree_api_auth.js.coffee similarity index 84% rename from app/assets/javascripts/admin/services/spree_api_auth.js.coffee rename to app/assets/javascripts/admin/index_utils/services/spree_api_auth.js.coffee index e606882bc5..3ed4dd9bf7 100644 --- a/app/assets/javascripts/admin/services/spree_api_auth.js.coffee +++ b/app/assets/javascripts/admin/index_utils/services/spree_api_auth.js.coffee @@ -1,4 +1,4 @@ -angular.module("ofn.admin").factory "SpreeApiAuth", ($q, $http, SpreeApiKey) -> +angular.module("admin.indexUtils").factory "SpreeApiAuth", ($q, $http, SpreeApiKey) -> new class SpreeApiAuth authorise: -> deferred = $q.defer() diff --git a/app/assets/javascripts/admin/index_utils/services/switch_class.js.coffee b/app/assets/javascripts/admin/index_utils/services/switch_class.js.coffee new file mode 100644 index 0000000000..c2a3419e2c --- /dev/null +++ b/app/assets/javascripts/admin/index_utils/services/switch_class.js.coffee @@ -0,0 +1,10 @@ +angular.module("admin.indexUtils").factory "switchClass", ($timeout) -> + return (element,classToAdd,removeClasses,timeout) -> + $timeout.cancel element.timeout if element.timeout + element.removeClass className for className in removeClasses + element.addClass classToAdd + intRegex = /^\d+$/ + if timeout && intRegex.test(timeout) + element.timeout = $timeout(-> + element.removeClass classToAdd + , timeout, true) diff --git a/app/assets/javascripts/admin/line_items/controllers/line_items_controller.js.coffee b/app/assets/javascripts/admin/line_items/controllers/line_items_controller.js.coffee new file mode 100644 index 0000000000..c6709eb3b4 --- /dev/null +++ b/app/assets/javascripts/admin/line_items/controllers/line_items_controller.js.coffee @@ -0,0 +1,185 @@ +angular.module("admin.lineItems").controller 'LineItemsCtrl', ($scope, $timeout, $http, $q, StatusMessage, Columns, Dereferencer, Orders, LineItems, Enterprises, OrderCycles, blankOption, VariantUnitManager, RequestMonitor) -> + $scope.initialized = false + $scope.RequestMonitor = RequestMonitor + $scope.filteredLineItems = [] + $scope.confirmDelete = true + $scope.startDate = formatDate daysFromToday -7 + $scope.endDate = formatDate daysFromToday 1 + $scope.bulkActions = [ { name: "Delete Selected", callback: 'deleteLineItems' } ] + $scope.selectedUnitsProduct = {}; + $scope.selectedUnitsVariant = {}; + $scope.sharedResource = false + $scope.columns = Columns.setColumns + order_no: { name: "Order No.", visible: false } + full_name: { name: "Name", visible: true } + email: { name: "Email", visible: false } + phone: { name: "Phone", visible: false } + order_date: { name: "Order Date", visible: true } + producer: { name: "Producer", visible: true } + order_cycle: { name: "Order Cycle", visible: false } + hub: { name: "Hub", visible: false } + variant: { name: "Variant", visible: true } + quantity: { name: "Quantity", visible: true } + max: { name: "Max", visible: true } + final_weight_volume: { name: "Weight/Volume", visible: false } + price: { name: "Price", visible: false } + + $scope.confirmRefresh = -> + LineItems.allSaved() || confirm("Unsaved changes exist and will be lost if you continue.") + + $scope.resetSelectFilters = -> + $scope.distributorFilter = blankOption().id + $scope.supplierFilter = blankOption().id + $scope.orderCycleFilter = blankOption().id + $scope.quickSearch = "" + + $scope.refreshData = -> + unless !$scope.orderCycleFilter? || $scope.orderCycleFilter == "0" + $scope.startDate = OrderCycles.orderCyclesByID[$scope.orderCycleFilter].first_order + $scope.endDate = OrderCycles.orderCyclesByID[$scope.orderCycleFilter].last_order + + RequestMonitor.load $scope.orders = Orders.index("q[state_not_eq]": "canceled", "q[completed_at_not_null]": "true", "q[completed_at_gt]": "#{parseDate($scope.startDate)}", "q[completed_at_lt]": "#{parseDate($scope.endDate)}") + RequestMonitor.load $scope.lineItems = LineItems.index("q[order][state_not_eq]": "canceled", "q[order][completed_at_not_null]": "true", "q[order][completed_at_gt]": "#{parseDate($scope.startDate)}", "q[order][completed_at_lt]": "#{parseDate($scope.endDate)}") + + unless $scope.initialized + RequestMonitor.load $scope.distributors = Enterprises.index(includeBlank: true, action: "for_line_items", ams_prefix: "basic", "q[sells_in][]": ["own", "any"]) + RequestMonitor.load $scope.orderCycles = OrderCycles.index(includeBlank: true, ams_prefix: "basic", as: "distributor", "q[orders_close_at_gt]": "#{daysFromToday(-90)}") + RequestMonitor.load $scope.suppliers = Enterprises.index(includeBlank: true, action: "for_line_items", ams_prefix: "basic", "q[is_primary_producer_eq]": "true") + + RequestMonitor.load $q.all([$scope.orders.$promise, $scope.distributors.$promise, $scope.orderCycles.$promise]).then -> + Dereferencer.dereferenceAttr $scope.orders, "distributor", Enterprises.enterprisesByID + Dereferencer.dereferenceAttr $scope.orders, "order_cycle", OrderCycles.orderCyclesByID + + RequestMonitor.load $q.all([$scope.orders.$promise, $scope.suppliers.$promise, $scope.lineItems.$promise]).then -> + Dereferencer.dereferenceAttr $scope.lineItems, "supplier", Enterprises.enterprisesByID + Dereferencer.dereferenceAttr $scope.lineItems, "order", Orders.ordersByID + $scope.bulk_order_form.$setPristine() + StatusMessage.clear() + unless $scope.initialized + $scope.initialized = true + $timeout -> + $scope.resetSelectFilters() + + $scope.refreshData() + + $scope.$watch 'bulk_order_form.$dirty', (newVal, oldVal) -> + if newVal == true + StatusMessage.display 'notice', "You have unsaved changes" + + $scope.submit = -> + if $scope.bulk_order_form.$valid + StatusMessage.display 'progress', "Saving..." + $q.all(LineItems.saveAll()).then(-> + StatusMessage.display 'success', "All changes saved" + $scope.bulk_order_form.$setPristine() + ).catch -> + StatusMessage.display 'failure', "Fields with red borders contain errors." + else + StatusMessage.display 'failure', "Fields with red borders contain errors." + + $scope.deleteLineItem = (lineItem) -> + if ($scope.confirmDelete && confirm("Are you sure?")) || !$scope.confirmDelete + LineItems.delete lineItem, => + $scope.lineItems.splice $scope.lineItems.indexOf(lineItem), 1 + + $scope.deleteLineItems = (lineItems) -> + existingState = $scope.confirmDelete + $scope.confirmDelete = false + $scope.deleteLineItem lineItem for lineItem in lineItems when lineItem.checked + $scope.confirmDelete = existingState + + $scope.allBoxesChecked = -> + checkedCount = $scope.filteredLineItems.reduce (count,lineItem) -> + count + (if lineItem.checked then 1 else 0 ) + , 0 + checkedCount == $scope.filteredLineItems.length + + $scope.toggleAllCheckboxes = -> + changeTo = !$scope.allBoxesChecked() + lineItem.checked = changeTo for lineItem in $scope.filteredLineItems + + $scope.setSelectedUnitsVariant = (unitsProduct,unitsVariant) -> + $scope.selectedUnitsProduct = unitsProduct + $scope.selectedUnitsVariant = unitsVariant + + $scope.sumUnitValues = -> + sum = $scope.filteredLineItems.reduce (sum,lineItem) -> + sum + lineItem.final_weight_volume + , 0 + + $scope.sumMaxUnitValues = -> + sum = $scope.filteredLineItems.reduce (sum,lineItem) -> + sum + lineItem.max_quantity * lineItem.units_variant.unit_value + , 0 + + $scope.allFinalWeightVolumesPresent = -> + for i,lineItem of $scope.filteredLineItems + return false if !lineItem.hasOwnProperty('final_weight_volume') || !(lineItem.final_weight_volume > 0) + true + + # How is this different to OptionValueNamer#name? + # Should it be extracted to that class or VariantUnitManager? + $scope.formattedValueWithUnitName = (value, unitsProduct, unitsVariant) -> + # A Units Variant is an API object which holds unit properies of a variant + if unitsProduct.hasOwnProperty("variant_unit") && (unitsProduct.variant_unit == "weight" || unitsProduct.variant_unit == "volume") && value > 0 + scale = VariantUnitManager.getScale(value, unitsProduct.variant_unit) + Math.round(value/scale * 1000)/1000 + " " + VariantUnitManager.getUnitName(scale, unitsProduct.variant_unit) + else + '' + + $scope.fulfilled = (sumOfUnitValues) -> + # A Units Variant is an API object which holds unit properies of a variant + if $scope.selectedUnitsProduct.hasOwnProperty("group_buy_unit_size") && $scope.selectedUnitsProduct.group_buy_unit_size > 0 && + $scope.selectedUnitsProduct.hasOwnProperty("variant_unit") && + ( $scope.selectedUnitsProduct.variant_unit == "weight" || $scope.selectedUnitsProduct.variant_unit == "volume" ) + Math.round( sumOfUnitValues / $scope.selectedUnitsProduct.group_buy_unit_size * 1000)/1000 + else + '' + + $scope.unitsVariantSelected = -> + !angular.equals($scope.selectedUnitsVariant,{}) + + $scope.weightAdjustedPrice = (lineItem) -> + if lineItem.final_weight_volume > 0 + unit_value = lineItem.final_weight_volume / lineItem.quantity + pristine_unit_value = LineItems.pristineByID[lineItem.id].final_weight_volume / LineItems.pristineByID[lineItem.id].quantity + lineItem.price = LineItems.pristineByID[lineItem.id].price * (unit_value / pristine_unit_value) + + $scope.unitValueLessThanZero = (lineItem) -> + if lineItem.units_variant.unit_value <= 0 + true + else + false + + $scope.updateOnQuantity = (lineItem) -> + if lineItem.quantity > 0 + lineItem.final_weight_volume = LineItems.pristineByID[lineItem.id].final_weight_volume * lineItem.quantity / LineItems.pristineByID[lineItem.id].quantity + $scope.weightAdjustedPrice(lineItem) + +daysFromToday = (days) -> + now = new Date + now.setHours(0) + now.setMinutes(0) + now.setSeconds(0) + now.setDate( now.getDate() + days ) + now + +formatDate = (date) -> + year = date.getFullYear() + month = twoDigitNumber date.getMonth() + 1 + day = twoDigitNumber date.getDate() + return year + "-" + month + "-" + day + +formatTime = (date) -> + hours = twoDigitNumber date.getHours() + mins = twoDigitNumber date.getMinutes() + secs = twoDigitNumber date.getSeconds() + return hours + ":" + mins + ":" + secs + +parseDate = (dateString) -> + new Date(Date.parse(dateString)) + +twoDigitNumber = (number) -> + twoDigits = "" + number + twoDigits = ("0" + number) if number < 10 + twoDigits diff --git a/app/assets/javascripts/admin/line_items/directives/confirm_change.js.coffee b/app/assets/javascripts/admin/line_items/directives/confirm_change.js.coffee new file mode 100644 index 0000000000..706b1c0ad7 --- /dev/null +++ b/app/assets/javascripts/admin/line_items/directives/confirm_change.js.coffee @@ -0,0 +1,19 @@ +# Used with the ngChange directive to prevent updates to the relevant model unless a callback returns true +angular.module("admin.lineItems").directive "confirmChange", -> + restrict: "A" + require: 'ngModel' + scope: + confirmChange: "&" + link: (scope, element, attrs, ngModel) -> + valid = null + + ngModel.$parsers.push (val) => + return val if val == valid + if scope.confirmChange() + # ngModel is changed, triggers ngChange callback + return valid = val + else + valid = ngModel.$modelValue + ngModel.$setViewValue(valid) + ngModel.$render() + return valid diff --git a/app/assets/javascripts/admin/line_items/directives/confirm_link_click.js.coffee b/app/assets/javascripts/admin/line_items/directives/confirm_link_click.js.coffee new file mode 100644 index 0000000000..b9b61c72cc --- /dev/null +++ b/app/assets/javascripts/admin/line_items/directives/confirm_link_click.js.coffee @@ -0,0 +1,9 @@ +# Used on a link to prevent link clicks unless a callback returns true (probably asking for user confirmation) +angular.module("admin.lineItems").directive "confirmLinkClick", -> + restrict: "A" + scope: + confirmLinkClick: "&" + link: (scope, element, attrs) -> + element.bind "click", (event) -> + unless scope.confirmLinkClick() + event.preventDefault() diff --git a/app/assets/javascripts/admin/line_items/filters/select_filter.js.coffee b/app/assets/javascripts/admin/line_items/filters/select_filter.js.coffee new file mode 100644 index 0000000000..5195057663 --- /dev/null +++ b/app/assets/javascripts/admin/line_items/filters/select_filter.js.coffee @@ -0,0 +1,8 @@ +angular.module("admin.lineItems").filter "selectFilter", (blankOption, RequestMonitor) -> + return (lineItems,selectedSupplier,selectedDistributor,selectedOrderCycle) -> + filtered = [] + unless RequestMonitor.loading + filtered.push lineItem for lineItem in lineItems when (angular.equals(selectedSupplier,"0") || lineItem.supplier.id == selectedSupplier) && + (angular.equals(selectedDistributor,"0") || lineItem.order.distributor.id == selectedDistributor) && + (angular.equals(selectedOrderCycle,"0") || lineItem.order.order_cycle.id == selectedOrderCycle) + filtered diff --git a/app/assets/javascripts/admin/filters/variant_filter.js.coffee b/app/assets/javascripts/admin/line_items/filters/variant_filter.js.coffee similarity index 82% rename from app/assets/javascripts/admin/filters/variant_filter.js.coffee rename to app/assets/javascripts/admin/line_items/filters/variant_filter.js.coffee index ddbc0f2711..8ddcf667e7 100644 --- a/app/assets/javascripts/admin/filters/variant_filter.js.coffee +++ b/app/assets/javascripts/admin/line_items/filters/variant_filter.js.coffee @@ -1,6 +1,6 @@ -angular.module("ofn.admin").filter "variantFilter", -> +angular.module("admin.lineItems").filter "variantFilter", -> return (lineItems,selectedUnitsProduct,selectedUnitsVariant,sharedResource) -> filtered = [] filtered.push lineItem for lineItem in lineItems when (angular.equals(selectedUnitsProduct,{}) || (lineItem.units_product.id == selectedUnitsProduct.id && (sharedResource || lineItem.units_variant.id == selectedUnitsVariant.id ) ) ) - filtered \ No newline at end of file + filtered diff --git a/app/assets/javascripts/admin/line_items/line_items.js.coffee b/app/assets/javascripts/admin/line_items/line_items.js.coffee new file mode 100644 index 0000000000..8128a50e8a --- /dev/null +++ b/app/assets/javascripts/admin/line_items/line_items.js.coffee @@ -0,0 +1 @@ +angular.module("admin.lineItems", ["admin.indexUtils", "admin.utils", "admin.products", "admin.orders", "admin.enterprises", "admin.orderCycles"]) diff --git a/app/assets/javascripts/admin/line_items/services/line_item_resource.js.coffee b/app/assets/javascripts/admin/line_items/services/line_item_resource.js.coffee new file mode 100644 index 0000000000..60ca925753 --- /dev/null +++ b/app/assets/javascripts/admin/line_items/services/line_item_resource.js.coffee @@ -0,0 +1,12 @@ +angular.module("admin.lineItems").factory 'LineItemResource', ($resource) -> + $resource('/admin/:orders/:order_number/line_items/:id.json', {}, { + 'index': + method: 'GET' + isArray: true + 'update': + method: 'PUT' + transformRequest: (data, headersGetter) => + line_item = {} + line_item[attr] = data[attr] for attr in ["price", "quantity", "final_weight_volume"] + angular.toJson(line_item: line_item) + }) diff --git a/app/assets/javascripts/admin/line_items/services/line_items.js.coffee b/app/assets/javascripts/admin/line_items/services/line_items.js.coffee new file mode 100644 index 0000000000..e78389c559 --- /dev/null +++ b/app/assets/javascripts/admin/line_items/services/line_items.js.coffee @@ -0,0 +1,63 @@ +angular.module("admin.lineItems").factory 'LineItems', ($q, LineItemResource) -> + new class LineItems + lineItemsByID: {} + pristineByID: {} + + index: (params={}, callback=null) -> + LineItemResource.index params, (data) => + @resetData() + for lineItem in data + @lineItemsByID[lineItem.id] = lineItem + @pristineByID[lineItem.id] = angular.copy(lineItem) + + (callback || angular.noop)(data) + + resetData: -> + @lineItemsByID = {} + @pristineByID = {} + + saveAll: -> + for id, lineItem of @lineItemsByID + lineItem.errors = {} # removes errors when line_item has been returned to original state + @save(lineItem) if !@isSaved(lineItem) + + save: (lineItem) -> + deferred = $q.defer() + lineItem.errors = {} + lineItem.$update({id: lineItem.id, orders: "orders", order_number: lineItem.order.number}) + .then( (data) => + @pristineByID[lineItem.id] = angular.copy(lineItem) + deferred.resolve(data) + ).catch (response) -> + lineItem.errors = response.data.errors if response.data.errors? + deferred.reject(response) + deferred.promise + + allSaved: -> + for id, lineItem of @lineItemsByID + return false unless @isSaved(lineItem) + true + + isSaved: (lineItem) -> + @diff(lineItem).length == 0 + + diff: (lineItem) -> + changed = [] + for attr, value of lineItem when not angular.equals(value, @pristineByID[lineItem.id][attr]) + changed.push attr if attr in ["price", "quantity", "final_weight_volume"] + changed + + resetAttribute: (lineItem, attribute) -> + lineItem[attribute] = @pristineByID[lineItem.id][attribute] + + delete: (lineItem, callback=null) -> + deferred = $q.defer() + lineItem.$delete({id: lineItem.id, orders: "orders", order_number: lineItem.order.number}) + .then( (data) => + delete @lineItemsByID[lineItem.id] + delete @pristineByID[lineItem.id] + (callback || angular.noop)(data) + deferred.resolve(data) + ).catch (response) -> + deferred.reject(response) + deferred.promise diff --git a/app/assets/javascripts/admin/order_cycle.js.erb.coffee b/app/assets/javascripts/admin/order_cycle.js.erb.coffee deleted file mode 100644 index 2b5ca05f42..0000000000 --- a/app/assets/javascripts/admin/order_cycle.js.erb.coffee +++ /dev/null @@ -1,196 +0,0 @@ -angular.module('admin.order_cycles', ['ngResource']) - .controller('AdminCreateOrderCycleCtrl', ['$scope', '$filter', 'OrderCycle', 'Enterprise', 'EnterpriseFee', 'ocInstance', ($scope, $filter, OrderCycle, Enterprise, EnterpriseFee, ocInstance) -> - $scope.enterprises = Enterprise.index(coordinator_id: ocInstance.coordinator_id) - $scope.supplied_products = Enterprise.supplied_products - $scope.enterprise_fees = EnterpriseFee.index(coordinator_id: ocInstance.coordinator_id) - - $scope.order_cycle = OrderCycle.new({ coordinator_id: ocInstance.coordinator_id}) - - $scope.loaded = -> - Enterprise.loaded && EnterpriseFee.loaded - - $scope.suppliedVariants = (enterprise_id) -> - Enterprise.suppliedVariants(enterprise_id) - - $scope.exchangeSelectedVariants = (exchange) -> - OrderCycle.exchangeSelectedVariants(exchange) - - $scope.setExchangeVariants = (exchange, variants, selected) -> - OrderCycle.setExchangeVariants(exchange, variants, selected) - - $scope.enterpriseTotalVariants = (enterprise) -> - Enterprise.totalVariants(enterprise) - - $scope.productSuppliedToOrderCycle = (product) -> - OrderCycle.productSuppliedToOrderCycle(product) - - $scope.variantSuppliedToOrderCycle = (variant) -> - OrderCycle.variantSuppliedToOrderCycle(variant) - - $scope.incomingExchangeVariantsFor = (enterprise_id) -> - $filter('filterExchangeVariants')(OrderCycle.incomingExchangesVariants(), $scope.order_cycle.visible_variants_for_outgoing_exchanges[enterprise_id]) - - $scope.exchangeDirection = (exchange) -> - OrderCycle.exchangeDirection(exchange) - - $scope.enterprisesWithFees = -> - $scope.enterprises[id] for id in OrderCycle.participatingEnterpriseIds() when $scope.enterpriseFeesForEnterprise(id).length > 0 - - $scope.toggleProducts = ($event, exchange) -> - $event.preventDefault() - OrderCycle.toggleProducts(exchange) - - $scope.enterpriseFeesForEnterprise = (enterprise_id) -> - EnterpriseFee.forEnterprise(parseInt(enterprise_id)) - - $scope.addSupplier = ($event) -> - $event.preventDefault() - OrderCycle.addSupplier($scope.new_supplier_id) - - $scope.addDistributor = ($event) -> - $event.preventDefault() - OrderCycle.addDistributor($scope.new_distributor_id) - - $scope.removeExchange = ($event, exchange) -> - $event.preventDefault() - OrderCycle.removeExchange(exchange) - - $scope.addCoordinatorFee = ($event) -> - $event.preventDefault() - OrderCycle.addCoordinatorFee() - - $scope.removeCoordinatorFee = ($event, index) -> - $event.preventDefault() - OrderCycle.removeCoordinatorFee(index) - - $scope.addExchangeFee = ($event, exchange) -> - $event.preventDefault() - OrderCycle.addExchangeFee(exchange) - - $scope.removeExchangeFee = ($event, exchange, index) -> - $event.preventDefault() - OrderCycle.removeExchangeFee(exchange, index) - - $scope.removeDistributionOfVariant = (variant_id) -> - OrderCycle.removeDistributionOfVariant(variant_id) - - $scope.submit = (event) -> - event.preventDefault() - OrderCycle.create() - ]) - - .controller('AdminEditOrderCycleCtrl', ['$scope', '$filter', '$location', 'OrderCycle', 'Enterprise', 'EnterpriseFee', ($scope, $filter, $location, OrderCycle, Enterprise, EnterpriseFee) -> - order_cycle_id = $location.absUrl().match(/\/admin\/order_cycles\/(\d+)/)[1] - $scope.enterprises = Enterprise.index(order_cycle_id: order_cycle_id) - $scope.supplied_products = Enterprise.supplied_products - $scope.enterprise_fees = EnterpriseFee.index(order_cycle_id: order_cycle_id) - - $scope.order_cycle = OrderCycle.load(order_cycle_id) - - $scope.loaded = -> - Enterprise.loaded && EnterpriseFee.loaded && OrderCycle.loaded - - $scope.suppliedVariants = (enterprise_id) -> - Enterprise.suppliedVariants(enterprise_id) - - $scope.exchangeSelectedVariants = (exchange) -> - OrderCycle.exchangeSelectedVariants(exchange) - - $scope.setExchangeVariants = (exchange, variants, selected) -> - OrderCycle.setExchangeVariants(exchange, variants, selected) - - $scope.enterpriseTotalVariants = (enterprise) -> - Enterprise.totalVariants(enterprise) - - $scope.productSuppliedToOrderCycle = (product) -> - OrderCycle.productSuppliedToOrderCycle(product) - - $scope.variantSuppliedToOrderCycle = (variant) -> - OrderCycle.variantSuppliedToOrderCycle(variant) - - $scope.incomingExchangeVariantsFor = (enterprise_id) -> - $filter('filterExchangeVariants')(OrderCycle.incomingExchangesVariants(), $scope.order_cycle.visible_variants_for_outgoing_exchanges[enterprise_id]) - - $scope.exchangeDirection = (exchange) -> - OrderCycle.exchangeDirection(exchange) - - $scope.enterprisesWithFees = -> - $scope.enterprises[id] for id in OrderCycle.participatingEnterpriseIds() when $scope.enterpriseFeesForEnterprise(id).length > 0 - - $scope.toggleProducts = ($event, exchange) -> - $event.preventDefault() - OrderCycle.toggleProducts(exchange) - - $scope.enterpriseFeesForEnterprise = (enterprise_id) -> - EnterpriseFee.forEnterprise(parseInt(enterprise_id)) - - $scope.addSupplier = ($event) -> - $event.preventDefault() - OrderCycle.addSupplier($scope.new_supplier_id) - - $scope.addDistributor = ($event) -> - $event.preventDefault() - OrderCycle.addDistributor($scope.new_distributor_id) - - $scope.removeExchange = ($event, exchange) -> - $event.preventDefault() - OrderCycle.removeExchange(exchange) - - $scope.addCoordinatorFee = ($event) -> - $event.preventDefault() - OrderCycle.addCoordinatorFee() - - $scope.removeCoordinatorFee = ($event, index) -> - $event.preventDefault() - OrderCycle.removeCoordinatorFee(index) - - $scope.addExchangeFee = ($event, exchange) -> - $event.preventDefault() - OrderCycle.addExchangeFee(exchange) - - $scope.removeExchangeFee = ($event, exchange, index) -> - $event.preventDefault() - OrderCycle.removeExchangeFee(exchange, index) - - $scope.removeDistributionOfVariant = (variant_id) -> - OrderCycle.removeDistributionOfVariant(variant_id) - - $scope.submit = (event) -> - event.preventDefault() - OrderCycle.update() - ]) - - .config(['$httpProvider', ($httpProvider) -> - $httpProvider.defaults.headers.common['X-CSRF-Token'] = $('meta[name=csrf-token]').attr('content') - ]) - - .directive('datetimepicker', ['$parse', ($parse) -> - (scope, element, attrs) -> - # using $parse instead of scope[attrs.datetimepicker] for cases - # where attrs.datetimepicker is 'foo.bar.lol' - $(element).datetimepicker - dateFormat: 'yy-mm-dd' - timeFormat: 'HH:mm:ss' - showOn: "button" - buttonImage: "<%= asset_path 'datepicker/cal.gif' %>" - buttonImageOnly: true - stepMinute: 15 - onSelect: (dateText, inst) -> - scope.$apply -> - parsed = $parse(attrs.datetimepicker) - parsed.assign(scope, dateText) - ]) - - .directive('ofnOnChange', -> - (scope, element, attrs) -> - element.bind 'change', -> - scope.$apply(attrs.ofnOnChange) - ) - - .directive('ofnSyncDistributions', -> - (scope, element, attrs) -> - element.bind 'change', -> - if !$(this).is(':checked') - scope.$apply -> - scope.removeDistributionOfVariant(attrs.ofnSyncDistributions) - ) diff --git a/app/assets/javascripts/admin/order_cycles/controllers/create.js.coffee b/app/assets/javascripts/admin/order_cycles/controllers/create.js.coffee new file mode 100644 index 0000000000..2c98d60f0e --- /dev/null +++ b/app/assets/javascripts/admin/order_cycles/controllers/create.js.coffee @@ -0,0 +1,83 @@ +angular.module('admin.orderCycles') + .controller 'AdminCreateOrderCycleCtrl', ($scope, $filter, OrderCycle, Enterprise, EnterpriseFee, ocInstance, StatusMessage) -> + $scope.enterprises = Enterprise.index(coordinator_id: ocInstance.coordinator_id) + $scope.supplier_enterprises = Enterprise.producer_enterprises + $scope.distributor_enterprises = Enterprise.hub_enterprises + $scope.supplied_products = Enterprise.supplied_products + $scope.enterprise_fees = EnterpriseFee.index(coordinator_id: ocInstance.coordinator_id) + + $scope.OrderCycle = OrderCycle + $scope.order_cycle = OrderCycle.new({ coordinator_id: ocInstance.coordinator_id}) + + $scope.StatusMessage = StatusMessage + + $scope.loaded = -> + Enterprise.loaded && EnterpriseFee.loaded + + $scope.suppliedVariants = (enterprise_id) -> + Enterprise.suppliedVariants(enterprise_id) + + $scope.exchangeSelectedVariants = (exchange) -> + OrderCycle.exchangeSelectedVariants(exchange) + + $scope.setExchangeVariants = (exchange, variants, selected) -> + OrderCycle.setExchangeVariants(exchange, variants, selected) + + $scope.enterpriseTotalVariants = (enterprise) -> + Enterprise.totalVariants(enterprise) + + $scope.productSuppliedToOrderCycle = (product) -> + OrderCycle.productSuppliedToOrderCycle(product) + + $scope.variantSuppliedToOrderCycle = (variant) -> + OrderCycle.variantSuppliedToOrderCycle(variant) + + $scope.incomingExchangeVariantsFor = (enterprise_id) -> + $filter('filterExchangeVariants')(OrderCycle.incomingExchangesVariants(), $scope.order_cycle.visible_variants_for_outgoing_exchanges[enterprise_id]) + + $scope.exchangeDirection = (exchange) -> + OrderCycle.exchangeDirection(exchange) + + $scope.enterprisesWithFees = -> + $scope.enterprises[id] for id in OrderCycle.participatingEnterpriseIds() when $scope.enterpriseFeesForEnterprise(id).length > 0 + + $scope.toggleProducts = ($event, exchange) -> + $event.preventDefault() + OrderCycle.toggleProducts(exchange) + + $scope.enterpriseFeesForEnterprise = (enterprise_id) -> + EnterpriseFee.forEnterprise(parseInt(enterprise_id)) + + $scope.addSupplier = ($event) -> + $event.preventDefault() + OrderCycle.addSupplier($scope.new_supplier_id) + + $scope.addDistributor = ($event) -> + $event.preventDefault() + OrderCycle.addDistributor($scope.new_distributor_id) + + $scope.removeExchange = ($event, exchange) -> + $event.preventDefault() + OrderCycle.removeExchange(exchange) + + $scope.addCoordinatorFee = ($event) -> + $event.preventDefault() + OrderCycle.addCoordinatorFee() + + $scope.removeCoordinatorFee = ($event, index) -> + $event.preventDefault() + OrderCycle.removeCoordinatorFee(index) + + $scope.addExchangeFee = ($event, exchange) -> + $event.preventDefault() + OrderCycle.addExchangeFee(exchange) + + $scope.removeExchangeFee = ($event, exchange, index) -> + $event.preventDefault() + OrderCycle.removeExchangeFee(exchange, index) + + $scope.removeDistributionOfVariant = (variant_id) -> + OrderCycle.removeDistributionOfVariant(variant_id) + + $scope.submit = (destination) -> + OrderCycle.create(destination) diff --git a/app/assets/javascripts/admin/order_cycles/controllers/edit.js.coffee b/app/assets/javascripts/admin/order_cycles/controllers/edit.js.coffee new file mode 100644 index 0000000000..fd426eb455 --- /dev/null +++ b/app/assets/javascripts/admin/order_cycles/controllers/edit.js.coffee @@ -0,0 +1,84 @@ +angular.module('admin.orderCycles') + .controller 'AdminEditOrderCycleCtrl', ($scope, $filter, $location, OrderCycle, Enterprise, EnterpriseFee, StatusMessage) -> + order_cycle_id = $location.absUrl().match(/\/admin\/order_cycles\/(\d+)/)[1] + $scope.enterprises = Enterprise.index(order_cycle_id: order_cycle_id) + $scope.supplier_enterprises = Enterprise.producer_enterprises + $scope.distributor_enterprises = Enterprise.hub_enterprises + $scope.supplied_products = Enterprise.supplied_products + $scope.enterprise_fees = EnterpriseFee.index(order_cycle_id: order_cycle_id) + + $scope.OrderCycle = OrderCycle + $scope.order_cycle = OrderCycle.load(order_cycle_id) + + $scope.StatusMessage = StatusMessage + + $scope.loaded = -> + Enterprise.loaded && EnterpriseFee.loaded && OrderCycle.loaded + + $scope.suppliedVariants = (enterprise_id) -> + Enterprise.suppliedVariants(enterprise_id) + + $scope.exchangeSelectedVariants = (exchange) -> + OrderCycle.exchangeSelectedVariants(exchange) + + $scope.setExchangeVariants = (exchange, variants, selected) -> + OrderCycle.setExchangeVariants(exchange, variants, selected) + + $scope.enterpriseTotalVariants = (enterprise) -> + Enterprise.totalVariants(enterprise) + + $scope.productSuppliedToOrderCycle = (product) -> + OrderCycle.productSuppliedToOrderCycle(product) + + $scope.variantSuppliedToOrderCycle = (variant) -> + OrderCycle.variantSuppliedToOrderCycle(variant) + + $scope.incomingExchangeVariantsFor = (enterprise_id) -> + $filter('filterExchangeVariants')(OrderCycle.incomingExchangesVariants(), $scope.order_cycle.visible_variants_for_outgoing_exchanges[enterprise_id]) + + $scope.exchangeDirection = (exchange) -> + OrderCycle.exchangeDirection(exchange) + + $scope.enterprisesWithFees = -> + $scope.enterprises[id] for id in OrderCycle.participatingEnterpriseIds() when $scope.enterpriseFeesForEnterprise(id).length > 0 + + $scope.toggleProducts = ($event, exchange) -> + $event.preventDefault() + OrderCycle.toggleProducts(exchange) + + $scope.enterpriseFeesForEnterprise = (enterprise_id) -> + EnterpriseFee.forEnterprise(parseInt(enterprise_id)) + + $scope.addSupplier = ($event) -> + $event.preventDefault() + OrderCycle.addSupplier($scope.new_supplier_id) + + $scope.addDistributor = ($event) -> + $event.preventDefault() + OrderCycle.addDistributor($scope.new_distributor_id) + + $scope.removeExchange = ($event, exchange) -> + $event.preventDefault() + OrderCycle.removeExchange(exchange) + + $scope.addCoordinatorFee = ($event) -> + $event.preventDefault() + OrderCycle.addCoordinatorFee() + + $scope.removeCoordinatorFee = ($event, index) -> + $event.preventDefault() + OrderCycle.removeCoordinatorFee(index) + + $scope.addExchangeFee = ($event, exchange) -> + $event.preventDefault() + OrderCycle.addExchangeFee(exchange) + + $scope.removeExchangeFee = ($event, exchange, index) -> + $event.preventDefault() + OrderCycle.removeExchangeFee(exchange, index) + + $scope.removeDistributionOfVariant = (variant_id) -> + OrderCycle.removeDistributionOfVariant(variant_id) + + $scope.submit = (destination) -> + OrderCycle.update(destination) diff --git a/app/assets/javascripts/admin/order_cycles/controllers/simple_create.js.coffee b/app/assets/javascripts/admin/order_cycles/controllers/simple_create.js.coffee index 771902ca5b..53f00cdc36 100644 --- a/app/assets/javascripts/admin/order_cycles/controllers/simple_create.js.coffee +++ b/app/assets/javascripts/admin/order_cycles/controllers/simple_create.js.coffee @@ -1,4 +1,6 @@ -angular.module('admin.order_cycles').controller "AdminSimpleCreateOrderCycleCtrl", ($scope, OrderCycle, Enterprise, EnterpriseFee, ocInstance) -> +angular.module('admin.orderCycles').controller "AdminSimpleCreateOrderCycleCtrl", ($scope, OrderCycle, Enterprise, EnterpriseFee, StatusMessage, ocInstance) -> + $scope.StatusMessage = StatusMessage + $scope.OrderCycle = OrderCycle $scope.order_cycle = OrderCycle.new {coordinator_id: ocInstance.coordinator_id}, => # TODO: make this a get method, which only fetches one enterprise $scope.enterprises = Enterprise.index {coordinator_id: ocInstance.coordinator_id}, (enterprises) => @@ -39,7 +41,6 @@ angular.module('admin.order_cycles').controller "AdminSimpleCreateOrderCycleCtrl $scope.enterpriseFeesForEnterprise = (enterprise_id) -> EnterpriseFee.forEnterprise(parseInt(enterprise_id)) - $scope.submit = (event) -> - event.preventDefault() + $scope.submit = (destination) -> OrderCycle.mirrorIncomingToOutgoingProducts() - OrderCycle.create() + OrderCycle.create(destination) diff --git a/app/assets/javascripts/admin/order_cycles/controllers/simple_edit.js.coffee b/app/assets/javascripts/admin/order_cycles/controllers/simple_edit.js.coffee index cab677407e..bf00bb3df1 100644 --- a/app/assets/javascripts/admin/order_cycles/controllers/simple_edit.js.coffee +++ b/app/assets/javascripts/admin/order_cycles/controllers/simple_edit.js.coffee @@ -1,9 +1,11 @@ -angular.module('admin.order_cycles').controller "AdminSimpleEditOrderCycleCtrl", ($scope, $location, OrderCycle, Enterprise, EnterpriseFee) -> +angular.module('admin.orderCycles').controller "AdminSimpleEditOrderCycleCtrl", ($scope, $location, OrderCycle, Enterprise, EnterpriseFee, StatusMessage) -> $scope.orderCycleId = -> $location.absUrl().match(/\/admin\/order_cycles\/(\d+)/)[1] + $scope.StatusMessage = StatusMessage $scope.enterprises = Enterprise.index(order_cycle_id: $scope.orderCycleId()) $scope.enterprise_fees = EnterpriseFee.index(order_cycle_id: $scope.orderCycleId()) + $scope.OrderCycle = OrderCycle $scope.order_cycle = OrderCycle.load $scope.orderCycleId(), (order_cycle) => $scope.init() @@ -32,7 +34,6 @@ angular.module('admin.order_cycles').controller "AdminSimpleEditOrderCycleCtrl", $event.preventDefault() OrderCycle.removeCoordinatorFee(index) - $scope.submit = (event) -> - event.preventDefault() + $scope.submit = (destination) -> OrderCycle.mirrorIncomingToOutgoingProducts() - OrderCycle.update() + OrderCycle.update(destination) diff --git a/app/assets/javascripts/admin/order_cycles/filters/filter_exchange_variants.js.coffee b/app/assets/javascripts/admin/order_cycles/filters/filter_exchange_variants.js.coffee index 2b22b1d60c..2fc5c9ff39 100644 --- a/app/assets/javascripts/admin/order_cycles/filters/filter_exchange_variants.js.coffee +++ b/app/assets/javascripts/admin/order_cycles/filters/filter_exchange_variants.js.coffee @@ -1,4 +1,4 @@ -angular.module("admin.order_cycles").filter "filterExchangeVariants", -> +angular.module("admin.orderCycles").filter "filterExchangeVariants", -> return (variants, rules) -> if variants? && rules? return (variant for variant in variants when variant in rules) diff --git a/app/assets/javascripts/admin/order_cycles/filters/visible_product_variants.js.coffee b/app/assets/javascripts/admin/order_cycles/filters/visible_product_variants.js.coffee index 8989af5b9d..d2e69ad64f 100644 --- a/app/assets/javascripts/admin/order_cycles/filters/visible_product_variants.js.coffee +++ b/app/assets/javascripts/admin/order_cycles/filters/visible_product_variants.js.coffee @@ -1,4 +1,4 @@ -angular.module("admin.order_cycles").filter "visibleProductVariants", -> +angular.module("admin.orderCycles").filter "visibleProductVariants", -> return (product, exchange, rules) -> variants = product.variants.concat( [{ "id": product.master_id}] ) return (variant for variant in variants when variant.id in rules[exchange.enterprise_id]) diff --git a/app/assets/javascripts/admin/order_cycles/filters/visible_products.js.coffee b/app/assets/javascripts/admin/order_cycles/filters/visible_products.js.coffee index 3afff183a1..40586854c1 100644 --- a/app/assets/javascripts/admin/order_cycles/filters/visible_products.js.coffee +++ b/app/assets/javascripts/admin/order_cycles/filters/visible_products.js.coffee @@ -1,3 +1,3 @@ -angular.module("admin.order_cycles").filter "visibleProducts", ($filter) -> +angular.module("admin.orderCycles").filter "visibleProducts", ($filter) -> return (products, exchange, rules) -> return (product for product in products when $filter('visibleProductVariants')(product, exchange, rules).length > 0) diff --git a/app/assets/javascripts/admin/order_cycles/order_cycles.js.coffee b/app/assets/javascripts/admin/order_cycles/order_cycles.js.coffee new file mode 100644 index 0000000000..a75fdad58c --- /dev/null +++ b/app/assets/javascripts/admin/order_cycles/order_cycles.js.coffee @@ -0,0 +1,32 @@ +angular.module('admin.orderCycles', ['ngResource', 'admin.utils', 'admin.indexUtils']) + + .config ($httpProvider) -> + $httpProvider.defaults.headers.common['X-CSRF-Token'] = $('meta[name=csrf-token]').attr('content') + + .directive 'datetimepicker', ($parse) -> + (scope, element, attrs) -> + # using $parse instead of scope[attrs.datetimepicker] for cases + # where attrs.datetimepicker is 'foo.bar.lol' + $(element).datetimepicker + dateFormat: 'yy-mm-dd' + timeFormat: 'HH:mm:ss' + showOn: "button" + buttonImage: "<%= asset_path 'datepicker/cal.gif' %>" + buttonImageOnly: true + stepMinute: 15 + onSelect: (dateText, inst) -> + scope.$apply -> + parsed = $parse(attrs.datetimepicker) + parsed.assign(scope, dateText) + + .directive 'ofnOnChange', -> + (scope, element, attrs) -> + element.bind 'change', -> + scope.$apply(attrs.ofnOnChange) + + .directive 'ofnSyncDistributions', -> + (scope, element, attrs) -> + element.bind 'change', -> + if !$(this).is(':checked') + scope.$apply -> + scope.removeDistributionOfVariant(attrs.ofnSyncDistributions) diff --git a/app/assets/javascripts/admin/order_cycles/services/enterprise.js.coffee b/app/assets/javascripts/admin/order_cycles/services/enterprise.js.coffee index 5e40f621dc..17f5bb385c 100644 --- a/app/assets/javascripts/admin/order_cycles/services/enterprise.js.coffee +++ b/app/assets/javascripts/admin/order_cycles/services/enterprise.js.coffee @@ -1,4 +1,4 @@ -angular.module('admin.order_cycles').factory('Enterprise', ($resource) -> +angular.module('admin.orderCycles').factory('Enterprise', ($resource) -> Enterprise = $resource('/admin/enterprises/for_order_cycle/:enterprise_id.json', {}, { 'index': method: 'GET' @@ -10,6 +10,8 @@ angular.module('admin.order_cycles').factory('Enterprise', ($resource) -> { Enterprise: Enterprise enterprises: {} + producer_enterprises: [] + hub_enterprises: [] supplied_products: [] loaded: false @@ -17,6 +19,8 @@ angular.module('admin.order_cycles').factory('Enterprise', ($resource) -> Enterprise.index params, (data) => for enterprise in data @enterprises[enterprise.id] = enterprise + @producer_enterprises.push(enterprise) if enterprise.is_primary_producer + @hub_enterprises.push(enterprise) if enterprise.sells == 'any' for product in enterprise.supplied_products @supplied_products.push(product) diff --git a/app/assets/javascripts/admin/order_cycles/services/enterprise_fee.js.coffee b/app/assets/javascripts/admin/order_cycles/services/enterprise_fee.js.coffee index fe443a7cf5..5d8ebf6d59 100644 --- a/app/assets/javascripts/admin/order_cycles/services/enterprise_fee.js.coffee +++ b/app/assets/javascripts/admin/order_cycles/services/enterprise_fee.js.coffee @@ -1,4 +1,4 @@ -angular.module('admin.order_cycles').factory('EnterpriseFee', ($resource) -> +angular.module('admin.orderCycles').factory('EnterpriseFee', ($resource) -> EnterpriseFee = $resource('/admin/enterprise_fees/for_order_cycle/:enterprise_fee_id.json', {}, { 'index': method: 'GET' diff --git a/app/assets/javascripts/admin/order_cycles/services/order_cycle.js.coffee b/app/assets/javascripts/admin/order_cycles/services/order_cycle.js.coffee index 95dc62ca4e..9ca5f1028d 100644 --- a/app/assets/javascripts/admin/order_cycles/services/order_cycle.js.coffee +++ b/app/assets/javascripts/admin/order_cycles/services/order_cycle.js.coffee @@ -1,15 +1,27 @@ -angular.module('admin.order_cycles').factory('OrderCycle', ($resource, $window) -> - OrderCycle = $resource '/admin/order_cycles/:action_name/:order_cycle_id.json', {}, { +angular.module('admin.orderCycles').factory 'OrderCycle', ($resource, $window, StatusMessage) -> + OrderCycleResource = $resource '/admin/order_cycles/:action_name/:order_cycle_id.json', {}, { 'index': { method: 'GET', isArray: true} 'new' : { method: 'GET', params: { action_name: "new" } } 'create': { method: 'POST'} 'update': { method: 'PUT'}} - { - order_cycle: {} + new class OrderCycle + order_cycle: {incoming_exchanges: [], outgoing_exchanges: []} + showProducts: {incoming: false, outgoing: false} loaded: false + exchangeIds: (direction) -> + parseInt(exchange.enterprise_id) for exchange in @exchangesByDirection(direction) + + novelSupplier: (enterprise) => + id = enterprise?.id || parseInt(enterprise) + @exchangeIds('incoming').indexOf(id) == -1 + + novelDistributor: (enterprise) => + id = enterprise?.id || parseInt(enterprise) + @exchangeIds('outgoing').indexOf(id) == -1 + exchangeSelectedVariants: (exchange) -> numActiveVariants = 0 numActiveVariants++ for id, active of exchange.variants when active @@ -21,6 +33,10 @@ angular.module('admin.order_cycles').factory('OrderCycle', ($resource, $window) toggleProducts: (exchange) -> exchange.showProducts = !exchange.showProducts + toggleAllProducts: (direction) -> + this.showProducts[direction] = !this.showProducts[direction] + exchange.showProducts = this.showProducts[direction] for exchange in this.exchangesByDirection(direction) + setExchangeVariants: (exchange, variants, selected) -> direction = if exchange.incoming then "incoming" else "outgoing" editable = @order_cycle["editable_variants_for_#{direction}_exchanges"][exchange.enterprise_id] || [] @@ -80,12 +96,18 @@ angular.module('admin.order_cycles').factory('OrderCycle', ($resource, $window) distributors = (exchange.enterprise_id for exchange in this.order_cycle.outgoing_exchanges) jQuery.unique(suppliers.concat(distributors)).sort() + exchangesByDirection: (direction) -> + if direction == 'incoming' + this.order_cycle.incoming_exchanges + else + this.order_cycle.outgoing_exchanges + removeDistributionOfVariant: (variant_id) -> for exchange in this.order_cycle.outgoing_exchanges exchange.variants[variant_id] = false new: (params, callback=null) -> - OrderCycle.new params, (oc) => + OrderCycleResource.new params, (oc) => delete oc.$promise delete oc.$resolved angular.extend(@order_cycle, oc) @@ -100,7 +122,7 @@ angular.module('admin.order_cycles').factory('OrderCycle', ($resource, $window) load: (order_cycle_id, callback=null) -> service = this - OrderCycle.get {order_cycle_id: order_cycle_id}, (oc) -> + OrderCycleResource.get {order_cycle_id: order_cycle_id}, (oc) -> delete oc.$promise delete oc.$resolved angular.extend(service.order_cycle, oc) @@ -123,22 +145,33 @@ angular.module('admin.order_cycles').factory('OrderCycle', ($resource, $window) this.order_cycle - create: -> - oc = new OrderCycle({order_cycle: this.dataForSubmit()}) - oc.$create (data) -> - if data['success'] - $window.location = '/admin/order_cycles' - else + create: (destination) -> + return unless @confirmNoDistributors() + oc = new OrderCycleResource({order_cycle: this.dataForSubmit()}) + oc.$create (data) -> + if data['success'] + $window.location = destination + else console.log('Failed to create order cycle') - update: -> - oc = new OrderCycle({order_cycle: this.dataForSubmit()}) - oc.$update {order_cycle_id: this.order_cycle.id}, (data) -> - if data['success'] - $window.location = '/admin/order_cycles' - else + update: (destination) -> + return unless @confirmNoDistributors() + oc = new OrderCycleResource({order_cycle: this.dataForSubmit()}) + oc.$update {order_cycle_id: this.order_cycle.id, reloading: (if destination? then 1 else 0)}, (data) => + if data['success'] + if destination? + $window.location = destination + else + StatusMessage.display 'success', 'Your order cycle has been updated.' + else console.log('Failed to update order cycle') + confirmNoDistributors: -> + if @order_cycle.outgoing_exchanges.length == 0 + confirm 'There are no distributors in this order cycle. This order cycle will not be visible to customers until you add one. Would you like to continue saving this order cycle?' + else + true + dataForSubmit: -> data = this.deepCopy() data = this.stripNonSubmittableAttributes(data) @@ -200,4 +233,3 @@ angular.module('admin.order_cycles').factory('OrderCycle', ($resource, $window) for id, active of incoming.variants outgoing.variants[id] = active - }) diff --git a/app/assets/javascripts/admin/order_cycles/services/order_cycle_resource.js.coffee b/app/assets/javascripts/admin/order_cycles/services/order_cycle_resource.js.coffee new file mode 100644 index 0000000000..4a5df3c44a --- /dev/null +++ b/app/assets/javascripts/admin/order_cycles/services/order_cycle_resource.js.coffee @@ -0,0 +1,8 @@ +angular.module("admin.orderCycles").factory 'OrderCycleResource', ($resource) -> + $resource('/admin/order_cycles/:id/:action.json', {}, { + 'index': + method: 'GET' + isArray: true + 'update': + method: 'PUT' + }) diff --git a/app/assets/javascripts/admin/order_cycles/services/order_cycles.js.coffee b/app/assets/javascripts/admin/order_cycles/services/order_cycles.js.coffee new file mode 100644 index 0000000000..a182f19f12 --- /dev/null +++ b/app/assets/javascripts/admin/order_cycles/services/order_cycles.js.coffee @@ -0,0 +1,40 @@ +angular.module("admin.orderCycles").factory 'OrderCycles', ($q, OrderCycleResource, blankOption) -> + new class OrderCycles + orderCyclesByID: {} + pristineByID: {} + + index: (params={}, callback=null) -> + includeBlank = !!params['includeBlank'] + delete params['includeBlank'] + OrderCycleResource.index(params, (data) => + for orderCycle in data + @orderCyclesByID[orderCycle.id] = orderCycle + @pristineByID[orderCycle.id] = angular.copy(orderCycle) + + (callback || angular.noop)(data) + + data.unshift(blankOption()) if includeBlank + data + ) + + save: (order_cycle) -> + deferred = $q.defer() + order_cycle.$update({id: order_cycle.id}) + .then( (data) => + @pristineByID[order_cycle.id] = angular.copy(order_cycle) + deferred.resolve(data) + ).catch (response) -> + deferred.reject(response) + deferred.promise + + saved: (order_cycle) -> + @diff(order_cycle).length == 0 + + diff: (order_cycle) -> + changed = [] + for attr, value of order_cycle when not angular.equals(value, @pristineByID[order_cycle.id][attr]) + changed.push attr unless attr is "$$hashKey" + changed + + resetAttribute: (order_cycle, attribute) -> + order_cycle[attribute] = @pristineByID[order_cycle.id][attribute] diff --git a/app/assets/javascripts/admin/orders/controllers/orders_controller.js.coffee b/app/assets/javascripts/admin/orders/controllers/orders_controller.js.coffee new file mode 100644 index 0000000000..6ad7f0bfb9 --- /dev/null +++ b/app/assets/javascripts/admin/orders/controllers/orders_controller.js.coffee @@ -0,0 +1,22 @@ +angular.module("admin.orders").controller "ordersCtrl", ($scope, $compile, $attrs, shops, orderCycles) -> + $scope.$compile = $compile + $scope.shops = shops + $scope.orderCycles = orderCycles + for oc in $scope.orderCycles + oc.name_and_status = "#{oc.name} (#{oc.status})" + + $scope.distributor_id = $attrs.ofnDistributorId + $scope.order_cycle_id = $attrs.ofnOrderCycleId + + $scope.validOrderCycle = (oc, index, array) -> + $scope.orderCycleHasDistributor oc, parseInt($scope.distributor_id) + + $scope.distributorHasOrderCycles = (distributor) -> + (oc for oc in orderCycles when @orderCycleHasDistributor(oc, distributor.id)).length > 0 + + $scope.orderCycleHasDistributor = (oc, distributor_id) -> + distributor_ids = (d.id for d in oc.distributors) + distributor_ids.indexOf(distributor_id) != -1 + + $scope.distributionChosen = -> + $scope.distributor_id && $scope.order_cycle_id diff --git a/app/assets/javascripts/admin/orders/orders.js.coffee b/app/assets/javascripts/admin/orders/orders.js.coffee new file mode 100644 index 0000000000..abfe576095 --- /dev/null +++ b/app/assets/javascripts/admin/orders/orders.js.coffee @@ -0,0 +1 @@ +angular.module("admin.orders", ['ngResource']) diff --git a/app/assets/javascripts/admin/orders/services/order_resource.js.coffee b/app/assets/javascripts/admin/orders/services/order_resource.js.coffee new file mode 100644 index 0000000000..ab360a2fc9 --- /dev/null +++ b/app/assets/javascripts/admin/orders/services/order_resource.js.coffee @@ -0,0 +1,8 @@ +angular.module("admin.orders").factory 'OrderResource', ($resource) -> + $resource('/admin/orders/:id/:action.json', {}, { + 'index': + method: 'GET' + isArray: true + 'update': + method: 'PUT' + }) diff --git a/app/assets/javascripts/admin/orders/services/orders.js.coffee b/app/assets/javascripts/admin/orders/services/orders.js.coffee new file mode 100644 index 0000000000..a7b5bc1b68 --- /dev/null +++ b/app/assets/javascripts/admin/orders/services/orders.js.coffee @@ -0,0 +1,34 @@ +angular.module("admin.orders").factory 'Orders', ($q, OrderResource) -> + new class Orders + ordersByID: {} + pristineByID: {} + + index: (params={}, callback=null) -> + OrderResource.index params, (data) => + for order in data + @ordersByID[order.id] = order + @pristineByID[order.id] = angular.copy(order) + + (callback || angular.noop)(data) + + save: (order) -> + deferred = $q.defer() + order.$update({id: order.number}) + .then( (data) => + @pristineByID[order.id] = angular.copy(order) + deferred.resolve(data) + ).catch (response) -> + deferred.reject(response) + deferred.promise + + saved: (order) -> + @diff(order).length == 0 + + diff: (order) -> + changed = [] + for attr, value of order when not angular.equals(value, @pristineByID[order.id][attr]) + changed.push attr unless attr is "$$hashKey" + changed + + resetAttribute: (order, attribute) -> + order[attribute] = @pristineByID[order.id][attribute] diff --git a/app/assets/javascripts/admin/products/units_controller.js.coffee b/app/assets/javascripts/admin/products/controllers/units_controller.js.coffee similarity index 100% rename from app/assets/javascripts/admin/products/units_controller.js.coffee rename to app/assets/javascripts/admin/products/controllers/units_controller.js.coffee diff --git a/app/assets/javascripts/admin/services/option_value_namer.js.coffee b/app/assets/javascripts/admin/products/services/option_value_namer.js.coffee similarity index 100% rename from app/assets/javascripts/admin/services/option_value_namer.js.coffee rename to app/assets/javascripts/admin/products/services/option_value_namer.js.coffee diff --git a/app/assets/javascripts/admin/services/variant_unit_manager.js.coffee b/app/assets/javascripts/admin/products/services/variant_unit_manager.js.coffee similarity index 100% rename from app/assets/javascripts/admin/services/variant_unit_manager.js.coffee rename to app/assets/javascripts/admin/products/services/variant_unit_manager.js.coffee diff --git a/app/assets/javascripts/admin/services/blank_option.js.coffee b/app/assets/javascripts/admin/services/blank_option.js.coffee deleted file mode 100644 index 42ff69a77b..0000000000 --- a/app/assets/javascripts/admin/services/blank_option.js.coffee +++ /dev/null @@ -1,2 +0,0 @@ -angular.module("ofn.admin").value "blankOption", -> - { id: "0", name: "All" } \ No newline at end of file diff --git a/app/assets/javascripts/admin/services/bulk_products.js.coffee b/app/assets/javascripts/admin/services/bulk_products.js.coffee index 6085d48fcc..4e06a87bd2 100644 --- a/app/assets/javascripts/admin/services/bulk_products.js.coffee +++ b/app/assets/javascripts/admin/services/bulk_products.js.coffee @@ -19,6 +19,7 @@ angular.module("ofn.admin").factory "BulkProducts", (PagedFetcher, dataFetcher) # when a respond_overrride for the clone action is used. id = data.product.id dataFetcher("/api/products/" + id + "?template=bulk_show").then (newProduct) => + @unpackProduct newProduct @insertProductAfter(product, newProduct) updateVariantLists: (serverProducts, productsWithUnsavedVariants) -> diff --git a/app/assets/javascripts/admin/services/data_submitter.js.coffee b/app/assets/javascripts/admin/services/data_submitter.js.coffee deleted file mode 100644 index 7d121ec645..0000000000 --- a/app/assets/javascripts/admin/services/data_submitter.js.coffee +++ /dev/null @@ -1,13 +0,0 @@ -angular.module("ofn.admin").factory "dataSubmitter", [ - "$http", "$q", "switchClass" - ($http, $q, switchClass) -> - return (changeObj) -> - deferred = $q.defer() - $http.put(changeObj.url).success((data) -> - switchClass changeObj.element, "update-success", ["update-pending", "update-error"], 3000 - deferred.resolve data - ).error -> - switchClass changeObj.element, "update-error", ["update-pending", "update-success"], false - deferred.reject() - deferred.promise -] \ No newline at end of file diff --git a/app/assets/javascripts/admin/services/display_properties.js.coffee b/app/assets/javascripts/admin/services/display_properties.js.coffee index 7288706032..3037c9f068 100644 --- a/app/assets/javascripts/admin/services/display_properties.js.coffee +++ b/app/assets/javascripts/admin/services/display_properties.js.coffee @@ -3,12 +3,10 @@ angular.module("ofn.admin").factory "DisplayProperties", -> displayProperties: {} showVariants: (product_id) -> - @initProduct product_id - @displayProperties[product_id].showVariants + @productProperties(product_id).showVariants setShowVariants: (product_id, showVariants) -> - @initProduct product_id - @displayProperties[product_id].showVariants = showVariants + @productProperties(product_id).showVariants = showVariants - initProduct: (product_id) -> + productProperties: (product_id) -> @displayProperties[product_id] ||= {showVariants: false} diff --git a/app/assets/javascripts/admin/services/enterprise_relationships.js.coffee b/app/assets/javascripts/admin/services/enterprise_relationships.js.coffee index e6dcbf15df..8c7d798138 100644 --- a/app/assets/javascripts/admin/services/enterprise_relationships.js.coffee +++ b/app/assets/javascripts/admin/services/enterprise_relationships.js.coffee @@ -26,7 +26,7 @@ angular.module("ofn.admin").factory 'EnterpriseRelationships', ($http, enterpris permission_presentation: (permission) -> switch permission - when "add_to_order_cycle" then "to add to order cycle" - when "manage_products" then "to manage products" - when "edit_profile" then "to edit profile" - when "create_variant_overrides" then "to override variant details" + when "add_to_order_cycle" then "add to order cycle" + when "manage_products" then "manage products" + when "edit_profile" then "edit profile" + when "create_variant_overrides" then "override variant details" diff --git a/app/assets/javascripts/admin/services/pending_changes.js.coffee b/app/assets/javascripts/admin/services/pending_changes.js.coffee deleted file mode 100644 index d72a4ac7bc..0000000000 --- a/app/assets/javascripts/admin/services/pending_changes.js.coffee +++ /dev/null @@ -1,32 +0,0 @@ -angular.module("ofn.admin").factory "pendingChanges",[ - "dataSubmitter" - (dataSubmitter) -> - pendingChanges: {} - - add: (id, attrName, changeObj) -> - @pendingChanges["#{id}"] = {} unless @pendingChanges.hasOwnProperty("#{id}") - @pendingChanges["#{id}"]["#{attrName}"] = changeObj - - removeAll: -> - @pendingChanges = {} - - remove: (id, attrName) -> - if @pendingChanges.hasOwnProperty("#{id}") - delete @pendingChanges["#{id}"]["#{attrName}"] - delete @pendingChanges["#{id}"] if @changeCount( @pendingChanges["#{id}"] ) < 1 - - submitAll: -> - all = [] - for id,lineItem of @pendingChanges - for attrName,changeObj of lineItem - all.push @submit(id, attrName, changeObj) - all - - submit: (id, attrName, change) -> - dataSubmitter(change).then (data) => - @remove id, attrName - change.element.dbValue = data["#{attrName}"] - - changeCount: (lineItem) -> - Object.keys(lineItem).length -] \ No newline at end of file diff --git a/app/assets/javascripts/admin/services/switch_class.js.coffee b/app/assets/javascripts/admin/services/switch_class.js.coffee deleted file mode 100644 index e39c52d1f6..0000000000 --- a/app/assets/javascripts/admin/services/switch_class.js.coffee +++ /dev/null @@ -1,13 +0,0 @@ -angular.module("ofn.admin").factory "switchClass", [ - "$timeout" - ($timeout) -> - return (element,classToAdd,removeClasses,timeout) -> - $timeout.cancel element.timeout if element.timeout - element.removeClass className for className in removeClasses - element.addClass classToAdd - intRegex = /^\d+$/ - if timeout && intRegex.test(timeout) - element.timeout = $timeout(-> - element.removeClass classToAdd - , timeout, true) -] \ No newline at end of file diff --git a/app/assets/javascripts/admin/services/variant_overrides.js.coffee b/app/assets/javascripts/admin/services/variant_overrides.js.coffee deleted file mode 100644 index 28d65eab03..0000000000 --- a/app/assets/javascripts/admin/services/variant_overrides.js.coffee +++ /dev/null @@ -1,23 +0,0 @@ -angular.module("ofn.admin").factory "VariantOverrides", (variantOverrides, Indexer) -> - new class VariantOverrides - variantOverrides: {} - - constructor: -> - for vo in variantOverrides - @variantOverrides[vo.hub_id] ||= {} - @variantOverrides[vo.hub_id][vo.variant_id] = vo - - ensureDataFor: (hubs, products) -> - for hub in hubs - @variantOverrides[hub.id] ||= {} - for product in products - for variant in product.variants - @variantOverrides[hub.id][variant.id] ||= - variant_id: variant.id - hub_id: hub.id - price: '' - count_on_hand: '' - - updateIds: (updatedVos) -> - for vo in updatedVos - @variantOverrides[vo.hub_id][vo.variant_id].id = vo.id \ No newline at end of file diff --git a/app/assets/javascripts/admin/taxons/directives/taxon_autocomplete.js.coffee b/app/assets/javascripts/admin/taxons/directives/taxon_autocomplete.js.coffee index b978a050ad..b1eac64569 100644 --- a/app/assets/javascripts/admin/taxons/directives/taxon_autocomplete.js.coffee +++ b/app/assets/javascripts/admin/taxons/directives/taxon_autocomplete.js.coffee @@ -1,4 +1,4 @@ -angular.module("admin.taxons").directive "ofnTaxonAutocomplete", (Taxons) -> +angular.module("admin.taxons").directive "ofnTaxonAutocomplete", (Taxons, $sanitize) -> # Adapted from Spree's existing taxon autocompletion scope: true link: (scope,element,attrs) -> @@ -18,7 +18,7 @@ angular.module("admin.taxons").directive "ofnTaxonAutocomplete", (Taxons) -> query: (query) -> query.callback { results: Taxons.findByTerm(query.term) } formatResult: (taxon) -> - taxon.name + $sanitize(taxon.name) formatSelection: (taxon) -> taxon.name diff --git a/app/assets/javascripts/admin/taxons/taxons.js.coffee b/app/assets/javascripts/admin/taxons/taxons.js.coffee index 863e6e8125..07de167ccf 100644 --- a/app/assets/javascripts/admin/taxons/taxons.js.coffee +++ b/app/assets/javascripts/admin/taxons/taxons.js.coffee @@ -1 +1 @@ -angular.module("admin.taxons", []) \ No newline at end of file +angular.module("admin.taxons", ['ngSanitize']) \ No newline at end of file diff --git a/app/assets/javascripts/admin/users/directives/user_select.js.coffee b/app/assets/javascripts/admin/users/directives/user_select.js.coffee index 94df1894d9..787ef2124b 100644 --- a/app/assets/javascripts/admin/users/directives/user_select.js.coffee +++ b/app/assets/javascripts/admin/users/directives/user_select.js.coffee @@ -1,19 +1,20 @@ -angular.module("admin.users").directive "userSelect", -> +angular.module("admin.users").directive "userSelect", ($sanitize) -> scope: user: '&userSelect' model: '=ngModel' - link: (scope,element,attrs) -> + link: (scope, element, attrs) -> setTimeout -> element.select2 multiple: false initSelection: (element, callback) -> - callback {id: scope.user().id, email: scope.user().email} + callback {id: scope.user()?.id, email: scope.user()?.email} ajax: url: '/admin/search/known_users' datatype: 'json' - data:(term, page) -> + data: (term, page) -> { q: term } results: (data, page) -> + item.email = $sanitize(item.email) for item in data { results: data } formatResult: (user) -> user.email diff --git a/app/assets/javascripts/admin/users/users.js.coffee b/app/assets/javascripts/admin/users/users.js.coffee index 6bfd47a894..a86a90a56c 100644 --- a/app/assets/javascripts/admin/users/users.js.coffee +++ b/app/assets/javascripts/admin/users/users.js.coffee @@ -1 +1 @@ -angular.module("admin.users", []) \ No newline at end of file +angular.module("admin.users", ['admin.utils']) \ No newline at end of file diff --git a/app/assets/javascripts/admin/utils/directives/save_bar.js.coffee b/app/assets/javascripts/admin/utils/directives/save_bar.js.coffee new file mode 100644 index 0000000000..13e4f84bc6 --- /dev/null +++ b/app/assets/javascripts/admin/utils/directives/save_bar.js.coffee @@ -0,0 +1,8 @@ +angular.module("admin.utils").directive "saveBar", (StatusMessage) -> + restrict: "E" + scope: + save: "&" + form: "=" + templateUrl: "admin/save_bar.html" + link: (scope, element, attrs) -> + scope.StatusMessage = StatusMessage diff --git a/app/assets/javascripts/admin/utils/directives/watchValueAs.js.coffee b/app/assets/javascripts/admin/utils/directives/watchValueAs.js.coffee new file mode 100644 index 0000000000..701858cd56 --- /dev/null +++ b/app/assets/javascripts/admin/utils/directives/watchValueAs.js.coffee @@ -0,0 +1,11 @@ +angular.module("admin.utils").directive "watchValueAs", -> + restrict: 'A' + scope: { + value: "=watchValueAs" + } + link: (scope, element, attrs) -> + scope.value = element.val() + + element.on "change blur load", -> + scope.$apply -> + scope.value = element.val() diff --git a/app/assets/javascripts/admin/utils/directives/with_tip.js.coffee b/app/assets/javascripts/admin/utils/directives/with_tip.js.coffee new file mode 100644 index 0000000000..51bb5b05bf --- /dev/null +++ b/app/assets/javascripts/admin/utils/directives/with_tip.js.coffee @@ -0,0 +1,8 @@ +angular.module("admin.utils").directive "ofnWithTip", ($sanitize)-> + link: (scope, element, attrs) -> + element.attr('data-powertip', $sanitize(attrs.ofnWithTip)) + element.powerTip + smartPlacement: true + fadeInTime: 50 + fadeOutTime: 50 + intentPollInterval: 300 diff --git a/app/assets/javascripts/admin/utils/services/blank_option.js.coffee b/app/assets/javascripts/admin/utils/services/blank_option.js.coffee new file mode 100644 index 0000000000..f4089aa20d --- /dev/null +++ b/app/assets/javascripts/admin/utils/services/blank_option.js.coffee @@ -0,0 +1,2 @@ +angular.module("admin.utils").value "blankOption", -> + { id: "0", name: "All" } diff --git a/app/assets/javascripts/admin/services/status_message.js.coffee b/app/assets/javascripts/admin/utils/services/status_message.js.coffee similarity index 82% rename from app/assets/javascripts/admin/services/status_message.js.coffee rename to app/assets/javascripts/admin/utils/services/status_message.js.coffee index aaa55cf339..6aac046a7f 100644 --- a/app/assets/javascripts/admin/services/status_message.js.coffee +++ b/app/assets/javascripts/admin/utils/services/status_message.js.coffee @@ -1,4 +1,4 @@ -angular.module("ofn.admin").factory "StatusMessage", ($timeout) -> +angular.module("admin.utils").factory "StatusMessage", ($timeout) -> new class StatusMessage types: progress: {timeout: false, style: {color: '#ff9906'}} @@ -11,6 +11,9 @@ angular.module("ofn.admin").factory "StatusMessage", ($timeout) -> text: "" style: {} + active: -> + @statusMessage.text != '' + display: (type, text) -> @statusMessage.text = text @statusMessage.style = @types[type].style @@ -20,6 +23,7 @@ angular.module("ofn.admin").factory "StatusMessage", ($timeout) -> @statusMessage.timeout = $timeout => @clear() , timeout, true + null # So we don't return weird timeouts clear: -> @statusMessage.text = '' diff --git a/app/assets/javascripts/admin/utils/utils.js.coffee b/app/assets/javascripts/admin/utils/utils.js.coffee index 4d58ae930a..094d3a5849 100644 --- a/app/assets/javascripts/admin/utils/utils.js.coffee +++ b/app/assets/javascripts/admin/utils/utils.js.coffee @@ -1 +1 @@ -angular.module("admin.utils", []) \ No newline at end of file +angular.module("admin.utils", ["ngSanitize"]) \ No newline at end of file diff --git a/app/assets/javascripts/admin/variant_overrides/controllers/variant_overrides_controller.js.coffee b/app/assets/javascripts/admin/variant_overrides/controllers/variant_overrides_controller.js.coffee new file mode 100644 index 0000000000..b65df80d66 --- /dev/null +++ b/app/assets/javascripts/admin/variant_overrides/controllers/variant_overrides_controller.js.coffee @@ -0,0 +1,102 @@ +angular.module("admin.variantOverrides").controller "AdminVariantOverridesCtrl", ($scope, $http, $timeout, Indexer, Columns, SpreeApiAuth, PagedFetcher, StatusMessage, hubs, producers, hubPermissions, VariantOverrides, DirtyVariantOverrides) -> + $scope.hubs = Indexer.index hubs + $scope.hub = null + $scope.products = [] + $scope.producers = producers + $scope.producersByID = Indexer.index producers + $scope.hubPermissions = hubPermissions + $scope.variantOverrides = VariantOverrides.variantOverrides + $scope.StatusMessage = StatusMessage + + $scope.columns = Columns.setColumns + producer: { name: "Producer", visible: true } + product: { name: "Product", visible: true } + sku: { name: "SKU", visible: false } + price: { name: "Price", visible: true } + on_hand: { name: "On Hand", visible: true } + on_demand: { name: "On Demand", visible: false } + reset: { name: "Reset Stock Level", visible: false } + inheritance: { name: "Inheritance", visible: false } + + $scope.resetSelectFilters = -> + $scope.producerFilter = 0 + $scope.query = '' + + $scope.resetSelectFilters() + + $scope.initialise = -> + SpreeApiAuth.authorise() + .then -> + $scope.spree_api_key_ok = true + $scope.fetchProducts() + .catch (message) -> + $scope.api_error_msg = message + + + $scope.fetchProducts = -> + url = "/api/products/overridable?page=::page::;per_page=100" + PagedFetcher.fetch url, (data) => $scope.addProducts data.products + + + $scope.addProducts = (products) -> + $scope.products = $scope.products.concat products + VariantOverrides.ensureDataFor hubs, products + + + $scope.selectHub = -> + $scope.hub = $scope.hubs[$scope.hub_id] + + $scope.displayDirty = -> + if DirtyVariantOverrides.count() > 0 + num = if DirtyVariantOverrides.count() == 1 then "one override" else "#{DirtyVariantOverrides.count()} overrides" + StatusMessage.display 'notice', "Changes to #{num} remain unsaved." + else + StatusMessage.clear() + + $scope.update = -> + if DirtyVariantOverrides.count() == 0 + StatusMessage.display 'alert', 'No changes to save.' + else + StatusMessage.display 'progress', 'Saving...' + DirtyVariantOverrides.save() + .success (updatedVos) -> + DirtyVariantOverrides.clear() + VariantOverrides.updateIds updatedVos + $scope.variant_overrides_form.$setPristine() + StatusMessage.display 'success', 'Changes saved.' + VariantOverrides.updateData updatedVos # Refresh page data + .error (data, status) -> + StatusMessage.display 'failure', $scope.updateError(data, status) + + + $scope.updateError = (data, status) -> + if status == 401 + "I couldn't get authorisation to save those changes, so they remain unsaved." + + else if status == 400 && data.errors? + errors = [] + for field, field_errors of data.errors + errors = errors.concat field_errors + errors = errors.join ', ' + "I had some trouble saving: #{errors}" + else + "Oh no! I was unable to save your changes." + + $scope.resetStock = -> + if DirtyVariantOverrides.count() > 0 + StatusMessage.display 'alert', 'Save changes first.' + $timeout -> + $scope.displayDirty() + , 3000 # 3 second delay + else + return unless $scope.hub_id? + StatusMessage.display 'progress', 'Changing on hand stock levels...' + $http + method: "POST" + url: "/admin/variant_overrides/bulk_reset" + data: { hub_id: $scope.hub_id } + .success (updatedVos) -> + VariantOverrides.updateData updatedVos + StatusMessage.display 'success', 'Stocks reset to defaults.' + .error (data, status) -> + $timeout -> StatusMessage.display 'failure', $scope.updateError(data, status) diff --git a/app/assets/javascripts/admin/variant_overrides/directives/track_inheritance.js.coffee b/app/assets/javascripts/admin/variant_overrides/directives/track_inheritance.js.coffee new file mode 100644 index 0000000000..20ac08c035 --- /dev/null +++ b/app/assets/javascripts/admin/variant_overrides/directives/track_inheritance.js.coffee @@ -0,0 +1,12 @@ +angular.module("admin.variantOverrides").directive "trackInheritance", (VariantOverrides, DirtyVariantOverrides) -> + require: "ngModel" + link: (scope, element, attrs, ngModel) -> + # This is a bit hacky, but it allows us to load the inherit property on the VO, but then not submit it + scope.inherit = angular.equals scope.variantOverrides[scope.hub.id][scope.variant.id], VariantOverrides.newFor scope.hub.id, scope.variant.id + + ngModel.$parsers.push (viewValue) -> + if ngModel.$dirty && viewValue + variantOverride = VariantOverrides.inherit(scope.hub.id, scope.variant.id) + DirtyVariantOverrides.add variantOverride + scope.displayDirty() + viewValue diff --git a/app/assets/javascripts/admin/directives/track_variant_override.js.coffee b/app/assets/javascripts/admin/variant_overrides/directives/track_variant_override.js.coffee similarity index 69% rename from app/assets/javascripts/admin/directives/track_variant_override.js.coffee rename to app/assets/javascripts/admin/variant_overrides/directives/track_variant_override.js.coffee index bb8117a757..919533967c 100644 --- a/app/assets/javascripts/admin/directives/track_variant_override.js.coffee +++ b/app/assets/javascripts/admin/variant_overrides/directives/track_variant_override.js.coffee @@ -1,9 +1,10 @@ -angular.module("ofn.admin").directive "ofnTrackVariantOverride", (DirtyVariantOverrides) -> +angular.module("admin.variantOverrides").directive "ofnTrackVariantOverride", (DirtyVariantOverrides) -> require: "ngModel" link: (scope, element, attrs, ngModel) -> ngModel.$parsers.push (viewValue) -> if ngModel.$dirty variantOverride = scope.variantOverrides[scope.hub.id][scope.variant.id] + scope.inherit = false DirtyVariantOverrides.add variantOverride scope.displayDirty() viewValue diff --git a/app/assets/javascripts/admin/filters/hub_permissions_filter.js.coffee b/app/assets/javascripts/admin/variant_overrides/filters/hub_permissions_filter.js.coffee similarity index 70% rename from app/assets/javascripts/admin/filters/hub_permissions_filter.js.coffee rename to app/assets/javascripts/admin/variant_overrides/filters/hub_permissions_filter.js.coffee index 5db7a6d40e..39b5e77839 100644 --- a/app/assets/javascripts/admin/filters/hub_permissions_filter.js.coffee +++ b/app/assets/javascripts/admin/variant_overrides/filters/hub_permissions_filter.js.coffee @@ -1,4 +1,4 @@ -angular.module("ofn.admin").filter "hubPermissions", ($filter) -> +angular.module("admin.variantOverrides").filter "hubPermissions", ($filter) -> return (products, hubPermissions, hub_id) -> return [] if !hub_id return $filter('filter')(products, ((product) -> hubPermissions[hub_id].indexOf(product.producer_id) > -1), true) diff --git a/app/assets/javascripts/admin/services/dirty_variant_overrides.js.coffee b/app/assets/javascripts/admin/variant_overrides/services/dirty_variant_overrides.js.coffee similarity index 88% rename from app/assets/javascripts/admin/services/dirty_variant_overrides.js.coffee rename to app/assets/javascripts/admin/variant_overrides/services/dirty_variant_overrides.js.coffee index 82e7772982..053c6cbfa1 100644 --- a/app/assets/javascripts/admin/services/dirty_variant_overrides.js.coffee +++ b/app/assets/javascripts/admin/variant_overrides/services/dirty_variant_overrides.js.coffee @@ -1,4 +1,4 @@ -angular.module("ofn.admin").factory "DirtyVariantOverrides", ($http) -> +angular.module("admin.variantOverrides").factory "DirtyVariantOverrides", ($http) -> new class DirtyVariantOverrides dirtyVariantOverrides: {} diff --git a/app/assets/javascripts/admin/variant_overrides/services/variant_overrides.js.coffee b/app/assets/javascripts/admin/variant_overrides/services/variant_overrides.js.coffee new file mode 100644 index 0000000000..4e1128572e --- /dev/null +++ b/app/assets/javascripts/admin/variant_overrides/services/variant_overrides.js.coffee @@ -0,0 +1,40 @@ + +angular.module("admin.variantOverrides").factory "VariantOverrides", (variantOverrides) -> + new class VariantOverrides + variantOverrides: {} + + constructor: -> + for vo in variantOverrides + @variantOverrides[vo.hub_id] ||= {} + @variantOverrides[vo.hub_id][vo.variant_id] = vo + + ensureDataFor: (hubs, products) -> + for hub_id, hub of hubs + @variantOverrides[hub.id] ||= {} + for product in products + for variant in product.variants + @inherit(hub.id, variant.id) unless @variantOverrides[hub.id][variant.id] + + inherit: (hub_id, variant_id) -> + # This method is called from the trackInheritance directive, to reinstate inheritance + @variantOverrides[hub_id][variant_id] ||= {} + angular.extend @variantOverrides[hub_id][variant_id], @newFor hub_id, variant_id + + newFor: (hub_id, variant_id) -> + # These properties need to match those checked in VariantOverrideSet.deletable? + hub_id: hub_id + variant_id: variant_id + sku: null + price: null + count_on_hand: null + on_demand: null + default_stock: null + resettable: false + + updateIds: (updatedVos) -> + for vo in updatedVos + @variantOverrides[vo.hub_id][vo.variant_id].id = vo.id + + updateData: (updatedVos) -> + for vo in updatedVos + @variantOverrides[vo.hub_id][vo.variant_id] = vo diff --git a/app/assets/javascripts/admin/variant_overrides/variant_overrides.js.coffee b/app/assets/javascripts/admin/variant_overrides/variant_overrides.js.coffee new file mode 100644 index 0000000000..ae46cd14c7 --- /dev/null +++ b/app/assets/javascripts/admin/variant_overrides/variant_overrides.js.coffee @@ -0,0 +1 @@ +angular.module("admin.variantOverrides", ["pasvaz.bindonce", "admin.indexUtils", "admin.utils", "admin.dropdown"]) diff --git a/app/assets/javascripts/darkswarm/all.js.coffee b/app/assets/javascripts/darkswarm/all.js.coffee index 68bc01b6c1..f4303f8037 100644 --- a/app/assets/javascripts/darkswarm/all.js.coffee +++ b/app/assets/javascripts/darkswarm/all.js.coffee @@ -15,6 +15,7 @@ #= require ../shared/bindonce.min.js #= require ../shared/ng-infinite-scroll.min.js #= require ../shared/angular-local-storage.js +#= require ../shared/angular-slideables.js #= require angularjs-file-upload diff --git a/app/assets/javascripts/darkswarm/cart.js.coffee b/app/assets/javascripts/darkswarm/cart.js.coffee index 471991143a..1e971c8bb4 100644 --- a/app/assets/javascripts/darkswarm/cart.js.coffee +++ b/app/assets/javascripts/darkswarm/cart.js.coffee @@ -15,5 +15,5 @@ $(document).ready -> $('td.cart-adjustments a').click -> $('.cart_adjustment').toggle() - $(this).html('Item Handling Fees (included in item totals)') + $(this).html(t('item_handling_fees')) false diff --git a/app/assets/javascripts/darkswarm/controllers/authentication/forgot_controller.js.coffee b/app/assets/javascripts/darkswarm/controllers/authentication/forgot_controller.js.coffee index 175e18d2a2..85920de958 100644 --- a/app/assets/javascripts/darkswarm/controllers/authentication/forgot_controller.js.coffee +++ b/app/assets/javascripts/darkswarm/controllers/authentication/forgot_controller.js.coffee @@ -7,6 +7,6 @@ Darkswarm.controller "ForgotCtrl", ($scope, $http, $location, AuthenticationServ $http.post("/user/spree_user/password", {spree_user: $scope.spree_user}).success (data)-> $scope.sent = true .error (data) -> - $scope.errors = "Email address not found" + $scope.errors = t 'email_not_found' else - $scope.errors = "You must provide an email address" + $scope.errors = t 'email_required' diff --git a/app/assets/javascripts/darkswarm/controllers/authentication/login_controller.js.coffee b/app/assets/javascripts/darkswarm/controllers/authentication/login_controller.js.coffee index 144cf0dfef..f91e137ca7 100644 --- a/app/assets/javascripts/darkswarm/controllers/authentication/login_controller.js.coffee +++ b/app/assets/javascripts/darkswarm/controllers/authentication/login_controller.js.coffee @@ -2,7 +2,7 @@ Darkswarm.controller "LoginCtrl", ($scope, $http, $window, AuthenticationService $scope.path = "/login" $scope.submit = -> - Loading.message = "Hold on a moment, we're logging you in" + Loading.message = t 'logging_in' $http.post("/user/spree_user/sign_in", {spree_user: $scope.spree_user}).success (data)-> if Redirections.after_login $window.location.href = $window.location.origin + Redirections.after_login diff --git a/app/assets/javascripts/darkswarm/controllers/checkout/payment_controller.js.coffee b/app/assets/javascripts/darkswarm/controllers/checkout/payment_controller.js.coffee index 374f0b35d5..924df3c447 100644 --- a/app/assets/javascripts/darkswarm/controllers/checkout/payment_controller.js.coffee +++ b/app/assets/javascripts/darkswarm/controllers/checkout/payment_controller.js.coffee @@ -3,18 +3,18 @@ Darkswarm.controller "PaymentCtrl", ($scope, $timeout) -> $scope.name = "payment" $scope.months = [ - {key: "January", value: "1"}, - {key: "February", value: "2"}, - {key: "March", value: "3"}, - {key: "April", value: "4"}, - {key: "May", value: "5"}, - {key: "June", value: "6"}, - {key: "July", value: "7"}, - {key: "August", value: "8"}, - {key: "September", value: "9"}, - {key: "October", value: "10"}, - {key: "November", value: "11"}, - {key: "December", value: "12"}, + {key: t("january"), value: "1"}, + {key: t("february"), value: "2"}, + {key: t("march"), value: "3"}, + {key: t("april"), value: "4"}, + {key: t("may"), value: "5"}, + {key: t("june"), value: "6"}, + {key: t("july"), value: "7"}, + {key: t("august"), value: "8"}, + {key: t("september"), value: "9"}, + {key: t("october"), value: "10"}, + {key: t("november"), value: "11"}, + {key: t("december"), value: "12"}, ] $scope.years = [moment().year()..(moment().year()+15)] diff --git a/app/assets/javascripts/darkswarm/controllers/enterprises_controller.js.coffee b/app/assets/javascripts/darkswarm/controllers/enterprises_controller.js.coffee index 1e43c17465..db995170dd 100644 --- a/app/assets/javascripts/darkswarm/controllers/enterprises_controller.js.coffee +++ b/app/assets/javascripts/darkswarm/controllers/enterprises_controller.js.coffee @@ -1,18 +1,67 @@ -Darkswarm.controller "EnterprisesCtrl", ($scope, Enterprises, Search, $document, $rootScope, HashNavigation, FilterSelectorsService, EnterpriseModal) -> +Darkswarm.controller "EnterprisesCtrl", ($scope, $rootScope, $timeout, Enterprises, Search, $document, HashNavigation, FilterSelectorsService, EnterpriseModal, enterpriseMatchesNameQueryFilter, distanceWithinKmFilter) -> $scope.Enterprises = Enterprises - $scope.totalActive = FilterSelectorsService.totalActive - $scope.clearAll = FilterSelectorsService.clearAll - $scope.filterText = FilterSelectorsService.filterText - $scope.FilterSelectorsService = FilterSelectorsService + $scope.producers_to_filter = Enterprises.producers + $scope.filterSelectors = FilterSelectorsService.createSelectors() $scope.query = Search.search() $scope.openModal = EnterpriseModal.open $scope.activeTaxons = [] $scope.show_profiles = false $scope.filtersActive = false + $scope.distanceMatchesShown = false + $scope.$watch "query", (query)-> + Enterprises.flagMatching query Search.search query + $rootScope.$broadcast 'enterprisesChanged' + $scope.distanceMatchesShown = false + + $timeout -> + Enterprises.calculateDistance query, $scope.firstNameMatch() + $rootScope.$broadcast 'enterprisesChanged' + + + $rootScope.$on "enterprisesChanged", -> + $scope.filterEnterprises() + $scope.updateVisibleMatches() + + + # When filter settings change, this could change which name match is at the top, or even + # result in no matches. This affects the reference point that the distance matches are + # calculated from, so we need to recalculate distances. + $scope.$watch '[activeTaxons, shippingTypes, show_profiles]', -> + $timeout -> + Enterprises.calculateDistance $scope.query, $scope.firstNameMatch() + $rootScope.$broadcast 'enterprisesChanged' + , true + $rootScope.$on "$locationChangeSuccess", (newRoute, oldRoute) -> if HashNavigation.active "hubs" $document.scrollTo $("#hubs"), 100, 200 + + + $scope.filterEnterprises = -> + es = Enterprises.hubs + $scope.nameMatches = enterpriseMatchesNameQueryFilter(es, true) + $scope.distanceMatches = enterpriseMatchesNameQueryFilter(es, false) + $scope.distanceMatches = distanceWithinKmFilter($scope.distanceMatches, 50) + + + $scope.updateVisibleMatches = -> + $scope.visibleMatches = if $scope.nameMatches.length == 0 || $scope.distanceMatchesShown + $scope.nameMatches.concat $scope.distanceMatches + else + $scope.nameMatches + + + $scope.showDistanceMatches = -> + $scope.distanceMatchesShown = true + $scope.updateVisibleMatches() + + + $scope.firstNameMatch = -> + if $scope.nameMatchesFiltered? + $scope.nameMatchesFiltered[0] + else + undefined diff --git a/app/assets/javascripts/darkswarm/controllers/group_enterprises_controller.js.coffee b/app/assets/javascripts/darkswarm/controllers/group_enterprises_controller.js.coffee index ea652b8105..13c1017386 100644 --- a/app/assets/javascripts/darkswarm/controllers/group_enterprises_controller.js.coffee +++ b/app/assets/javascripts/darkswarm/controllers/group_enterprises_controller.js.coffee @@ -1,8 +1,5 @@ Darkswarm.controller "GroupEnterprisesCtrl", ($scope, Search, FilterSelectorsService, EnterpriseModal) -> - $scope.totalActive = FilterSelectorsService.totalActive - $scope.clearAll = FilterSelectorsService.clearAll - $scope.filterText = FilterSelectorsService.filterText - $scope.FilterSelectorsService = FilterSelectorsService + $scope.filterSelectors = FilterSelectorsService.createSelectors() $scope.query = Search.search() $scope.openModal = EnterpriseModal.open $scope.activeTaxons = [] diff --git a/app/assets/javascripts/darkswarm/controllers/group_page_controller.js.coffee b/app/assets/javascripts/darkswarm/controllers/group_page_controller.js.coffee index b30d409b97..ed52f46bb7 100644 --- a/app/assets/javascripts/darkswarm/controllers/group_page_controller.js.coffee +++ b/app/assets/javascripts/darkswarm/controllers/group_page_controller.js.coffee @@ -1,14 +1,22 @@ -Darkswarm.controller "GroupPageCtrl", ($scope, group_enterprises, Enterprises, MapConfiguration, OfnMap) -> +Darkswarm.controller "GroupPageCtrl", ($scope, group_enterprises, Enterprises, MapConfiguration, OfnMap, visibleFilter) -> $scope.Enterprises = Enterprises - group_enterprises_ids = group_enterprises.map (enterprise) => - enterprise.id - is_in_group = (enterprise) -> - group_enterprises_ids.indexOf(enterprise.id) != -1 + all_enterprises_by_id = Enterprises.enterprises_by_id - $scope.group_producers = Enterprises.producers.filter is_in_group - $scope.group_hubs = Enterprises.hubs.filter is_in_group + dereferenced_enterprises = group_enterprises.map (enterprise) => + all_enterprises_by_id[enterprise.id] + + visible_enterprises = visibleFilter dereferenced_enterprises + + # TODO: this is duplicate code with app/assets/javascripts/darkswarm/services/enterprises.js.coffee + # It would be better to load only the needed enterprises (group + related shops). + $scope.group_producers = visible_enterprises.filter (enterprise) -> + enterprise.category in ["producer_hub", "producer_shop", "producer"] + $scope.group_hubs = visible_enterprises.filter (enterprise) -> + enterprise.category in ["hub", "hub_profile", "producer_hub", "producer_shop"] + + $scope.producers_to_filter = $scope.group_producers $scope.map = angular.copy MapConfiguration.options - $scope.mapMarkers = OfnMap.enterprise_markers group_enterprises + $scope.mapMarkers = OfnMap.enterprise_markers visible_enterprises diff --git a/app/assets/javascripts/darkswarm/controllers/home_controller.js.coffee b/app/assets/javascripts/darkswarm/controllers/home_controller.js.coffee new file mode 100644 index 0000000000..928f9a4fa1 --- /dev/null +++ b/app/assets/javascripts/darkswarm/controllers/home_controller.js.coffee @@ -0,0 +1,5 @@ +Darkswarm.controller "HomeCtrl", ($scope) -> + $scope.brandStoryExpanded = false + + $scope.toggleBrandStory = -> + $scope.brandStoryExpanded = !$scope.brandStoryExpanded diff --git a/app/assets/javascripts/darkswarm/controllers/line_item_controller.js.coffee b/app/assets/javascripts/darkswarm/controllers/line_item_controller.js.coffee index ea62163868..864f25177a 100644 --- a/app/assets/javascripts/darkswarm/controllers/line_item_controller.js.coffee +++ b/app/assets/javascripts/darkswarm/controllers/line_item_controller.js.coffee @@ -1,4 +1,5 @@ Darkswarm.controller "LineItemCtrl", ($scope)-> - $scope.$watch "line_item.quantity", (newValue, oldValue)-> + $scope.$watch '[line_item.quantity, line_item.max_quantity]', (newValue, oldValue)-> if newValue != oldValue $scope.Cart.orderChanged() + , true \ No newline at end of file diff --git a/app/assets/javascripts/darkswarm/controllers/order_cycle_controller.js.coffee b/app/assets/javascripts/darkswarm/controllers/order_cycle_controller.js.coffee index 2f1394e90a..42d4c1c44d 100644 --- a/app/assets/javascripts/darkswarm/controllers/order_cycle_controller.js.coffee +++ b/app/assets/javascripts/darkswarm/controllers/order_cycle_controller.js.coffee @@ -1,6 +1,4 @@ -# TODO this SUCKS. Fix it - -Darkswarm.controller "OrderCycleCtrl", ($scope, OrderCycle, $timeout) -> +Darkswarm.controller "OrderCycleCtrl", ($scope, $timeout, OrderCycle) -> $scope.order_cycle = OrderCycle.order_cycle $scope.OrderCycle = OrderCycle @@ -9,11 +7,26 @@ Darkswarm.controller "OrderCycleCtrl", ($scope, OrderCycle, $timeout) -> # That takes an expression instead of a trigger, and binds to that $timeout => if !$scope.OrderCycle.selected() - $("#order_cycle_id").trigger("openTrigger") + $("#order_cycle_id").trigger("openTrigger") -Darkswarm.controller "OrderCycleChangeCtrl", ($scope, OrderCycle, Products, $timeout) -> +Darkswarm.controller "OrderCycleChangeCtrl", ($scope, $timeout, OrderCycle, Products, Variants, Cart) -> + # Track previous order cycle id for use with revertOrderCycle() + $scope.previous_order_cycle_id = OrderCycle.order_cycle.order_cycle_id + $scope.$watch 'order_cycle.order_cycle_id', (newValue, oldValue)-> + $scope.previous_order_cycle_id = oldValue + $scope.changeOrderCycle = -> - OrderCycle.push_order_cycle Products.update + OrderCycle.push_order_cycle $scope.orderCycleChanged $timeout -> - $("#order_cycle_id").trigger("closeTrigger") + $("#order_cycle_id").trigger("closeTrigger") + + $scope.revertOrderCycle = -> + $scope.order_cycle.order_cycle_id = $scope.previous_order_cycle_id + + $scope.orderCycleChanged = -> + # push_order_cycle clears the cart server-side. Here we call Cart.clear() to clear the + # client-side cart. + Variants.clear() + Cart.clear() + Products.update() diff --git a/app/assets/javascripts/darkswarm/controllers/products_controller.js.coffee b/app/assets/javascripts/darkswarm/controllers/products_controller.js.coffee index 8443301765..4f149db1e5 100644 --- a/app/assets/javascripts/darkswarm/controllers/products_controller.js.coffee +++ b/app/assets/javascripts/darkswarm/controllers/products_controller.js.coffee @@ -1,10 +1,8 @@ Darkswarm.controller "ProductsCtrl", ($scope, $rootScope, Products, OrderCycle, FilterSelectorsService, Cart, Taxons, Properties) -> $scope.Products = Products $scope.Cart = Cart - $scope.totalActive = FilterSelectorsService.totalActive - $scope.clearAll = FilterSelectorsService.clearAll - $scope.filterText = FilterSelectorsService.filterText - $scope.FilterSelectorsService = FilterSelectorsService + $scope.taxonSelectors = FilterSelectorsService.createSelectors() + $scope.propertySelectors = FilterSelectorsService.createSelectors() $scope.filtersActive = true $scope.limit = 3 $scope.order_cycle = OrderCycle.order_cycle @@ -33,4 +31,5 @@ Darkswarm.controller "ProductsCtrl", ($scope, $rootScope, Products, OrderCycle, $scope.clearAll = -> $scope.query = "" - FilterSelectorsService.clearAll() + $scope.taxonSelectors.clearAll() + $scope.propertySelectors.clearAll() diff --git a/app/assets/javascripts/darkswarm/darkswarm.js.coffee b/app/assets/javascripts/darkswarm/darkswarm.js.coffee index e8ea9dce3c..a062ead058 100644 --- a/app/assets/javascripts/darkswarm/darkswarm.js.coffee +++ b/app/assets/javascripts/darkswarm/darkswarm.js.coffee @@ -10,6 +10,7 @@ window.Darkswarm = angular.module("Darkswarm", ["ngResource", 'google-maps', 'duScroll', 'angularFileUpload', + 'angularSlideables' ]).config ($httpProvider, $tooltipProvider, $locationProvider, $anchorScrollProvider) -> $httpProvider.defaults.headers.post['X-CSRF-Token'] = $('meta[name="csrf-token"]').attr('content') $httpProvider.defaults.headers.put['X-CSRF-Token'] = $('meta[name="csrf-token"]').attr('content') diff --git a/app/assets/javascripts/darkswarm/directives/change_hub.js.coffee b/app/assets/javascripts/darkswarm/directives/change_hub.js.coffee new file mode 100644 index 0000000000..8629edfce2 --- /dev/null +++ b/app/assets/javascripts/darkswarm/directives/change_hub.js.coffee @@ -0,0 +1,16 @@ +Darkswarm.directive "ofnChangeHub", (CurrentHub, Cart) -> + # Compares scope.hub with CurrentHub. Will trigger an confirmation if they are different, + # and Cart isn't empty + restrict: "A" + scope: + hub: "=ofnChangeHub" + link: (scope, elm, attr)-> + cart_will_need_emptying = -> + CurrentHub.hub?.id and CurrentHub.hub.id isnt scope.hub.id and !Cart.empty() + + if cart_will_need_emptying() + elm.bind 'click', (ev)-> + if confirm t('confirm_hub_change') + Cart.clear() + else + ev.preventDefault() diff --git a/app/assets/javascripts/darkswarm/directives/change_order_cycle.js.coffee b/app/assets/javascripts/darkswarm/directives/change_order_cycle.js.coffee new file mode 100644 index 0000000000..b2478a6fad --- /dev/null +++ b/app/assets/javascripts/darkswarm/directives/change_order_cycle.js.coffee @@ -0,0 +1,22 @@ +Darkswarm.directive "ofnChangeOrderCycle", (OrderCycle, Cart, storage) -> + # Compares chosen order cycle with pre-set OrderCycle. Will trigger + # a confirmation if they are different, and Cart isn't empty + restrict: "A" + scope: true + link: (scope, elm, attr)-> + order_cycle_id = -> + parseInt elm.val() + + cart_needs_emptying = -> + OrderCycle.order_cycle?.order_cycle_id && OrderCycle.order_cycle.order_cycle_id != order_cycle_id() && !Cart.empty() + + elm.bind 'change', (ev)-> + if cart_needs_emptying() + if confirm t('confirm_oc_change') + Cart.clear() + scope.changeOrderCycle() + else + scope.$apply -> + scope.revertOrderCycle() + else + scope.changeOrderCycle() diff --git a/app/assets/javascripts/darkswarm/directives/empties_cart.js.coffee b/app/assets/javascripts/darkswarm/directives/empties_cart.js.coffee deleted file mode 100644 index 51e960867b..0000000000 --- a/app/assets/javascripts/darkswarm/directives/empties_cart.js.coffee +++ /dev/null @@ -1,13 +0,0 @@ -Darkswarm.directive "ofnEmptiesCart", (CurrentHub, Cart, Navigation, storage) -> - # Compares scope.hub with CurrentHub. Will trigger an confirmation if they are different, - # and Cart isn't empty - restrict: "A" - scope: - hub: "=ofnEmptiesCart" - link: (scope, elm, attr)-> - if CurrentHub.hub?.id and CurrentHub.hub.id isnt scope.hub.id and !Cart.empty() - elm.bind 'click', (ev)-> - ev.preventDefault() - if confirm "Are you sure? This will change your selected Hub and remove any items in your shopping cart." - storage.clearAll() # One day this will have to be moar GRANULAR - Navigation.go scope.hub.path diff --git a/app/assets/javascripts/darkswarm/directives/filter_selector.js.coffee b/app/assets/javascripts/darkswarm/directives/filter_selector.js.coffee index 95aee1b413..2090c481f7 100644 --- a/app/assets/javascripts/darkswarm/directives/filter_selector.js.coffee +++ b/app/assets/javascripts/darkswarm/directives/filter_selector.js.coffee @@ -1,9 +1,10 @@ -Darkswarm.directive "filterSelector", (FilterSelectorsService)-> +Darkswarm.directive "filterSelector", -> # Automatically builds activeSelectors for taxons # Lots of magic here restrict: 'E' replace: true scope: + selectorSet: '=' objects: "&" activeSelectors: "=?" allSelectors: "=?" # Optional @@ -22,17 +23,11 @@ Darkswarm.directive "filterSelector", (FilterSelectorsService)-> .map (selector)-> selector.object.id - # This can be called from a parent scope - # when data has been loaded, in order to pass - # selectors up - scope.$on 'loadFilterSelectors', -> - scope.allSelectors = scope.selectors() if attr.allSelectors? - - scope.$watchCollection "selectors()", (newValue, oldValue) -> - scope.allSelectors = scope.selectors() if attr.allSelectors? + scope.$watchCollection "objects()", (newValue, oldValue) -> + scope.allSelectors = scope.buildSelectors() # Build a list of selectors - scope.selectors = -> + scope.buildSelectors = -> # Generate a selector for each object. # NOTE: THESE ARE MEMOIZED to stop new selectors from being created constantly, otherwise function always returns non-identical results # This means the $digest cycle can never close and times out @@ -42,7 +37,7 @@ Darkswarm.directive "filterSelector", (FilterSelectorsService)-> if selector = selectors_by_id[id] selectors.push selector else - selector = selectors_by_id[id] = FilterSelectorsService.new + selector = selectors_by_id[id] = scope.selectorSet.new object: object selectors.push selector selectors diff --git a/app/assets/javascripts/darkswarm/directives/integer.js.coffee b/app/assets/javascripts/darkswarm/directives/integer.js.coffee new file mode 100644 index 0000000000..e162246122 --- /dev/null +++ b/app/assets/javascripts/darkswarm/directives/integer.js.coffee @@ -0,0 +1,5 @@ +Darkswarm.directive "integer", -> + restrict: 'A' + link: (scope, elem, attr) -> + elem.bind 'input', -> + elem.val Math.round(elem.val()) diff --git a/app/assets/javascripts/darkswarm/directives/map_search.js.coffee b/app/assets/javascripts/darkswarm/directives/map_search.js.coffee index 88456db2a3..f67407ffb3 100644 --- a/app/assets/javascripts/darkswarm/directives/map_search.js.coffee +++ b/app/assets/javascripts/darkswarm/directives/map_search.js.coffee @@ -3,7 +3,7 @@ Darkswarm.directive 'mapSearch', ($timeout)-> restrict: 'E' require: '^googleMap' replace: true - template: '' + template: '' link: (scope, elem, attrs, ctrl)-> $timeout => map = ctrl.getMap() diff --git a/app/assets/javascripts/darkswarm/directives/page_alert.js.coffee b/app/assets/javascripts/darkswarm/directives/page_alert.js.coffee new file mode 100644 index 0000000000..659bd227e9 --- /dev/null +++ b/app/assets/javascripts/darkswarm/directives/page_alert.js.coffee @@ -0,0 +1,14 @@ +Darkswarm.directive "ofnPageAlert", ($timeout) -> + restrict: 'A' + scope: true + link: (scope, elem, attrs) -> + container_elems = $(".off-canvas-wrap .inner-wrap, .off-canvas-wrap .inner-wrap .fixed, .page-alert") + + # Wait a moment after page load before showing the alert. Otherwise we often miss the + # start of the animation. + $timeout -> + container_elems.addClass("move-down") + , 1000 + + scope.close = -> + container_elems.removeClass("move-down") diff --git a/app/assets/javascripts/darkswarm/directives/registration_limit_modal.js.coffee b/app/assets/javascripts/darkswarm/directives/registration_limit_modal.js.coffee index 2b38b3d31f..fce9a7ee59 100644 --- a/app/assets/javascripts/darkswarm/directives/registration_limit_modal.js.coffee +++ b/app/assets/javascripts/darkswarm/directives/registration_limit_modal.js.coffee @@ -9,5 +9,5 @@ Darkswarm.directive "ofnRegistrationLimitModal", (Navigation, $modal, Loading) - scope.modalInstance.result.then scope.close, scope.close scope.close = -> - Loading.message = "Taking you back to the home page" + Loading.message = t 'going_back_to_home_page' Navigation.go "/" diff --git a/app/assets/javascripts/darkswarm/directives/scroll_after_load.js.coffee b/app/assets/javascripts/darkswarm/directives/scroll_after_load.js.coffee index 55c5a311da..e29dcbb8f8 100644 --- a/app/assets/javascripts/darkswarm/directives/scroll_after_load.js.coffee +++ b/app/assets/javascripts/darkswarm/directives/scroll_after_load.js.coffee @@ -2,10 +2,9 @@ Darkswarm.directive 'scrollAfterLoad', ($timeout, $location, $document)-> # Scroll to an element on page load restrict: "A" link: (scope, element, attr) -> - if scope.$last is true - $(window).load -> - $timeout -> - elem = $("##{$location.hash()}") - if elem.length > 0 - $document.scrollTo elem , 100, 200, (x)-> - x * (2 - x) + elem = element + $(window).load -> + $timeout -> + if elem? + $document.scrollTo elem, 100, 200, (x) -> + x * (2 - x) diff --git a/app/assets/javascripts/darkswarm/directives/shipping_type_selector.js.coffee b/app/assets/javascripts/darkswarm/directives/shipping_type_selector.js.coffee index 07656d84b7..15fda75820 100644 --- a/app/assets/javascripts/darkswarm/directives/shipping_type_selector.js.coffee +++ b/app/assets/javascripts/darkswarm/directives/shipping_type_selector.js.coffee @@ -1,4 +1,4 @@ -Darkswarm.directive "shippingTypeSelector", (FilterSelectorsService)-> +Darkswarm.directive "shippingTypeSelector", -> # Builds selector for shipping types restrict: 'E' replace: true @@ -8,10 +8,10 @@ Darkswarm.directive "shippingTypeSelector", (FilterSelectorsService)-> pickup: false delivery: false - scope.selectors = - delivery: FilterSelectorsService.new + scope.selectors = + delivery: scope.filterSelectors.new icon: "ofn-i_039-delivery" - pickup: FilterSelectorsService.new + pickup: scope.filterSelectors.new icon: "ofn-i_038-takeaway" scope.emit = -> diff --git a/app/assets/javascripts/darkswarm/directives/single_line_selectors.coffee b/app/assets/javascripts/darkswarm/directives/single_line_selectors.coffee index 93896a41f9..ea5d8568d2 100644 --- a/app/assets/javascripts/darkswarm/directives/single_line_selectors.coffee +++ b/app/assets/javascripts/darkswarm/directives/single_line_selectors.coffee @@ -2,6 +2,7 @@ Darkswarm.directive 'singleLineSelectors', ($timeout, $filter) -> restrict: 'E' templateUrl: "single_line_selectors.html" scope: + selectors: "=" objects: "&" activeSelectors: "=" selectorName: "@activeSelectors" diff --git a/app/assets/javascripts/darkswarm/filters/dates.js.coffee b/app/assets/javascripts/darkswarm/filters/dates.js.coffee index d223411e99..7b5dd861d5 100644 --- a/app/assets/javascripts/darkswarm/filters/dates.js.coffee +++ b/app/assets/javascripts/darkswarm/filters/dates.js.coffee @@ -5,6 +5,6 @@ Darkswarm.filter "date_in_words", -> Darkswarm.filter "sensible_timeframe", (date_in_wordsFilter)-> (date) -> if moment().add('days', 2) < moment(date) - "Orders open" + t 'orders_open' else - "Closing #{date_in_wordsFilter(date)}" + t('closing') + date_in_wordsFilter(date) diff --git a/app/assets/javascripts/darkswarm/filters/distance_within_km.js.coffee b/app/assets/javascripts/darkswarm/filters/distance_within_km.js.coffee new file mode 100644 index 0000000000..c6a37a2b40 --- /dev/null +++ b/app/assets/javascripts/darkswarm/filters/distance_within_km.js.coffee @@ -0,0 +1,5 @@ +Darkswarm.filter 'distanceWithinKm', -> + (enterprises, range) -> + enterprises ||= [] + enterprises.filter (enterprise) -> + enterprise.distance / 1000 <= range diff --git a/app/assets/javascripts/darkswarm/filters/enterpriseMatchesNameQuery.js.coffee b/app/assets/javascripts/darkswarm/filters/enterpriseMatchesNameQuery.js.coffee new file mode 100644 index 0000000000..6e786a9f62 --- /dev/null +++ b/app/assets/javascripts/darkswarm/filters/enterpriseMatchesNameQuery.js.coffee @@ -0,0 +1,4 @@ +Darkswarm.filter 'enterpriseMatchesNameQuery', -> + (enterprises, matches_name_query) -> + enterprises.filter (enterprise) -> + enterprise.matches_name_query == matches_name_query diff --git a/app/assets/javascripts/darkswarm/filters/translate.js.coffee b/app/assets/javascripts/darkswarm/filters/translate.js.coffee new file mode 100644 index 0000000000..55606da2cf --- /dev/null +++ b/app/assets/javascripts/darkswarm/filters/translate.js.coffee @@ -0,0 +1,7 @@ +Darkswarm.filter "translate", -> + (key, options) -> + t(key, options) + +Darkswarm.filter "t", -> + (key, options) -> + t(key, options) diff --git a/app/assets/javascripts/darkswarm/i18n.js.erb b/app/assets/javascripts/darkswarm/i18n.js.erb new file mode 100644 index 0000000000..ddae11908e --- /dev/null +++ b/app/assets/javascripts/darkswarm/i18n.js.erb @@ -0,0 +1,9 @@ +<%# Defines a global I18n object containing the language of the current locale %> +<% + # Invalidate this asset if locale changes. + Dir[Rails.root.join('config', 'locales', "#{I18n.default_locale}.yml").to_s].each do |f| + depend_on(f) + end +%> +<%- I18n.backend.send(:init_translations) unless I18n.backend.initialized? %> +window.I18n = <%= I18n.backend.send(:translations)[I18n.default_locale].with_indifferent_access.to_json.html_safe %> diff --git a/app/assets/javascripts/darkswarm/i18n.translate.js.coffee b/app/assets/javascripts/darkswarm/i18n.translate.js.coffee new file mode 100644 index 0000000000..c455b6d9e1 --- /dev/null +++ b/app/assets/javascripts/darkswarm/i18n.translate.js.coffee @@ -0,0 +1,12 @@ +# Declares the translation function t. +# You can use translate('login') or t('login') in Javascript. +window.translate = (key, options = {}) -> + unless 'I18n' of window + console.log 'The I18n object is undefined. Cannot translate text.' + return key + return key unless key of I18n + text = I18n[key] + for name, value of options + text = text.split("%{#{name}}").join(value) + text +window.t = window.translate diff --git a/app/assets/javascripts/darkswarm/mixins/fieldset_mixin.js.coffee b/app/assets/javascripts/darkswarm/mixins/fieldset_mixin.js.coffee index ee4058221c..e5aa1375c0 100644 --- a/app/assets/javascripts/darkswarm/mixins/fieldset_mixin.js.coffee +++ b/app/assets/javascripts/darkswarm/mixins/fieldset_mixin.js.coffee @@ -32,10 +32,10 @@ window.FieldsetMixin = ($scope)-> errors = for error, invalid of $scope.error(path) if invalid switch error - when "required" then "can't be blank" - when "number" then "must be number" - when "email" then "must be email address" + when "required" then t('error_required') + when "number" then t('error_number') + when "email" then t('error_email') #server_errors = $scope.Order.errors[path.replace('order.', '')] #errors.push server_errors if server_errors? - (errors.filter (error) -> error?).join ", " \ No newline at end of file + (errors.filter (error) -> error?).join ", " diff --git a/app/assets/javascripts/darkswarm/services/authentication_service.js.coffee b/app/assets/javascripts/darkswarm/services/authentication_service.js.coffee index c83022b579..10f75675f8 100644 --- a/app/assets/javascripts/darkswarm/services/authentication_service.js.coffee +++ b/app/assets/javascripts/darkswarm/services/authentication_service.js.coffee @@ -28,6 +28,6 @@ Darkswarm.factory "AuthenticationService", (Navigation, $modal, $location, Redir if location.pathname in ["/", "/checkout"] Navigation.navigate "/" else - Loading.message = "Taking you back to the home page" + Loading.message = t 'going_back_to_home_page' location.hash = "" location.pathname = "/" diff --git a/app/assets/javascripts/darkswarm/services/cart.js.coffee b/app/assets/javascripts/darkswarm/services/cart.js.coffee index bbb0b5fab4..f1c9421a15 100644 --- a/app/assets/javascripts/darkswarm/services/cart.js.coffee +++ b/app/assets/javascripts/darkswarm/services/cart.js.coffee @@ -1,9 +1,12 @@ -Darkswarm.factory 'Cart', (CurrentOrder, Variants, $timeout, $http)-> +Darkswarm.factory 'Cart', (CurrentOrder, Variants, $timeout, $http, storage)-> # Handles syncing of current cart/order state to server new class Cart dirty: false + update_running: false + update_enqueued: false order: CurrentOrder.order line_items: CurrentOrder.order?.line_items || [] + constructor: -> for line_item in @line_items line_item.variant.line_item = line_item @@ -12,15 +15,31 @@ Darkswarm.factory 'Cart', (CurrentOrder, Variants, $timeout, $http)-> orderChanged: => @unsaved() + + if !@update_running + @scheduleUpdate() + else + @update_enqueued = true + + scheduleUpdate: => if @promise $timeout.cancel(@promise) @promise = $timeout @update, 1000 update: => + @update_running = true $http.post('/orders/populate', @data()).success (data, status)=> @saved() + @update_running = false + @popQueue() if @update_enqueued + .error (response, status)=> - # TODO what shall we do here? + @scheduleRetry(status) + @update_running = false + + popQueue: => + @update_enqueued = false + @scheduleUpdate() data: => variants = {} @@ -30,6 +49,12 @@ Darkswarm.factory 'Cart', (CurrentOrder, Variants, $timeout, $http)-> max_quantity: li.max_quantity {variants: variants} + scheduleRetry: (status) => + console.log "Error updating cart: #{status}. Retrying in 3 seconds..." + $timeout => + console.log "Retrying cart update" + @orderChanged() + , 3000 saved: => @dirty = false @@ -38,7 +63,7 @@ Darkswarm.factory 'Cart', (CurrentOrder, Variants, $timeout, $http)-> unsaved: => @dirty = true $(window).bind "beforeunload", -> - "Your order hasn't been saved yet. Give us a few seconds to finish!" + t 'order_not_saved_yet' line_items_present: => @line_items.filter (li)-> @@ -63,6 +88,10 @@ Darkswarm.factory 'Cart', (CurrentOrder, Variants, $timeout, $http)-> exists = @line_items.some (li)-> li.variant == variant @create_line_item(variant) unless exists + clear: -> + @line_items = [] + storage.clearAll() # One day this will have to be moar GRANULAR + create_line_item: (variant)-> variant.extended_name = @extendedVariantName(variant) variant.line_item = diff --git a/app/assets/javascripts/darkswarm/services/checkout.js.coffee b/app/assets/javascripts/darkswarm/services/checkout.js.coffee index a12ed1ae45..652ecd02f9 100644 --- a/app/assets/javascripts/darkswarm/services/checkout.js.coffee +++ b/app/assets/javascripts/darkswarm/services/checkout.js.coffee @@ -6,7 +6,7 @@ Darkswarm.factory 'Checkout', (CurrentOrder, ShippingMethods, PaymentMethods, $h ship_address_same_as_billing: true submit: -> - Loading.message = "Submitting your order: please wait" + Loading.message = t 'submitting_order' $http.put('/checkout', {order: @preprocess()}).success (data, status)=> Navigation.go data.path .error (response, status)=> diff --git a/app/assets/javascripts/darkswarm/services/enterprise_registration_service.js.coffee b/app/assets/javascripts/darkswarm/services/enterprise_registration_service.js.coffee index 77b2204316..102da60f19 100644 --- a/app/assets/javascripts/darkswarm/services/enterprise_registration_service.js.coffee +++ b/app/assets/javascripts/darkswarm/services/enterprise_registration_service.js.coffee @@ -11,7 +11,7 @@ Darkswarm.factory "EnterpriseRegistrationService", ($http, RegistrationService, @enterprise[key] = value create: => - Loading.message = "Creating " + @enterprise.name + Loading.message = t('creating') + " " + @enterprise.name $http( method: "POST" url: "/api/enterprises" @@ -26,12 +26,15 @@ Darkswarm.factory "EnterpriseRegistrationService", ($http, RegistrationService, RegistrationService.select('about') ).error((data) => Loading.clear() - alert('Failed to create your enterprise.\nPlease ensure all fields are completely filled out.') + if data?.errors? + errors = ("#{k.capitalize()} #{v[0]}" for k, v of data.errors when v.length > 0) + alert t('failed_to_create_enterprise') + "\n" + errors.join('\n') + else + alert(t('failed_to_create_enterprise_unknown')) ) - # RegistrationService.select('about') update: (step) => - Loading.message = "Updating " + @enterprise.name + Loading.message = t('updating') + " " + @enterprise.name $http( method: "PUT" url: "/api/enterprises/#{@enterprise.id}" @@ -44,9 +47,8 @@ Darkswarm.factory "EnterpriseRegistrationService", ($http, RegistrationService, RegistrationService.select(step) ).error((data) -> Loading.clear() - alert('Failed to update your enterprise.\nPlease ensure all fields are completely filled out.') + alert(t('failed_to_update_enterprise_unknown')) ) - # RegistrationService.select(step) prepare: => enterprise = {} diff --git a/app/assets/javascripts/darkswarm/services/enterprises.js.coffee b/app/assets/javascripts/darkswarm/services/enterprises.js.coffee index 6040f1d150..2dddfbc7ef 100644 --- a/app/assets/javascripts/darkswarm/services/enterprises.js.coffee +++ b/app/assets/javascripts/darkswarm/services/enterprises.js.coffee @@ -1,4 +1,4 @@ -Darkswarm.factory 'Enterprises', (enterprises, CurrentHub, Taxons, Dereferencer, visibleFilter)-> +Darkswarm.factory 'Enterprises', (enterprises, CurrentHub, Taxons, Dereferencer, visibleFilter, Matcher, Geo, $rootScope)-> new class Enterprises enterprises_by_id: {} constructor: -> @@ -28,3 +28,36 @@ Darkswarm.factory 'Enterprises', (enterprises, CurrentHub, Taxons, Dereferencer, Dereferencer.dereference enterprise.taxons, Taxons.taxons_by_id Dereferencer.dereference enterprise.supplied_taxons, Taxons.taxons_by_id + flagMatching: (query) -> + for enterprise in @enterprises + enterprise.matches_name_query = if query? && query.length > 0 + Matcher.match([enterprise.name], query) + else + false + + calculateDistance: (query, firstMatching) -> + if query?.length > 0 + if firstMatching? + @setDistanceFrom firstMatching + else + @calculateDistanceGeo query + else + @resetDistance() + + calculateDistanceGeo: (query) -> + Geo.geocode query, (results, status) => + $rootScope.$apply => + if status == Geo.OK + #console.log "Geocoded #{query} -> #{results[0].geometry.location}." + @setDistanceFrom results[0].geometry.location + else + console.log "Geocoding failed for the following reason: #{status}" + @resetDistance() + + setDistanceFrom: (locatable) -> + for enterprise in @enterprises + enterprise.distance = Geo.distanceBetween enterprise, locatable + $rootScope.$broadcast 'enterprisesChanged' + + resetDistance: -> + enterprise.distance = null for enterprise in @enterprises diff --git a/app/assets/javascripts/darkswarm/services/filter_selectors.js.coffee b/app/assets/javascripts/darkswarm/services/filter_selectors.js.coffee index 47ed12806d..e94a098b3a 100644 --- a/app/assets/javascripts/darkswarm/services/filter_selectors.js.coffee +++ b/app/assets/javascripts/darkswarm/services/filter_selectors.js.coffee @@ -1,8 +1,11 @@ +# Returns a factory with the only function `createSelectors()`. +# That function creates objects managing a list of filter selectors. Darkswarm.factory "FilterSelectorsService", -> # This stores all filters so we can access in-use counts etc - # Accessed via activeSelector Directive - new class FilterSelectorsService - selectors: [] + class FilterSelectors + constructor: -> + @selectors = [] + new: (obj = {})-> obj.active = false @selectors.push obj @@ -16,13 +19,18 @@ Darkswarm.factory "FilterSelectorsService", -> filterText: (active)=> total = @totalActive() if total == 0 - if active then "Hide filters" else "Filter by" + if active then t('hide_filters') else t('filter_by') else if total == 1 - "1 filter applied" + t 'one_filter_applied' else - "#{@totalActive()} filters applied" + @totalActive() + t('x_filters_applied') clearAll: => for selector in @selectors selector.active = false selector.emit() + + # Creates instances of `FilterSelectors` + new class FilterSelectorsService + createSelectors: -> + new FilterSelectors diff --git a/app/assets/javascripts/darkswarm/services/geo.js.erb.coffee b/app/assets/javascripts/darkswarm/services/geo.js.erb.coffee new file mode 100644 index 0000000000..2f96722b08 --- /dev/null +++ b/app/assets/javascripts/darkswarm/services/geo.js.erb.coffee @@ -0,0 +1,23 @@ +Darkswarm.service "Geo", -> + new class Geo + OK: google.maps.GeocoderStatus.OK + + # Usage: + # Geo.geocode address, (results, status) -> + # if status == Geo.OK + # console.log results[0].geometry.location + # else + # console.log "Error: #{status}" + geocode: (address, callback) -> + geocoder = new google.maps.Geocoder() + geocoder.geocode {'address': address, 'region': "<%= Spree::Country.find_by_id(Spree::Config[:default_country_id]).iso %>"}, callback + + distanceBetween: (src, dst) -> + google.maps.geometry.spherical.computeDistanceBetween @toLatLng(src), @toLatLng(dst) + + # Wrap an object in a google.maps.LatLng if it has not been already + toLatLng: (locatable) -> + if locatable.lat? + locatable + else + new google.maps.LatLng locatable.latitude, locatable.longitude \ No newline at end of file diff --git a/app/assets/javascripts/darkswarm/services/map.js.coffee b/app/assets/javascripts/darkswarm/services/map.js.coffee index 7ff9f553f2..96768e8379 100644 --- a/app/assets/javascripts/darkswarm/services/map.js.coffee +++ b/app/assets/javascripts/darkswarm/services/map.js.coffee @@ -9,8 +9,8 @@ Darkswarm.factory "OfnMap", (Enterprises, EnterpriseModal, visibleFilter) -> # Adding methods to each enterprise extend: (enterprise) -> new class MapMarker - # We're whitelisting attributes because GMaps tries to crawl - # our data, and our data is recursive, so it breaks + # We cherry-pick attributes because GMaps tries to crawl + # our data, and our data is cyclic, so it breaks latitude: enterprise.latitude longitude: enterprise.longitude icon: enterprise.icon diff --git a/app/assets/javascripts/darkswarm/services/map_configuration.js.coffee b/app/assets/javascripts/darkswarm/services/map_configuration.js.coffee index 8f1d735357..9c5a375d8c 100644 --- a/app/assets/javascripts/darkswarm/services/map_configuration.js.coffee +++ b/app/assets/javascripts/darkswarm/services/map_configuration.js.coffee @@ -7,5 +7,5 @@ Darkswarm.factory "MapConfiguration", -> zoom: 12 additional_options: {} #mapTypeId: 'satellite' - styles: [{"featureType":"landscape","stylers":[{"saturation":-100},{"lightness":65},{"visibility":"on"}]},{"featureType":"poi","stylers":[{"saturation":-100},{"lightness":51},{"visibility":"simplified"}]},{"featureType":"road.highway","stylers":[{"saturation":-100},{"visibility":"simplified"}]},{"featureType":"road.arterial","stylers":[{"saturation":-100},{"lightness":30},{"visibility":"on"}]},{"featureType":"road.local","stylers":[{"saturation":-100},{"lightness":40},{"visibility":"on"}]},{"featureType":"transit","stylers":[{"saturation":-100},{"visibility":"simplified"}]},{"featureType":"administrative.province","stylers":[{"visibility":"off"}]},{"featureType":"water","elementType":"labels","stylers":[{"visibility":"on"},{"lightness":-25},{"saturation":-100}]},{"featureType":"water","elementType":"geometry","stylers":[{"hue":"#ffff00"},{"lightness":-25},{"saturation":-97}]}] + styles: [{"featureType":"landscape","stylers":[{"saturation":-100},{"lightness":65},{"visibility":"on"}]},{"featureType":"poi","stylers":[{"saturation":-100},{"lightness":51},{"visibility":"simplified"}]},{"featureType":"road.highway","stylers":[{"saturation":-100},{"visibility":"simplified"}]},{"featureType":"road.arterial","stylers":[{"saturation":-100},{"lightness":30},{"visibility":"on"}]},{"featureType":"road.local","stylers":[{"saturation":-100},{"lightness":40},{"visibility":"on"}]},{"featureType":"transit","stylers":[{"saturation":-100},{"visibility":"simplified"}]},{"featureType":"administrative.province","stylers":[{"visibility":"off"}]},{"featureType":"water","elementType":"labels","stylers":[{"visibility":"on"},{"lightness":-25},{"saturation":-100}]},{"featureType":"water","elementType":"geometry","stylers":[{"hue":"#ffff00"},{"lightness":-25},{"saturation":-97}]},{"featureType":"road","elementType": "labels.icon","stylers":[{"visibility":"off"}]}] diff --git a/app/assets/javascripts/darkswarm/services/products.js.coffee b/app/assets/javascripts/darkswarm/services/products.js.coffee index a07ae1f466..475ed5be0b 100644 --- a/app/assets/javascripts/darkswarm/services/products.js.coffee +++ b/app/assets/javascripts/darkswarm/services/products.js.coffee @@ -10,12 +10,26 @@ Darkswarm.factory 'Products', ($resource, Enterprises, Dereferencer, Taxons, Pro update: => @loading = true - @products = $resource("/shop/products").query (products)=> - @extend() && @dereference() + @products = [] + $resource("/shop/products").query (products)=> + @products = products + + @extend() + @dereference() @registerVariants() @registerVariantsWithCart() @loading = false - @ + + extend: -> + for product in @products + if product.variants?.length > 0 + prices = (v.price for v in product.variants) + product.price = Math.min.apply(null, prices) + product.hasVariants = product.variants?.length > 0 + + product.primaryImage = product.images[0]?.small_url if product.images + product.primaryImageOrMissing = product.primaryImage || "/assets/noimage/small.png" + product.largeImage = product.images[0]?.large_url if product.images dereference: -> for product in @products @@ -32,8 +46,9 @@ Darkswarm.factory 'Products', ($resource, Enterprises, Dereferencer, Taxons, Pro if product.variants product.variants = (Variants.register variant for variant in product.variants) variant.product = product for variant in product.variants - product.master.product = product - product.master = Variants.register product.master if product.master + if product.master + product.master.product = product + product.master = Variants.register product.master registerVariantsWithCart: -> for product in @products @@ -41,14 +56,3 @@ Darkswarm.factory 'Products', ($resource, Enterprises, Dereferencer, Taxons, Pro for variant in product.variants Cart.register_variant variant Cart.register_variant product.master if product.master - - extend: -> - for product in @products - if product.variants?.length > 0 - prices = (v.price for v in product.variants) - product.price = Math.min.apply(null, prices) - product.hasVariants = product.variants?.length > 0 - - product.primaryImage = product.images[0]?.small_url if product.images - product.primaryImageOrMissing = product.primaryImage || "/assets/noimage/small.png" - product.largeImage = product.images[0]?.large_url if product.images diff --git a/app/assets/javascripts/darkswarm/services/registration_service.js.coffee b/app/assets/javascripts/darkswarm/services/registration_service.js.coffee index 530d118025..bb45a7ad79 100644 --- a/app/assets/javascripts/darkswarm/services/registration_service.js.coffee +++ b/app/assets/javascripts/darkswarm/services/registration_service.js.coffee @@ -19,5 +19,5 @@ angular.module('Darkswarm').factory "RegistrationService", (Navigation, $modal, @current_step close: -> - Loading.message = "Taking you back to the home page" + Loading.message = t 'going_back_to_home_page' Navigation.go "/" diff --git a/app/assets/javascripts/darkswarm/services/variants.js.coffee b/app/assets/javascripts/darkswarm/services/variants.js.coffee index 6562bd9e0b..3048049ad5 100644 --- a/app/assets/javascripts/darkswarm/services/variants.js.coffee +++ b/app/assets/javascripts/darkswarm/services/variants.js.coffee @@ -1,6 +1,10 @@ Darkswarm.factory 'Variants', -> new class Variants variants: {} + + clear: -> + @variants = {} + register: (variant)-> @variants[variant.id] ||= @extend variant diff --git a/app/assets/javascripts/darkswarm/util.js.coffee b/app/assets/javascripts/darkswarm/util.js.coffee new file mode 100644 index 0000000000..e6485284b3 --- /dev/null +++ b/app/assets/javascripts/darkswarm/util.js.coffee @@ -0,0 +1,2 @@ +String.prototype.capitalize = -> + this.charAt(0).toUpperCase() + this.slice(1) diff --git a/app/assets/javascripts/distributors.js.coffee b/app/assets/javascripts/distributors.js.coffee deleted file mode 100644 index 761567942f..0000000000 --- a/app/assets/javascripts/distributors.js.coffee +++ /dev/null @@ -1,3 +0,0 @@ -# Place all the behaviors and hooks related to the matching controller here. -# All this logic will automatically be available in application.js. -# You can use CoffeeScript in this file: http://jashkenas.github.com/coffee-script/ diff --git a/app/assets/javascripts/shared/angular-slideables.js b/app/assets/javascripts/shared/angular-slideables.js new file mode 100644 index 0000000000..0279c0f4f2 --- /dev/null +++ b/app/assets/javascripts/shared/angular-slideables.js @@ -0,0 +1,55 @@ +/* + * Angular Slideables - A "pure" Angular implementation of jQuery-style slideToggle() + * Source: https://github.com/EricWVGG/AngularSlideables + * By Eric Jacobsen, used under MIT licence + */ + +angular.module('angularSlideables', []) +.directive('slideable', function () { + return { + restrict:'C', + compile: function (element, attr) { + // wrap tag + var contents = element.html(); + element.html('
' + contents + '
'); + + return function postLink(scope, element, attrs) { + // default properties + attrs.duration = (!attrs.duration) ? '1s' : attrs.duration; + attrs.easing = (!attrs.easing) ? 'ease-in-out' : attrs.easing; + element.css({ + 'overflow': 'hidden', + 'height': '0px', + 'transitionProperty': 'height', + 'transitionDuration': attrs.duration, + 'transitionTimingFunction': attrs.easing + }); + }; + } + }; +}) +.directive('slideToggle', function() { + return { + restrict: 'A', + link: function(scope, element, attrs) { + var target, content; + + attrs.expanded = false; + + element.bind('click', function() { + if (!target) target = document.querySelector(attrs.slideToggle); + if (!content) content = target.querySelector('.slideable_content'); + + if(!attrs.expanded) { + content.style.border = '1px solid rgba(0,0,0,0)'; + var y = content.clientHeight; + content.style.border = 0; + target.style.height = y + 'px'; + } else { + target.style.height = '0px'; + } + attrs.expanded = !attrs.expanded; + }); + } + } +}); diff --git a/app/assets/javascripts/shared/ng-tags-input.min.js b/app/assets/javascripts/shared/ng-tags-input.min.js new file mode 100644 index 0000000000..9a1acd6e0d --- /dev/null +++ b/app/assets/javascripts/shared/ng-tags-input.min.js @@ -0,0 +1 @@ +/*! ngTagsInput v2.3.0 License: MIT */!function(){"use strict";var a={backspace:8,tab:9,enter:13,escape:27,space:32,up:38,down:40,left:37,right:39,"delete":46,comma:188},b=9007199254740991,c=["text","email","url"],d=angular.module("ngTagsInput",[]);d.directive("tagsInput",["$timeout","$document","$window","tagsInputConfig","tiUtil",function(d,e,f,g,h){function i(a,b,c,d){var e,f,g,i={};return e=function(b){return h.safeToString(b[a.displayProperty])},f=function(b,c){b[a.displayProperty]=c},g=function(b){var d=e(b);return d&&d.length>=a.minLength&&d.length<=a.maxLength&&a.allowedTagsPattern.test(d)&&!h.findInObjectArray(i.items,b,a.keyProperty||a.displayProperty)&&c({$tag:b})},i.items=[],i.addText=function(a){var b={};return f(b,a),i.add(b)},i.add=function(c){var d=e(c);return a.replaceSpacesWithDashes&&(d=h.replaceSpacesWithDashes(d)),f(c,d),g(c)?(i.items.push(c),b.trigger("tag-added",{$tag:c})):d&&b.trigger("invalid-tag",{$tag:c}),c},i.remove=function(a){var c=i.items[a];return d({$tag:c})?(i.items.splice(a,1),i.clearSelection(),b.trigger("tag-removed",{$tag:c}),c):void 0},i.select=function(a){0>a?a=i.items.length-1:a>=i.items.length&&(a=0),i.index=a,i.selected=i.items[a]},i.selectPrior=function(){i.select(--i.index)},i.selectNext=function(){i.select(++i.index)},i.removeSelected=function(){return i.remove(i.index)},i.clearSelection=function(){i.selected=null,i.index=-1},i.clearSelection(),i}function j(a){return-1!==c.indexOf(a)}return{restrict:"E",require:"ngModel",scope:{tags:"=ngModel",onTagAdding:"&",onTagAdded:"&",onInvalidTag:"&",onTagRemoving:"&",onTagRemoved:"&"},replace:!1,transclude:!0,templateUrl:"ngTagsInput/tags-input.html",controller:["$scope","$attrs","$element",function(a,c,d){a.events=h.simplePubSub(),g.load("tagsInput",a,c,{template:[String,"ngTagsInput/tag-item.html"],type:[String,"text",j],placeholder:[String,"Add a tag"],tabindex:[Number,null],removeTagSymbol:[String,String.fromCharCode(215)],replaceSpacesWithDashes:[Boolean,!0],minLength:[Number,3],maxLength:[Number,b],addOnEnter:[Boolean,!0],addOnSpace:[Boolean,!1],addOnComma:[Boolean,!0],addOnBlur:[Boolean,!0],addOnPaste:[Boolean,!1],pasteSplitPattern:[RegExp,/,/],allowedTagsPattern:[RegExp,/.+/],enableEditingLastTag:[Boolean,!1],minTags:[Number,0],maxTags:[Number,b],displayProperty:[String,"text"],keyProperty:[String,""],allowLeftoverText:[Boolean,!1],addFromAutocompleteOnly:[Boolean,!1],spellcheck:[Boolean,!0]}),a.tagList=new i(a.options,a.events,h.handleUndefinedResult(a.onTagAdding,!0),h.handleUndefinedResult(a.onTagRemoving,!0)),this.registerAutocomplete=function(){var b=d.find("input");return{addTag:function(b){return a.tagList.add(b)},focusInput:function(){b[0].focus()},getTags:function(){return a.tags},getCurrentTagText:function(){return a.newTag.text},getOptions:function(){return a.options},on:function(b,c){return a.events.on(b,c),this}}},this.registerTagItem=function(){return{getOptions:function(){return a.options},removeTag:function(b){a.disabled||a.tagList.remove(b)}}}}],link:function(b,c,g,i){var j,k=[a.enter,a.comma,a.space,a.backspace,a["delete"],a.left,a.right],l=b.tagList,m=b.events,n=b.options,o=c.find("input"),p=["minTags","maxTags","allowLeftoverText"];j=function(){i.$setValidity("maxTags",b.tags.length<=n.maxTags),i.$setValidity("minTags",b.tags.length>=n.minTags),i.$setValidity("leftoverText",b.hasFocus||n.allowLeftoverText?!0:!b.newTag.text)},i.$isEmpty=function(a){return!a||!a.length},b.newTag={text:"",invalid:null,setText:function(a){this.text=a,m.trigger("input-change",a)}},b.track=function(a){return a[n.keyProperty||n.displayProperty]},b.$watch("tags",function(a){b.tags=h.makeObjectArray(a,n.displayProperty),l.items=b.tags}),b.$watch("tags.length",function(){j()}),g.$observe("disabled",function(a){b.disabled=a}),b.eventHandlers={input:{change:function(a){m.trigger("input-change",a)},keydown:function(a){m.trigger("input-keydown",a)},focus:function(){b.hasFocus||(b.hasFocus=!0,m.trigger("input-focus"))},blur:function(){d(function(){var a=e.prop("activeElement"),d=a===o[0],f=c[0].contains(a);(d||!f)&&(b.hasFocus=!1,m.trigger("input-blur"))})},paste:function(a){a.getTextData=function(){var b=a.clipboardData||a.originalEvent&&a.originalEvent.clipboardData;return b?b.getData("text/plain"):f.clipboardData.getData("Text")},m.trigger("input-paste",a)}},host:{click:function(){b.disabled||o[0].focus()}}},m.on("tag-added",b.onTagAdded).on("invalid-tag",b.onInvalidTag).on("tag-removed",b.onTagRemoved).on("tag-added",function(){b.newTag.setText("")}).on("tag-added tag-removed",function(){i.$setViewValue(b.tags)}).on("invalid-tag",function(){b.newTag.invalid=!0}).on("option-change",function(a){-1!==p.indexOf(a.name)&&j()}).on("input-change",function(){l.clearSelection(),b.newTag.invalid=null}).on("input-focus",function(){c.triggerHandler("focus"),i.$setValidity("leftoverText",!0)}).on("input-blur",function(){n.addOnBlur&&!n.addFromAutocompleteOnly&&l.addText(b.newTag.text),c.triggerHandler("blur"),j()}).on("input-keydown",function(c){var d,e,f,g,h=c.keyCode,i=c.shiftKey||c.altKey||c.ctrlKey||c.metaKey,j={};if(!i&&-1!==k.indexOf(h)){if(j[a.enter]=n.addOnEnter,j[a.comma]=n.addOnComma,j[a.space]=n.addOnSpace,d=!n.addFromAutocompleteOnly&&j[h],e=(h===a.backspace||h===a["delete"])&&l.selected,g=h===a.backspace&&0===b.newTag.text.length&&n.enableEditingLastTag,f=(h===a.backspace||h===a.left||h===a.right)&&0===b.newTag.text.length&&!n.enableEditingLastTag,d)l.addText(b.newTag.text);else if(g){var m;l.selectPrior(),m=l.removeSelected(),m&&b.newTag.setText(m[n.displayProperty])}else e?l.removeSelected():f&&(h===a.left||h===a.backspace?l.selectPrior():h===a.right&&l.selectNext());(d||f||e||g)&&c.preventDefault()}}).on("input-paste",function(a){if(n.addOnPaste){var b=a.getTextData(),c=b.split(n.pasteSplitPattern);c.length>1&&(c.forEach(function(a){l.addText(a)}),a.preventDefault())}})}}}]),d.directive("tiTagItem",["tiUtil",function(a){return{restrict:"E",require:"^tagsInput",template:'',scope:{data:"="},link:function(b,c,d,e){var f=e.registerTagItem(),g=f.getOptions();b.$$template=g.template,b.$$removeTagSymbol=g.removeTagSymbol,b.$getDisplayText=function(){return a.safeToString(b.data[g.displayProperty])},b.$removeTag=function(){f.removeTag(b.$index)},b.$watch("$parent.$index",function(a){b.$index=a})}}}]),d.directive("autoComplete",["$document","$timeout","$sce","$q","tagsInputConfig","tiUtil",function(b,c,d,e,f,g){function h(a,b,c){var d,f,h,i={};return h=function(){return b.tagsInput.keyProperty||b.tagsInput.displayProperty},d=function(a,c){return a.filter(function(a){return!g.findInObjectArray(c,a,h(),function(a,c){return b.tagsInput.replaceSpacesWithDashes&&(a=g.replaceSpacesWithDashes(a),c=g.replaceSpacesWithDashes(c)),g.defaultComparer(a,c)})})},i.reset=function(){f=null,i.items=[],i.visible=!1,i.index=-1,i.selected=null,i.query=null},i.show=function(){b.selectFirstMatch?i.select(0):i.selected=null,i.visible=!0},i.load=g.debounce(function(c,j){i.query=c;var k=e.when(a({$query:c}));f=k,k.then(function(a){k===f&&(a=g.makeObjectArray(a.data||a,h()),a=d(a,j),i.items=a.slice(0,b.maxResultsToShow),i.items.length>0?i.show():i.reset())})},b.debounceDelay),i.selectNext=function(){i.select(++i.index)},i.selectPrior=function(){i.select(--i.index)},i.select=function(a){0>a?a=i.items.length-1:a>=i.items.length&&(a=0),i.index=a,i.selected=i.items[a],c.trigger("suggestion-selected",a)},i.reset(),i}function i(a,b){var c=a.find("li").eq(b),d=c.parent(),e=c.prop("offsetTop"),f=c.prop("offsetHeight"),g=d.prop("clientHeight"),h=d.prop("scrollTop");h>e?d.prop("scrollTop",e):e+f>g+h&&d.prop("scrollTop",e+f-g)}return{restrict:"E",require:"^tagsInput",scope:{source:"&"},templateUrl:"ngTagsInput/auto-complete.html",controller:["$scope","$element","$attrs",function(a,b,c){a.events=g.simplePubSub(),f.load("autoComplete",a,c,{template:[String,"ngTagsInput/auto-complete-match.html"],debounceDelay:[Number,100],minLength:[Number,3],highlightMatchedText:[Boolean,!0],maxResultsToShow:[Number,10],loadOnDownArrow:[Boolean,!1],loadOnEmpty:[Boolean,!1],loadOnFocus:[Boolean,!1],selectFirstMatch:[Boolean,!0],displayProperty:[String,""]}),a.suggestionList=new h(a.source,a.options,a.events),this.registerAutocompleteMatch=function(){return{getOptions:function(){return a.options},getQuery:function(){return a.suggestionList.query}}}}],link:function(b,c,d,e){var f,g=[a.enter,a.tab,a.escape,a.up,a.down],h=b.suggestionList,j=e.registerAutocomplete(),k=b.options,l=b.events;k.tagsInput=j.getOptions(),f=function(a){return a&&a.length>=k.minLength||!a&&k.loadOnEmpty},b.addSuggestionByIndex=function(a){h.select(a),b.addSuggestion()},b.addSuggestion=function(){var a=!1;return h.selected&&(j.addTag(angular.copy(h.selected)),h.reset(),j.focusInput(),a=!0),a},b.track=function(a){return a[k.tagsInput.keyProperty||k.tagsInput.displayProperty]},j.on("tag-added invalid-tag input-blur",function(){h.reset()}).on("input-change",function(a){f(a)?h.load(a,j.getTags()):h.reset()}).on("input-focus",function(){var a=j.getCurrentTagText();k.loadOnFocus&&f(a)&&h.load(a,j.getTags())}).on("input-keydown",function(c){var d=c.keyCode,e=!1;if(-1!==g.indexOf(d))return h.visible?d===a.down?(h.selectNext(),e=!0):d===a.up?(h.selectPrior(),e=!0):d===a.escape?(h.reset(),e=!0):(d===a.enter||d===a.tab)&&(e=b.addSuggestion()):d===a.down&&b.options.loadOnDownArrow&&(h.load(j.getCurrentTagText(),j.getTags()),e=!0),e?(c.preventDefault(),c.stopImmediatePropagation(),!1):void 0}),l.on("suggestion-selected",function(a){i(c,a)})}}}]),d.directive("tiAutocompleteMatch",["$sce","tiUtil",function(a,b){return{restrict:"E",require:"^autoComplete",template:'',scope:{data:"="},link:function(c,d,e,f){var g=f.registerAutocompleteMatch(),h=g.getOptions();c.$$template=h.template,c.$index=c.$parent.$index,c.$highlight=function(c){return h.highlightMatchedText&&(c=b.safeHighlight(c,g.getQuery())),a.trustAsHtml(c)},c.$getDisplayText=function(){return b.safeToString(c.data[h.displayProperty||h.tagsInput.displayProperty])}}}}]),d.directive("tiTranscludeAppend",function(){return function(a,b,c,d,e){e(function(a){b.append(a)})}}),d.directive("tiAutosize",["tagsInputConfig",function(a){return{restrict:"A",require:"ngModel",link:function(b,c,d,e){var f,g,h=a.getTextAutosizeThreshold();f=angular.element(''),f.css("display","none").css("visibility","hidden").css("width","auto").css("white-space","pre"),c.parent().append(f),g=function(a){var b,e=a;return angular.isString(e)&&0===e.length&&(e=d.placeholder),e&&(f.text(e),f.css("display",""),b=f.prop("offsetWidth"),f.css("display","none")),c.css("width",b?b+h+"px":""),a},e.$parsers.unshift(g),e.$formatters.unshift(g),d.$observe("placeholder",function(a){e.$modelValue||g(a)})}}}]),d.directive("tiBindAttrs",function(){return function(a,b,c){a.$watch(c.tiBindAttrs,function(a){angular.forEach(a,function(a,b){c.$set(b,a)})},!0)}}),d.provider("tagsInputConfig",function(){var a={},b={},c=3;this.setDefaults=function(b,c){return a[b]=c,this},this.setActiveInterpolation=function(a,c){return b[a]=c,this},this.setTextAutosizeThreshold=function(a){return c=a,this},this.$get=["$interpolate",function(d){var e={};return e[String]=function(a){return a},e[Number]=function(a){return parseInt(a,10)},e[Boolean]=function(a){return"true"===a.toLowerCase()},e[RegExp]=function(a){return new RegExp(a)},{load:function(c,f,g,h){var i=function(){return!0};f.options={},angular.forEach(h,function(h,j){var k,l,m,n,o,p;k=h[0],l=h[1],m=h[2]||i,n=e[k],o=function(){var b=a[c]&&a[c][j];return angular.isDefined(b)?b:l},p=function(a){f.options[j]=a&&m(a)?n(a):o()},b[c]&&b[c][j]?g.$observe(j,function(a){p(a),f.events.trigger("option-change",{name:j,newValue:a})}):p(g[j]&&d(g[j])(f.$parent))})},getTextAutosizeThreshold:function(){return c}}}]}),d.factory("tiUtil",["$timeout",function(a){var b={};return b.debounce=function(b,c){var d;return function(){var e=arguments;a.cancel(d),d=a(function(){b.apply(null,e)},c)}},b.makeObjectArray=function(a,b){return a=a||[],a.length>0&&!angular.isObject(a[0])&&a.forEach(function(c,d){a[d]={},a[d][b]=c}),a},b.findInObjectArray=function(a,c,d,e){var f=null;return e=e||b.defaultComparer,a.some(function(a){return e(a[d],c[d])?(f=a,!0):void 0}),f},b.defaultComparer=function(a,c){return b.safeToString(a).toLowerCase()===b.safeToString(c).toLowerCase()},b.safeHighlight=function(a,c){function d(a){return a.replace(/([.?*+^$[\]\\(){}|-])/g,"\\$1")}if(!c)return a;a=b.encodeHTML(a),c=b.encodeHTML(c);var e=new RegExp("&[^;]+;|"+d(c),"gi");return a.replace(e,function(a){return a.toLowerCase()===c.toLowerCase()?""+a+"":a})},b.safeToString=function(a){return angular.isUndefined(a)||null==a?"":a.toString().trim()},b.encodeHTML=function(a){return b.safeToString(a).replace(/&/g,"&").replace(//g,">")},b.handleUndefinedResult=function(a,b){return function(){var c=a.apply(null,arguments);return angular.isUndefined(c)?b:c}},b.replaceSpacesWithDashes=function(a){return b.safeToString(a).replace(/\s/g,"-")},b.simplePubSub=function(){var a={};return{on:function(b,c){return b.split(" ").forEach(function(b){a[b]||(a[b]=[]),a[b].push(c)}),this},trigger:function(c,d){var e=a[c]||[];return e.every(function(a){return b.handleUndefinedResult(a,!0)(d)}),this}}},b}]),d.run(["$templateCache",function(a){a.put("ngTagsInput/tags-input.html",'
'),a.put("ngTagsInput/tag-item.html",' '),a.put("ngTagsInput/auto-complete.html",'
'),a.put("ngTagsInput/auto-complete-match.html",'')}])}(); \ No newline at end of file diff --git a/app/assets/javascripts/templates/admin/links_dropdown.html.haml b/app/assets/javascripts/templates/admin/links_dropdown.html.haml new file mode 100644 index 0000000000..1f44f2418c --- /dev/null +++ b/app/assets/javascripts/templates/admin/links_dropdown.html.haml @@ -0,0 +1,10 @@ +.ofn-drop-down + %span + %i.icon-check + Actions + %i{ 'ng-class' => "expanded && 'icon-caret-up' || !expanded && 'icon-caret-down'" } + %div.menu{ 'ng-show' => "expanded", style: 'width: 200px' } + %a.menu_item{ 'ng-repeat' => "link in links", href: '{{link.url}}', target: "{{link.target || '_self'}}", data: { method: "{{ link.method || 'get' }}", confirm: "{{link.confirm}}" }, style: 'display: inline-block; width: 100%' } + %span{ :style => 'text-align: center; display: inline-block; width: 20%'} + %i{ ng: { class: "link.icon" } } + %span{ style: "display: inline-block; width: auto"} {{ link.name }} diff --git a/app/assets/javascripts/templates/admin/panel.html.haml b/app/assets/javascripts/templates/admin/panel.html.haml new file mode 100644 index 0000000000..be0c98109f --- /dev/null +++ b/app/assets/javascripts/templates/admin/panel.html.haml @@ -0,0 +1,2 @@ +%td{ colspan: "{{columnCount}}" } + .panel{ ng: { include: "template" } } diff --git a/app/assets/javascripts/templates/admin/panels/enterprise_package.html.haml b/app/assets/javascripts/templates/admin/panels/enterprise_package.html.haml new file mode 100644 index 0000000000..7f143d1071 --- /dev/null +++ b/app/assets/javascripts/templates/admin/panels/enterprise_package.html.haml @@ -0,0 +1,131 @@ +.row.enterprise_package_panel{ ng: { controller: 'indexPackagePanelCtrl' } } + .alpha.eight.columns + %div{ ng: { if: "!enterprise.is_primary_producer", switch: "enterprise.sells" } } + .info{ ng: { switch: { when: "none" } } } + %h3 Hub Profile + + %p + %strong COST: ALWAYS FREE + + %p People can find and contact you on the Open Food Network. Your enterprise will be visible on the map, and will be searchable in listings. + + %p Having a profile, and making connections within your local food system through the Open Food Network will always be free. + + .info{ ng: { switch: { when: "any" } } } + %h3 Hub Shop + + %p + %strong + %monthly-pricing-description{ joiner: "comma" } + + %p Your enterprise is the backbone of your local food system. You aggregate produce from other enterprises and can sell it through your shop on the Open Food Network. + + %p Hubs can take many forms, whether they be a food co-op, a buying group, a veggie-box program, or a local grocery store. + + %p If you also want to sell your own products, you will need to switch this enterprise to be a producer. + + .info{ ng: { switch: { default: true } } } + %h3 + Please Choose a Package + %i.icon-arrow-right + + %p + %strong Your enterprise will not be fully activated until a package is selected from the options on the left. + + %p + Click on an option to see more detailed information about each package, and hit the red SAVE button when you are done! + + + + %div{ ng: { if: "enterprise.is_primary_producer", switch: "enterprise.sells" } } + .info{ ng: { switch: { when: "none" } } } + %h3 Profile Only + + %p + %strong COST: ALWAYS FREE + + %p A profile makes you visible and contactable to others and is a way to share your story. + + %p If you prefer to focus on producing food, and want to leave the work of selling it to someone else, you won't require a shop on the Open Food Network. + + %p Add your products to Open Food Network, allowing hubs to stock your products in their stores. + + .info{ ng: { switch: { when: "own" } } } + %h3 Producer Shop + + %p + %strong + %monthly-pricing-description{ joiner: "comma" } + + %p Sell your products directly to customers through your very own Open Food Network shopfront. + + %p A Producer Shop is for your produce only, if you want to sell produce grown/produced off site, please select 'Producer Hub'. + + .info{ ng: { switch: { when: "any" } } } + %h3 Producer Hub + + %p + %strong + %monthly-pricing-description{ joiner: "comma" } + + %p Your enterprise is the backbone of your local food system. You can sell your own produce as well as produce aggregated from other enterprises through your shopfront on the Open Food Network. + + %p Producer Hubs can take many forms, whether they be a CSA, a veggie-box program, or a food co-op with a rooftop garden. + + %p The Open Food Network aims to support as many hub models as possible, so no matter your situation, we want to provide the tools you need to run your organisation or local food business. + + .info{ ng: { switch: { default: true } } } + %h3 + Please Choose a Package + %i.icon-arrow-right + + %p + %strong Your producer enterprise will not be fully activated until a package is selected from the options on the left. + + %p + Click on an option to see more detailed information about each package, and hit the red SAVE button when you are done! + + .omega.eight.columns{ ng: { switch: "enterprise.is_primary_producer" } } + %div{ ng: { switch: { when: "false" } } } + %a.button.selector.hub-profile{ ng: { click: "enterprise.owned && (enterprise.sells='none')", class: "{selected: enterprise.sells=='none', disabled: !enterprise.owned}" } } + .top + %h3 Profile Only + %p Get a listing + .bottom ALWAYS FREE + %a.button.selector.hub{ ng: { click: "enterprise.owned && (enterprise.sells='any')", class: "{selected: enterprise.sells=='any', disabled: !enterprise.owned}" } } + .top + %h3 Hub Shop + %p Sell produce from others + .bottom + %monthly-pricing-description{ joiner: "newline" } + + %div{ ng: { switch: { when: "true" } } } + %a.button.selector.producer-profile{ ng: { click: "enterprise.owned && (enterprise.sells='none')", class: "{selected: enterprise.sells=='none', disabled: !enterprise.owned}" } } + .top + %h3 Profile Only + %p Get a listing + .bottom ALWAYS FREE + %a.button.selector.producer-shop{ ng: { click: "enterprise.owned && (enterprise.sells='own')", class: "{selected: enterprise.sells=='own', disabled: !enterprise.owned}" } } + .top + %h3 Producer Shop + %p Sell your own produce + .bottom + %monthly-pricing-description{ joiner: "newline" } + + %a.button.selector.producer-hub{ ng: { click: "enterprise.owned && (enterprise.sells='any')", class: "{selected: enterprise.sells=='any', disabled: !enterprise.owned}" } } + .top + %h3 Producer Hub + %p Sell produce from self and others + .bottom + %monthly-pricing-description{ joiner: "newline" } + + %a.button.update.fullwidth{ ng: { show: "enterprise.owned", class: "{disabled: saved() && !saving, saving: saving}", click: "save()" } } + %span{ ng: {hide: "saved() || saving" } } + SAVE + %i.icon-save + %span{ ng: {show: "saved() && !saving" } } + SAVED + %i.icon-ok-sign + %span{ ng: {show: "saving" } } + SAVING + %i.icon-refresh diff --git a/app/assets/javascripts/templates/admin/panels/enterprise_producer.html.haml b/app/assets/javascripts/templates/admin/panels/enterprise_producer.html.haml new file mode 100644 index 0000000000..ea8554892f --- /dev/null +++ b/app/assets/javascripts/templates/admin/panels/enterprise_producer.html.haml @@ -0,0 +1,39 @@ +.row.enterprise_producer_panel{ ng: { controller: 'indexProducerPanelCtrl' } } + + .alpha.eight.columns + .info{ ng: { show: "enterprise.is_primary_producer==true" } } + %h3 Producer + %p Producers make yummy things to eat &/or drink. You're a producer if you grow it, raise it, brew it, bake it, ferment it, milk it or mould it. + + %p Producers can also perform other functions, such as aggregating food from other enterprises and selling it through a shop on the Open Food Network. + + + .info{ ng: { show: "enterprise.is_primary_producer==false" } } + %h3 Non-Producer + %p Non-producers do not produce any food themselves, meaning that they cannot create their own products for sale through the Open Food Network. + + %p Instead, non-producers specialise in linking producers to the end eater, whether it be by aggregating, grading, packing, selling or delivering food. + + .omega.eight.columns + %a.button.selector.producer{ ng: { click: 'enterprise.owned && changeToProducer()', class: "{selected: enterprise.is_primary_producer==true, disabled: !enterprise.owned}" } } + .top + %h3 PRODUCER + %p Producers of food + .bottom eg. GROWERS, BAKERS, BREWERS, MAKERS + + %a.button.selector.non-producer{ ng: { click: 'enterprise.owned && changeToNonProducer()', class: "{selected: enterprise.is_primary_producer==false, disabled: !enterprise.owned}" } } + .top + %h3 Non-Producer + %p All other food enterprises + .bottom eg. Grocery stores, Food co-ops, Buying groups + + %a.button.update.fullwidth{ ng: { show: "enterprise.owned", class: "{disabled: saved() && !saving, saving: saving}", click: "save()" } } + %span{ ng: {hide: "saved() || saving" } } + SAVE + %i.icon-save + %span{ ng: {show: "saved() && !saving" } } + SAVED + %i.icon-ok-sign + %span{ ng: {show: "saving" } } + SAVING + %i.icon-refresh diff --git a/app/assets/javascripts/templates/admin/panels/enterprise_status.html.haml b/app/assets/javascripts/templates/admin/panels/enterprise_status.html.haml new file mode 100644 index 0000000000..39392a497d --- /dev/null +++ b/app/assets/javascripts/templates/admin/panels/enterprise_status.html.haml @@ -0,0 +1,29 @@ +.row.enterprise_status_panel{ ng: { controller: 'indexStatusPanelCtrl' } } + .alpha.omega.sixteen.columns + + %h4.status-ok.text-center{ ng: { show: "issues.length == 0 && warnings.length == 0" } } + %i.icon-ok-sign + {{ object.name }} is set up and ready to go! + + %table{ ng: { show: "issues.length > 0 || warnings.length > 0" } } + %thead + %th.severity + Severity + %th.description + Description + %th.resolve + Resolve + %tr{ ng: { repeat: "issue in issues"} } + %td.severity + %i.icon-warning-sign.issue + %td.description + %span{ bo: { bind: "issue.description" } } + %td.resolve + %div{ ng: { bind: { html: "issue.link" } } } + %tr{ ng: { repeat: "warning in warnings"} } + %td.severity + %i.icon-warning-sign.warning + %td.description + %span{ bo: { bind: "warning.description" } } + %td.resolve + %div{ ng: { bind: { html: "warning.link" } } } diff --git a/app/assets/javascripts/templates/admin/save_bar.html.haml b/app/assets/javascripts/templates/admin/save_bar.html.haml new file mode 100644 index 0000000000..452e81f6e3 --- /dev/null +++ b/app/assets/javascripts/templates/admin/save_bar.html.haml @@ -0,0 +1,6 @@ +#save-bar.animate-show{ ng: { show: 'form.$dirty || StatusMessage.active()' } } + .twelve.columns.alpha + %h5#status-message{ ng: { style: 'StatusMessage.statusMessage.style' } } + {{ StatusMessage.statusMessage.text || " " }} + .four.columns.omega.text-right + %input.red{type: "button", value: "Save Changes", ng: { disabled: '!form.$dirty', click: "save()" } } diff --git a/app/assets/javascripts/templates/enterprise_modal.html.haml b/app/assets/javascripts/templates/enterprise_modal.html.haml index 6b13f6ee78..350cd6ed80 100644 --- a/app/assets/javascripts/templates/enterprise_modal.html.haml +++ b/app/assets/javascripts/templates/enterprise_modal.html.haml @@ -1,4 +1,5 @@ %ng-include{src: "'partials/enterprise_header.html'"} %ng-include{src: "'partials/enterprise_details.html'"} %ng-include{src: "'partials/hub_details.html'"} +%ng-include{src: "'partials/producer_details.html'"} %ng-include{src: "'partials/close.html'"} diff --git a/app/assets/javascripts/templates/filter_selector.html.haml b/app/assets/javascripts/templates/filter_selector.html.haml index c6990c369f..a53c4f44db 100644 --- a/app/assets/javascripts/templates/filter_selector.html.haml +++ b/app/assets/javascripts/templates/filter_selector.html.haml @@ -1,4 +1,4 @@ -%div{bindonce:true, style: "display: inline-block" } +%ul{ bindonce: true } %active-selector{ ng: { repeat: "selector in allSelectors", show: "ifDefined(selector.fits, true)" } } %render-svg{path: "{{selector.object.icon}}", ng: { if: "selector.object.icon"} } %span{"bo-text" => "selector.object.name"} diff --git a/app/assets/javascripts/templates/forgot.html.haml b/app/assets/javascripts/templates/forgot.html.haml index 646d5832c2..c1990d778a 100644 --- a/app/assets/javascripts/templates/forgot.html.haml +++ b/app/assets/javascripts/templates/forgot.html.haml @@ -1,5 +1,5 @@ %tab#forgot{"ng-controller" => "ForgotCtrl", - heading: "Forgot Password?", + heading: "{{'forgot_password' | t}}", active: "active(path)", select: "select(path)"} @@ -8,7 +8,7 @@ .row .large-12.columns .alert-box.success.radius{"ng-show" => "sent"} - An email with instructions on resetting your password has been sent! + {{'password_reset_sent' | t}} %div{"ng-show" => "!sent"} .alert-box.alert{"ng-show" => "errors != null"} @@ -16,7 +16,7 @@ .row .large-12.columns - %label{for: "email"} Your email + %label{for: "email"} {{'signup_email' | t}} %input.title.input-text{name: "email", type: "email", id: "email", @@ -27,4 +27,4 @@ %input.button.primary{name: "commit", tabindex: "3", type: "submit", - value: "Reset password"} + value: "{{'reset_password' | t}}"} diff --git a/app/assets/javascripts/templates/login.html.haml b/app/assets/javascripts/templates/login.html.haml index 992dfd265a..44d2e08ced 100644 --- a/app/assets/javascripts/templates/login.html.haml +++ b/app/assets/javascripts/templates/login.html.haml @@ -1,5 +1,5 @@ %tab#login-content{"ng-controller" => "LoginCtrl", - heading: "Log in", + heading: "{{'label_login' | t}}", active: "active(path)", select: "select(path)"} %form{"ng-submit" => "submit()"} @@ -9,7 +9,7 @@ {{ errors }} .row .large-12.columns - %label{for: "email"} Email + %label{for: "email"} {{'email' | t}} %input.title.input-text{name: "email", type: "email", id: "email", @@ -17,7 +17,7 @@ "ng-model" => "spree_user.email"} .row .large-12.columns - %label{for: "password"} Password + %label{for: "password"} {{'password' | t}} %input.title.input-text{name: "password", type: "password", id: "password", @@ -31,10 +31,10 @@ id: "remember_me", value: "1", "ng-model" => "spree_user.remember_me"} - %label{for: "remember_me"} Remember Me + %label{for: "remember_me"} {{'remember_me' | t}} .row .large-12.columns %input.button.primary{name: "commit", tabindex: "3", type: "submit", - value: "Log in"} + value: "{{'label_login' | t}}"} diff --git a/app/assets/javascripts/templates/partials/contact.html.haml b/app/assets/javascripts/templates/partials/contact.html.haml index 165fd69d80..12aa5ff061 100644 --- a/app/assets/javascripts/templates/partials/contact.html.haml +++ b/app/assets/javascripts/templates/partials/contact.html.haml @@ -1,11 +1,11 @@ %div.contact-container{bindonce: true} - %div.modal-centered{"bo-if" => "enterprise.email || enterprise.website || enterprise.phone"} - %p.modal-header Contact + %div.modal-centered{"bo-if" => "enterprise.email_address || enterprise.website || enterprise.phone"} + %p.modal-header {{'contact' | t}} %p{"bo-if" => "enterprise.phone", "bo-text" => "enterprise.phone"} - %p.word-wrap{"ng-if" => "enterprise.email"} - %a{"bo-href" => "enterprise.email | stripUrl", target: "_blank", mailto: true} - %span.email{"bo-bind" => "enterprise.email | stripUrl"} + %p.word-wrap{"ng-if" => "enterprise.email_address"} + %a{"bo-href" => "enterprise.email_address | stripUrl", target: "_blank", mailto: true} + %span.email{"bo-bind" => "enterprise.email_address | stripUrl"} %p.word-wrap{"ng-if" => "enterprise.website"} %a{"bo-href-i" => "http://{{enterprise.website | stripUrl}}", target: "_blank", "bo-bind" => "enterprise.website | stripUrl"} diff --git a/app/assets/javascripts/templates/partials/enterprise_details.html.haml b/app/assets/javascripts/templates/partials/enterprise_details.html.haml index 3a9d97f59e..fc0a638c8d 100644 --- a/app/assets/javascripts/templates/partials/enterprise_details.html.haml +++ b/app/assets/javascripts/templates/partials/enterprise_details.html.haml @@ -3,7 +3,7 @@ / TODO: Rob add logic for taxons and properties too: / %div{"ng-if" => "enterprise.long_description.length > 0 || enterprise.logo"} %div - %p.modal-header About + %p.modal-header {{'label_about' | t}} / TODO: Rob - add in taxons and properties and property pop-overs -# TODO: Add producer taxons and properties here diff --git a/app/assets/javascripts/templates/partials/enterprise_header.html.haml b/app/assets/javascripts/templates/partials/enterprise_header.html.haml index c0d9d5d9e0..44175a57fe 100644 --- a/app/assets/javascripts/templates/partials/enterprise_header.html.haml +++ b/app/assets/javascripts/templates/partials/enterprise_header.html.haml @@ -2,7 +2,7 @@ .highlight-top.row .small-12.medium-7.large-8.columns %h3{"ng-if" => "enterprise.is_distributor"} - %a{"bo-href" => "enterprise.path", "ofn-empties-cart" => "enterprise"} + %a{"bo-href" => "enterprise.path", "ofn-change-hub" => "enterprise"} %i{"ng-class" => "enterprise.icon_font"} %span{"bo-text" => "enterprise.name"} %h3{"ng-if" => "!enterprise.is_distributor", "ng-class" => "{'is_producer' : enterprise.is_primary_producer}"} @@ -10,4 +10,4 @@ %span{"bo-text" => "enterprise.name"} .small-12.medium-5.large-4.columns.text-right.small-only-text-left %p{"bo-bind" => "[enterprise.address.city, enterprise.address.state_name] | printArray"} - %img.hero-img{"bo-src" => "enterprise.promo_image"} + %img.hero-img{"bo-src" => "enterprise.promo_image"} diff --git a/app/assets/javascripts/templates/partials/follow.html.haml b/app/assets/javascripts/templates/partials/follow.html.haml index 4e2a00086a..c04567a357 100644 --- a/app/assets/javascripts/templates/partials/follow.html.haml +++ b/app/assets/javascripts/templates/partials/follow.html.haml @@ -1,5 +1,5 @@ %div.modal-centered{bindonce: true, "bo-if" => "enterprise.twitter || enterprise.facebook || enterprise.linkedin || enterprise.instagram"} - %p.modal-header Follow + %p.modal-header {{'follow' | t}} .follow-icons %span{"bo-if" => "enterprise.twitter"} %a{"bo-href-i" => "http://twitter.com/{{enterprise.twitter}}", target: "_blank"} diff --git a/app/assets/javascripts/templates/partials/hub_actions.html.haml b/app/assets/javascripts/templates/partials/hub_actions.html.haml deleted file mode 100644 index f5b451ef3c..0000000000 --- a/app/assets/javascripts/templates/partials/hub_actions.html.haml +++ /dev/null @@ -1,16 +0,0 @@ -.row.pad-top{bindonce: true, "ng-if" => "enterprise.hubs.length > 0 && enterprise.is_distributor"} - .cta-container.small-12.columns - %label - Shop for - %strong{"bo-text" => "enterprise.name"} - products at: - %a.cta-hub{"ng-repeat" => "hub in enterprise.hubs", - "bo-href" => "hub.path", - "bo-class" => "{primary: hub.active, secondary: !hub.active}", - "ofn-empties-cart" => "hub"} - %i.ofn-i_033-open-sign{"bo-if" => "hub.active"} - %i.ofn-i_032-closed-sign{"bo-if" => "!hub.active"} - .hub-name{"bo-text" => "hub.name"} - .button-address{"bo-bind" => "[hub.address.city, hub.address.state_name] | printArray"} - / %i.ofn-i_007-caret-right - diff --git a/app/assets/javascripts/templates/partials/hub_details.html.haml b/app/assets/javascripts/templates/partials/hub_details.html.haml index 8be5ceddac..fa75f72253 100644 --- a/app/assets/javascripts/templates/partials/hub_details.html.haml +++ b/app/assets/javascripts/templates/partials/hub_details.html.haml @@ -2,21 +2,21 @@ .cta-container.small-12.columns .row .small-4.columns - %label{"active-table-hub-link" => "enterprise", change: "Change shop to:", shop: "Shop now at:"} + %label{"active-table-hub-link" => "enterprise", change: "{{'change_shop' | t}}", shop: "{{'shop_at' | t}}"} .small-8.columns.right %label.right{"bo-if" => "enterprise.pickup || enterprise.delivery"} - Delivery options: - %span{"bo-if" => "enterprise.pickup"} + {{'hubs_delivery_options' | t}}: + %span{"bo-if" => "enterprise.pickup"} %i.ofn-i_038-takeaway - Pickup - %span{"bo-if" => "enterprise.delivery"} + {{'hubs_pickup' | t}} + %span{"bo-if" => "enterprise.delivery"} %i.ofn-i_039-delivery - Delivery + {{'hubs_delivery' | t}} .row .columns.small-12 - %a.cta-hub{"bo-href" => "enterprise.path", + %a.cta-hub{"bo-href" => "enterprise.path", "ng-class" => "{primary: enterprise.active, secondary: !enterprise.active}", - "ofn-empties-cart" => "enterprise"} + "ofn-change-hub" => "enterprise"} %i.ofn-i_033-open-sign{"bo-if" => "enterprise.active"} %i.ofn-i_032-closed-sign{"bo-if" => "!enterprise.active"} .hub-name{"bo-text" => "enterprise.name"} diff --git a/app/assets/javascripts/templates/partials/producer_details.html.haml b/app/assets/javascripts/templates/partials/producer_details.html.haml new file mode 100644 index 0000000000..599b977dfc --- /dev/null +++ b/app/assets/javascripts/templates/partials/producer_details.html.haml @@ -0,0 +1,20 @@ +-# Show places to buy products from this producer, when there are any +-# Do not show this for producer shops selling only their own produce, +-# Since a shopping link will already have been displayed in hub_details.html.haml +.row.active_table_row.pad-top{bindonce: true, "ng-if" => "enterprise.is_primary_producer && enterprise.hubs.length > 0 && !(enterprise.hubs.length == 1 && enterprise.hubs[0] == enterprise)"} + .columns.small-12 + .row + .columns.small-12.fat + %div{"bo-if" => "enterprise.name"} + %label{"bo-html" => "'shop_for_products_html' | t:{enterprise: enterprise.name}"} + %div.show-for-medium-up{"bo-if" => "!enterprise.name"} +   + .row.cta-container + .columns.small-12 + %a.cta-hub{"ng-repeat" => "hub in enterprise.hubs | filter:{id: '!'+enterprise.id} | orderBy:'-active'", + "bo-href" => "hub.path", "ofn-empties-cart" => "hub", + "bo-class" => "{primary: hub.active, secondary: !hub.active}"} + %i.ofn-i_033-open-sign{"bo-if" => "hub.active"} + %i.ofn-i_032-closed-sign{"bo-if" => "!hub.active"} + .hub-name{"bo-text" => "hub.name"} + .button-address{"bo-bind" => "[hub.address.city, hub.address.state_name] | printArray"} diff --git a/app/assets/javascripts/templates/price_breakdown.html.haml b/app/assets/javascripts/templates/price_breakdown.html.haml index 21104b71a0..993c20bc9b 100644 --- a/app/assets/javascripts/templates/price_breakdown.html.haml +++ b/app/assets/javascripts/templates/price_breakdown.html.haml @@ -4,35 +4,35 @@ .collapsed{"ng-show" => "!expanded"} %price-percentage{percentage: 'variant.basePricePercentage'} %a{"ng-click" => "expanded = !expanded"} - Full price breakdown + %span{"bo-text" => "'price_breakdown' | t"} %i.ofn-i_005-caret-down .expanded{"ng-show" => "expanded"} %ul %li.cost .right {{ variant.price | localizeCurrency }} - Item cost + %span{"bo-text" => "'item_cost' | t"} %li.admin-fee{"bo-if" => "variant.fees.admin"} .right {{ variant.fees.admin | localizeCurrency }} - Admin fee + %span{"bo-text" => "'admin_fee' | t"} %li.sales-fee{"bo-if" => "variant.fees.sales"} .right {{ variant.fees.sales | localizeCurrency }} - Sales fee + %span{"bo-text" => "'sales_fee' | t"} %li.packing-fee{"bo-if" => "variant.fees.packing"} .right {{ variant.fees.packing | localizeCurrency }} - Packing fee + %span{"bo-text" => "'packing_fee' | t"} %li.transport-fee{"bo-if" => "variant.fees.transport"} .right {{ variant.fees.transport | localizeCurrency }} - Transport fee + %span{"bo-text" => "'transport_fee' | t"} %li.fundraising-fee{"bo-if" => "variant.fees.fundraising"} .right {{ variant.fees.fundraising | localizeCurrency }} - Fundraising fee + %span{"bo-text" => "'fundraising_fee' | t"} %li.total %strong .right = {{ variant.price_with_fees | localizeCurrency }}   %a{"ng-click" => "expanded = !expanded"} - Price graph + %span{"bo-text" => "'price_graph' | t"} %i.ofn-i_006-caret-up diff --git a/app/assets/javascripts/templates/price_percentage.html.haml b/app/assets/javascripts/templates/price_percentage.html.haml index e577d86d7a..7892ebc0ab 100644 --- a/app/assets/javascripts/templates/price_percentage.html.haml +++ b/app/assets/javascripts/templates/price_percentage.html.haml @@ -1,5 +1,5 @@ .progress - .right Fees + .right {{'fees' | t}} .meter - Item cost + {{'item_cost' | t}} diff --git a/app/assets/javascripts/templates/product_modal.html.haml b/app/assets/javascripts/templates/product_modal.html.haml index 3db8035acc..e13f6df60d 100644 --- a/app/assets/javascripts/templates/product_modal.html.haml +++ b/app/assets/javascripts/templates/product_modal.html.haml @@ -3,18 +3,16 @@ .columns.small-12.large-6.product-header %h3{"bo-text" => "product.name"} %span - %em from - %span.avenir{"bo-text" => "enterprise.name"} + %em {{'products_from' | t}} + %span{"bo-text" => "enterprise.name"} %br .filter-shopfront.taxon-selectors.inline-block - %ul - %filter-selector{ objects: "[product] | taxonsOf" } + %filter-selector{ objects: "[product] | taxonsOf" } .filter-shopfront.property-selectors.inline-block - %ul - %filter-selector{ objects: "[product] | propertiesWithValuesOf" } + %filter-selector{ objects: "[product] | propertiesWithValuesOf" } %div{"ng-if" => "product.description"} %hr diff --git a/app/assets/javascripts/templates/registration/about.html.haml b/app/assets/javascripts/templates/registration/about.html.haml index be9948b95d..40d02e3d32 100644 --- a/app/assets/javascripts/templates/registration/about.html.haml +++ b/app/assets/javascripts/templates/registration/about.html.haml @@ -3,9 +3,9 @@ .row .small-12.columns %header - %h2 Nice one! + %h2 {{'enterprise_about_headline' | t}} %h5 - Now let's flesh out the details about + {{'enterprise_about_message' | t}} %span{ ng: { class: "{brick: !enterprise.is_primary_producer, turquoise: enterprise.is_primary_producer}" } } {{ enterprise.name }} @@ -13,45 +13,45 @@ .row .small-12.columns .alert-box.info{ "ofn-inline-alert" => true, ng: { show: "visible" } } - %h6 Success! {{ enterprise.name }} added to the Open Food Network - %span If you exit the wizard at any stage, login and go to admin to edit or update your enterprise details. + %h6{ "ng-bind" => "'enterprise_success' | t:{enterprise: enterprise.name}" } + %span {{'enterprise_registration_exit_message' | t}} %a.close{ ng: { click: "close()" } } × .small-12.large-8.columns .row .small-12.columns .field - %label{ for: 'enterprise_description' } Short Description: - %input.chunky{ id: 'enterprise_description', placeholder: "A short sentence describing your enterprise", ng: { model: 'enterprise.description' } } + %label{ for: 'enterprise_description' } {{'enterprise_description' | t}}: + %input.chunky{ id: 'enterprise_description', placeholder: "{{'enterprise_description_placeholder' | t}}", ng: { model: 'enterprise.description' } } .row .small-12.columns .field - %label{ for: 'enterprise_long_desc' } Long Description: - %textarea.chunky{ id: 'enterprise_long_desc', rows: 6, placeholder: "This is your opportunity to tell the story of your enterprise - what makes you different and wonderful? We'd suggest keeping your description to under 600 characters or 150 words.", ng: { model: 'enterprise.long_description' } } - %small {{ enterprise.long_description.length }} characters / up to 600 recommended + %label{ for: 'enterprise_long_desc' } {{'enterprise_long_desc' | t}}: + %textarea.chunky{ id: 'enterprise_long_desc', rows: 6, placeholder: "{{'enterprise_long_desc_placeholder' | t}}", ng: { model: 'enterprise.long_description' } } + %small{ "ng-bind" => "'enterprise_long_desc_length' | t:{num: enterprise.long_description.length}" } .small-12.large-4.columns .row .small-12.columns .field - %label{ for: 'enterprise_abn' } ABN: - %input.chunky{ id: 'enterprise_abn', placeholder: "eg. 99 123 456 789", ng: { model: 'enterprise.abn' } } + %label{ for: 'enterprise_abn' } {{'enterprise_abn' | t}}: + %input.chunky{ id: 'enterprise_abn', placeholder: "{{'enterprise_abn_placeholder' | t}}", ng: { model: 'enterprise.abn' } } .row .small-12.columns .field - %label{ for: 'enterprise_acn' } ACN: - %input.chunky{ id: 'enterprise_acn', placeholder: "eg. 123 456 789", ng: { model: 'enterprise.acn' } } + %label{ for: 'enterprise_acn' } {{'enterprise_acn' | t}}: + %input.chunky{ id: 'enterprise_acn', placeholder: "{{'enterprise_acn_placeholder' | t}}", ng: { model: 'enterprise.acn' } } .row .small-12.columns .field %label{ for: 'enterprise_charges_sales_tax' }= t(:charges_sales_tax) %input{ id: 'enterprise_charges_sales_tax_true', type: 'radio', name: 'charges_sales_tax', value: 'true', required: true, ng: { model: 'enterprise.charges_sales_tax' } } - %label{ for: 'enterprise_charges_sales_tax_true' } Yes + %label{ for: 'enterprise_charges_sales_tax_true' } {{'say_yes' | t}} %input{ id: 'enterprise_charges_sales_tax_false', type: 'radio', name: 'charges_sales_tax', value: 'false', required: true, ng: { model: 'enterprise.charges_sales_tax' } } - %label{ for: 'enterprise_charges_sales_tax_false' } No + %label{ for: 'enterprise_charges_sales_tax_false' } {{'say_no' | t}} %span.error.small-12.columns{ ng: { show: "about.charges_sales_tax.$error.required && submitted" } } - You need to make a selection. + {{'enterprise_tax_required' | t}} .row.buttons.pad-top .small-12.columns - %input.button.primary.right{ type: "submit", value: "Continue" } + %input.button.primary.right{ type: "submit", value: "{{'continue' | t}}" } diff --git a/app/assets/javascripts/templates/registration/contact.html.haml b/app/assets/javascripts/templates/registration/contact.html.haml index 916a614c0b..6e7389d73a 100644 --- a/app/assets/javascripts/templates/registration/contact.html.haml +++ b/app/assets/javascripts/templates/registration/contact.html.haml @@ -3,45 +3,31 @@ .row .small-12.columns %header - %h2 Greetings! - %h5 - Who is responsible for managing {{ enterprise.name }}? + %h2 {{'registration_greeting' | t}} + %h5{ "ng-bind" => "'who_is_managing_enterprise' | t:{enterprise: enterprise.name}" } %form{ name: 'contact', novalidate: true, ng: { controller: "RegistrationFormCtrl", submit: "selectIfValid('type',contact)" } } .row.content .small-12.medium-12.large-7.columns .row .small-12.columns.field - %label{ for: 'enterprise_contact' } Primary Contact: + %label{ for: 'enterprise_contact' } {{'enterprise_contact' | t}}: %input.chunky.small-12.columns{ id: 'enterprise_contact', name: 'contact', required: true, placeholder: "Contact Name", ng: { model: 'enterprise.contact' } } %span.error.small-12.columns{ ng: { show: "contact.contact.$error.required && submitted" } } - You need to enter a primary contact. + {{'enterprise_contact_required' | t}} .row .small-12.columns.field - %label{ for: 'enterprise_email' } Email address: + %label{ for: 'enterprise_email' } {{'enterprise_email' | t}}: %input.chunky.small-12.columns{ id: 'enterprise_email', name: 'email', type: 'email', required: true, placeholder: "eg. charlie@thefarm.com", ng: { model: 'enterprise.email' } } %span.error.small-12.columns{ ng: { show: "(contact.email.$error.email || contact.email.$error.required) && submitted" } } - You need to enter valid email address. + {{'enterprise_email_required' | t}} .row .small-12.columns.field - %label{ for: 'enterprise_phone' } Phone number: + %label{ for: 'enterprise_phone' } {{'enterprise_phone' | t}}: %input.chunky.small-12.columns{ id: 'enterprise_phone', name: 'phone', placeholder: "eg. (03) 1234 5678", ng: { model: 'enterprise.phone' } } .small-12.medium-12.large-5.hide-for-small-only - / %h6 - / Contact display - / %i.ofn-i_013-help.has-tip{ 'data-tooltip' => true, title: "Choose how you want to display your contact details on the Open Food Network."} - / .row - / .small-12.columns - / %label.indent-checkbox - / %input{ type: 'checkbox', id: 'contact_name_profile', ng: { model: 'enterprise.name_in_profile' } }   Display name in profile - / .small-12.columns - / %label.indent-checkbox - / %input{ type: 'checkbox', id: 'contact_email_profile', ng: { model: 'enterprise.email_in_profile' } }   Display email in profile - / .small-12.columns - / %label.indent-checkbox - / %input{ type: 'checkbox', id: 'contact_phone_profile', ng: { model: 'enterprise.phone_in_profile' } }   Display phone in profile .row.buttons .small-12.columns - %input.button.secondary{ type: "button", value: "Back", ng: { click: "select('details')" } } - %input.button.primary.right{ type: "submit", value: "Continue" } + %input.button.secondary{ type: "button", value: "{{'back' | t}}", ng: { click: "select('details')" } } + %input.button.primary.right{ type: "submit", value: "{{'continue' | t}}" } diff --git a/app/assets/javascripts/templates/registration/details.html.haml b/app/assets/javascripts/templates/registration/details.html.haml index f1c0503f78..f03a48ec59 100644 --- a/app/assets/javascripts/templates/registration/details.html.haml +++ b/app/assets/javascripts/templates/registration/details.html.haml @@ -3,75 +3,62 @@ .row .small-12.columns %header - %h2 Let's Get Started - %h5{ bo: { if: "enterprise.type != 'own'" } } Woot! First we need to know a little bit about your enterprise: - %h5{ bo: { if: "enterprise.type == 'own'" } } Woot! First we need to know a little bit about your farm: + %h2 {{'registration_detail_headline' | t}} + %h5{ bo: { if: "enterprise.type != 'own'" } } {{'registration_detail_enterprise' | t}} + %h5{ bo: { if: "enterprise.type == 'own'" } } {{'registration_detail_producer' | t}} %form{ name: 'details', novalidate: true, ng: { controller: "RegistrationFormCtrl", submit: "selectIfValid('contact',details)" } } .row .small-12.medium-9.large-12.columns.end .field - %label{ for: 'enterprise_name', bo: { if: "enterprise.type != 'own'" } } Enterprise Name: - %label{ for: 'enterprise_name', bo: { if: "enterprise.type == 'own'" } } Farm Name: - %input.chunky{ id: 'enterprise_name', name: 'name', placeholder: "e.g. Charlie's Awesome Farm", required: true, ng: { model: 'enterprise.name' } } + %label{ for: 'enterprise_name', bo: { if: "enterprise.type != 'own'" } } {{'registration_detail_name_enterprise' | t}} + %label{ for: 'enterprise_name', bo: { if: "enterprise.type == 'own'" } } {{'registration_detail_name_producer' | t}} + %input.chunky{ id: 'enterprise_name', name: 'name', placeholder: "{{'registration_detail_name_placeholder' | t}}", required: true, ng: { model: 'enterprise.name' } } %span.error{ ng: { show: "details.name.$error.required && submitted" } } - Please choose a unique name for your enterprise + {{'registration_detail_name_error' | t}} .row .small-12.medium-9.large-6.columns .field - %label{ for: 'enterprise_address' } Address line 1: - %input.chunky{ id: 'enterprise_address', name: 'address1', required: true, placeholder: "e.g. 123 Cranberry Drive", required: true, ng: { model: 'enterprise.address.address1' } } + %label{ for: 'enterprise_address' } {{'registration_detail_address1' | t}} + %input.chunky{ id: 'enterprise_address', name: 'address1', required: true, placeholder: "{{'registration_detail_address1_placeholder' | t}}", required: true, ng: { model: 'enterprise.address.address1' } } %span.error{ ng: { show: "details.address1.$error.required && submitted" } } - Please enter an address + {{'registration_detail_address1_error' | t}} .field - %label{ for: 'enterprise_address2' } Address line 2: + %label{ for: 'enterprise_address2' } {{'registration_detail_address2' | t}} %input.chunky{ id: 'enterprise_address2', name: 'address2', required: false, placeholder: "", required: false, ng: { model: 'enterprise.address.address2' } } .small-12.medium-9.large-6.columns.end .row .small-12.medium-8.large-8.columns .field - %label{ for: 'enterprise_city' } Suburb: - %input.chunky{ id: 'enterprise_city', name: 'city', required: true, placeholder: "e.g. Northcote", ng: { model: 'enterprise.address.city' } } + %label{ for: 'enterprise_city' } {{'registration_detail_suburb' | t}} + %input.chunky{ id: 'enterprise_city', name: 'city', required: true, placeholder: "{{'registration_detail_suburb_placeholder' | t}}", ng: { model: 'enterprise.address.city' } } %span.error{ ng: { show: "details.city.$error.required && submitted" } } - Please enter a suburb + {{'registration_detail_suburb_error' | t}} .small-12.medium-4.large-4.columns .field - %label{ for: 'enterprise_zipcode' } Postcode: - %input.chunky{ id: 'enterprise_zipcode', name: 'zipcode', required: true, placeholder: "e.g. 3070", ng: { model: 'enterprise.address.zipcode' } } + %label{ for: 'enterprise_zipcode' } {{'registration_detail_postcode' | t}} + %input.chunky{ id: 'enterprise_zipcode', name: 'zipcode', required: true, placeholder: "{{'registration_detail_postcode_placeholder' | t}}", ng: { model: 'enterprise.address.zipcode' } } %span.error{ ng: { show: "details.zipcode.$error.required && submitted" } } - Postcode required + {{'registration_detail_postcode_error' | t}} .row .small-12.medium-4.large-4.columns .field - %label{ for: 'enterprise_state' } State: + %label{ for: 'enterprise_state' } {{'registration_detail_state' | t}} %select.chunky{ id: 'enterprise_state', name: 'state', ng: { model: 'enterprise.address.state_id', options: 's.id as s.abbr for s in enterprise.country.states', show: 'countryHasStates()', required: 'countryHasStates()' } } %span.error{ ng: { show: "details.state.$error.required && submitted" } } - State required + {{'registration_detail_state_error' | t}} .small-12.medium-8.large-8.columns .field - %label{ for: 'enterprise_country' } Country: + %label{ for: 'enterprise_country' } {{'registration_detail_country' | t}} %select.chunky{ id: 'enterprise_country', name: 'country', required: true, ng: { model: 'enterprise.country', options: 'c as c.name for c in countries' } } %span.error{ ng: { show: "details.country.$error.required && submitted" } } - Please select a country - / .small-12.medium-12.large-5.hide-for-small-only - / %h6 - / Location display - / %i.ofn-i_013-help.has-tip{ 'data-tooltip' => true, title: "Choose how you want to display your enterprise's address on the Open Food Network. By default, full location is shown everywhere including street name and number."} - / .row - / .small-12.columns - / %label.indent-checkbox - / %input{ type: 'checkbox', id: 'enterpise_suburb_only', ng: { model: 'enterprise.suburb_only' } } - / Hide my street name and street number from the public (ie. only show the suburb) - / .small-12.columns - / %label.indent-checkbox - / %input{ type: 'checkbox', id: 'enterprise_on_map', ng: { model: 'enterprise.on_map' } } - / Blur my location on the map (show an approximate, not exact pin) + {{'registration_detail_country_error' | t}} .row.buttons .small-12.columns %hr - %input.button.primary.right{ type: "submit", value: "Continue" } + %input.button.primary.right{ type: "submit", value: "{{'continue' | t}}" } diff --git a/app/assets/javascripts/templates/registration/finished.html.haml b/app/assets/javascripts/templates/registration/finished.html.haml index f647a2d8bb..5c7b3b6ad0 100644 --- a/app/assets/javascripts/templates/registration/finished.html.haml +++ b/app/assets/javascripts/templates/registration/finished.html.haml @@ -2,23 +2,14 @@ .row .small-12.columns.pad-top %header - %h2 Finished! + %h2 {{'registration_finished_headline' | t}} .panel.callout - %p - Thanks for filling out the details for - %span{ ng: { class: "{brick: !enterprise.is_primary_producer, turquoise: enterprise.is_primary_producer}" } } - {{ enterprise.name }} - %p You can change or update your enterprise at any stage by logging into Open Food Network and going to Admin. + %p{ "ng-bind" => "'registration_finished_thanks' | t:{enterprise: enterprise.name}" } + %p {{'registration_finished_login' | t}} .row .small-12.columns.text-center - %h4 - Activate - %span{ ng: { class: "{brick: !enterprise.is_primary_producer, turquoise: enterprise.is_primary_producer}" } } - {{ enterprise.name }} + %h4{ "ng-bind" => "'registration_finished_activate' | t:{enterprise: enterprise.name}" } - %p - We've sent a confirmation email to - %strong {{ enterprise.email }}. - %br Please follow the instructions there to make your enterprise visible on the Open Food Network. + %p{ "ng-bind-html" => "t('registration_finished_activate_instruction_html', {email: enterprise.email})"} - %a.button.primary{ type: "button", href: "/" } Open Food Network home > + %a.button.primary{ type: "button", href: "/" } {{'registration_finished_action' | t}} > diff --git a/app/assets/javascripts/templates/registration/images.html.haml b/app/assets/javascripts/templates/registration/images.html.haml index 5b2ac39b5a..60a1ca602b 100644 --- a/app/assets/javascripts/templates/registration/images.html.haml +++ b/app/assets/javascripts/templates/registration/images.html.haml @@ -3,8 +3,8 @@ .row .small-12.columns %header - %h2 Thanks! - %h5 Let's upload some pretty pictures so your profile looks great! :) + %h2 {{'registration_images_headline' | t}} + %h5 {{'registration_images_description' | t}} %form{ name: 'images', novalidate: true, ng: { controller: "RegistrationFormCtrl", submit: "select('social')" } } .row{ ng: { repeat: 'image_step in imageSteps', show: "imageStep == image_step" } } @@ -18,5 +18,5 @@ .row.buttons.pad-top{ ng: { if: "imageStep == 'promo'" } } .small-12.columns - %input.button.secondary{ type: "button", value: "Back", ng: { click: "imageSelect('logo')" } } - %input.button.primary.right{ type: "submit", value: "Continue" } + %input.button.secondary{ type: "button", value: "{{'back' | t}}", ng: { click: "imageSelect('logo')" } } + %input.button.primary.right{ type: "submit", value: "{{'continue' | t}}" } diff --git a/app/assets/javascripts/templates/registration/images/logo.html.haml b/app/assets/javascripts/templates/registration/images/logo.html.haml index 7dd08c90dc..842cbcbe11 100644 --- a/app/assets/javascripts/templates/registration/images/logo.html.haml +++ b/app/assets/javascripts/templates/registration/images/logo.html.haml @@ -4,42 +4,42 @@ .row .small-12.columns.center %h4 - Step 1. Select Logo Image + {{'select_logo' | t}} .row .small-12.columns.center %span.small - Tip: Square images will work best, preferably at least 300×300px + {{'logo_tip' | t}} .row.pad-top .small-12.columns .image-select.small-12.columns - %label.small-12.columns.button{ for: 'image-select' } Choose a logo image + %label.small-12.columns.button{ for: 'image-select' } {{'logo_label' | t}} %input#image-select{ type: 'file', hidden: true, 'nv-file-select' => true, uploader: "imageUploader", options: '{ alias: imageStep }' } .row.show-for-large-up .large-12.columns %span#or.large-12.columns - OR + {{'action_or' | t}} .row.show-for-large-up .large-12.columns #image-over{ 'nv-file-over' => true, uploader: "imageUploader" } - Drag and drop your logo here + {{'logo_drag' | t}} .small-12.medium-12.large-6.columns .row .small-12.columns.center .row .small-12.columns.center %h4 - Step 2. Review Your Logo + {{'review_logo' | t}} .row .small-12.columns.center %span.small - Tip: for best results, your logo should fill the available space + {{'review_logo_tip' | t}} .row.pad-top .small-12.columns.center #image-placeholder.logo %img{ ng: { show: "imageSrc() && !imageUploader.isUploading", src: '{{ imageSrc() }}' } } .message{ ng: { hide: "imageSrc() || imageUploader.isUploading" } } - Your logo will appear here for review once uploaded + {{'logo_placeholder' | t}} .loading{ ng: { hide: "!imageUploader.isUploading" } } %img.spinner{ src: "/assets/spinning-circles.svg" } %br/ - Uploading... + {{'uploading' | t}} diff --git a/app/assets/javascripts/templates/registration/images/promo.html.haml b/app/assets/javascripts/templates/registration/images/promo.html.haml index 6c3327c942..342db5c08d 100644 --- a/app/assets/javascripts/templates/registration/images/promo.html.haml +++ b/app/assets/javascripts/templates/registration/images/promo.html.haml @@ -2,42 +2,42 @@ .row .small-12.columns.center %h4 - Step 3. Select Promo Image + {{'select_promo_image' | t}} .row .small-12.medium-12.large-5.columns.center .row .small-12.columns.center %span.small - Tip: Shown as a banner, preferred size is 1200×260px + {{'promo_image_tip' | t}} .row.pad-top .small-12.columns .image-select.small-12.columns - %label.small-12.columns.button{ for: 'image-select' } Choose a promo image + %label.small-12.columns.button{ for: 'image-select' } {{'promo_image_label' | t}} %input#image-select{ type: 'file', hidden: true, 'nv-file-select' => true, uploader: "imageUploader", options: '{ alias: imageStep }' } .large-2.columns %span#or.horizontal.large-12.columns - OR + {{'action_or' | t}} .large-5.columns #image-over{ 'nv-file-over' => true, uploader: "imageUploader" } - Drag and drop your promo here + {{'promo_image_drag' | t}} .small-12.medium-12.large-12.columns.pad-top .row .small-12.columns.center %h4 - Step 4. Review Your Promo Banner + {{'review_promo_image' | t}} .row .small-12.columns.center .row .small-12.columns.center %span.small - Tip: for best results, your promo image should fill the available space + {{'review_promo_image_tip' | t}} .row.pad-top .small-12.columns.center #image-placeholder.promo %img{ ng: { show: "imageSrc() && !imageUploader.isUploading", src: '{{ imageSrc() }}' } } .message{ ng: { hide: "imageSrc() || imageUploader.isUploading" } } - Your logo will appear here for review once uploaded + {{'promo_image_placeholder' | t}} .loading{ ng: { hide: "!imageUploader.isUploading" } } %img.spinner{ src: "/assets/spinning-circles.svg" } %br/ - Uploading... + {{'uploading' | t}} diff --git a/app/assets/javascripts/templates/registration/introduction.html.haml b/app/assets/javascripts/templates/registration/introduction.html.haml index 60a8547b4a..c0adb0c357 100644 --- a/app/assets/javascripts/templates/registration/introduction.html.haml +++ b/app/assets/javascripts/templates/registration/introduction.html.haml @@ -1,45 +1,41 @@ .row .small-12.columns %header - %h2 Hi there! + %h2 {{'registration_greeting' | t}} %h4 %small %i.ofn-i_040-hub - Create your enterprise profile + {{'registration_intro' | t}} .hide-for-large-up %hr - %input.button.small.primary{ type: "button", value: "Let's get started!", ng: { click: "select('details')" } } + %input.button.small.primary{ type: "button", value: "{{'registration_action' | t}}", ng: { click: "select('details')" } } %hr .row{ 'data-equalizer' => true } .small-12.medium-12.large-6.columns.pad-top{ 'data-equalizer-watch' => true } - %h5 You'll need: + %h5 {{'registration_checklist' | t}}: %ul.check-list %li - 5-10 minutes + {{'registration_time' | t}} %li - Enterprise address + {{'registration_enterprise_address' | t}} %li - Primary contact details + {{'registration_contact_details' | t}} %li - Your logo image + {{'registration_logo' | t}} %li - Landscape image for your profile + {{'registration_promo_image' | t}} %li - 'About Us' text + {{'registration_about_us' | t}} .small-9.medium-8.large-5.columns.pad-top.end{ 'data-equalizer-watch' => true} %h5 - What do I get? - %p - Your profile helps people - %strong find - and - %strong contact - you on the Open Food Network. - %p Use this space to tell the story of your enterprise, to help drive connections to your social and online presence. + {{'registration_outcome_headline' | t}} + %p{ "ng-bind-html" => "t('registration_outcome1_html')" } + %p {{'registration_outcome2' | t}} + %p {{'registration_outcome3' | t}} .row.show-for-large-up .small-12.columns %hr - %input.button.primary.right{ type: "button", value: "Let's get started!", ng: { click: "select('details')" } } + %input.button.primary.right{ type: "button", value: "{{'registration_action' | t}}", ng: { click: "select('details')" } } diff --git a/app/assets/javascripts/templates/registration/limit_reached.html.haml b/app/assets/javascripts/templates/registration/limit_reached.html.haml index 778d980289..09e9866fd9 100644 --- a/app/assets/javascripts/templates/registration/limit_reached.html.haml +++ b/app/assets/javascripts/templates/registration/limit_reached.html.haml @@ -1,16 +1,16 @@ .row .small-12.columns %header - %h2 Oh no! - %h4 You have reached the limit! + %h2 {{'limit_reached_headline' | t}} + %h4 {{'limit_reached_message' | t}} .row .small-12.medium-3.large-2.columns.text-right.hide-for-small-only %img{:src => "/assets/potatoes.png"} .small-12.medium-9.large-10.columns %p - You have reached the limit for the number of enterprises you are allowed to own on the + {{'limit_reached_text' | t}} %strong Open Food Network. .row .small-12.columns %hr - %input.button.primary{ type: "button", value: "Return to the homepage", ng: { click: "close()" } } + %input.button.primary{ type: "button", value: "{{'limit_reached_action' | t}}", ng: { click: "close()" } } diff --git a/app/assets/javascripts/templates/registration/social.html.haml b/app/assets/javascripts/templates/registration/social.html.haml index 4f448734f1..ac40ef9c56 100644 --- a/app/assets/javascripts/templates/registration/social.html.haml +++ b/app/assets/javascripts/templates/registration/social.html.haml @@ -4,12 +4,8 @@ .row .small-12.columns %header - %h2 Final step! - %h5 - How can people find - %span{ ng: { class: "{brick: !enterprise.is_primary_producer, turquoise: enterprise.is_primary_producer}" } } - {{ enterprise.name }} - online? + %h2 {{'enterprise_final_step' | t}} + %h5{ "ng-bind" => "'enterprise_social_text' | t:{enterprise: enterprise.name}" } %form{ name: 'social', novalidate: true, ng: { controller: "RegistrationFormCtrl", submit: "update('finished',social)" } } .row.content @@ -17,33 +13,33 @@ .row .small-12.columns .field - %label{ for: 'enterprise_website' } Website: - %input.chunky{ id: 'enterprise_website', placeholder: "eg. openfoodnetwork.org.au", ng: { model: 'enterprise.website' } } + %label{ for: 'enterprise_website' } {{'website' | t}}: + %input.chunky{ id: 'enterprise_website', placeholder: "{{'website_placeholder' | t}}", ng: { model: 'enterprise.website' } } .row .small-12.columns .field - %label{ for: 'enterprise_facebook' } Facebook: - %input.chunky{ id: 'enterprise_facebook', placeholder: "eg. www.facebook.com/PageNameHere", ng: { model: 'enterprise.facebook' } } + %label{ for: 'enterprise_facebook' } {{'facebook' | t}}: + %input.chunky{ id: 'enterprise_facebook', placeholder: "{{'facebook_placeholder' | t}}", ng: { model: 'enterprise.facebook' } } .row .small-12.columns .field - %label{ for: 'enterprise_linkedin' } LinkedIn: - %input.chunky{ id: 'enterprise_linkedin', placeholder: "eg. www.linkedin.com/YourNameHere", ng: { model: 'enterprise.linkedin' } } + %label{ for: 'enterprise_linkedin' } {{'linkedin' | t}}: + %input.chunky{ id: 'enterprise_linkedin', placeholder: "{{'linkedin_placeholder' | t}}", ng: { model: 'enterprise.linkedin' } } .small-12.large-5.columns .row .small-12.columns .field - %label{ for: 'enterprise_twitter' } Twitter: - %input.chunky{ id: 'enterprise_twitter', placeholder: "eg. @twitter_handle", ng: { model: 'enterprise.twitter' } } + %label{ for: 'enterprise_twitter' } {{'twitter' | t}}: + %input.chunky{ id: 'enterprise_twitter', placeholder: "{{'twitter_placeholder' | t}}", ng: { model: 'enterprise.twitter' } } .row .small-12.columns .field - %label{ for: 'enterprise_instagram' } Instagram: - %input.chunky{ id: 'enterprise_instagram', placeholder: "eg. @instagram_handle", ng: { model: 'enterprise.instagram' } } + %label{ for: 'enterprise_instagram' } {{'instagram' | t}}: + %input.chunky{ id: 'enterprise_instagram', placeholder: "{{'instagram_placeholder' | t}}", ng: { model: 'enterprise.instagram' } } .row.buttons .small-12.columns - %input.button.secondary{ type: "button", value: "Back", ng: { click: "select('images')" } } - %input.button.primary.right{ type: "submit", value: "Continue" } + %input.button.secondary{ type: "button", value: "{{'back' | t}}", ng: { click: "select('images')" } } + %input.button.primary.right{ type: "submit", value: "{{'continue' | t}}" } diff --git a/app/assets/javascripts/templates/registration/type.html.haml b/app/assets/javascripts/templates/registration/type.html.haml index 48d45cb66a..c63b40239e 100644 --- a/app/assets/javascripts/templates/registration/type.html.haml +++ b/app/assets/javascripts/templates/registration/type.html.haml @@ -5,12 +5,9 @@ .row .small-12.columns %header - %h2 - Last step to add - %span{ ng: { class: "{brick: !enterprise.is_primary_producer, turquoise: enterprise.is_primary_producer}" } } - {{ enterprise.name }}! + %h2{ "ng-bind" => "'registration_type_headline' | t:{enterprise: enterprise.name}" } %h4 - Are you a producer? + {{'registration_type_question' | t}} %form{ name: 'type', novalidate: true, ng: { controller: "RegistrationFormCtrl", submit: "create(type)" } } .row#enterprise-types{ 'data-equalizer' => true, bo: { if: "enterprise.type != 'own'" } } @@ -19,28 +16,32 @@ .small-12.medium-6.large-6.columns{ 'data-equalizer-watch' => true } %a.btnpanel#producer-panel{ href: "#", ng: { click: "enterprise.is_primary_producer = true", class: "{selected: enterprise.is_primary_producer}" } } %i.ofn-i_059-producer - %h4 Yes, I'm a producer + %h4 {{'registration_type_producer' | t}} .small-12.medium-6.large-6.columns{ 'data-equalizer-watch' => true } %a.btnpanel#hub-panel{ href: "#", ng: { click: "enterprise.is_primary_producer = false", class: "{selected: enterprise.is_primary_producer == false}" } } %i.ofn-i_063-hub - %h4 No, I'm not a producer + %h4 {{'registration_type_no_producer' | t}} .row .small-12.columns %input.chunky{ id: 'enterprise_is_primary_producer', name: 'is_primary_producer', hidden: true, required: true, ng: { model: 'enterprise.is_primary_producer' } } %span.error{ ng: { show: "type.is_primary_producer.$error.required && submitted" } } - Please choose one. Are you are producer? + {{'registration_type_error' | t}} .row .small-12.columns .panel.callout .left %i.ofn-i_013-help   - %p Producers make yummy things to eat &/or drink. You're a producer if you grow it, raise it, brew it, bake it, ferment it, milk it or mould it. - / %p Hubs connect the producer to the eater. Hubs can be co-ops, independent retailers, buying groups, wholesalers, CSA box schemes, farm-gate stalls, etc. + %p {{'registration_type_producer_help' | t}} + .panel.callout + .left + %i.ofn-i_013-help +   + %p {{'registration_type_no_producer_help' | t}} .row.buttons .small-12.columns - %input.button.secondary{ type: "button", value: "Back", ng: { click: "select('contact')" } } - %input.button.primary.right{ type: "submit", value: "Continue" } + %input.button.secondary{ type: "button", value: "{{'back' | t}}", ng: { click: "select('contact')" } } + %input.button.primary.right{ type: "submit", value: "{{'create_profile' | t}}" } diff --git a/app/assets/javascripts/templates/registration_authentication.html.haml b/app/assets/javascripts/templates/registration_authentication.html.haml index c7bfbabeca..f277c50abc 100644 --- a/app/assets/javascripts/templates/registration_authentication.html.haml +++ b/app/assets/javascripts/templates/registration_authentication.html.haml @@ -1,7 +1,7 @@ .container .row.modal-centered - %h2 Welcome to the Open Food Network! - %h5 Start By Signing Up (or logging in): + %h2 {{'welcome_to_ofn' | t}} + %h5 {{'signup_or_login' | t}}: %div{"ng-controller" => "AuthenticationCtrl"} %tabset %ng-include{src: "'signup.html'"} @@ -9,9 +9,9 @@ %ng-include{src: "'forgot.html'"} %div{ ng: { show: "active('/signup')"} } %hr - Already have an account? + {{'have_an_account' | t}} %a{ href: "", ng: { click: "select('/login')"}} - Log in now. + {{'action_login' | t}} %a.close-reveal-modal{"ng-click" => "$close()"} %i.ofn-i_009-close diff --git a/app/assets/javascripts/templates/shipping_type_selector.html.haml b/app/assets/javascripts/templates/shipping_type_selector.html.haml index 8feb23e59f..7774ab62c0 100644 --- a/app/assets/javascripts/templates/shipping_type_selector.html.haml +++ b/app/assets/javascripts/templates/shipping_type_selector.html.haml @@ -1,3 +1,4 @@ -%active-selector{"ng-repeat" => "(name, selector) in selectors"} - %i{"ng-class" => "selector.icon"} - {{ name | capitalize }} +%ul.small-block-grid-2.medium-block-grid-4.large-block-grid-2 + %active-selector{"ng-repeat" => "(name, selector) in selectors"} + %i{"ng-class" => "selector.icon"} + {{ name | capitalize }} diff --git a/app/assets/javascripts/templates/shop_variant.html.haml b/app/assets/javascripts/templates/shop_variant.html.haml index 6d050d0bde..00e4f70d7c 100644 --- a/app/assets/javascripts/templates/shop_variant.html.haml +++ b/app/assets/javascripts/templates/shop_variant.html.haml @@ -5,12 +5,13 @@ .bulk-buy.inline{"bo-if" => "variant.product.group_buy"} %i.ofn-i_056-bulk>< %em>< - \ Bulk + \ {{'bulk' | t}} -# WITHOUT GROUP BUY .small-5.medium-3.large-3.columns.text-right{"bo-if" => "!variant.product.group_buy"} %input{type: :number, + integer: true, value: nil, min: 0, placeholder: "0", @@ -26,17 +27,20 @@ %span.bulk-input %input.bulk.first{type: :number, value: nil, + integer: true, min: 0, "ng-model" => "variant.line_item.quantity", - placeholder: "min", + placeholder: "{{'shop_variant_quantity_min' | t}}", "ofn-disable-scroll" => true, max: "{{variant.on_demand && 9999 || variant.count_on_hand }}", name: "variants[{{variant.id}}]", id: "variants_{{variant.id}}"} - %span.bulk-input{"bo-if" => "variant.product.group_buy"} + %span.bulk-input %input.bulk.second{type: :number, + "ng-disabled" => "!variant.line_item.quantity", + integer: true, min: 0, "ng-model" => "variant.line_item.max_quantity", - placeholder: "max", + placeholder: "{{'shop_variant_quantity_max' | t}}", "ofn-disable-scroll" => true, max: "{{variant.on_demand && 9999 || variant.count_on_hand }}", name: "variant_attributes[{{variant.id}}][max_quantity]"} diff --git a/app/assets/javascripts/templates/signup.html.haml b/app/assets/javascripts/templates/signup.html.haml index 28bec19b49..2e780aabb5 100644 --- a/app/assets/javascripts/templates/signup.html.haml +++ b/app/assets/javascripts/templates/signup.html.haml @@ -1,11 +1,11 @@ %tab#sign-up-content{"ng-controller" => "SignupCtrl", - heading: "Sign up", + heading: "{{'label_signup' | t}}", active: "active(path)", select: "select(path)"} %form{"ng-submit" => "submit()"} .row .large-12.columns - %label{for: "email"} Your email + %label{for: "email"} {{'signup_email' | t}} %input.title.input-text{name: "email", type: "email", id: "email", @@ -15,7 +15,7 @@ {{ errors.email.join(' ') }} .row .large-12.columns - %label{for: "password"} Choose a password + %label{for: "password"} {{'choose_password' | t}} %input.title.input-text{name: "password", type: "password", id: "password", @@ -26,7 +26,7 @@ {{ errors.password.join(' ') }} .row .large-12.columns - %label{for: "password_confirmation"} Confirm password + %label{for: "password_confirmation"} {{'confirm_password' | t}} %input.title.input-text{name: "password_confirmation", type: "password", id: "password_confirmation", @@ -38,4 +38,4 @@ %input.button.primary{name: "commit", tabindex: "3", type: "submit", - value: "Sign up now"} + value: "{{'action_signup' | t}}"} diff --git a/app/assets/javascripts/templates/single_line_selectors.html.haml b/app/assets/javascripts/templates/single_line_selectors.html.haml index d54ce57bbb..0f5cdf0fa6 100644 --- a/app/assets/javascripts/templates/single_line_selectors.html.haml +++ b/app/assets/javascripts/templates/single_line_selectors.html.haml @@ -1,8 +1,8 @@ -%ul - -# In order for the single-line-selector scope to have access to the available selectors, - %filter-selector{objects: "objects()", "active-selectors" => "activeSelectors", "all-selectors" => "allSelectors" } +-# In order for the single-line-selector scope to have access to the available selectors, +%filter-selector{"selector-set" => "selectors", objects: "objects()", "active-selectors" => "activeSelectors", "all-selectors" => "allSelectors" } - %li.more{ ng: { show: "overFlowSelectors().length > 0 || fitting" } } +%ul{ ng: { if: "overFlowSelectors().length > 0 || fitting" } } + %li.more %a.dropdown{ data: { dropdown: "{{ 'show-more-' + selectorName }}" }, ng: { class: "{active: selectedOverFlowSelectors().length > 0}" } } %span + {{ overFlowSelectors().length }} more diff --git a/app/assets/stylesheets/admin/account.css.scss b/app/assets/stylesheets/admin/account.css.scss new file mode 100644 index 0000000000..7d58147d91 --- /dev/null +++ b/app/assets/stylesheets/admin/account.css.scss @@ -0,0 +1,17 @@ +.row.invoice_title { + margin-bottom: 0px; +} + +table.invoice_summary { + margin-bottom: 70px; + + tr.total { + font-weight: bold; + } +} + +.invoice_title { + .balance { + color: #9fc820; + } +} diff --git a/app/assets/stylesheets/admin/all.css b/app/assets/stylesheets/admin/all.css index 9b3603fe7b..e0d668b95b 100644 --- a/app/assets/stylesheets/admin/all.css +++ b/app/assets/stylesheets/admin/all.css @@ -10,6 +10,7 @@ *= require shared/jquery-ui-timepicker-addon *= require shared/textAngular.min + *= require shared/ng-tags-input.min *= require_self *= require_tree . diff --git a/app/assets/stylesheets/admin/animations.css.sass b/app/assets/stylesheets/admin/animations.css.sass new file mode 100644 index 0000000000..88284c7487 --- /dev/null +++ b/app/assets/stylesheets/admin/animations.css.sass @@ -0,0 +1,35 @@ +@-webkit-keyframes slideInUp + 0% + -webkit-transform: translateY(20px) + transform: translateY(20px) + 100% + -webkit-transform: translateY(0) + transform: translateY(0) + +// @-webkit-keyframes slideOutDown +// 0% +// -webkit-transform: translateY(0) +// transform: translateY(0) +// 100% +// -webkit-transform: translateY(20px) +// transform: translateY(20px) + +.animate-show + -webkit-animation-name: slideInUp + animation-name: slideInUp + -webkit-animation-duration: 0.3s + animation-duration: 0.3s + -webkit-animation-fill-mode: both + animation-fill-mode: both + // line-height: 20px + // opacity: 1 + + // &.ng-hide + // -webkit-animation-name: slideOutDown + // animation-name: slideOutDown + // -webkit-animation-duration: 0.5s + // animation-duration: 0.5s + // -webkit-animation-fill-mode: both + // animation-fill-mode: both + // // line-height: 20px + // // opacity: 1 diff --git a/app/assets/stylesheets/admin/components/save_bar.sass b/app/assets/stylesheets/admin/components/save_bar.sass new file mode 100644 index 0000000000..c6b1236490 --- /dev/null +++ b/app/assets/stylesheets/admin/components/save_bar.sass @@ -0,0 +1,9 @@ +#save-bar + position: fixed + bottom: 0px + padding: 8px 10px + font-weight: bold + background-color: #fff + color: #5498da + h5 + color: #5498da diff --git a/app/assets/stylesheets/admin/dashboard_item.css.sass b/app/assets/stylesheets/admin/dashboard_item.css.sass index 13b16084f6..4bb4855660 100644 --- a/app/assets/stylesheets/admin/dashboard_item.css.sass +++ b/app/assets/stylesheets/admin/dashboard_item.css.sass @@ -25,7 +25,7 @@ div.dashboard_item border: 1px solid #5498da position: relative - a.with-tip + a[ofn-with-tip] position: absolute right: 5px bottom: 5px @@ -35,7 +35,7 @@ div.dashboard_item border-width: 3px h3 color: #DA5354 - + &.orange border-color: #DA7F52 border-width: 3px diff --git a/app/assets/stylesheets/admin/disabled.css.scss b/app/assets/stylesheets/admin/disabled.css.scss new file mode 100644 index 0000000000..a393c5d80b --- /dev/null +++ b/app/assets/stylesheets/admin/disabled.css.scss @@ -0,0 +1,13 @@ +label.disabled { + color: #c3c3c3; + pointer-events: none; +} + +input[type='button']:disabled { + background-color: #c3c3c3; + color: #ffffff; +} + +.select2-container-disabled { + pointer-events: none; +} diff --git a/app/assets/stylesheets/admin/dropdown.css.scss b/app/assets/stylesheets/admin/dropdown.css.scss new file mode 100644 index 0000000000..2dfa369c87 --- /dev/null +++ b/app/assets/stylesheets/admin/dropdown.css.scss @@ -0,0 +1,70 @@ +#content-header .ofn-drop-down { + border: none; + background-color: #5498da; + color: #fff; + float: none; + margin-left: 3px; +} + +.ofn-drop-down:hover, .ofn-drop-down.expanded { + border: 1px solid #adadad; + color: #575757; +} + +.ofn-drop-down { + padding: 7px 15px; + border-radius: 3px; + border: 1px solid #d4d4d4; + background-color: #f5f5f5; + position: relative; + display: block; + float: left; + color: #828282; + cursor: pointer; + -moz-user-select: none; + -khtml-user-select: none; + -webkit-user-select: none; + -ms-user-select: none; + user-select: none; + text-align: center; + + &.right { + float: right; + } + + &:hover, &.expanded { + border: 1px solid #adadad; + color: #575757; + } + + > span { + width: auto; + text-transform: uppercase; + font-size: 85%; + font-weight: 600; + } + + .menu { + margin-top: 1px; + position: absolute; + float: none; + top:100%; + left: 0px; + padding: 5px 0px; + border: 1px solid #adadad; + background-color: #ffffff; + box-shadow: 1px 3px 10px #888888; + z-index: 100; + + .menu_item { + margin: 0px; + padding: 2px 0px; + color: #454545; + text-align: left; + } + + .menu_item:hover { + background-color: #ededed; + } + } +} diff --git a/app/assets/stylesheets/admin/enterprise_index_panels.css.scss b/app/assets/stylesheets/admin/enterprise_index_panels.css.scss new file mode 100644 index 0000000000..2ce2916497 --- /dev/null +++ b/app/assets/stylesheets/admin/enterprise_index_panels.css.scss @@ -0,0 +1,113 @@ +.enterprise_package_panel, .enterprise_producer_panel { + .info { + p { + font-size: 1rem; + margin: 10px 0px; + } + } + + a.update { + cursor: pointer; + margin-bottom: 10px; + font-size: 1.3rem; + background-color: #DA5354; + &:hover { + background-color: #CD4E4F; + } + &.disabled { + background-color: #C1C1C1; + } + &.saving { + background-color: #FF9848; + i.icon-refresh { + -webkit-animation: spin 2s infinite linear; + animation: spin 2s infinite linear; + } + } + span{ + i{ + font-size: 1.5rem; + margin-left: 10px; + } + } + } + + a.selector { + display: block; + position: relative; + margin-bottom: 20px; + border: 2px solid black; + text-align: center; + // width: 100%; + cursor: pointer; + &, & * { + color: white; + } + &:hover { + &:after { + border-top-color: #9fc820; + } + } + &.disabled{ + background-color: #C1C1C1; + } + .bottom { + background: repeating-linear-gradient(60deg, rgba(84, 152, 218, 0), rgba(84, 152, 218, 0) 5px, rgba(255, 255, 255, 0.25) 5px, rgba(255, 255, 255, 0.25) 10px); + margin-top: 1em; + margin-left: -15px; + margin-right: -15px; + padding: 5px; + text-transform: uppercase; + } + &.selected { + background-color: #000000; + + &:after { + top: 50%; + left: 0; + border: solid transparent; + content: " "; + height: 0; + width: 0; + position: absolute; + pointer-events: none; + + border-top: 20px solid transparent; + border-bottom: 20px solid transparent; + border-right: 20px solid #000000; + margin-top: -20px; + margin-left: -20px; + } + } + } +} + +.enterprise_status_panel { + .status-ok { + margin: 30px 0px; + i.icon-ok-sign { + color: #9fc820; + font-size: 1.5rem; + } + } + + td.description{ + font-size: 0.9rem; + } + + td.severity { + text-align: center; + + i { + font-size: 1.5rem; + + &.issue{ + color: #da5354; + } + + &.warning{ + color: #ff9848; + } + } + } +} diff --git a/app/assets/stylesheets/admin/filters_and_controls.css.scss b/app/assets/stylesheets/admin/filters_and_controls.css.scss new file mode 100644 index 0000000000..8dd188d9cd --- /dev/null +++ b/app/assets/stylesheets/admin/filters_and_controls.css.scss @@ -0,0 +1,3 @@ +.filters, .controls, .divider { + margin-bottom: 15px; +} diff --git a/app/assets/stylesheets/admin/index_panels.css.scss b/app/assets/stylesheets/admin/index_panels.css.scss new file mode 100644 index 0000000000..a910e41d14 --- /dev/null +++ b/app/assets/stylesheets/admin/index_panels.css.scss @@ -0,0 +1,130 @@ +tr.panel-toggle-row { + td.panel-toggle{ + -webkit-touch-callout: none; + -webkit-user-select: none; + -khtml-user-select: none; + -moz-user-select: none; + -ms-user-select: none; + user-select: none; + + position: relative; + + i.icon-chevron::before { + font-size: 1.2rem; + content: "\f078"; + } + + &.error::before { + font-family: FontAwesome; + font-weight: normal; + font-style: normal; + display: inline-block; + text-decoration: inherit; + position: absolute; + top: 5px; + right: 5px; + font-size: 2rem; + -webkit-font-smoothing: antialiased; + content: "\f071"; + color: #da5354; + } + + &.status { + i.icon-status::before { + font-size: 1.5rem; + opacity: 0.5; + } + + i.issue::before { + content: "\f071"; + color: #da5354; + } + + i.warning::before { + content: "\f071"; + color: #ff9848; + } + + i.ok::before { + content: "\f058"; + color: #9fc820; + } + } + + &:hover { + cursor: pointer; + background-color: #d0e2f6; + * { + color: #1b3c56; + } + + i.icon-status::before { + opacity: 1.0; + } + } + } + + &.expanded{ + td { + border-bottom: 2px solid #444444; + + &.selected { + background-color: #ffffff; + border-left: 2px solid #444444; + border-right: 2px solid #444444; + border-top: 2px solid #444444; + border-bottom: none; + + &:hover { + background-color: #ffffff; + } + + * { + color: #1b3c56; + } + + i.icon-status::before { + opacity: 1.0; + } + + i.icon-chevron::before { + content: "\f077"; + } + } + } + } +} + +tr.panel-row { + display: none; + + &:hover { + td { + background-color: #ffffff; + } + } + + >td { + border-color: #444444; + padding: 0; + .panel { + border-left: 1px solid #444444; + border-right: 1px solid #444444; + border-bottom: 1px solid #444444; + + .row{ + margin: 0px -4px; + + padding: 20px 0px; + + .column.alpha, .columns.alpha { + padding-left: 20px; + } + + .column.omega, .columns.omega { + padding-right: 20px; + } + } + } + } +} diff --git a/app/assets/stylesheets/admin/openfoodnetwork.css.scss b/app/assets/stylesheets/admin/openfoodnetwork.css.scss index 8518b47e50..e0916b4e79 100644 --- a/app/assets/stylesheets/admin/openfoodnetwork.css.scss +++ b/app/assets/stylesheets/admin/openfoodnetwork.css.scss @@ -6,6 +6,10 @@ text-align: right; } +.underline { + text-decoration: underline; +} + table .blank-action { display: inline-block; width: 29px; @@ -31,6 +35,9 @@ text-angular .ta-editor { margin-bottom: 0; } +input.red { + background-color: #DA5354; +} input.search { margin-bottom: 1em; @@ -72,8 +79,13 @@ form.order_cycle { margin: 5px; } + .exchange-products { + display: -webkit-flex; + display: flex; + -webkit-flex-flow: row wrap; + flex-flow: row wrap; + } .exchange-product { - float: left; overflow: auto; width: 18%; min-height: 7.5em; @@ -84,8 +96,9 @@ form.order_cycle { .exchange-product-details { clear: both; margin-bottom: 1em; + min-height: 6em; - .supplier { + .name { font-weight: bold; } } @@ -171,58 +184,6 @@ table#listing_enterprise_groups { } } -.ofn_drop_down { - padding: 7px 15px; - border-radius: 3px; - border: 1px solid #d4d4d4; - background-color: #f5f5f5; - position: relative; - display: block; - float: left; - color: #828282; - cursor: pointer; - -moz-user-select: none; - -khtml-user-select: none; - -webkit-user-select: none; - -ms-user-select: none; - user-select: none; - text-align: center; - - > span { - text-transform: uppercase; - font-size: 85%; - font-weight: 600; - } - - .menu { - margin-top: 1px; - position: absolute; - float: none; - top:100%; - left: 0px; - padding: 5px 0px; - border: 1px solid #adadad; - background-color: #ffffff; - box-shadow: 1px 3px 10px #888888; - - .menu_item { - margin: 0px; - padding: 2px 0px; - color: #454545; - text-align: left; - } - - .menu_item:hover { - background-color: #ededed; - } - } -} - -.ofn_drop_down:hover, .ofn_drop_down.expanded { - border: 1px solid #adadad; - color: #575757; -} - .field_with_errors > input { border-color: red; } diff --git a/app/assets/stylesheets/admin/orders.css.scss b/app/assets/stylesheets/admin/orders.css.scss index c0c1dac86b..544abfa899 100644 --- a/app/assets/stylesheets/admin/orders.css.scss +++ b/app/assets/stylesheets/admin/orders.css.scss @@ -1,23 +1,32 @@ -.filter_select, .date_filter { - margin-bottom: 10px; +input, div { + &.update-pending { + border: solid 1px orange; + } } -.filter_clear { - input { - background-color: #DA5354; +input.show-dirty { + &.ng-dirty { + border: solid 1px orange; + &.update-error { + border: solid 1px #DA5354; + } + } +} + +span.error { + color: #DA5354; +} + +input, div { + &.update-error { + border: solid 1px #DA5354; } } -input.update-pending { - border: solid 1px orange; -} - -input.update-error { - border: solid 1px red; -} - -input.update-success { - border: solid 1px #9fc820; +input, div { + &.update-success { + border: solid 1px #9fc820; + } } .no-close .ui-dialog-titlebar-close { @@ -42,4 +51,22 @@ div#group_buy_calculation { .row span { text-align: center; } -} \ No newline at end of file +} + +.input-symbol { + position: relative; + &.before { + + span { + position: absolute; + transform: translate(0,-50%); + top:50%; + pointer-events:none; + margin-left: 1em; + } + + input { + text-indent:1em; + } + } +} diff --git a/app/assets/stylesheets/admin/select2.css.scss b/app/assets/stylesheets/admin/select2.css.scss new file mode 100644 index 0000000000..daba11d099 --- /dev/null +++ b/app/assets/stylesheets/admin/select2.css.scss @@ -0,0 +1,10 @@ +.select2-container { + .select2-choice { + .select2-arrow { + width: 22px; + border: none; + background-image: none; + background-color: transparent; + } + } +} diff --git a/app/assets/stylesheets/admin/validation.css.scss b/app/assets/stylesheets/admin/validation.css.scss new file mode 100644 index 0000000000..c97335a6c4 --- /dev/null +++ b/app/assets/stylesheets/admin/validation.css.scss @@ -0,0 +1,7 @@ +input.ng-invalid { + border: solid 1px red; + + &.update-pending { + border: solid 1px red; + } +} diff --git a/app/assets/stylesheets/darkswarm/_shop-filters.css.sass b/app/assets/stylesheets/darkswarm/_shop-filters.css.sass index 3b31481ee1..cf80c63035 100644 --- a/app/assets/stylesheets/darkswarm/_shop-filters.css.sass +++ b/app/assets/stylesheets/darkswarm/_shop-filters.css.sass @@ -92,24 +92,23 @@ span.filter-label opacity: 0.75 -.filter-shopfront.taxon-selectors, .filter-shopfront.property-selectors - background: transparent - - single-line-selectors - overflow-x: hidden - white-space: nowrap - - .f-dropdown - overflow-x: auto - white-space: normal - - ul - margin: 0 - ul, ul li - list-style: none - - .filter-shopfront + &.taxon-selectors, &.property-selectors + background: transparent + + single-line-selectors + overflow-x: hidden + white-space: nowrap + + .f-dropdown + overflow-x: auto + white-space: normal + + ul + margin: 0 + display: inline-block + ul, ul li + list-style: none // Shopfront taxons &.taxon-selectors diff --git a/app/assets/stylesheets/darkswarm/_shop-navigation.css.sass b/app/assets/stylesheets/darkswarm/_shop-navigation.css.sass index 17b68a3e46..a2f2304d3e 100644 --- a/app/assets/stylesheets/darkswarm/_shop-navigation.css.sass +++ b/app/assets/stylesheets/darkswarm/_shop-navigation.css.sass @@ -1,3 +1,4 @@ +@import typography .darkswarm @@ -20,7 +21,7 @@ margin-right: 12px location - font-family: "AvenirBla_IE", "AvenirBla" + @include headingFont @media all and (max-width: 768px) location, location + small display: block @@ -63,6 +64,7 @@ @media all and (max-width: 768px) font-size: 0.875em closing + @include headingFont @media all and (max-width: 768px) font-size: 1.2em padding-bottom: 10px diff --git a/app/assets/stylesheets/darkswarm/_shop-product-thumb.css.sass b/app/assets/stylesheets/darkswarm/_shop-product-thumb.css.sass index b5dca200c5..701e8005b0 100644 --- a/app/assets/stylesheets/darkswarm/_shop-product-thumb.css.sass +++ b/app/assets/stylesheets/darkswarm/_shop-product-thumb.css.sass @@ -13,7 +13,7 @@ height: 7rem float: left display: block - z-index: 999999 + z-index: 1 background-color: white overflow: hidden i @@ -56,4 +56,4 @@ width: 0rem height: 0rem - \ No newline at end of file + diff --git a/app/assets/stylesheets/darkswarm/all.scss b/app/assets/stylesheets/darkswarm/all.scss index 80e43cdc8b..6cfc32e605 100644 --- a/app/assets/stylesheets/darkswarm/all.scss +++ b/app/assets/stylesheets/darkswarm/all.scss @@ -2,13 +2,15 @@ * This is a manifest file that'll automatically include all the stylesheets available in this directory * and any sub-directories. You're free to add application-wide styles to this file and they'll appear at * the top of the compiled file, but it's generally better to create a new file per style scope. - + *= require_self - *= require foundation - *= require_tree . */ +@import 'variables'; +@import 'foundation'; @import 'foundation-icons'; +@import '*'; + ofn-modal { display: block; } \ No newline at end of file diff --git a/app/assets/stylesheets/darkswarm/all_split2.css b/app/assets/stylesheets/darkswarm/all_split2.css new file mode 100644 index 0000000000..e231ca5d02 --- /dev/null +++ b/app/assets/stylesheets/darkswarm/all_split2.css @@ -0,0 +1,3 @@ +/* + *= require 'darkswarm/all' + */ diff --git a/app/assets/stylesheets/darkswarm/animations.sass b/app/assets/stylesheets/darkswarm/animations.sass index 62e87e7224..d452b69d39 100644 --- a/app/assets/stylesheets/darkswarm/animations.sass +++ b/app/assets/stylesheets/darkswarm/animations.sass @@ -55,7 +55,7 @@ 100% opacity: 1 -@-webkit-keyframes spin +@-webkit-keyframes spin 0% -webkit-transform: rotate(0deg) transform: rotate(0deg) @@ -104,12 +104,10 @@ .animate-repeat - -webkit-transform: translateZ(0) - transform: translateZ(0) &.ng-move, &.ng-enter, &.ng-leave - -webkit-transition: all 300ms linear - transition: all 300ms linear - + -webkit-transition: all 300ms linear + transition: all 300ms linear + &.ng-leave opacity: 1 &.ng-leave-active @@ -178,7 +176,7 @@ product.animate-repeat overflow: hidden max-height: 0 opacity: 0 !important - + // &.ng-hide-add-active, &.ng-hide-remove-active &.ng-hide-add, &.ng-hide-remove @@ -197,7 +195,7 @@ product.animate-repeat &.ng-hide opacity: 0 !important - + // &.ng-hide-add-active, &.ng-hide-remove-active &.ng-hide-add, &.ng-hide-remove @@ -206,8 +204,8 @@ product.animate-repeat it as hidden. */ display: block !important - - + + @mixin csstrans @@ -217,7 +215,3 @@ product.animate-repeat -o-transition: all 300ms ease transition: all 300ms ease -webkit-transform-style: preserve-3d - - - - diff --git a/app/assets/stylesheets/darkswarm/big-input.sass b/app/assets/stylesheets/darkswarm/big-input.sass index 165404fff3..0660fda460 100644 --- a/app/assets/stylesheets/darkswarm/big-input.sass +++ b/app/assets/stylesheets/darkswarm/big-input.sass @@ -6,14 +6,14 @@ //Big search used in active table search \\ @mixin big-input($input, $inputhvr, $inputactv) - @include avenir + @include headingFont @include csstrans @include border-radius(0.5rem) background: rgba(255,255,255,0.1) border: 2px solid $input font-size: 2rem box-shadow: 0 - padding: 0.75rem 1rem 0.35rem + padding: 0.5rem 1rem height: auto width: 100% margin-bottom: 0.5rem @@ -33,7 +33,7 @@ background: white background: rgba(255,255,255,0.5) text-shadow: 0 0 10px #ffffff - padding: 1.5rem 1rem 1rem + padding: 1rem letter-spacing: 0.02rem outline: none @@ -44,7 +44,7 @@ letter-spacing: 0 @mixin medium-input($input, $inputhvr, $inputactv) - @include avenir + @include headingFont @include csstrans @include border-radius(0.5rem) background: rgba(255,255,255,0.1) diff --git a/app/assets/stylesheets/darkswarm/branding.css.sass b/app/assets/stylesheets/darkswarm/branding.css.sass index da2600818e..76614599cf 100644 --- a/app/assets/stylesheets/darkswarm/branding.css.sass +++ b/app/assets/stylesheets/darkswarm/branding.css.sass @@ -1,3 +1,11 @@ +$ofn-brand: #f27052 +// e.g. australia, uk, norway specific color + +$ofn-grey: #808184 + + +// old colors: + $clr-brick: #c1122b $clr-brick-light: #f5e6e7 $clr-brick-light-trans: rgba(245, 230, 231, 0.9) diff --git a/app/assets/stylesheets/darkswarm/footer.sass b/app/assets/stylesheets/darkswarm/footer.sass index 9e3f9dac55..76dd0f4384 100644 --- a/app/assets/stylesheets/darkswarm/footer.sass +++ b/app/assets/stylesheets/darkswarm/footer.sass @@ -1,15 +1,78 @@ @import branding @import mixins +@import animations footer - background: $dark-grey - border-top: 1px dotted white - @include panepadding .row - &, & * + p a + font-size: 0.875rem + a, a * + @include csstrans color: white - a, a * - color: $clr-brick-light-bright &:hover, &:active, &:focus - color: $clr-brick-bright - @include textsoftpress + color: rgba(white, 1) + text-decoration: underline + + .footer-global + background-color: $ofn-grey + padding-top: 60px + padding-bottom: 40px + .logo + width: 200px + height: 200px + background: $ofn-grey + @include border-radius(120px) + margin: -140px auto 0 auto + img + margin-top: 36px + + .alert-box + background-color: transparent + border: none + padding: 0 + a.big-alert + @include csstrans + width: 100% + border: 1px solid rgba($dark-grey, 0.35) + background-image: url("/assets/tile-wide.png") + background-position: center center + background-color: #bbb + padding: 12px 0 8px 0 + display: block + &, & * + @include csstrans + color: #333 + strong + letter-spacing: 0.5px + &:hover, &:active, &:focus + text-decoration: none + border-color: white + &, & * + color: rgba(white, 1) + .row + &, p, h1, h2, h3, h4, h5, h6 + color: $disabled-bright + + .footer-local + background: lighten($dark-grey, 3%) + @include panepadding + .row + &, p, h1, h2, h3, h4, h5, h6 + color: $disabled-med + p.secure-icon i + font-size: 10rem + color: rgba(white, 0.1) + p.secure-text + color: rgba($disabled-med, 0.35) + .social-icons + margin-bottom: 0.25rem + margin-top: 0.75rem + a + i + font-size: 1.5rem + color: white + &:hover, &:active, &:focus + text-decoration: none + i + color: lighten($dark-grey, 60%) + text-shadow: 2px 2px 0 black diff --git a/app/assets/stylesheets/darkswarm/groups.css.sass b/app/assets/stylesheets/darkswarm/groups.css.sass index 7f70bf7211..765689049d 100644 --- a/app/assets/stylesheets/darkswarm/groups.css.sass +++ b/app/assets/stylesheets/darkswarm/groups.css.sass @@ -1,14 +1,20 @@ @import branding @import mixins +@import typography // Search page #groups - background-color: $clr-brick-light + background-color: lighten($clr-brick, 56%) background-image: url("/assets/groups.svg") - background-position: center 15px + background-position: center 50px background-repeat: no-repeat - padding-bottom: 20px - + background-size: 922px 922px + @include sidepaddingSm + @include panepadding + h1, p.text + font-weight: 300 + h1 + font-size: 350% a > .group-name &:hover, &:focus, &:active text-decoration: underline @@ -65,7 +71,7 @@ margin-bottom: -2px margin-right: 2px text-transform: capitalize - @include avenir + @include headingFont @include border-radius(1em 0.25em 0 0) @include gradient($disabled-light, $disabled-bright) @media screen and (min-width: 768px) @@ -95,11 +101,13 @@ // Producers tab .producers background-image: none + background-color: initial .active_table .active_table_node a.is_distributor, .active_table .active_table_node a.is_distributor i.ofn-i_059-producer color: $clr-turquoise + padding: 0 // Hubs tab .hubs background-image: none padding-top: 0 padding-bottom: 0 - \ No newline at end of file + diff --git a/app/assets/stylesheets/darkswarm/header.css.sass b/app/assets/stylesheets/darkswarm/header.css.sass index f0bf6072d6..523afca824 100644 --- a/app/assets/stylesheets/darkswarm/header.css.sass +++ b/app/assets/stylesheets/darkswarm/header.css.sass @@ -1,18 +1,20 @@ +@import variables + nav.top-bar margin-bottom: 0px a.icon &:hover text-decoration: none - height: 45px + height: $topbar-height color: white i font-size: 29px - line-height: 45px + line-height: $topbar-height span font-size: 13px display: inline-block - line-height: 45px - height: 45px + line-height: $topbar-height + height: $topbar-height vertical-align: top body > section[role='main'] diff --git a/app/assets/stylesheets/darkswarm/home.css.sass b/app/assets/stylesheets/darkswarm/home.css.sass deleted file mode 100644 index e69de29bb2..0000000000 diff --git a/app/assets/stylesheets/darkswarm/home_panes.css.sass b/app/assets/stylesheets/darkswarm/home_panes.css.sass index 568b6ebc66..ed74450317 100644 --- a/app/assets/stylesheets/darkswarm/home_panes.css.sass +++ b/app/assets/stylesheets/darkswarm/home_panes.css.sass @@ -1,57 +1,149 @@ @import branding @import mixins - -// Styling for big panes on homepage - -#beta.pane - .row - @include panepadding - background-image: url("/assets/home/macbook.png") - background-repeat: no-repeat - background-position: center bottom - padding-bottom: 280px - &, & * - max-width: 610px +@import typography +@import animations +@import variables -#map.pane - @include darkbg - .row - @include panepadding - background-image: url("/assets/home/maps-bg.svg") - background-repeat: no-repeat - background-position: left center - -#groups.pane - @include darkbg - .row - @include panepadding - background-image: url("/assets/home/groups-bg.svg") - background-repeat: no-repeat - background-position: left center - -#producers.pane - @include turqbg - @include panepadding - background-image: url("/assets/home/producers-bg.svg") - background-repeat: no-repeat - background-position: center center - background-size: 80% 80% - - @media all and (max-width: 768px) - background-position: center top - background-size: 100% 100% - .row - .pricing-table - .title - color: $clr-turquoise-light - .price - background-color: rgba(240, 240, 240, 0.6) - .description, .bullet-item, .cta-button - background-color: rgba(255, 255, 255, 0.8) - -// Responsive -@media all and (max-width: 768px) - #map.pane, #groups.pane, #producers.pane +// Styling for big panes on homepage \\ +#panes + .pane .row - background-position: center center \ No newline at end of file + @include panepadding + padding-top: 75px + padding-bottom: 75px + &.header + padding-bottom: 0 + &.content + padding-top: 0 + + +// Background styles \\ +#system.pane + background-color: white + +#brand-story.pane, #cta.pane, #shops.pane + @include tiledPane + +#stats.pane + background-image: url("/assets/home/background-blurred-oranges.jpg") + background-position: center center + background-color: $ofn-grey + @include fullbg + @include paneWhiteText + + +// Content styles \\ +#brand-story.pane + .row + h2 + font-weight: 300 + font-size: 88px + p + @include bodyFont + font-size: 1.5rem + font-weight: 300 + @media all and (max-width: 768px) + h2 + font-size: 52px + p + font-size: 1.3rem + + a.text-vbig i + font-size: 75px + +#system.pane + .row .row + padding-bottom: 0 + @media all and (max-width: 640px) + .row .row + padding: 0 + + .home-icon-box + background-image: url("/assets/ofn-o.png") + background-position: center center + background-repeat: no-repeat + background-size: auto 100% + padding: 3rem 0 + text-align: center + margin-top: 2rem + @media all and (min-width: 642px) + margin-top: 0 + i + font-size: 4rem + a + display: block + width: 64px + height: 64px + margin: 0 auto + background-color: $brand-colour + background-position: center center + background-repeat: no-repeat + background-size: auto 100% + &.search + background-image: url("/assets/icon-mask-magnifier.png") + &.shop + background-image: url("/assets/icon-mask-apple.png") + &.pick-up-delivery + background-image: url("/assets/icon-mask-truck.png") + + h2 + font-size: 70px + font-weight: 300 + color: $brand-colour + @media all and (max-width: 640px) + font-size: 45px + + + a + color: $brand-colour + + .home-icon-box-bottom + margin-top: 1rem + width: 100% + padding-left: 1rem + padding-right: 1rem + @media all and (min-width: 480px) + padding-left: 3rem + padding-right: 3rem + @media all and (min-width: 642px) + padding-left: 1rem + padding-right: 1rem + h4 + color: $brand-colour + border-bottom: 2px solid lighten($brand-colour, 20%) + text-align: center + padding: 1rem 0 + margin: 1.5rem 0 + +#cta.pane, #stats.pane + h2 + font-weight: 300 + font-size: 45px + margin-bottom: 2rem + @media all and (max-width: 830px) + font-size: 35px + +#stats.pane + .row.header + padding-bottom: 2rem + + h4 + font-weight: 300 + text-transform: uppercase + margin: 1.5rem 0 + display: inline-block + strong + display: block + font-weight: normal + font-size: 75px + +#shops.pane + @include paneWhiteText + h2 + margin-bottom: 2rem + font-size: 4.4rem + font-weight: 300 + +#shops-signup.pane + background-color: $brand-colour diff --git a/app/assets/stylesheets/darkswarm/home_tagline.css.sass b/app/assets/stylesheets/darkswarm/home_tagline.css.sass index ed85e07d60..fadaae70b0 100644 --- a/app/assets/stylesheets/darkswarm/home_tagline.css.sass +++ b/app/assets/stylesheets/darkswarm/home_tagline.css.sass @@ -1,25 +1,33 @@ @import branding @import mixins +@import variables // Styling for brand intro / tagline on homepage #tagline - background-color: black - background-image: url("/assets/home/ofn_bg_1.jpg") - @include fullbg - height: 400px - padding: 40px 0px - h1, h2, p - color: white - h1 - margin-bottom: 1em - h2 - font-size: 1.6875rem - max-width: 610px - margin: 0 auto + width: 100% + &:before + content: "" + @include fullbg + background-color: $ofn-grey + background-image: url("/assets/home/home.jpg") + position: fixed + left: 0 + right: 0 + bottom: 0 + z-index: -1 + width: 100% + height: 100% - a - color: white - &:hover, &:active, &:focus - color: $clr-brick-light-bright - @include textsoftpress + h1 + margin-top: 2rem + @media all and (min-width: 768px) + margin-top: 10rem + img + max-width: 45% + @media all and (min-height: 500px) + max-width: 80% + + margin-bottom: 2rem + @media all and (min-width: 768px) + margin-bottom: 5rem diff --git a/app/assets/stylesheets/darkswarm/hub_node.css.sass b/app/assets/stylesheets/darkswarm/hub_node.css.sass index 94950fa22c..4d175681f2 100644 --- a/app/assets/stylesheets/darkswarm/hub_node.css.sass +++ b/app/assets/stylesheets/darkswarm/hub_node.css.sass @@ -30,9 +30,18 @@ float: right margin-left: 0.5rem + //Hub Link + @media all and (max-width: 640px) + a.hub + display: block + //Hub Name span.hub-name-listing font-weight: 700 + &:after + content: ">>" + display: inline-block + margin-left: 5px //CLOSED row &.closed diff --git a/app/assets/stylesheets/darkswarm/hubs.css.sass b/app/assets/stylesheets/darkswarm/hubs.css.sass index 43f2b8cbea..a351170d99 100644 --- a/app/assets/stylesheets/darkswarm/hubs.css.sass +++ b/app/assets/stylesheets/darkswarm/hubs.css.sass @@ -2,8 +2,9 @@ @import mixins #hubs - background-repeat: repeat - background-image: url("/assets/subtle_white_feathers.png") - // background: $clr-brick-ultra-light url("/assets/home/shopping-bg.jpg") - // @include fullwidthbg + background-color: lighten($ofn-grey, 43%) @include panepadding + @include sidepaddingSm + + .name-matches, .distance-matches + margin-top: 4em \ No newline at end of file diff --git a/app/assets/stylesheets/darkswarm/menu.css.sass b/app/assets/stylesheets/darkswarm/menu.css.sass index b256c6165c..41b86f72ff 100644 --- a/app/assets/stylesheets/darkswarm/menu.css.sass +++ b/app/assets/stylesheets/darkswarm/menu.css.sass @@ -1,49 +1,75 @@ +@import compass @import branding @import mixins @import typography +@import variables -.fixed .top-bar - @include box-shadow(0 2px 3px 0 rgba(0,0,0,0.25)) - nav @include textpress + text-shadow: none + + // Create center style for nav ul (foundation provides left and right) + text-align: center + .top-bar-section + // Avoid menu items blocking logo + li:not(.has-form), li:not(.has-form) a:not(.button), li:not(.has-form) a:not(.button):hover + background-color: transparent + + ul.center + display: inline-block + // By default, we center between the left and right uls, but we want to be centered + // relative to the whole page. The difference in width between the other uls is 74px, + // so we offset by that amount here. + margin-left: -74px + .joyride-tip-guide .button text-shadow: none // Default overrides - big menu - .top-bar-section ul li.ofn-logo > a - display: table-cell - vertical-align: middle - .top-bar-section .has-dropdown > a - padding-right: 15px !important - + padding-right: $topbar-height / 3 !important + i.ofn-i_022-cog font-size: 24px - line-height: 45px - + line-height: $topbar-height + .top-bar-section .has-dropdown > a:after display: none .top-bar-section ul li > a font-size: 0.75rem - height: 45px + height: $topbar-height opacity: 0.8 &:hover, &:focus, &:active opacity: 1 + @include transition(all 0.3s ease-in-out) + + .top-bar-section ul li.ofn-logo > a + display: table-cell + vertical-align: middle + opacity: 1 .nav-branded - color: $clr-brick-light-bright + color: $brand-colour span font-size: 13px .nav-primary - @include avenir + @include headingFont font-size: 0.875rem + font-weight: 300 + ul .nav-primary + text-transform: uppercase + ul.dropdown + border: 1px solid $smoke + border-top: none // Mobile Menu +.tab-bar + background-color: white + .off-canvas-wrap.move-right .tab-bar .menu-icon @include box-shadow(inset 0 0 6px 2px rgba(0,0,0,0.5)) @@ -55,29 +81,57 @@ nav -webkit-box-shadow: 0 0px 0 1px #666, 0 7px 0 1px #666, 0 14px 0 1px #666 box-shadow: 0 0px 0 1px #666, 0 7px 0 1px #666, 0 14px 0 1px #666 +.tab-bar .menu-icon span::after + box-shadow: 0 0 0 1px black, 0 7px 0 1px black, 0 14px 0 1px black + +.tab-bar .ofn-logo + padding: 9px 0 0 9px + +.left-off-canvas-menu + background-color: white + .off-canvas-wrap.move-right ul.off-canvas-list font-size: 0.875rem .li-menu - @include avenir + @include headingFont font-size: 1rem a - color: rgba(255, 255, 255, 0.9) + color: rgba(0, 0, 0, 0.9) + li a + color: rgba(0, 0, 0, 0.9) + &:hover + background-color: transparent + color: $brand-colour + @include transition(all 0.3s ease-in-out) .off-canvas-wrap.move-right ul.off-canvas-list i font-size: 1.5rem margin-right: 0.25rem -// Responsive + +// Responsive + +@media screen and (max-width: 1450px) + nav .top-bar-section + ul li a, .has-dropdown > a + padding: 0 $topbar-height / 8 !important + + ul.center + margin-left: -24px + @media screen and (min-width: 1025px) body.off-canvas // padding required to placehold for fixed menu bar - padding-top: 45px + padding-top: $topbar-height + + @media screen and (max-width: 1025px) - section.right + body.off-canvas + // padding required to placehold for fixed menu bar + padding-top: 0 + section.right .nav-branded padding: 0 1em - - \ No newline at end of file diff --git a/app/assets/stylesheets/darkswarm/mixins.sass b/app/assets/stylesheets/darkswarm/mixins.sass index 6925e84f72..4e779bd2a5 100644 --- a/app/assets/stylesheets/darkswarm/mixins.sass +++ b/app/assets/stylesheets/darkswarm/mixins.sass @@ -5,10 +5,46 @@ // Generic \\ +@mixin tiledPane + background-image: url("/assets/tile-wide.png") + background-color: $brand-colour + background-position: center center + @include paneWhiteText + @mixin panepadding padding-top: 100px padding-bottom: 100px +@mixin paneWhiteText + &, & * + color: white + +@mixin sidepaddingSm + padding-left: 10px + padding-right: 10px + @media all and (min-width: 768px) + padding-left: 20px + padding-right: 20px + @media all and (min-width: 1024px) + padding-left: 50px + padding-right: 50px + @media all and (min-width: 1200px) + padding-left: 100px + padding-right: 100px + +@mixin sidepaddingBg + padding-left: 20px + padding-right: 20px + @media all and (min-width: 768px) + padding-left: 40px + padding-right: 40px + @media all and (min-width: 1024px) + padding-left: 100px + padding-right: 100px + @media all and (min-width: 1200px) + padding-left: 200px + padding-right: 200px + @mixin disabled color: $disabled-bright @@ -53,9 +89,6 @@ // Typography \\ -@mixin avenir - font-family: "AvenirBla_IE", "AvenirBla" - @mixin textpress text-shadow: 0 -1px 1px #111111, 0 1px 2px #222222 @@ -108,7 +141,7 @@ color: $clr-turquoise-bright @mixin fullbg - background-position: center center + background-position: center center background-repeat: no-repeat -webkit-background-size: cover -moz-background-size: cover @@ -116,7 +149,7 @@ background-size: cover @mixin fullwidthbg - background-position: center top + background-position: center top background-repeat: no-repeat background-size: 100% auto @@ -137,4 +170,3 @@ // W3C filter: progid:DXImageTransform.Microsoft.gradient( startColorstr='$gradient-clr1', endColorstr='$gradient-clr2',GradientType=0 ) // IE6-8 - diff --git a/app/assets/stylesheets/darkswarm/modals.css.sass b/app/assets/stylesheets/darkswarm/modals.css.sass index 560153c5b1..5844a8b600 100644 --- a/app/assets/stylesheets/darkswarm/modals.css.sass +++ b/app/assets/stylesheets/darkswarm/modals.css.sass @@ -48,3 +48,7 @@ dialog .close-reveal-modal, .reveal-modal .close-reveal-modal &:hover, &:active, &:focus background-color: rgba(205,205,205,1) color: #333 + +// Prevent body from scrolling when a modal is open +body.modal-open + overflow: hidden diff --git a/app/assets/stylesheets/darkswarm/page_alert.css.sass b/app/assets/stylesheets/darkswarm/page_alert.css.sass new file mode 100644 index 0000000000..b7d6ea643e --- /dev/null +++ b/app/assets/stylesheets/darkswarm/page_alert.css.sass @@ -0,0 +1,58 @@ +@import branding +@import animations +@import "compass/css3/transition" + +// Basic style \\ +.page-alert + .alert-box + height: 55px + overflow: hidden + border: 1px solid rgba($dark-grey, 0.35) + border-left: none + border-right: none + background-color: #bbb + background-image: url("/assets/tile-wide.png") + background-position: center center + padding: 12px 0 8px 0 + margin: 0 + + h6 + @media all and (max-width: 480px) + font-size: 10px + line-height: 24px + + a.alert-cta + &, & * + @include csstrans + color: #333 + strong + letter-spacing: 0.5px + &:hover, &:active, &:focus + &, & * + text-decoration: none + color: white + + +// Show-hide animation \\ +.off-canvas-wrap .inner-wrap, .off-canvas-wrap .inner-wrap .fixed, nav.tab-bar + @include transition(all, 1000ms, ease-in-out) + + &.move-down + margin-top: 55px + @include transition(all, 1000ms, ease-in-out) + + +.off-canvas-wrap .inner-wrap .page-alert.fixed + top: -55px + z-index: 1 + // TODO: Compass to disable transition + -moz-transition: none + -webkit-transition: none + -o-transition: color 0 ease-in + transition: none + +.off-canvas-wrap.move-right .inner-wrap.move-down + .page-alert + top: -55px * 2 + .left-off-canvas-menu + top: -55px diff --git a/app/assets/stylesheets/darkswarm/producers.css.sass b/app/assets/stylesheets/darkswarm/producers.css.sass index 8f0e7edfcb..2055a45035 100644 --- a/app/assets/stylesheets/darkswarm/producers.css.sass +++ b/app/assets/stylesheets/darkswarm/producers.css.sass @@ -2,9 +2,13 @@ @import mixins .producers - @include fullwidthbg - background-image: url("/assets/producers/producers-pg-bg.jpg") + background-color: lighten($clr-turquoise, 68%) + background-image: url("/assets/producers.svg") + background-position: center 50px background-repeat: no-repeat + background-size: 922px 763px + @include sidepaddingSm + @include panepadding a color: $clr-turquoise &:hover, &:active, &:focus diff --git a/app/assets/stylesheets/darkswarm/registration.css.sass b/app/assets/stylesheets/darkswarm/registration.css.sass index f63eb30421..ba56701600 100644 --- a/app/assets/stylesheets/darkswarm/registration.css.sass +++ b/app/assets/stylesheets/darkswarm/registration.css.sass @@ -4,7 +4,6 @@ #registration-modal header text-align: center - // background-color: #efefef @media all and (max-width: 64em) text-align: left .container diff --git a/app/assets/stylesheets/darkswarm/shop.css.sass b/app/assets/stylesheets/darkswarm/shop.css.sass index 9d38398f36..5422c4463a 100644 --- a/app/assets/stylesheets/darkswarm/shop.css.sass +++ b/app/assets/stylesheets/darkswarm/shop.css.sass @@ -12,7 +12,6 @@ @import shop-popovers .darkswarm - products display: block padding-top: 20px diff --git a/app/assets/stylesheets/darkswarm/shopping-cart.css.sass b/app/assets/stylesheets/darkswarm/shopping-cart.css.sass index cb71913831..6fd48e0970 100644 --- a/app/assets/stylesheets/darkswarm/shopping-cart.css.sass +++ b/app/assets/stylesheets/darkswarm/shopping-cart.css.sass @@ -13,6 +13,11 @@ right: 10px top: 55px width: 480px + + @media screen and (min-width: 641px) + overflow-y: auto + max-height: calc(95vh - 55px) + @media screen and (max-width: 640px) width: 96% @@ -38,6 +43,7 @@ padding: 4px 12px color: #fff .buttons + margin-bottom: 0.1em .button height: auto top: 0px @@ -47,7 +53,7 @@ .cart-item-delete a.delete font-size: 1.125em - + .item-thumb-image display: none @media screen and (min-width: 640px) diff --git a/app/assets/stylesheets/darkswarm/signup.css.sass b/app/assets/stylesheets/darkswarm/signup.css.sass new file mode 100644 index 0000000000..cd6ebf458a --- /dev/null +++ b/app/assets/stylesheets/darkswarm/signup.css.sass @@ -0,0 +1,121 @@ +@import branding +@import mixins +@import typography +@import animations +@import variables + + +#producer-signup.pane, #shops-signup.pane + @include tiledPane + + h2 + margin-bottom: 2rem + font-size: 4.4rem + font-weight: 300 + +#producer-details.pane, #hub-details.pane, .groups-details.pane + background-color: lighten($ofn-grey, 44%) + + +#producer-case-studies, #shops-case-studies + padding-top: 100px + padding-bottom: 100px + background-color: $brand-colour + background-image: url("/assets/hubs-bg.jpg") + background-position: center center + -webkit-filter: brightness(1.1) + filter: brightness(1.1) + h2 + color: $brand-colour + font-size: 3rem + .case-study + background-color: rgba(255, 255, 255, 0.5) + padding: 1rem + margin-top: 2rem + text-align: center + .case-study-img + background-color: white + margin-bottom: 1rem + @media all and (min-width: 768px) + float: right + margin-left: 2rem + @media all and (min-width: 640px) + text-align: left + h4, a + color: $brand-colour + a + &, & * + @include csstrans + opacity: 1 + &:hover, &:focus, &:active + &, & * + opacity: 0.75 + + +// Signup tables \\ +table.signup-table + width: 100% + border: 0 + +table.signup-table.hubs-table, table.signup-table.producers-table + tr + td + background-color: white + border-bottom: 1px solid rgba($ofn-grey, 0.3) + td:nth-child(2) + background-color: lighten($ofn-grey, 46%) + td:nth-child(3) + background-color: lighten($ofn-grey, 41%) + td:last-child + &, & i + color: $brand-colour + border-bottom: 1px solid rgba($brand-colour, 0.3) + background-color: lighten($brand-colour, 48%) + thead + background-color: transparent + tr + td + border-bottom: 1px solid transparent + td:nth-child(1) + background-color: transparent + td:nth-child(2) + background: lighten($ofn-grey, 44%) + td:nth-child(3) + background: lighten($ofn-grey, 38%) + td:last-child + &, & * + color: white + background: $brand-colour + h5 + text-transform: uppercase + color: $ofn-grey + font-weight: 400 + font-size: 0.875rem + margin-bottom: 0.25em + + tfoot + background-color: transparent + tr + td + border-bottom: 1px solid transparent + td:nth-child(1) + background-color: transparent + td:nth-child(2) + background: lighten($ofn-grey, 44%) + td:nth-child(3) + background: lighten($ofn-grey, 38%) + td:last-child + &, & * + color: white + background: $brand-colour + h2 + .text-small + text-transform: uppercase + display: inline-block + font-weight: 400 + line-height: 1.5 + @include headingFont + +// Detail \\ +.enterprise-type-flowchart + float: right diff --git a/app/assets/stylesheets/darkswarm/style.css b/app/assets/stylesheets/darkswarm/style.css.scss old mode 100755 new mode 100644 similarity index 100% rename from app/assets/stylesheets/darkswarm/style.css rename to app/assets/stylesheets/darkswarm/style.css.scss diff --git a/app/assets/stylesheets/darkswarm/tabs.css.sass b/app/assets/stylesheets/darkswarm/tabs.css.sass index 0c911eb66a..12134a19e1 100644 --- a/app/assets/stylesheets/darkswarm/tabs.css.sass +++ b/app/assets/stylesheets/darkswarm/tabs.css.sass @@ -37,7 +37,7 @@ text-align: left a - @include avenir + @include headingFont background: transparent text-transform: uppercase line-height: 1 diff --git a/app/assets/stylesheets/darkswarm/typography.css.sass b/app/assets/stylesheets/darkswarm/typography.css.sass index f549420d83..8c151db832 100644 --- a/app/assets/stylesheets/darkswarm/typography.css.sass +++ b/app/assets/stylesheets/darkswarm/typography.css.sass @@ -1,30 +1,27 @@ @import branding -//@import mixins -@font-face - font-family: 'AvenirBla_IE' - src: url("/AveniBla.eot") format("opentype") +@mixin headingFont + font-family: 'Oswald', sans-serif -@font-face - font-family: 'AvenirBla' - src: url("/AvenirLTStd-Black.otf") format("opentype") +@mixin bodyFont + font-family: 'Roboto', Arial, sans-serif -@font-face - font-family: 'AvenirMed_IE' - src: url("/AveniMed.eot") format("opentype") +$headingFont: 'Oswald' +$bodyFont: 'Roboto' -@font-face - font-family: 'AvenirMed' - src: url("/AvenirLTStd-Medium.otf") format("opentype") - -$font-helvetica: "Helvetica Neue", "HelveticaNeue", "Helvetica", Helvetica, Arial, sans-serif - +body + @include bodyFont + font-weight: 400 a color: $clr-brick &:hover, &:focus, &:active text-decoration: none color: $clr-brick-bright +.text-vbig + font-size: 2rem + font-weight: 300 + .text-big font-size: 1.5rem font-weight: 300 @@ -35,17 +32,17 @@ small, .small .text-small font-size: 0.875rem margin-bottom: 0.5rem - font-family: $font-helvetica + font-family: $bodyFont &, & * font-size: 0.875rem .text-normal font-weight: 400 - font-family: $font-helvetica + font-family: $bodyFont .text-skinny font-weight: 300 - font-family: $font-helvetica + font-family: $bodyFont .word-wrap word-wrap: break-word @@ -55,7 +52,7 @@ small, .small .pre-line white-space: pre-line - + .light color: #999 display: inline @@ -69,13 +66,17 @@ small, .small .brick color: $clr-brick -@mixin avenir - font-family: "AvenirBla_IE", "AvenirBla" +.hr-light + border-color: rgba(#ddd, 0.25) -h1, h2, h3, h4, h5, h6, .avenir - @include avenir +h1, h2, h3, h4, h5, h6 + @include headingFont padding: 0px +.inline-header + display: inline-block + margin: 0px + ul.bullet-list, ul.check-list margin: 0 0 0 1.25em !important li @@ -108,15 +109,15 @@ ul.check-list .not-bold font-weight: normal -strong.avenir - font-weight: normal // Avenir is basically bold anyway +.footer-pad + padding-bottom: 100px + - // These selectors match the default Foundation selectors // For clean overriden magic table tr th, table tr td color: #333333 -table thead tr th, table thead tr td, table tfoot tr th, table tfoot tr td +table thead tr th, table thead tr td, table tfoot tr th, table tfoot tr td color: #333333 span.email diff --git a/app/assets/stylesheets/darkswarm/ui.css.sass b/app/assets/stylesheets/darkswarm/ui.css.sass index d18adae1b4..5c049a2bba 100644 --- a/app/assets/stylesheets/darkswarm/ui.css.sass +++ b/app/assets/stylesheets/darkswarm/ui.css.sass @@ -1,18 +1,19 @@ @import foundation/components/buttons @import branding @import mixins +@import typography // Button class extensions .neutral-btn @include button @include border-radius(0.5em) - font-family: 'Open Sans', Calibri, Candara, Segoe, "Segoe UI", Optima, Arial, sans-serif + font-family: $bodyFont background-color: transparent border: 2px solid rgba(200, 200, 200, 1) color: #999 -.neutral-btn:hover, .neutral-btn:active, .neutral-btn:focus +.neutral-btn:hover, .neutral-btn:active, .neutral-btn:focus background-color: rgba(200, 200, 200, 0.2) border: 2px solid rgba(200, 200, 200, 0.8) @@ -20,7 +21,7 @@ border-color: #000 color: #000 -.neutral-btn.dark:hover, .neutral-btn.dark:active, .neutral-btn.dark:focus +.neutral-btn.dark:hover, .neutral-btn.dark:active, .neutral-btn.dark:focus background-color: rgba(0, 0, 0, 0.1) border: 2px solid rgba(0, 0, 0, 0.8) text-shadow: 0 1px 0 #fff @@ -29,18 +30,18 @@ border-color: #fff color: #fff -.neutral-btn.light:hover, .neutral-btn.light:active, .neutral-btn.light:focus +.neutral-btn.light:hover, .neutral-btn.light:active, .neutral-btn.light:focus background-color: rgba(255, 255, 255, 0.2) border: 2px solid rgba(255, 255, 255, 0.8) - text-shadow: 0 1px 0 $clr-brick + text-shadow: 0 1px 0 rgba(0,0,0,0.2) .neutral-btn.turquoise border-color: $clr-turquoise color: $clr-turquoise -.neutral-btn.turquoise:hover, .neutral-btn.turquoise:active, .neutral-btn.turquoise:focus +.neutral-btn.turquoise:hover, .neutral-btn.turquoise:active, .neutral-btn.turquoise:focus background-color: rgba(0, 0, 0, 0.1) - text-shadow: 0 1px 0 #fff + // text-shadow: 0 1px 0 #fff // Rewrite foundation's .primary button style @@ -49,7 +50,7 @@ outline: none // Turn off blue highlight on chrome .button.primary, button.primary - font-family: 'Open Sans', Calibri, Candara, Segoe, "Segoe UI", Optima, Arial, sans-serif + font-family: $bodyFont background: $clr-brick color: white @@ -58,7 +59,7 @@ text-shadow: 0 1px 0 $clr-brick button.success, .button.success - background: #0096ad + background: #0096ad .button.success:hover, .button.success:active, .button.success:focus, button.success:hover, button.success:active, button.success:focus background: #14b6cc @@ -77,11 +78,20 @@ button.success, .button.success label margin: 0 0.2rem float: right - -// Responsive +// Transparent button +.button.transparent + @include headingFont + font-size: 20px + text-transform: uppercase + background: rgba(0, 0, 0, 0.3) + border: 3px solid #fff + text-shadow: none + &:hover + background: rgba(0, 0, 0, 0.6) + color: #fff + +// Responsive @media screen and (min-width: 768px) [role="main"] padding: 0 - - diff --git a/app/assets/stylesheets/darkswarm/variables.css.sass b/app/assets/stylesheets/darkswarm/variables.css.sass index 1020f7cbaa..ea87d11fc4 100644 --- a/app/assets/stylesheets/darkswarm/variables.css.sass +++ b/app/assets/stylesheets/darkswarm/variables.css.sass @@ -1 +1,32 @@ -// necessary; user to overwrite Foundation variables css \ No newline at end of file +@import "foundation/functions" +@import "foundation/components/global" + +// Brand guide colours: +// International: #81c26e +// Australia: #f35746 +// Africa: #f35e32 +// South Africa: #f9a72b +// Norway: #4b83cc +// Scandanavia: #0c8bbc +// UK: #e6373f + +$brand-colour: #f27052 + + +// Topbar +$topbar-height: rem-calc(75) +$topbar-link-padding: $topbar-height / 3 + +$topbar-bg: $white +$topbar-bg-color: $topbar-bg + +$topbar-link-color: $black +$topbar-link-color-hover: $brand-colour +$topbar-link-color-active: $black +$topbar-link-color-active-hover: $white +$topbar-link-bg-hover: $white + +$topbar-dropdown-link-color: $black +$topbar-dropdown-bg: $white +$topbar-dropdown-link-bg: $white +$topbar-dropdown-link-bg-hover: $white diff --git a/app/assets/stylesheets/mail/email.css.sass b/app/assets/stylesheets/mail/email.css.sass index 5f0a91bd72..4256357923 100644 --- a/app/assets/stylesheets/mail/email.css.sass +++ b/app/assets/stylesheets/mail/email.css.sass @@ -66,7 +66,7 @@ table.order-summary padding-right: 5px h4 margin-top: 15px - tfoot + tfoot tr:first-child td border-top: 1px solid black padding-top: 5px @@ -303,3 +303,7 @@ ul width: auto!important img.float-right float: none!important + +.inline-header + display: inline-block + margin: 0px diff --git a/app/assets/stylesheets/shared/ng-tags-input.min.css b/app/assets/stylesheets/shared/ng-tags-input.min.css new file mode 100755 index 0000000000..ee4a4a98d5 --- /dev/null +++ b/app/assets/stylesheets/shared/ng-tags-input.min.css @@ -0,0 +1 @@ +tags-input{display:block}tags-input *,tags-input :after,tags-input :before{-moz-box-sizing:border-box;-webkit-box-sizing:border-box;box-sizing:border-box}tags-input .host{position:relative;margin-top:5px;margin-bottom:5px;height:100%}tags-input .host:active{outline:0}tags-input .tags{-moz-appearance:textfield;-webkit-appearance:textfield;padding:1px;overflow:hidden;word-wrap:break-word;cursor:text;background-color:#fff;border:1px solid #a9a9a9;box-shadow:1px 1px 1px 0 #d3d3d3 inset;height:100%}tags-input .tags.focused{outline:0;-webkit-box-shadow:0 0 3px 1px rgba(5,139,242,.6);-moz-box-shadow:0 0 3px 1px rgba(5,139,242,.6);box-shadow:0 0 3px 1px rgba(5,139,242,.6)}tags-input .tags .tag-list{margin:0;padding:0;list-style-type:none}tags-input .tags .tag-item{margin:2px;padding:0 5px;display:inline-block;float:left;font:14px "Helvetica Neue",Helvetica,Arial,sans-serif;height:26px;line-height:25px;border:1px solid #acacac;border-radius:3px;background:-webkit-linear-gradient(top,#f0f9ff 0,#cbebff 47%,#a1dbff 100%);background:linear-gradient(to bottom,#f0f9ff 0,#cbebff 47%,#a1dbff 100%)}tags-input .tags .tag-item.selected{background:-webkit-linear-gradient(top,#febbbb 0,#fe9090 45%,#ff5c5c 100%);background:linear-gradient(to bottom,#febbbb 0,#fe9090 45%,#ff5c5c 100%)}tags-input .tags .tag-item .remove-button{margin:0 0 0 5px;padding:0;border:none;background:0 0;cursor:pointer;vertical-align:middle;font:700 16px Arial,sans-serif;color:#585858}tags-input .tags .tag-item .remove-button:active{color:red}tags-input .tags .input{border:0;outline:0;margin:2px;padding:0;padding-left:5px;float:left;height:26px;font:14px "Helvetica Neue",Helvetica,Arial,sans-serif}tags-input .tags .input.invalid-tag{color:red}tags-input .tags .input::-ms-clear{display:none}tags-input.ng-invalid .tags{-webkit-box-shadow:0 0 3px 1px rgba(255,0,0,.6);-moz-box-shadow:0 0 3px 1px rgba(255,0,0,.6);box-shadow:0 0 3px 1px rgba(255,0,0,.6)}tags-input[disabled] .host:focus{outline:0}tags-input[disabled] .tags{background-color:#eee;cursor:default}tags-input[disabled] .tags .tag-item{opacity:.65;background:-webkit-linear-gradient(top,#f0f9ff 0,rgba(203,235,255,.75)47%,rgba(161,219,255,.62)100%);background:linear-gradient(to bottom,#f0f9ff 0,rgba(203,235,255,.75)47%,rgba(161,219,255,.62)100%)}tags-input[disabled] .tags .tag-item .remove-button{cursor:default}tags-input[disabled] .tags .tag-item .remove-button:active{color:#585858}tags-input[disabled] .tags .input{background-color:#eee;cursor:default}tags-input .autocomplete{margin-top:5px;position:absolute;padding:5px 0;z-index:999;width:100%;background-color:#fff;border:1px solid rgba(0,0,0,.2);-webkit-box-shadow:0 5px 10px rgba(0,0,0,.2);-moz-box-shadow:0 5px 10px rgba(0,0,0,.2);box-shadow:0 5px 10px rgba(0,0,0,.2)}tags-input .autocomplete .suggestion-list{margin:0;padding:0;list-style-type:none;max-height:280px;overflow-y:auto;position:relative}tags-input .autocomplete .suggestion-item{padding:5px 10px;cursor:pointer;white-space:nowrap;overflow:hidden;text-overflow:ellipsis;font:16px "Helvetica Neue",Helvetica,Arial,sans-serif;color:#000;background-color:#fff}tags-input .autocomplete .suggestion-item.selected,tags-input .autocomplete .suggestion-item.selected em{color:#fff;background-color:#0097cf}tags-input .autocomplete .suggestion-item em{font:normal bold 16px "Helvetica Neue",Helvetica,Arial,sans-serif;color:#000;background-color:#fff} \ No newline at end of file diff --git a/app/controllers/admin/account_controller.rb b/app/controllers/admin/account_controller.rb new file mode 100644 index 0000000000..00531cd426 --- /dev/null +++ b/app/controllers/admin/account_controller.rb @@ -0,0 +1,6 @@ +class Admin::AccountController < Spree::Admin::BaseController + + def show + @invoices = spree_current_user.account_invoices + end +end diff --git a/app/controllers/admin/accounts_and_billing_settings_controller.rb b/app/controllers/admin/accounts_and_billing_settings_controller.rb new file mode 100644 index 0000000000..0f3b986cef --- /dev/null +++ b/app/controllers/admin/accounts_and_billing_settings_controller.rb @@ -0,0 +1,74 @@ +require 'open_food_network/accounts_and_billing_settings_validator' + +class Admin::AccountsAndBillingSettingsController < Spree::Admin::BaseController + before_filter :load_distributors, only: [:edit, :update, :start_job] + before_filter :load_jobs, only: [:edit, :update, :start_job] + before_filter :load_settings, only: [:edit, :update, :start_job] + before_filter :require_valid_settings, only: [:update, :start_job] + before_filter :require_known_job, only: [:start_job] + + def update + Spree::Config.set(params[:settings]) + flash[:success] = t(:successfully_updated, :resource => t(:billing_and_account_settings)) + redirect_to_edit + end + + def start_job + if @update_account_invoices_job || @finalize_account_invoices_job + flash[:error] = "A task is already running, please wait until it has finished" + else + new_job = "#{params[:job][:name]}".camelize.constantize.new + Delayed::Job.enqueue new_job + flash[:success] = "Task Queued" + end + + redirect_to_edit + end + + def show_methods + @enterprise = Enterprise.find_by_id(params[:enterprise_id]) + @shipping_methods = @enterprise.shipping_methods + @payment_methods = @enterprise.payment_methods + render partial: 'method_settings' + end + + private + + def redirect_to_edit + redirect_to main_app.edit_admin_accounts_and_billing_settings_path + end + + def require_valid_settings + render :edit unless @settings.valid? + end + + def known_jobs + ['update_account_invoices', 'finalize_account_invoices'] + end + + def require_known_job + unless known_jobs.include?(params[:job][:name]) + flash[:error] = "Unknown Task: #{params[:job][:name].to_s}" + redirect_to_edit + end + end + + def load_settings + @settings = OpenFoodNetwork::AccountsAndBillingSettingsValidator.new(params[:settings] || { + accounts_distributor_id: Spree::Config[:accounts_distributor_id], + default_accounts_payment_method_id: Spree::Config[:default_accounts_payment_method_id], + default_accounts_shipping_method_id: Spree::Config[:default_accounts_shipping_method_id], + auto_update_invoices: Spree::Config[:auto_update_invoices], + auto_finalize_invoices: Spree::Config[:auto_finalize_invoices] + }) + end + + def load_distributors + @distributors = Enterprise.is_distributor.select([:id, :name]) + end + + def load_jobs + @update_account_invoices_job = Delayed::Job.where("handler LIKE (?)", "%UpdateAccountInvoices%").last + @finalize_account_invoices_job = Delayed::Job.where("handler LIKE (?)", "%FinalizeAccountInvoices%").last + end +end diff --git a/app/controllers/admin/business_model_configuration_controller.rb b/app/controllers/admin/business_model_configuration_controller.rb new file mode 100644 index 0000000000..312a2e3208 --- /dev/null +++ b/app/controllers/admin/business_model_configuration_controller.rb @@ -0,0 +1,31 @@ +require 'open_food_network/business_model_configuration_validator' + +class Admin::BusinessModelConfigurationController < Spree::Admin::BaseController + before_filter :load_settings, only: [:edit, :update] + before_filter :require_valid_settings, only: [:update] + + def update + Spree::Config.set(params[:settings]) + flash[:success] = t(:successfully_updated, :resource => t(:business_model_configuration)) + redirect_to_edit + end + + private + + def redirect_to_edit + redirect_to main_app.edit_admin_business_model_configuration_path + end + + def load_settings + @settings = OpenFoodNetwork::BusinessModelConfigurationValidator.new(params[:settings] || { + account_invoices_monthly_fixed: Spree::Config[:account_invoices_monthly_fixed], + account_invoices_monthly_rate: Spree::Config[:account_invoices_monthly_rate], + account_invoices_monthly_cap: Spree::Config[:account_invoices_monthly_cap], + account_invoices_tax_rate: Spree::Config[:account_invoices_tax_rate] + }) + end + + def require_valid_settings + render :edit unless @settings.valid? + end +end diff --git a/app/controllers/admin/contents_controller.rb b/app/controllers/admin/contents_controller.rb new file mode 100644 index 0000000000..87c86997d9 --- /dev/null +++ b/app/controllers/admin/contents_controller.rb @@ -0,0 +1,29 @@ +module Admin + class ContentsController < Spree::Admin::BaseController + def edit + @preference_sections = [{name: 'Header', preferences: [:logo, :logo_mobile, :logo_mobile_svg]}, + {name: 'Home page', preferences: [:home_hero, :home_show_stats]}, + {name: 'Producer signup page', preferences: [:producer_signup_pricing_table_html, :producer_signup_case_studies_html, :producer_signup_detail_html]}, + {name: 'Hub signup page', preferences: [:hub_signup_pricing_table_html, :hub_signup_case_studies_html, :hub_signup_detail_html]}, + {name: 'Group signup page', preferences: [:group_signup_pricing_table_html, :group_signup_case_studies_html, :group_signup_detail_html]}, + {name: 'Footer', preferences: [:footer_logo, + :footer_facebook_url, :footer_twitter_url, :footer_instagram_url, :footer_linkedin_url, :footer_googleplus_url, :footer_pinterest_url, + :footer_email, :footer_links_md, :footer_about_url, :footer_tos_url]}] + end + + def update + params.each do |name, value| + if ContentConfig.has_preference?(name) || ContentConfig.has_attachment?(name) + ContentConfig.send("#{name}=", value) + end + end + + # Save any uploaded images + ContentConfig.save + + flash[:success] = t(:successfully_updated, :resource => "Your content") + + redirect_to main_app.edit_admin_content_path + end + end +end diff --git a/app/controllers/admin/customers_controller.rb b/app/controllers/admin/customers_controller.rb new file mode 100644 index 0000000000..b1ceb88c2f --- /dev/null +++ b/app/controllers/admin/customers_controller.rb @@ -0,0 +1,29 @@ +module Admin + class CustomersController < ResourceController + before_filter :load_managed_shops, only: :index, if: :html_request? + respond_to :json + + def index + respond_to do |format| + format.html + format.json do + render json: ActiveModel::ArraySerializer.new( @collection, + each_serializer: Api::Admin::CustomerSerializer, spree_current_user: spree_current_user + ).to_json + end + end + end + + private + + def collection + return Customer.where("1=0") unless json_request? && params[:enterprise_id].present? + enterprise = Enterprise.managed_by(spree_current_user).find_by_id(params[:enterprise_id]) + Customer.of(enterprise) + end + + def load_managed_shops + @shops = Enterprise.managed_by(spree_current_user).is_distributor + end + end +end diff --git a/app/controllers/admin/enterprise_fees_controller.rb b/app/controllers/admin/enterprise_fees_controller.rb index b8ea46689f..866c05ea54 100644 --- a/app/controllers/admin/enterprise_fees_controller.rb +++ b/app/controllers/admin/enterprise_fees_controller.rb @@ -89,7 +89,7 @@ module Admin end def collection_actions - [:index, :for_order_cycle] + [:index, :for_order_cycle, :bulk_update] end def current_enterprise diff --git a/app/controllers/admin/enterprise_groups_controller.rb b/app/controllers/admin/enterprise_groups_controller.rb index cb3ac80935..022b2f7d61 100644 --- a/app/controllers/admin/enterprise_groups_controller.rb +++ b/app/controllers/admin/enterprise_groups_controller.rb @@ -9,7 +9,7 @@ module Admin def move_up EnterpriseGroup.with_isolation_level_serializable do - @enterprise_group = EnterpriseGroup.find_by_permalink params[:enterprise_group_id] + @enterprise_group = find_resource @enterprise_group.move_higher end redirect_to main_app.admin_enterprise_groups_path @@ -17,7 +17,7 @@ module Admin def move_down EnterpriseGroup.with_isolation_level_serializable do - @enterprise_group = EnterpriseGroup.find_by_permalink params[:enterprise_group_id] + @enterprise_group = find_resource @enterprise_group.move_lower end redirect_to main_app.admin_enterprise_groups_path @@ -34,9 +34,11 @@ module Admin alias_method_chain :build_resource, :address # Overriding method on Spree's resource controller, - # so that resources are found using permalink + # so that resources are found using permalink. + # The ! version is important to raise a RecordNotFound error. def find_resource - EnterpriseGroup.find_by_permalink(params[:id]) + permalink = params[:id] || params[:enterprise_group_id] + EnterpriseGroup.find_by_permalink!(permalink) end private diff --git a/app/controllers/admin/enterprises_controller.rb b/app/controllers/admin/enterprises_controller.rb index a0b55ad3c8..13d5772385 100644 --- a/app/controllers/admin/enterprises_controller.rb +++ b/app/controllers/admin/enterprises_controller.rb @@ -1,7 +1,9 @@ +require 'open_food_network/referer_parser' + module Admin class EnterprisesController < ResourceController before_filter :load_enterprise_set, :only => :index - before_filter :load_countries, :except => [:index, :set_sells, :check_permalink] + before_filter :load_countries, :except => [:index, :register, :check_permalink] before_filter :load_methods_and_fees, :only => [:new, :edit, :update, :create] before_filter :load_groups, :only => [:new, :edit, :update, :create] before_filter :load_taxons, :only => [:new, :edit, :update, :create] @@ -16,34 +18,58 @@ module Admin before_filter :load_properties, only: [:edit, :update] before_filter :setup_property, only: [:edit] - helper 'spree/products' include ActionView::Helpers::TextHelper include OrderCyclesHelper - def set_sells - enterprise = Enterprise.find_by_permalink(params[:id]) || Enterprise.find(params[:id]) - attributes = { sells: params[:sells] } - attributes[:producer_profile_only] = params[:sells] == "none" && !!params[:producer_profile_only] - attributes[:shop_trial_start_date] = Time.now if params[:sells] == "own" + def index + respond_to do |format| + format.html + format.json { render_as_json @collection, ams_prefix: params[:ams_prefix], spree_current_user: spree_current_user } + end + end - if %w(none own).include?(params[:sells]) - if params[:sells] == 'own' && enterprise.shop_trial_start_date - expiry = enterprise.shop_trial_start_date + Enterprise::SHOP_TRIAL_LENGTH.days - if Time.now > expiry - flash[:error] = "Sorry, but you've already had a trial. Expired on: #{expiry.strftime('%Y-%m-%d')}" - else - attributes.delete :shop_trial_start_date - enterprise.update_attributes(attributes) - flash[:notice] = "Welcome back! Your trial expires on: #{expiry.strftime('%Y-%m-%d')}" - end - elsif enterprise.update_attributes(attributes) - flash[:success] = "Congratulations! Registration for #{enterprise.name} is complete!" + def welcome + render layout: "spree/layouts/bare_admin" + end + + def update + invoke_callbacks(:update, :before) + if @object.update_attributes(params[object_name]) + invoke_callbacks(:update, :after) + flash[:success] = flash_message_for(@object, :successfully_updated) + respond_with(@object) do |format| + format.html { redirect_to location_after_save } + format.js { render :layout => false } + format.json { render_as_json @object, ams_prefix: 'index', spree_current_user: spree_current_user } end else - flash[:error] = "Unauthorised" + invoke_callbacks(:update, :fails) + respond_with(@object) do |format| + format.json { render json: { errors: @object.errors.messages }, status: :unprocessable_entity } + end + end + end + + def register + if params[:sells] == 'unspecified' + flash[:error] = "Please select a package" + return render :welcome, layout: "spree/layouts/bare_admin" + end + + attributes = { sells: params[:sells], visible: true } + + if ['own', 'any'].include? params[:sells] + attributes[:shop_trial_start_date] = @enterprise.shop_trial_start_date || Time.zone.now + end + + if @enterprise.update_attributes(attributes) + flash[:success] = "Congratulations! Registration for #{@enterprise.name} is complete!" + redirect_to admin_path + else + flash[:error] = "Could not complete registration for #{@enterprise.name}" + render :welcome, layout: "spree/layouts/bare_admin" end - redirect_to admin_path end def bulk_update @@ -70,9 +96,15 @@ module Admin def for_order_cycle respond_to do |format| format.json do - render json: ActiveModel::ArraySerializer.new( @collection, - each_serializer: Api::Admin::ForOrderCycle::EnterpriseSerializer, spree_current_user: spree_current_user - ).to_json + render json: @collection, each_serializer: Api::Admin::ForOrderCycle::EnterpriseSerializer, spree_current_user: spree_current_user + end + end + end + + def for_line_items + respond_to do |format| + format.json do + render_as_json @collection, ams_prefix: 'basic', spree_current_user: spree_current_user end end end @@ -96,7 +128,7 @@ module Admin private def load_enterprise_set - @enterprise_set = EnterpriseSet.new collection + @enterprise_set = EnterpriseSet.new(collection) if spree_current_user.admin? end def load_countries @@ -110,6 +142,18 @@ module Admin coordinator = Enterprise.find_by_id(params[:coordinator_id]) if params[:coordinator_id] order_cycle = OrderCycle.new(coordinator: coordinator) if order_cycle.nil? && coordinator.present? return OpenFoodNetwork::OrderCyclePermissions.new(spree_current_user, order_cycle).visible_enterprises + when :index + if spree_current_user.admin? + OpenFoodNetwork::Permissions.new(spree_current_user). + editable_enterprises. + order('is_primary_producer ASC, name') + elsif json_request? + OpenFoodNetwork::Permissions.new(spree_current_user).editable_enterprises.ransack(params[:q]).result + else + Enterprise.where("1=0") + end + when :for_line_items + OpenFoodNetwork::Permissions.new(spree_current_user).visible_enterprises.ransack(params[:q]).result else # TODO was ordered with is_distributor DESC as well, not sure why or how we want to sort this now OpenFoodNetwork::Permissions.new(spree_current_user). @@ -119,7 +163,7 @@ module Admin end def collection_actions - [:index, :for_order_cycle, :bulk_update] + [:index, :for_order_cycle, :for_line_items, :bulk_update] end def load_methods_and_fees @@ -139,13 +183,15 @@ module Admin def check_can_change_bulk_sells unless spree_current_user.admin? params[:enterprise_set][:collection_attributes].each do |i, enterprise_params| - enterprise_params.delete :sells + enterprise_params.delete :sells unless spree_current_user == Enterprise.find_by_id(enterprise_params[:id]).owner end end end def check_can_change_sells - params[:enterprise].delete :sells unless spree_current_user.admin? + unless spree_current_user.admin? || spree_current_user == @enterprise.owner + params[:enterprise].delete :sells + end end def override_owner @@ -199,12 +245,17 @@ module Admin # Overriding method on Spree's resource controller def location_after_save - refered_from_edit = URI(request.referer).path == main_app.edit_admin_enterprise_path(@enterprise) + referer_path = OpenFoodNetwork::RefererParser::path(request.referer) + refered_from_edit = referer_path =~ /\/edit$/ if params[:enterprise].key?(:producer_properties_attributes) && !refered_from_edit main_app.admin_enterprises_path else main_app.edit_admin_enterprise_path(@enterprise) end end + + def ams_prefix_whitelist + [:index, :basic] + end end end diff --git a/app/controllers/admin/order_cycles_controller.rb b/app/controllers/admin/order_cycles_controller.rb index fe1157a7f3..300a2ca24e 100644 --- a/app/controllers/admin/order_cycles_controller.rb +++ b/app/controllers/admin/order_cycles_controller.rb @@ -5,18 +5,26 @@ module Admin class OrderCyclesController < ResourceController include OrderCyclesHelper - before_filter :load_data_for_index, :only => :index + prepend_before_filter :load_data_for_index, :only => :index before_filter :require_coordinator, only: :new before_filter :remove_protected_attrs, only: [:update] before_filter :remove_unauthorized_bulk_attrs, only: [:bulk_update] around_filter :protect_invalid_destroy, only: :destroy + def index + respond_to do |format| + format.html + format.json do + render_as_json @collection, ams_prefix: params[:ams_prefix], current_user: spree_current_user + end + end + end def show respond_to do |format| format.html format.json do - render json: Api::Admin::OrderCycleSerializer.new(@order_cycle, current_user: spree_current_user).to_json + render_as_json @order_cycle, current_user: spree_current_user end end end @@ -25,7 +33,7 @@ module Admin respond_to do |format| format.html format.json do - render json: Api::Admin::OrderCycleSerializer.new(@order_cycle, current_user: spree_current_user).to_json + render_as_json @order_cycle, current_user: spree_current_user end end end @@ -53,20 +61,17 @@ module Admin respond_to do |format| if @order_cycle.update_attributes(params[:order_cycle]) OpenFoodNetwork::OrderCycleFormApplicator.new(@order_cycle, spree_current_user).go! - - flash[:notice] = 'Your order cycle has been updated.' - format.html { redirect_to admin_order_cycles_path } - format.json { render :json => {:success => true} } + flash[:notice] = 'Your order cycle has been updated.' if params[:reloading] == '1' + format.json { render :json => {:success => true} } else - format.html format.json { render :json => {:success => false} } end end end def bulk_update - @order_cycle_set = OrderCycleSet.new(params[:order_cycle_set]) - if @order_cycle_set.save + @order_cycle_set = params[:order_cycle_set] && OrderCycleSet.new(params[:order_cycle_set]) + if @order_cycle_set.andand.save redirect_to main_app.admin_order_cycles_path, :notice => 'Order cycles have been updated.' else render :index @@ -79,21 +84,46 @@ module Admin redirect_to main_app.admin_order_cycles_path, :notice => "Your order cycle #{@order_cycle.name} has been cloned." end + # Send notifications to all producers who are part of the order cycle + def notify_producers + Delayed::Job.enqueue OrderCycleNotificationJob.new(params[:id].to_i) + + redirect_to main_app.admin_order_cycles_path, :notice => 'Emails to be sent to producers have been queued for sending.' + end + protected - def collection(show_more=false) - ocs = OrderCycle.accessible_by(spree_current_user) + def collection + ocs = if params[:as] == "distributor" + OrderCycle.ransack(params[:q]).result. + involving_managed_distributors_of(spree_current_user).order('updated_at DESC') + elsif params[:as] == "producer" + OrderCycle.ransack(params[:q]).result. + involving_managed_producers_of(spree_current_user).order('updated_at DESC') + else + OrderCycle.ransack(params[:q]).result.accessible_by(spree_current_user) + end ocs.undated + ocs.soonest_closing + ocs.soonest_opening + - (show_more ? ocs.closed : ocs.recently_closed) + ocs.closed + end + + def collection_actions + [:index] end private def load_data_for_index @show_more = !!params[:show_more] - @order_cycle_set = OrderCycleSet.new :collection => collection(@show_more) + unless @show_more || params[:q].andand[:orders_close_at_gt].present? + # Split ransack params into all those that currently exist and new ones to limit returned ocs to recent or undated + params[:q] = { + g: [ params.delete(:q) || {}, { m: 'or', orders_close_at_gt: 31.days.ago, orders_close_at_null: true } ] + } + end + @order_cycle_set = OrderCycleSet.new :collection => (@collection = collection) end def require_coordinator @@ -132,12 +162,22 @@ module Admin end def remove_unauthorized_bulk_attrs - params[:order_cycle_set][:collection_attributes].each do |i, hash| - order_cycle = OrderCycle.find(hash[:id]) - unless Enterprise.managed_by(spree_current_user).include?(order_cycle.andand.coordinator) - params[:order_cycle_set][:collection_attributes].delete i + if params.key? :order_cycle_set + params[:order_cycle_set][:collection_attributes].each do |i, hash| + order_cycle = OrderCycle.find(hash[:id]) + unless Enterprise.managed_by(spree_current_user).include?(order_cycle.andand.coordinator) + params[:order_cycle_set][:collection_attributes].delete i + end end end end + + def ams_prefix_whitelist + [:basic] + end + + def collection_actions + [:index, :bulk_update] + end end end diff --git a/app/controllers/admin/producer_properties_controller.rb b/app/controllers/admin/producer_properties_controller.rb index 66656d9aa0..aca3e3f418 100644 --- a/app/controllers/admin/producer_properties_controller.rb +++ b/app/controllers/admin/producer_properties_controller.rb @@ -12,7 +12,7 @@ module Admin end def load_enterprise - @enterprise = Enterprise.find_by_permalink params[:enterprise_id] + @enterprise = Enterprise.find_by_permalink! params[:enterprise_id] end def load_properties diff --git a/app/controllers/admin/variant_overrides_controller.rb b/app/controllers/admin/variant_overrides_controller.rb index 9425565f6e..c886f80652 100644 --- a/app/controllers/admin/variant_overrides_controller.rb +++ b/app/controllers/admin/variant_overrides_controller.rb @@ -4,32 +4,43 @@ module Admin class VariantOverridesController < ResourceController include OpenFoodNetwork::SpreeApiKeyLoader + prepend_before_filter :load_data + before_filter :load_collection, only: [:bulk_update] before_filter :load_spree_api_key, only: :index - before_filter :load_data + def index end def bulk_update - collection_hash = Hash[params[:variant_overrides].each_with_index.map { |vo, i| [i, vo] }] - vo_set = VariantOverrideSet.new @variant_overrides, collection_attributes: collection_hash - # Ensure we're authorised to update all variant overrides - vo_set.collection.each { |vo| authorize! :update, vo } + @vo_set.collection.each { |vo| authorize! :update, vo } - if vo_set.save + if @vo_set.save # Return saved VOs with IDs - render json: vo_set.collection, each_serializer: Api::Admin::VariantOverrideSerializer + render json: @vo_set.collection, each_serializer: Api::Admin::VariantOverrideSerializer else - if vo_set.errors.present? - render json: { errors: vo_set.errors }, status: 400 + if @vo_set.errors.present? + render json: { errors: @vo_set.errors }, status: 400 else render nothing: true, status: 500 end end end + def bulk_reset + # Ensure we're authorised to update all variant overrides. + @collection.each { |vo| authorize! :bulk_reset, vo } + @collection.each(&:reset_stock!) + + if collection_errors.present? + render json: { errors: collection_errors }, status: 400 + else + render json: @collection, each_serializer: Api::Admin::VariantOverrideSerializer + end + end + private @@ -43,10 +54,28 @@ module Admin @hub_permissions = OpenFoodNetwork::Permissions.new(spree_current_user). variant_override_enterprises_per_hub - @variant_overrides = VariantOverride.for_hubs(@hubs) + end + + def load_collection + collection_hash = Hash[params[:variant_overrides].each_with_index.map { |vo, i| [i, vo] }] + @vo_set = VariantOverrideSet.new @variant_overrides, collection_attributes: collection_hash end def collection + @variant_overrides = VariantOverride.for_hubs(params[:hub_id] || @hubs) + end + + def collection_actions + [:index, :bulk_update, :bulk_reset] + end + + # This has been pulled from ModelSet as it is useful for compiling a list of errors on any generic collection (not necessarily a ModelSet) + # Could be pulled down into a lower level controller if it is useful in other high level controllers + def collection_errors + errors = ActiveModel::Errors.new self + full_messages = @collection.map { |element| element.errors.full_messages }.flatten + full_messages.each { |fm| errors.add(:base, fm) } + errors end end end diff --git a/app/controllers/api/enterprises_controller.rb b/app/controllers/api/enterprises_controller.rb index 41b24f30a1..909f8a3567 100644 --- a/app/controllers/api/enterprises_controller.rb +++ b/app/controllers/api/enterprises_controller.rb @@ -12,12 +12,6 @@ module Api render params[:template] || :bulk_index end - def accessible - permitted = OpenFoodNetwork::Permissions.new(current_api_user).order_cycle_enterprises - @enterprises = permitted.ransack(params[:q]).result - render params[:template] || :bulk_index - end - def create authorize! :create, Enterprise diff --git a/app/controllers/application_controller.rb b/app/controllers/application_controller.rb index 53763ad274..7dfd404b69 100644 --- a/app/controllers/application_controller.rb +++ b/app/controllers/application_controller.rb @@ -1,7 +1,10 @@ +require 'open_food_network/referer_parser' + class ApplicationController < ActionController::Base protect_from_forgery include EnterprisesHelper + helper CssSplitter::ApplicationHelper def redirect_to(options = {}, response_status = {}) ::Rails.logger.error("Redirected by #{caller(1).first rescue "unknown"}") @@ -9,7 +12,8 @@ class ApplicationController < ActionController::Base end def set_checkout_redirect - if request.referer and referer_path = URI(request.referer).path + referer_path = OpenFoodNetwork::RefererParser::path(request.referer) + if referer_path session["spree_user_return_to"] = [main_app.checkout_path].include?(referer_path) ? referer_path : root_path end end diff --git a/app/controllers/base_controller.rb b/app/controllers/base_controller.rb index 1d74df5706..9d2c3cff85 100644 --- a/app/controllers/base_controller.rb +++ b/app/controllers/base_controller.rb @@ -12,14 +12,12 @@ class BaseController < ApplicationController before_filter :check_order_cycle_expiry - def load_active_distributors - @active_distributors ||= Enterprise.distributors_with_active_order_cycles - end private def set_order_cycles @order_cycles = OrderCycle.with_distributor(@distributor).active + .order(@distributor.preferred_shopfront_order_cycle_order) # And default to the only order cycle if there's only the one if @order_cycles.count == 1 diff --git a/app/controllers/checkout_controller.rb b/app/controllers/checkout_controller.rb index 0c42ceb987..1cffc05734 100644 --- a/app/controllers/checkout_controller.rb +++ b/app/controllers/checkout_controller.rb @@ -1,3 +1,5 @@ +require 'open_food_network/last_used_address' + class CheckoutController < Spree::CheckoutController layout 'darkswarm' @@ -12,9 +14,6 @@ class CheckoutController < Spree::CheckoutController include EnterprisesHelper def edit - # Because this controller doesn't inherit from our BaseController - # We need to duplicate the code here - @active_distributors ||= Enterprise.distributors_with_active_order_cycles end def update @@ -26,7 +25,7 @@ class CheckoutController < Spree::CheckoutController return if redirect_to_paypal_express_form_if_needed end - if @order.next + if advance_order_state(@order) state_callback(:after) else if @order.errors.present? @@ -86,6 +85,16 @@ class CheckoutController < Spree::CheckoutController params[:order] end + # Perform order.next, guarding against StaleObjectErrors + def advance_order_state(order) + tries ||= 3 + order.next + + rescue ActiveRecord::StaleObjectError + retry unless (tries -= 1).zero? + false + end + def update_failed clear_ship_address @@ -122,7 +131,11 @@ class CheckoutController < Spree::CheckoutController def before_address associate_user - last_used_bill_address, last_used_ship_address = find_last_used_addresses(@order.email) + + lua = OpenFoodNetwork::LastUsedAddress.new(@order.email) + last_used_bill_address = lua.last_used_bill_address.andand.clone + last_used_ship_address = lua.last_used_ship_address.andand.clone + preferred_bill_address, preferred_ship_address = spree_current_user.bill_address, spree_current_user.ship_address if spree_current_user.respond_to?(:bill_address) && spree_current_user.respond_to?(:ship_address) @order.bill_address ||= preferred_bill_address || last_used_bill_address || Spree::Address.default @order.ship_address ||= preferred_ship_address || last_used_ship_address || Spree::Address.default diff --git a/app/controllers/discourse_sso_controller.rb b/app/controllers/discourse_sso_controller.rb new file mode 100644 index 0000000000..0b067f6c92 --- /dev/null +++ b/app/controllers/discourse_sso_controller.rb @@ -0,0 +1,55 @@ +require 'discourse/single_sign_on' + +class DiscourseSsoController < ApplicationController + include SharedHelper + include DiscourseHelper + + before_filter :require_config + + def login + if require_activation? + redirect_to discourse_url + else + redirect_to discourse_login_url + end + end + + def sso + if spree_current_user + begin + redirect_to sso_url + rescue TypeError + render text: "Bad SingleSignOn request.", status: :bad_request + end + else + redirect_to login_path + end + end + + private + + def sso_url + secret = discourse_sso_secret! + discourse_url = discourse_url! + sso = Discourse::SingleSignOn.parse(request.query_string, secret) + sso.email = spree_current_user.email + sso.username = spree_current_user.login + sso.external_id = spree_current_user.id + sso.sso_secret = secret + sso.admin = admin_user? + sso.require_activation = require_activation? + sso.to_url(discourse_sso_url) + end + + def require_config + raise ActionController::RoutingError.new('Not Found') unless discourse_configured? + end + + def require_activation? + !admin_user? && !email_validated? + end + + def email_validated? + spree_current_user.enterprises.confirmed.map(&:email).include?(spree_current_user.email) + end +end diff --git a/app/controllers/enterprise_confirmations_controller.rb b/app/controllers/enterprise_confirmations_controller.rb index d275260599..0375a04d49 100644 --- a/app/controllers/enterprise_confirmations_controller.rb +++ b/app/controllers/enterprise_confirmations_controller.rb @@ -40,7 +40,7 @@ class EnterpriseConfirmationsController < DeviseController def new_user_reset_path(resource) password = Devise.friendly_token.first(8) user = Spree::User.create(email: resource.email, password: password, password_confirmation: password) - user.send_reset_password_instructions + user.send_reset_password_instructions_without_delay resource.users << user spree.edit_spree_user_password_path(user, :reset_password_token => user.reset_password_token, return_to: spree.admin_path) end diff --git a/app/controllers/enterprises_controller.rb b/app/controllers/enterprises_controller.rb index 097139556c..8a875c76bf 100644 --- a/app/controllers/enterprises_controller.rb +++ b/app/controllers/enterprises_controller.rb @@ -4,60 +4,11 @@ class EnterprisesController < BaseController include OrderCyclesHelper # These prepended filters are in the reverse order of execution - prepend_before_filter :load_active_distributors, :set_order_cycles, :require_distributor_chosen, :reset_order, only: :shop + prepend_before_filter :set_order_cycles, :require_distributor_chosen, :reset_order, only: :shop before_filter :clean_permalink, only: :check_permalink respond_to :js, only: :permalink_checker - def index - @enterprises = Enterprise.all - end - - def suppliers - @suppliers = Enterprise.is_primary_producer - end - - def distributors - @distributors = Enterprise.is_distributor - - respond_to do |format| - format.js do - @distributor_details = Hash[@distributors.map { |d| [d.id, render_to_string(:partial => 'enterprises/distributor_details', :locals => {:distributor => d})] }] - end - format.html do - @distributors - end - end - end - - def show - @enterprise = Enterprise.find_by_permalink(params[:id]) || Enterprise.find(params[:id]) - - # User can view this page if they've already chosen their distributor, or if this page - # is for a supplier, they may use it to select a distributor that sells this supplier's - # products. - unless current_distributor || @enterprise.is_primary_producer - redirect_to spree.root_path and return - end - - - options = {:enterprise_id => params[:id]} - options.merge(params.reject { |k,v| k == :id }) - - @products = [] - - if @enterprise.is_primary_producer - @distributors = Enterprise.distributing_any_product_of(@enterprise.supplied_products).by_name.all - end - - if current_order_cycle - @searcher = Spree::Config.searcher_class.new(options) - @products = @searcher.retrieve_products - - order_cycle_products = current_order_cycle.products_distributed_by(current_distributor) - @products = @products & order_cycle_products - end - end def check_permalink return render text: params[:permalink], status: 409 if Enterprise.find_by_permalink params[:permalink] diff --git a/app/controllers/groups_controller.rb b/app/controllers/groups_controller.rb index fe43f0a0fa..43a3f49abe 100644 --- a/app/controllers/groups_controller.rb +++ b/app/controllers/groups_controller.rb @@ -1,6 +1,5 @@ class GroupsController < BaseController layout 'darkswarm' - before_filter :load_active_distributors def index @groups = EnterpriseGroup.on_front_page.by_position diff --git a/app/controllers/home_controller.rb b/app/controllers/home_controller.rb index 76e179ed22..07ba097f85 100644 --- a/app/controllers/home_controller.rb +++ b/app/controllers/home_controller.rb @@ -1,11 +1,12 @@ class HomeController < BaseController layout 'darkswarm' - before_filter :load_active_distributors - - def index - end - def about_us + def index + if ContentConfig.home_show_stats + @num_distributors = Enterprise.is_distributor.activated.visible.count + @num_producers = Enterprise.is_primary_producer.activated.visible.count + @num_users = Spree::User.joins(:orders).merge(Spree::Order.complete).count('DISTINCT spree_users.*') + @num_orders = Spree::Order.complete.count + end end end - diff --git a/app/controllers/map_controller.rb b/app/controllers/map_controller.rb index a980ba8f40..46a6f5852a 100644 --- a/app/controllers/map_controller.rb +++ b/app/controllers/map_controller.rb @@ -1,6 +1,5 @@ class MapController < BaseController layout 'darkswarm' - before_filter :load_active_distributors def index end diff --git a/app/controllers/producers_controller.rb b/app/controllers/producers_controller.rb index b101a95b7f..42d1d401e5 100644 --- a/app/controllers/producers_controller.rb +++ b/app/controllers/producers_controller.rb @@ -1,7 +1,6 @@ class ProducersController < BaseController layout 'darkswarm' - before_filter :load_active_distributors - + def index end end diff --git a/app/controllers/shop_controller.rb b/app/controllers/shop_controller.rb index 73861def5c..ef78605b41 100644 --- a/app/controllers/shop_controller.rb +++ b/app/controllers/shop_controller.rb @@ -10,12 +10,20 @@ class ShopController < BaseController end def products - # Can we make this query less slow? - # if @products = products_for_shop + + enterprise_fee_calculator = OpenFoodNetwork::EnterpriseFeeCalculator.new current_distributor, current_order_cycle + render status: 200, - json: ActiveModel::ArraySerializer.new(@products, each_serializer: Api::ProductSerializer, - current_order_cycle: current_order_cycle, current_distributor: current_distributor).to_json + json: ActiveModel::ArraySerializer.new(@products, + each_serializer: Api::ProductSerializer, + current_order_cycle: current_order_cycle, + current_distributor: current_distributor, + variants: variants_for_shop_by_id, + master_variants: master_variants_for_shop_by_id, + enterprise_fee_calculator: enterprise_fee_calculator, + ).to_json + else render json: "", status: 404 end @@ -38,10 +46,12 @@ class ShopController < BaseController def products_for_shop if current_order_cycle + scoper = OpenFoodNetwork::ScopeProductToHub.new(current_distributor) + current_order_cycle. valid_products_distributed_by(current_distributor). order(taxon_order). - each { |p| p.scope_to_hub current_distributor }. + each { |p| scoper.scope(p) }. select { |p| !p.deleted? && p.has_stock_for_distribution?(current_order_cycle, current_distributor) } end end @@ -56,4 +66,31 @@ class ShopController < BaseController "name ASC" end end + + def all_variants_for_shop + # We use the in_stock? method here instead of the in_stock scope because we need to + # look up the stock as overridden by VariantOverrides, and the scope method is not affected + # by them. + scoper = OpenFoodNetwork::ScopeVariantToHub.new(current_distributor) + Spree::Variant. + for_distribution(current_order_cycle, current_distributor). + each { |v| scoper.scope(v) }. + select(&:in_stock?) + end + + def variants_for_shop_by_id + index_by_product_id all_variants_for_shop.reject(&:is_master) + end + + def master_variants_for_shop_by_id + index_by_product_id all_variants_for_shop.select(&:is_master) + end + + def index_by_product_id(variants) + variants.inject({}) do |vs, v| + vs[v.product_id] ||= [] + vs[v.product_id] << v + vs + end + end end diff --git a/app/controllers/shops_controller.rb b/app/controllers/shops_controller.rb new file mode 100644 index 0000000000..3305811e07 --- /dev/null +++ b/app/controllers/shops_controller.rb @@ -0,0 +1,6 @@ +class ShopsController < BaseController + layout 'darkswarm' + + def index + end +end diff --git a/app/controllers/spree/admin/adjustments_controller_decorator.rb b/app/controllers/spree/admin/adjustments_controller_decorator.rb new file mode 100644 index 0000000000..12c9ae2c60 --- /dev/null +++ b/app/controllers/spree/admin/adjustments_controller_decorator.rb @@ -0,0 +1,44 @@ +module Spree + module Admin + AdjustmentsController.class_eval do + before_filter :set_included_tax, only: [:create, :update] + before_filter :set_default_tax_rate, only: :edit + + + private + + # Choose a default tax rate to show on the edit form. The adjustment stores its included + # tax in dollars, but doesn't store the source of the tax (ie. TaxRate that generated it). + # We guess which tax rate here, choosing: + # 1. A tax rate that will compute to the same amount as the existing tax + # 2. If that's not present, the first tax rate that's valid for the current order + # When we have to go with 2, we show an error message to ask the admin to check that the + # correct tax is being applied. + def set_default_tax_rate + if @adjustment.included_tax > 0 + trs = TaxRate.match(@order) + tr_yielding_matching_tax = trs.select { |tr| tr.compute_tax(@adjustment.amount) == @adjustment.included_tax }.first.andand.id + tr_valid_for_order = TaxRate.match(@order).first.andand.id + + @tax_rate_id = tr_yielding_matching_tax || tr_valid_for_order + + if tr_yielding_matching_tax.nil? + @adjustment.errors.add :tax_rate_id, "^Please check that the tax rate for this adjustment is correct." + end + end + end + + + def set_included_tax + if params[:tax_rate_id].present? + tax_rate = TaxRate.find params[:tax_rate_id] + amount = params[:adjustment][:amount].to_f + params[:adjustment][:included_tax] = tax_rate.compute_tax amount + + else + params[:adjustment][:included_tax] = 0 + end + end + end + end +end diff --git a/app/controllers/spree/admin/base_controller_decorator.rb b/app/controllers/spree/admin/base_controller_decorator.rb index 85904590c3..9888967f58 100644 --- a/app/controllers/spree/admin/base_controller_decorator.rb +++ b/app/controllers/spree/admin/base_controller_decorator.rb @@ -58,4 +58,31 @@ Spree::Admin::BaseController.class_eval do "Until you set these up, customers will not be able to shop at this hub." end end + + def html_request? + request.format.html? + end + + def json_request? + request.format.json? + end + + def render_as_json(data, options={}) + ams_prefix = options.delete :ams_prefix + if [Array, ActiveRecord::Relation].include? data.class + render options.merge(json: data, each_serializer: serializer(ams_prefix)) + else + render options.merge(json: data, serializer: serializer(ams_prefix)) + end + end + + def serializer(ams_prefix) + if ams_prefix.nil? || ams_prefix_whitelist.include?(ams_prefix.to_sym) + prefix = ams_prefix.andand.classify || "" + name = controller_name.classify + "Api::Admin::#{prefix}#{name}Serializer".constantize + else + raise "Suffix '#{ams_prefix}' not found in ams_prefix_whitelist for #{self.class.name}." + end + end end diff --git a/app/controllers/spree/admin/line_items_controller_decorator.rb b/app/controllers/spree/admin/line_items_controller_decorator.rb index ca83baa00b..5e95681f95 100644 --- a/app/controllers/spree/admin/line_items_controller_decorator.rb +++ b/app/controllers/spree/admin/line_items_controller_decorator.rb @@ -1,8 +1,52 @@ Spree::Admin::LineItemsController.class_eval do + prepend_before_filter :load_order, except: :index + around_filter :apply_enterprise_fees_with_lock, only: :update + + respond_to :json + + # TODO make updating line items faster by creating a bulk update method + + def index + respond_to do |format| + format.json do + order_params = params[:q].andand.delete :order + orders = OpenFoodNetwork::Permissions.new(spree_current_user).editable_orders.ransack(order_params).result + line_items = OpenFoodNetwork::Permissions.new(spree_current_user).editable_line_items.where(order_id: orders).ransack(params[:q]) + render_as_json line_items.result.reorder('order_id ASC, id ASC') + end + end + end + + def create + variant = Spree::Variant.find(params[:line_item][:variant_id]) + OpenFoodNetwork::ScopeVariantToHub.new(@order.distributor).scope(variant) + + @line_item = @order.add_variant(variant, params[:line_item][:quantity].to_i) + + if @order.save + respond_with(@line_item) do |format| + format.html { render :partial => 'spree/admin/orders/form', :locals => { :order => @order.reload } } + end + else + respond_with(@line_item) do |format| + format.js { render :action => 'create', :locals => { :order => @order.reload } } + end + end + end + + private - def load_order - @order = Spree::Order.find_by_number!(params[:order_id]) - authorize! :update, @order + def load_order + @order = Spree::Order.find_by_number!(params[:order_id]) + authorize! :update, @order + end + + def apply_enterprise_fees_with_lock + authorize! :read, @order + @order.with_lock do + yield + @order.update_distribution_charge! end + end end diff --git a/app/controllers/spree/admin/orders_controller_decorator.rb b/app/controllers/spree/admin/orders_controller_decorator.rb index f7d674048e..b5a2bb4f95 100644 --- a/app/controllers/spree/admin/orders_controller_decorator.rb +++ b/app/controllers/spree/admin/orders_controller_decorator.rb @@ -2,6 +2,7 @@ require 'open_food_network/spree_api_key_loader' Spree::Admin::OrdersController.class_eval do include OpenFoodNetwork::SpreeApiKeyLoader + helper CheckoutHelper before_filter :load_spree_api_key, :only => :bulk_management # We need to add expections for collection actions other than :index here @@ -9,6 +10,8 @@ Spree::Admin::OrdersController.class_eval do # in an auth failure as the @order object is nil for collection actions before_filter :check_authorization, except: [:bulk_management, :managed] + before_filter :load_distribution_choices, only: [:new, :edit, :update] + # After updating an order, the fees should be updated as well # Currently, adding or deleting line items does not trigger updating the # fees! This is a quick fix for that. @@ -16,21 +19,53 @@ Spree::Admin::OrdersController.class_eval do # instead of the update_distribution_charge method. after_filter :update_distribution_charge, :only => :update - respond_override :index => { :html => - { :success => lambda { - # Filter orders to only show those distributed by current user (or all for admin user) - @orders = @search.result.includes([:user, :shipments, :payments]). - distributed_by_user(spree_current_user). - page(params[:page]). - per(params[:per_page] || Spree::Config[:orders_per_page]) - # Filter orders by distributor - if params[:distributor_ids] - @orders = @orders.where(distributor_id: params[:distributor_ids]) + before_filter :require_distributor_abn, only: :invoice + + + respond_to :html, :json + + # Mostly the original Spree method, tweaked to allow us to ransack with completed_at in a sane way + def index + params[:q] ||= {} + params[:q][:completed_at_not_null] ||= '1' if Spree::Config[:show_only_complete_orders_by_default] + @show_only_completed = params[:q][:completed_at_not_null].present? + params[:q][:s] ||= @show_only_completed ? 'completed_at desc' : 'created_at desc' + + # As date params are deleted if @show_only_completed, store + # the original date so we can restore them into the params + # after the search + created_at_gt = params[:q][:created_at_gt] + created_at_lt = params[:q][:created_at_lt] + + params[:q].delete(:inventory_units_shipment_id_null) if params[:q][:inventory_units_shipment_id_null] == "0" + + if !params[:q][:created_at_gt].blank? + params[:q][:created_at_gt] = Time.zone.parse(params[:q][:created_at_gt]).beginning_of_day rescue "" + end + + if !params[:q][:created_at_lt].blank? + params[:q][:created_at_lt] = Time.zone.parse(params[:q][:created_at_lt]).end_of_day rescue "" + end + + # Changed this to stop completed_at being overriden when present + if @show_only_completed + params[:q][:completed_at_gt] = params[:q].delete(:created_at_gt) unless params[:q][:completed_at_gt] + params[:q][:completed_at_lt] = params[:q].delete(:created_at_lt) unless params[:q][:completed_at_gt] + end + + @orders = orders + + # Restore dates + params[:q][:created_at_gt] = created_at_gt + params[:q][:created_at_lt] = created_at_lt + + respond_with(@orders) do |format| + format.html + format.json do + render_as_json @orders end - if params[:order_cycle_ids] - @orders = @orders.where(order_cycle_id: params[:order_cycle_ids]) - end - } } } + end + end # Overwrite to use confirm_email_for_customer instead of confirm_email. # This uses a new template. See mailers/spree/order_mailer_decorator.rb. @@ -41,13 +76,53 @@ Spree::Admin::OrdersController.class_eval do respond_with(@order) { |format| format.html { redirect_to :back } } end + def invoice + pdf = render_to_string pdf: "invoice-#{@order.number}.pdf", template: "spree/admin/orders/invoice", formats: [:html], encoding: "UTF-8" + Spree::OrderMailer.invoice_email(@order.id, pdf).deliver + flash[:success] = t(:invoice_email_sent) + + respond_with(@order) { |format| format.html { redirect_to edit_admin_order_path(@order) } } + end + + def print + render pdf: "invoice-#{@order.number}", template: "spree/admin/orders/invoice", encoding: "UTF-8" + end + def update_distribution_charge @order.update_distribution_charge! end - def managed - permissions = OpenFoodNetwork::Permissions.new(spree_current_user) - @orders = permissions.editable_orders.ransack(params[:q]).result.page(params[:page]).per(params[:per_page]) - render json: @orders, each_serializer: Api::Admin::OrderSerializer + private + + def orders + if json_request? + @search = OpenFoodNetwork::Permissions.new(spree_current_user).editable_orders.ransack(params[:q]) + @search.result.reorder('id ASC') + else + @search = Spree::Order.accessible_by(current_ability, :index).ransack(params[:q]) + + # Replaced this search to filter orders to only show those distributed by current user (or all for admin user) + @search.result.includes([:user, :shipments, :payments]). + distributed_by_user(spree_current_user). + page(params[:page]). + per(params[:per_page] || Spree::Config[:orders_per_page]) + end + end + + def require_distributor_abn + unless @order.distributor.abn.present? + flash[:error] = t(:must_have_valid_business_number, enterprise_name: @order.distributor.name) + respond_with(@order) { |format| format.html { redirect_to edit_admin_order_path(@order) } } + end + end + + def load_distribution_choices + @shops = Enterprise.is_distributor.managed_by(spree_current_user).by_name + + ocs = OrderCycle.managed_by(spree_current_user) + @order_cycles = ocs.soonest_closing + + ocs.soonest_opening + + ocs.closed + + ocs.undated end end diff --git a/app/controllers/spree/admin/overview_controller_decorator.rb b/app/controllers/spree/admin/overview_controller_decorator.rb index 6eea9c6ea4..ffd2569c1f 100644 --- a/app/controllers/spree/admin/overview_controller_decorator.rb +++ b/app/controllers/spree/admin/overview_controller_decorator.rb @@ -5,16 +5,22 @@ Spree::Admin::OverviewController.class_eval do @product_count = Spree::Product.active.managed_by(spree_current_user).count @order_cycle_count = OrderCycle.active.managed_by(spree_current_user).count + unspecified = spree_current_user.owned_enterprises.where(sells: 'unspecified') + outside_referral = !URI(request.referer.to_s).path.match(/^\/admin/) + if OpenFoodNetwork::Permissions.new(spree_current_user).manages_one_enterprise? && !spree_current_user.admin? @enterprise = @enterprises.first - if @enterprise.sells == "unspecified" - render "welcome", layout: "spree/layouts/bare_admin" + if outside_referral && unspecified.any? + redirect_to main_app.welcome_admin_enterprise_path(@enterprise) else render "single_enterprise_dashboard" end else - render "multi_enterprise_dashboard" + if outside_referral && unspecified.any? + redirect_to main_app.admin_enterprises_path + else + render "multi_enterprise_dashboard" + end end end end - diff --git a/app/controllers/spree/admin/products_controller_decorator.rb b/app/controllers/spree/admin/products_controller_decorator.rb index 1591586f76..5b1bb347a1 100644 --- a/app/controllers/spree/admin/products_controller_decorator.rb +++ b/app/controllers/spree/admin/products_controller_decorator.rb @@ -1,4 +1,5 @@ require 'open_food_network/spree_api_key_loader' +require 'open_food_network/referer_parser' Spree::Admin::ProductsController.class_eval do include OpenFoodNetwork::SpreeApiKeyLoader @@ -53,7 +54,8 @@ Spree::Admin::ProductsController.class_eval do protected def location_after_save - if URI(request.referer).path == '/admin/products/bulk_edit' + referer_path = OpenFoodNetwork::RefererParser::path(request.referer) + if referer_path == '/admin/products/bulk_edit' bulk_edit_admin_products_url else location_after_save_original diff --git a/app/controllers/spree/admin/reports_controller_decorator.rb b/app/controllers/spree/admin/reports_controller_decorator.rb index 34f2d640c2..8441b2d596 100644 --- a/app/controllers/spree/admin/reports_controller_decorator.rb +++ b/app/controllers/spree/admin/reports_controller_decorator.rb @@ -1,12 +1,18 @@ require 'csv' require 'open_food_network/order_and_distributor_report' require 'open_food_network/products_and_inventory_report' +require 'open_food_network/lettuce_share_report' require 'open_food_network/group_buy_report' require 'open_food_network/order_grouper' require 'open_food_network/customers_report' require 'open_food_network/users_and_enterprises_report' require 'open_food_network/order_cycle_management_report' +require 'open_food_network/packing_report' require 'open_food_network/sales_tax_report' +require 'open_food_network/xero_invoices_report' +require 'open_food_network/bulk_coop_report' +require 'open_food_network/payments_report' +require 'open_food_network/orders_and_fulfillments_report' Spree::Admin::ReportsController.class_eval do @@ -21,7 +27,8 @@ Spree::Admin::ReportsController.class_eval do ], products_and_inventory: [ ['All products', :all_products], - ['Inventory (on hand)', :inventory] + ['Inventory (on hand)', :inventory], + ['LettuceShare', :lettuce_share] ], customers: [ ["Mailing List", :mailing_list], @@ -30,11 +37,15 @@ Spree::Admin::ReportsController.class_eval do order_cycle_management: [ ["Payment Methods Report", :payment_methods], ["Delivery Report", :delivery] + ], + packing: [ + ["Pack By Customer", :pack_by_customer], + ["Pack By Supplier", :pack_by_supplier] ] } # Fetches user's distributors, suppliers and order_cycles - before_filter :load_data, only: [:customers, :products_and_inventory, :order_cycle_management] + before_filter :load_data, only: [:customers, :products_and_inventory, :order_cycle_management, :packing] # Render a partial for orders and fulfillment description respond_override :index => { :html => { :success => lambda { @@ -46,7 +57,9 @@ Spree::Admin::ReportsController.class_eval do render_to_string(partial: 'customers_description', layout: false, locals: {report_types: REPORT_TYPES[:customers]}).html_safe @reports[:order_cycle_management][:description] = render_to_string(partial: 'order_cycle_management_description', layout: false, locals: {report_types: REPORT_TYPES[:order_cycle_management]}).html_safe - } } } + @reports[:packing][:description] = + render_to_string(partial: 'packing_description', layout: false, locals: {report_types: REPORT_TYPES[:packing]}).html_safe +} } } # Overide spree reports list. @@ -64,33 +77,73 @@ Spree::Admin::ReportsController.class_eval do end def order_cycle_management + prepare_date_params params + + # -- Prepare form options + my_distributors = Enterprise.is_distributor.managed_by(spree_current_user) + my_suppliers = Enterprise.is_primary_producer.managed_by(spree_current_user) + + # My distributors and any distributors distributing products I supply + @distributors = my_distributors | Enterprise.with_distributed_products_outer.merge(Spree::Product.in_any_supplier(my_suppliers)) + # My suppliers and any suppliers supplying products I distribute + @suppliers = my_suppliers | my_distributors.map { |d| Spree::Product.in_distributor(d) }.flatten.map(&:supplier).uniq + @order_cycles = OrderCycle.active_or_complete.accessible_by(spree_current_user).order('orders_close_at DESC') + @report_types = REPORT_TYPES[:order_cycle_management] @report_type = params[:report_type] + + # -- Build Report with Order Grouper @report = OpenFoodNetwork::OrderCycleManagementReport.new spree_current_user, params + @table = @report.table_items + csv_file_name = "#{params[:report_type]}_#{timestamp}.csv" - @search = Spree::Order.complete.not_state(:canceled).managed_by(spree_current_user).search(params[:q]) - @orders = @search.result + render_report(@report.header, @table, params[:csv], "order_cycle_management_#{timestamp}.csv") + end - render_report(@report.header, @report.table, params[:csv], "order_cycle_management_#{timestamp}.csv") + def packing + # -- Prepare date parameters + prepare_date_params params + + # -- Prepare form options + my_distributors = Enterprise.is_distributor.managed_by(spree_current_user) + my_suppliers = Enterprise.is_primary_producer.managed_by(spree_current_user) + + # My distributors and any distributors distributing products I supply + @distributors = my_distributors | Enterprise.with_distributed_products_outer.merge(Spree::Product.in_any_supplier(my_suppliers)) + # My suppliers and any suppliers supplying products I distribute + @suppliers = my_suppliers | my_distributors.map { |d| Spree::Product.in_distributor(d) }.flatten.map(&:supplier).uniq + @order_cycles = OrderCycle.active_or_complete.accessible_by(spree_current_user).order('orders_close_at DESC') + @report_types = REPORT_TYPES[:packing] + @report_type = params[:report_type] + + # -- Build Report with Order Grouper + @report = OpenFoodNetwork::PackingReport.new spree_current_user, params + order_grouper = OpenFoodNetwork::OrderGrouper.new @report.rules, @report.columns + @table = order_grouper.table(@report.table_items) + csv_file_name = "#{params[:report_type]}_#{timestamp}.csv" + + render_report(@report.header, @table, params[:csv], "packing_#{timestamp}.csv") end def orders_and_distributors - params[:q] ||= {} + prepare_date_params params - if params[:q][:completed_at_gt].blank? - params[:q][:completed_at_gt] = Time.zone.now.beginning_of_month - else - params[:q][:completed_at_gt] = Time.zone.parse(params[:q][:completed_at_gt]).beginning_of_day rescue Time.zone.now.beginning_of_month - end - - if params[:q] && !params[:q][:completed_at_lt].blank? - params[:q][:completed_at_lt] = Time.zone.parse(params[:q][:completed_at_lt]).end_of_day rescue "" - end - params[:q][:meta_sort] ||= "completed_at.desc" - - @search = Spree::Order.complete.not_state(:canceled).managed_by(spree_current_user).search(params[:q]) + permissions = OpenFoodNetwork::Permissions.new(spree_current_user) + @search = permissions.visible_orders.complete.not_state(:canceled).search(params[:q]) orders = @search.result + # If empty array is passed in, the where clause will return all line_items, which is bad + orders_with_hidden_details = + permissions.editable_orders.empty? ? orders : orders.where('id NOT IN (?)', permissions.editable_orders) + + orders.select{ |order| orders_with_hidden_details.include? order }.each do |order| + # TODO We should really be hiding customer code here too, but until we + # have an actual association between order and customer, it's a bit tricky + order.bill_address.andand.assign_attributes(firstname: "HIDDEN", lastname: "", phone: "", address1: "", address2: "", city: "", zipcode: "", state: nil) + order.ship_address.andand.assign_attributes(firstname: "HIDDEN", lastname: "", phone: "", address1: "", address2: "", city: "", zipcode: "", state: nil) + order.assign_attributes(email: "HIDDEN") + end + @report = OpenFoodNetwork::OrderAndDistributorReport.new orders unless params[:csv] render :html => @report @@ -104,24 +157,11 @@ Spree::Admin::ReportsController.class_eval do end def sales_tax - params[:q] ||= {} - - if params[:q][:completed_at_gt].blank? - params[:q][:completed_at_gt] = Time.zone.now.beginning_of_month - else - params[:q][:completed_at_gt] = Time.zone.parse(params[:q][:completed_at_gt]).beginning_of_day rescue Time.zone.now.beginning_of_month - end - - if params[:q] && !params[:q][:completed_at_lt].blank? - params[:q][:completed_at_lt] = Time.zone.parse(params[:q][:completed_at_lt]).end_of_day rescue "" - end - params[:q][:meta_sort] ||= "completed_at.desc" - - @search = Spree::Order.complete.not_state(:canceled).managed_by(spree_current_user).search(params[:q]) - orders = @search.result + prepare_date_params params @distributors = Enterprise.is_distributor.managed_by(spree_current_user) - @report = OpenFoodNetwork::SalesTaxReport.new orders + @report = OpenFoodNetwork::SalesTaxReport.new spree_current_user, params + unless params[:csv] render :html => @report else @@ -134,300 +174,47 @@ Spree::Admin::ReportsController.class_eval do end def bulk_coop - params[:q] ||= {} - - if params[:q][:completed_at_gt].blank? - params[:q][:completed_at_gt] = Time.zone.now.beginning_of_month - else - params[:q][:completed_at_gt] = Time.zone.parse(params[:q][:completed_at_gt]).beginning_of_day rescue Time.zone.now.beginning_of_month - end - - if params[:q] && !params[:q][:completed_at_lt].blank? - params[:q][:completed_at_lt] = Time.zone.parse(params[:q][:completed_at_lt]).end_of_day rescue "" - end - params[:q][:meta_sort] ||= "completed_at.desc" - - @search = Spree::Order.complete.not_state(:canceled).managed_by(spree_current_user).search(params[:q]) - - orders = @search.result - @line_items = orders.map { |o| o.line_items.managed_by(spree_current_user) }.flatten + # -- Prepare date parameters + prepare_date_params params + # -- Prepare form options @distributors = Enterprise.is_distributor.managed_by(spree_current_user) @report_type = params[:report_type] - case params[:report_type] - when "bulk_coop_supplier_report" + # -- Build Report with Order Grouper + @report = OpenFoodNetwork::BulkCoopReport.new spree_current_user, params + order_grouper = OpenFoodNetwork::OrderGrouper.new @report.rules, @report.columns + @table = order_grouper.table(@report.table_items) + csv_file_name = "bulk_coop_#{params[:report_type]}_#{timestamp}.csv" - header = ["Supplier", "Product", "Unit Size", "Variant", "Weight", "Sum Total", "Sum Max Total", "Units Required", "Remainder"] - - columns = [ proc { |lis| lis.first.variant.product.supplier.name }, - proc { |lis| lis.first.variant.product.name }, - proc { |lis| lis.first.variant.product.group_buy ? (lis.first.variant.product.group_buy_unit_size || 0.0) : "" }, - proc { |lis| lis.first.variant.full_name }, - proc { |lis| lis.first.variant.weight || 0 }, - proc { |lis| lis.sum { |li| li.quantity } }, - proc { |lis| lis.sum { |li| li.max_quantity || 0 } }, - proc { |lis| "" }, - proc { |lis| "" } ] - - rules = [ { group_by: proc { |li| li.variant.product.supplier }, - sort_by: proc { |supplier| supplier.name } }, - { group_by: proc { |li| li.variant.product }, - sort_by: proc { |product| product.name }, - summary_columns: [ proc { |lis| lis.first.variant.product.supplier.name }, - proc { |lis| lis.first.variant.product.name }, - proc { |lis| lis.first.variant.product.group_buy ? (lis.first.variant.product.group_buy_unit_size || 0.0) : "" }, - proc { |lis| "" }, - proc { |lis| "" }, - proc { |lis| lis.sum { |li| (li.quantity || 0) * (li.variant.weight || 0) } }, - proc { |lis| lis.sum { |li| (li.max_quantity || 0) * (li.variant.weight || 0) } }, - proc { |lis| ( (lis.first.variant.product.group_buy_unit_size || 0).zero? ? 0 : ( lis.sum { |li| ( [li.max_quantity || 0, li.quantity || 0].max ) * (li.variant.weight || 0) } / lis.first.variant.product.group_buy_unit_size ) ).floor }, - proc { |lis| lis.sum { |li| ( [li.max_quantity || 0, li.quantity || 0].max) * (li.variant.weight || 0) } - ( ( (lis.first.variant.product.group_buy_unit_size || 0).zero? ? 0 : ( lis.sum { |li| ( [li.max_quantity || 0, li.quantity || 0].max) * (li.variant.weight || 0) } / lis.first.variant.product.group_buy_unit_size ) ).floor * (lis.first.variant.product.group_buy_unit_size || 0) ) } ] }, - { group_by: proc { |li| li.variant }, - sort_by: proc { |variant| variant.full_name } } ] - - when "bulk_coop_allocation" - - header = ["Customer", "Product", "Unit Size", "Variant", "Weight", "Sum Total", "Sum Max Total", "Total Allocated", "Remainder"] - - columns = [ proc { |lis| lis.first.order.bill_address.firstname + " " + lis.first.order.bill_address.lastname }, - proc { |lis| lis.first.variant.product.name }, - proc { |lis| lis.first.variant.product.group_buy ? (lis.first.variant.product.group_buy_unit_size || 0.0) : "" }, - proc { |lis| lis.first.variant.full_name }, - proc { |lis| lis.first.variant.weight || 0 }, - proc { |lis| lis.sum { |li| li.quantity } }, - proc { |lis| lis.sum { |li| li.max_quantity || 0 } }, - proc { |lis| "" }, - proc { |lis| "" } ] - - rules = [ { group_by: proc { |li| li.variant.product }, - sort_by: proc { |product| product.name }, - summary_columns: [ proc { |lis| "TOTAL" }, - proc { |lis| lis.first.variant.product.name }, - proc { |lis| lis.first.variant.product.group_buy ? (lis.first.variant.product.group_buy_unit_size || 0.0) : "" }, - proc { |lis| "" }, - proc { |lis| "" }, - proc { |lis| lis.sum { |li| li.quantity * (li.variant.weight || 0) } }, - proc { |lis| lis.sum { |li| (li.max_quantity || 0) * (li.variant.weight || 0) } }, - proc { |lis| ( (lis.first.variant.product.group_buy_unit_size || 0).zero? ? 0 : ( lis.sum { |li| ( [li.max_quantity || 0, li.quantity || 0].max ) * (li.variant.weight || 0) } / lis.first.variant.product.group_buy_unit_size ) ).floor * (lis.first.variant.product.group_buy_unit_size || 0) }, - proc { |lis| lis.sum { |li| ( [li.max_quantity || 0, li.quantity || 0].max ) * (li.variant.weight || 0) } - ( ( (lis.first.variant.product.group_buy_unit_size || 0).zero? ? 0 : ( lis.sum { |li| ( [li.max_quantity || 0, li.quantity || 0].max ) * (li.variant.weight || 0) } / lis.first.variant.product.group_buy_unit_size ) ).floor * (lis.first.variant.product.group_buy_unit_size || 0) ) } ] }, - { group_by: proc { |li| li.variant }, - sort_by: proc { |variant| variant.full_name } }, - { group_by: proc { |li| li.order }, - sort_by: proc { |order| order.to_s } } ] - - when "bulk_coop_packing_sheets" - - header = ["Customer", "Product", "Variant", "Sum Total"] - - columns = [ proc { |lis| lis.first.order.bill_address.firstname + " " + lis.first.order.bill_address.lastname }, - proc { |lis| lis.first.variant.product.name }, - proc { |lis| lis.first.variant.full_name }, - proc { |lis| lis.sum { |li| li.quantity } } ] - - rules = [ { group_by: proc { |li| li.variant.product }, - sort_by: proc { |product| product.name } }, - { group_by: proc { |li| li.variant }, - sort_by: proc { |variant| variant.full_name } }, - { group_by: proc { |li| li.order }, - sort_by: proc { |order| order.to_s } } ] - - when "bulk_coop_customer_payments" - - header = ["Customer", "Date of Order", "Total Cost", "Amount Owing", "Amount Paid"] - - columns = [ proc { |lis| lis.first.order.bill_address.firstname + " " + lis.first.order.bill_address.lastname }, - proc { |lis| lis.first.order.completed_at.to_s }, - proc { |lis| lis.map { |li| li.order }.uniq.sum { |o| o.total } }, - proc { |lis| lis.map { |li| li.order }.uniq.sum { |o| o.outstanding_balance } }, - proc { |lis| lis.map { |li| li.order }.uniq.sum { |o| o.payment_total } } ] - - rules = [ { group_by: proc { |li| li.order }, - sort_by: proc { |order| order.completed_at } } ] - - else # List all line items - - header = ["Supplier", "Product", "Unit Size", "Variant", "Weight", "Sum Total", "Sum Max Total", "Units Required", "Remainder"] - - columns = [ proc { |lis| lis.first.variant.product.supplier.name }, - proc { |lis| lis.first.variant.product.name }, - proc { |lis| lis.first.variant.product.group_buy ? (lis.first.variant.product.group_buy_unit_size || 0.0) : "" }, - proc { |lis| lis.first.variant.full_name }, - proc { |lis| lis.first.variant.weight || 0 }, - proc { |lis| lis.sum { |li| li.quantity } }, - proc { |lis| lis.sum { |li| li.max_quantity || 0 } }, - proc { |lis| "" }, - proc { |lis| "" } ] - - rules = [ { group_by: proc { |li| li.variant.product.supplier }, - sort_by: proc { |supplier| supplier.name } }, - { group_by: proc { |li| li.variant.product }, - sort_by: proc { |product| product.name }, - summary_columns: [ proc { |lis| lis.first.variant.product.supplier.name }, - proc { |lis| lis.first.variant.product.name }, - proc { |lis| lis.first.variant.product.group_buy ? (lis.first.variant.product.group_buy_unit_size || 0.0) : "" }, - proc { |lis| "" }, - proc { |lis| "" }, - proc { |lis| lis.sum { |li| li.quantity * (li.variant.weight || 0) } }, - proc { |lis| lis.sum { |li| (li.max_quantity || 0) * (li.variant.weight || 0) } }, - proc { |lis| ( (lis.first.variant.product.group_buy_unit_size || 0).zero? ? 0 : ( lis.sum { |li| ( [li.max_quantity || 0, li.quantity || 0].max ) * (li.variant.weight || 0) } / lis.first.variant.product.group_buy_unit_size ) ).floor }, - proc { |lis| lis.sum { |li| ( [li.max_quantity || 0, li.quantity || 0].max ) * (li.variant.weight || 0) } - ( ( (lis.first.variant.product.group_buy_unit_size || 0).zero? ? 0 : ( lis.sum { |li| ( [li.max_quantity || 0, li.quantity || 0].max ) * (li.variant.weight || 0) } / lis.first.variant.product.group_buy_unit_size ) ).floor * (lis.first.variant.product.group_buy_unit_size || 0) ) } ] }, - { group_by: proc { |li| li.variant }, - sort_by: proc { |variant| variant.full_name } } ] - - end - - order_grouper = OpenFoodNetwork::OrderGrouper.new rules, columns - - @header = header - @table = order_grouper.table(@line_items) - csv_file_name = "bulk_coop_#{timestamp}.csv" - - render_report(@header, @table, params[:csv], csv_file_name) + render_report(@report.header, @table, params[:csv], csv_file_name) end def payments - params[:q] ||= {} - - if params[:q][:completed_at_gt].blank? - params[:q][:completed_at_gt] = Time.zone.now.beginning_of_month - else - params[:q][:completed_at_gt] = Time.zone.parse(params[:q][:completed_at_gt]).beginning_of_day rescue Time.zone.now.beginning_of_month - end - - if params[:q] && !params[:q][:completed_at_lt].blank? - params[:q][:completed_at_lt] = Time.zone.parse(params[:q][:completed_at_lt]).end_of_day rescue "" - end - params[:q][:meta_sort] ||= "completed_at.desc" - - @search = Spree::Order.complete.not_state(:canceled).managed_by(spree_current_user).search(params[:q]) - - orders = @search.result - payments = orders.map { |o| o.payments.select { |payment| payment.completed? } }.flatten # Only select completed payments + # -- Prepare Date Params + prepare_date_params params + # -- Prepare Form Options @distributors = Enterprise.is_distributor.managed_by(spree_current_user) @report_type = params[:report_type] - case params[:report_type] - when "payments_by_payment_type" - table_items = payments - - header = ["Payment State", "Distributor", "Payment Type", "Total (#{currency_symbol})"] - - columns = [ proc { |payments| payments.first.order.payment_state }, - proc { |payments| payments.first.order.distributor.name }, - proc { |payments| payments.first.payment_method.name }, - proc { |payments| payments.sum { |payment| payment.amount } } ] - - rules = [ { group_by: proc { |payment| payment.order.payment_state }, - sort_by: proc { |payment_state| payment_state } }, - { group_by: proc { |payment| payment.order.distributor }, - sort_by: proc { |distributor| distributor.name } }, - { group_by: proc { |payment| Spree::PaymentMethod.unscoped { payment.payment_method } }, - sort_by: proc { |method| method.name } } ] - - when "itemised_payment_totals" - table_items = orders - - header = ["Payment State", "Distributor", "Product Total (#{currency_symbol})", "Shipping Total (#{currency_symbol})", "Outstanding Balance (#{currency_symbol})", "Total (#{currency_symbol})"] - - columns = [ proc { |orders| orders.first.payment_state }, - proc { |orders| orders.first.distributor.name }, - proc { |orders| orders.sum { |o| o.item_total } }, - proc { |orders| orders.sum { |o| o.ship_total } }, - proc { |orders| orders.sum { |o| o.outstanding_balance } }, - proc { |orders| orders.sum { |o| o.total } } ] - - rules = [ { group_by: proc { |order| order.payment_state }, - sort_by: proc { |payment_state| payment_state } }, - { group_by: proc { |order| order.distributor }, - sort_by: proc { |distributor| distributor.name } } ] - - when "payment_totals" - table_items = orders - - header = ["Payment State", "Distributor", "Product Total (#{currency_symbol})", "Shipping Total (#{currency_symbol})", "Total (#{currency_symbol})", "EFT (#{currency_symbol})", "PayPal (#{currency_symbol})", "Outstanding Balance (#{currency_symbol})"] - - columns = [ proc { |orders| orders.first.payment_state }, - proc { |orders| orders.first.distributor.name }, - proc { |orders| orders.sum { |o| o.item_total } }, - proc { |orders| orders.sum { |o| o.ship_total } }, - proc { |orders| orders.sum { |o| o.total } }, - proc { |orders| orders.sum { |o| o.payments.select { |payment| payment.completed? && (payment.payment_method.name.to_s.include? "EFT") }.sum { |payment| payment.amount } } }, - proc { |orders| orders.sum { |o| o.payments.select { |payment| payment.completed? && (payment.payment_method.name.to_s.include? "PayPal") }.sum{ |payment| payment.amount } } }, - proc { |orders| orders.sum { |o| o.outstanding_balance } } ] - - rules = [ { group_by: proc { |order| order.payment_state }, - sort_by: proc { |payment_state| payment_state } }, - { group_by: proc { |order| order.distributor }, - sort_by: proc { |distributor| distributor.name } } ] - - else - table_items = payments - - header = ["Payment State", "Distributor", "Payment Type", "Total (#{currency_symbol})"] - - columns = [ proc { |payments| payments.first.order.payment_state }, - proc { |payments| payments.first.order.distributor.name }, - proc { |payments| payments.first.payment_method.name }, - proc { |payments| payments.sum { |payment| payment.amount } } ] - - rules = [ { group_by: proc { |payment| payment.order.payment_state }, - sort_by: proc { |payment_state| payment_state } }, - { group_by: proc { |payment| payment.order.distributor }, - sort_by: proc { |distributor| distributor.name } }, - { group_by: proc { |payment| payment.payment_method }, - sort_by: proc { |method| method.name } } ] - - end - - order_grouper = OpenFoodNetwork::OrderGrouper.new rules, columns - - @header = header - @table = order_grouper.table(table_items) + # -- Build Report with Order Grouper + @report = OpenFoodNetwork::PaymentsReport.new spree_current_user, params + order_grouper = OpenFoodNetwork::OrderGrouper.new @report.rules, @report.columns + @table = order_grouper.table(@report.table_items) csv_file_name = "payments_#{timestamp}.csv" - render_report(@header, @table, params[:csv], csv_file_name) - + render_report(@report.header, @table, params[:csv], csv_file_name) end def orders_and_fulfillment - # -- Prepare parameters - params[:q] ||= {} - - if params[:q][:completed_at_gt].blank? - params[:q][:completed_at_gt] = Time.zone.now.beginning_of_month - else - params[:q][:completed_at_gt] = Time.zone.parse(params[:q][:completed_at_gt]) rescue Time.zone.now.beginning_of_month - end - - if params[:q] && !params[:q][:completed_at_lt].blank? - params[:q][:completed_at_lt] = Time.zone.parse(params[:q][:completed_at_lt]) rescue "" - end - params[:q][:meta_sort] ||= "completed_at.desc" + # -- Prepare Date Params + prepare_date_params params + # -- Prepare Form Options permissions = OpenFoodNetwork::Permissions.new(spree_current_user) - - # -- Search - - @search = Spree::Order.complete.not_state(:canceled).search(params[:q]) - orders = permissions.visible_orders.merge(@search.result) - - @line_items = permissions.visible_line_items.merge(Spree::LineItem.where(order_id: orders)) - @line_items = @line_items.supplied_by_any(params[:supplier_id_in]) if params[:supplier_id_in].present? - - line_items_with_hidden_details = @line_items.where('"spree_line_items"."id" NOT IN (?)', permissions.editable_line_items) - @line_items.select{ |li| line_items_with_hidden_details.include? li }.each do |line_item| - # TODO We should really be hiding customer code here too, but until we - # have an actual association between order and customer, it's a bit tricky - line_item.order.bill_address.assign_attributes(firstname: "HIDDEN", lastname: "", phone: "", address1: "", address2: "", city: "", zipcode: "", state: nil) - line_item.order.ship_address.assign_attributes(firstname: "HIDDEN", lastname: "", phone: "", address1: "", address2: "", city: "", zipcode: "", state: nil) - line_item.order.assign_attributes(email: "HIDDEN") - end - # My distributors and any distributors distributing products I supply @distributors = permissions.visible_enterprises_for_order_reports.is_distributor - # My suppliers and any suppliers supplying products I distribute @suppliers = permissions.visible_enterprises_for_order_reports.is_primary_producer @@ -437,239 +224,25 @@ Spree::Admin::ReportsController.class_eval do @report_types = REPORT_TYPES[:orders_and_fulfillment] @report_type = params[:report_type] - # -- Format according to report type - case params[:report_type] - when "order_cycle_supplier_totals" - table_items = @line_items - @include_blank = 'All' + @include_blank = 'All' - header = ["Producer", "Product", "Variant", "Amount", "Total Units", "Curr. Cost per Unit", "Total Cost", "Status", "Incoming Transport"] - - columns = [ proc { |line_items| line_items.first.variant.product.supplier.name }, - proc { |line_items| line_items.first.variant.product.name }, - proc { |line_items| line_items.first.variant.full_name }, - proc { |line_items| line_items.sum { |li| li.quantity } }, - proc { |line_items| total_units(line_items) }, - proc { |line_items| line_items.first.price }, - proc { |line_items| line_items.sum { |li| li.amount } }, - proc { |line_items| "" }, - proc { |line_items| "incoming transport" } ] - - rules = [ { group_by: proc { |line_item| line_item.variant.product.supplier }, - sort_by: proc { |supplier| supplier.name } }, - { group_by: proc { |line_item| line_item.variant.product }, - sort_by: proc { |product| product.name } }, - { group_by: proc { |line_item| line_item.variant }, - sort_by: proc { |variant| variant.full_name } } ] - - when "order_cycle_supplier_totals_by_distributor" - table_items = @line_items - @include_blank = 'All' - - header = ["Producer", "Product", "Variant", "To Hub", "Amount", "Curr. Cost per Unit", "Total Cost", "Shipping Method"] - - columns = [ proc { |line_items| line_items.first.variant.product.supplier.name }, - proc { |line_items| line_items.first.variant.product.name }, - proc { |line_items| line_items.first.variant.full_name }, - proc { |line_items| line_items.first.order.distributor.name }, - proc { |line_items| line_items.sum { |li| li.quantity } }, - proc { |line_items| line_items.first.price }, - proc { |line_items| line_items.sum { |li| li.amount } }, - proc { |line_items| "shipping method" } ] - - rules = [ { group_by: proc { |line_item| line_item.variant.product.supplier }, - sort_by: proc { |supplier| supplier.name } }, - { group_by: proc { |line_item| line_item.variant.product }, - sort_by: proc { |product| product.name } }, - { group_by: proc { |line_item| line_item.variant }, - sort_by: proc { |variant| variant.full_name }, - summary_columns: [ proc { |line_items| "" }, - proc { |line_items| "" }, - proc { |line_items| "" }, - proc { |line_items| "TOTAL" }, - proc { |line_items| "" }, - proc { |line_items| "" }, - proc { |line_items| line_items.sum { |li| li.amount } }, - proc { |line_items| "" } ] }, - { group_by: proc { |line_item| line_item.order.distributor }, - sort_by: proc { |distributor| distributor.name } } ] - - when "order_cycle_distributor_totals_by_supplier" - table_items = @line_items - @include_blank = 'All' - - header = ["Hub", "Producer", "Product", "Variant", "Amount", "Curr. Cost per Unit", "Total Cost", "Total Shipping Cost", "Shipping Method"] - - columns = [ proc { |line_items| line_items.first.order.distributor.name }, - proc { |line_items| line_items.first.variant.product.supplier.name }, - proc { |line_items| line_items.first.variant.product.name }, - proc { |line_items| line_items.first.variant.full_name }, - proc { |line_items| line_items.sum { |li| li.quantity } }, - proc { |line_items| line_items.first.price }, - proc { |line_items| line_items.sum { |li| li.amount } }, - proc { |line_items| "" }, - proc { |line_items| "shipping method" } ] - - rules = [ { group_by: proc { |line_item| line_item.order.distributor }, - sort_by: proc { |distributor| distributor.name }, - summary_columns: [ proc { |line_items| "" }, - proc { |line_items| "TOTAL" }, - proc { |line_items| "" }, - proc { |line_items| "" }, - proc { |line_items| "" }, - proc { |line_items| "" }, - proc { |line_items| line_items.sum { |li| li.amount } }, - proc { |line_items| line_items.map { |li| li.order }.uniq.sum { |o| o.ship_total } }, - proc { |line_items| "" } ] }, - { group_by: proc { |line_item| line_item.variant.product.supplier }, - sort_by: proc { |supplier| supplier.name } }, - { group_by: proc { |line_item| line_item.variant.product }, - sort_by: proc { |product| product.name } }, - { group_by: proc { |line_item| line_item.variant }, - sort_by: proc { |variant| variant.full_name } } ] - - when "order_cycle_customer_totals" - table_items = @line_items - @include_blank = 'All' - - header = ["Hub", "Customer", "Email", "Phone", "Producer", "Product", "Variant", - "Amount", "Item (#{currency_symbol})", "Item + Fees (#{currency_symbol})", "Admin & Handling (#{currency_symbol})", "Ship (#{currency_symbol})", "Total (#{currency_symbol})", "Paid?", - "Shipping", "Delivery?", - "Ship Street", "Ship Street 2", "Ship City", "Ship Postcode", "Ship State", - "Comments", "SKU", - "Order Cycle", "Payment Method", "Customer Code", "Tags", - "Billing Street 1", "Billing Street 2", "Billing City", "Billing Postcode", "Billing State" - ] - - rsa = proc { |line_items| line_items.first.order.shipping_method.andand.require_ship_address } - - columns = [ - proc { |line_items| line_items.first.order.distributor.name }, - proc { |line_items| line_items.first.order.bill_address.firstname + " " + line_items.first.order.bill_address.lastname }, - proc { |line_items| line_items.first.order.email }, - proc { |line_items| line_items.first.order.bill_address.phone }, - proc { |line_items| line_items.first.variant.product.supplier.name }, - proc { |line_items| line_items.first.variant.product.name }, - proc { |line_items| line_items.first.variant.full_name }, - - proc { |line_items| line_items.sum { |li| li.quantity } }, - proc { |line_items| line_items.sum { |li| li.amount } }, - proc { |line_items| line_items.sum { |li| li.amount_with_adjustments } }, - proc { |line_items| "" }, - proc { |line_items| "" }, - proc { |line_items| "" }, - proc { |line_items| "" }, - - proc { |line_items| line_items.first.order.shipping_method.andand.name }, - proc { |line_items| rsa.call(line_items) ? 'Y' : 'N' }, - - proc { |line_items| line_items.first.order.ship_address.andand.address1 if rsa.call(line_items) }, - proc { |line_items| line_items.first.order.ship_address.andand.address2 if rsa.call(line_items) }, - proc { |line_items| line_items.first.order.ship_address.andand.city if rsa.call(line_items) }, - proc { |line_items| line_items.first.order.ship_address.andand.zipcode if rsa.call(line_items) }, - proc { |line_items| line_items.first.order.ship_address.andand.state if rsa.call(line_items) }, - - proc { |line_items| "" }, - proc { |line_items| line_items.first.variant.product.sku }, - - proc { |line_items| line_items.first.order.order_cycle.andand.name }, - proc { |line_items| line_items.first.order.payments.first.andand.payment_method.andand.name }, - proc { |line_items| line_items.first.order.user.andand.customer_of(line_items.first.order.distributor).andand.code }, - proc { |line_items| "" }, - - proc { |line_items| line_items.first.order.bill_address.andand.address1 }, - proc { |line_items| line_items.first.order.bill_address.andand.address2 }, - proc { |line_items| line_items.first.order.bill_address.andand.city }, - proc { |line_items| line_items.first.order.bill_address.andand.zipcode }, - proc { |line_items| line_items.first.order.bill_address.andand.state } ] - - rules = [ { group_by: proc { |line_item| line_item.order.distributor }, - sort_by: proc { |distributor| distributor.name } }, - { group_by: proc { |line_item| line_item.order }, - sort_by: proc { |order| order.bill_address.lastname + " " + order.bill_address.firstname }, - summary_columns: [ - proc { |line_items| line_items.first.order.distributor.name }, - proc { |line_items| line_items.first.order.bill_address.firstname + " " + line_items.first.order.bill_address.lastname }, - proc { |line_items| "" }, - proc { |line_items| "" }, - proc { |line_items| "" }, - proc { |line_items| "TOTAL" }, - proc { |line_items| "" }, - - proc { |line_items| "" }, - proc { |line_items| line_items.sum { |li| li.amount } }, - proc { |line_items| line_items.sum { |li| li.amount_with_adjustments } }, - proc { |line_items| line_items.map { |li| li.order }.uniq.sum { |o| o.admin_and_handling_total } }, - proc { |line_items| line_items.map { |li| li.order }.uniq.sum { |o| o.ship_total } }, - proc { |line_items| line_items.map { |li| li.order }.uniq.sum { |o| o.total } }, - proc { |line_items| line_items.all? { |li| li.order.paid? } ? "Yes" : "No" }, - - proc { |line_items| "" }, - proc { |line_items| "" }, - - proc { |line_items| "" }, - proc { |line_items| "" }, - proc { |line_items| "" }, - proc { |line_items| "" }, - proc { |line_items| "" }, - - proc { |line_items| line_items.first.order.special_instructions } , - proc { |line_items| "" }, - - proc { |line_items| line_items.first.order.order_cycle.andand.name }, - proc { |line_items| line_items.first.order.payments.first.andand.payment_method.andand.name }, - proc { |line_items| "" }, - proc { |line_items| "" }, - - proc { |line_items| "" }, - proc { |line_items| "" }, - proc { |line_items| "" }, - proc { |line_items| "" }, - proc { |line_items| "" } - ] }, - - { group_by: proc { |line_item| line_item.variant.product }, - sort_by: proc { |product| product.name } }, - { group_by: proc { |line_item| line_item.variant }, - sort_by: proc { |variant| variant.full_name } } ] - - else - table_items = @line_items - @include_blank = 'All' - - header = ["Producer", "Product", "Variant", "Amount", "Curr. Cost per Unit", "Total Cost", "Status", "Incoming Transport"] - - columns = [ proc { |line_items| line_items.first.variant.product.supplier.name }, - proc { |line_items| line_items.first.variant.product.name }, - proc { |line_items| line_items.first.variant.full_name }, - proc { |line_items| line_items.sum { |li| li.quantity } }, - proc { |line_items| line_items.first.price }, - proc { |line_items| line_items.sum { |li| li.quantity * li.price } }, - proc { |line_items| "" }, - proc { |line_items| "incoming transport" } ] - - rules = [ { group_by: proc { |line_item| line_item.variant.product.supplier }, - sort_by: proc { |supplier| supplier.name } }, - { group_by: proc { |line_item| line_item.variant.product }, - sort_by: proc { |product| product.name } }, - { group_by: proc { |line_item| line_item.variant }, - sort_by: proc { |variant| variant.full_name } } ] - - end - - order_grouper = OpenFoodNetwork::OrderGrouper.new rules, columns - - @header = header - @table = order_grouper.table(table_items) + # -- Build Report with Order Grouper + @report = OpenFoodNetwork::OrdersAndFulfillmentsReport.new spree_current_user, params + order_grouper = OpenFoodNetwork::OrderGrouper.new @report.rules, @report.columns + @table = order_grouper.table(@report.table_items) csv_file_name = "#{params[:report_type]}_#{timestamp}.csv" - render_report(@header, @table, params[:csv], csv_file_name) + render_report(@report.header, @table, params[:csv], csv_file_name) end def products_and_inventory @report_types = REPORT_TYPES[:products_and_inventory] - @report = OpenFoodNetwork::ProductsAndInventoryReport.new spree_current_user, params + if params[:report_type] != 'lettuce_share' + @report = OpenFoodNetwork::ProductsAndInventoryReport.new spree_current_user, params + else + @report = OpenFoodNetwork::LettuceShareReport.new spree_current_user, params + end render_report(@report.header, @report.table, params[:csv], "products_and_inventory_#{timestamp}.csv") end @@ -679,7 +252,22 @@ Spree::Admin::ReportsController.class_eval do render_report(@report.header, @report.table, params[:csv], "users_and_enterprises_#{timestamp}.csv") end - def render_report (header, table, create_csv, csv_file_name) + def xero_invoices + if request.get? + params[:q] ||= {} + params[:q][:completed_at_gt] = Time.zone.today.beginning_of_month + params[:invoice_date] = Time.zone.today + params[:due_date] = Time.zone.today + 1.month + end + @distributors = Enterprise.is_distributor.managed_by(spree_current_user) + @order_cycles = OrderCycle.active_or_complete.accessible_by(spree_current_user).order('orders_close_at DESC') + + @report = OpenFoodNetwork::XeroInvoicesReport.new spree_current_user, params + render_report(@report.header, @report.table, params[:csv], "xero_invoices_#{timestamp}.csv") + end + + + def render_report(header, table, create_csv, csv_file_name) unless create_csv render :html => table else @@ -693,6 +281,20 @@ Spree::Admin::ReportsController.class_eval do private + def prepare_date_params(params) + # -- Prepare parameters + params[:q] ||= {} + if params[:q][:completed_at_gt].blank? + params[:q][:completed_at_gt] = Time.zone.now.beginning_of_month + else + params[:q][:completed_at_gt] = Time.zone.parse(params[:q][:completed_at_gt]) rescue Time.zone.now.beginning_of_month + end + if params[:q] && !params[:q][:completed_at_lt].blank? + params[:q][:completed_at_lt] = Time.zone.parse(params[:q][:completed_at_lt]) rescue "" + end + params[:q][:meta_sort] ||= "completed_at.desc" + end + def load_data # Load distributors either owned by the user or selling their enterprises products. my_distributors = Enterprise.is_distributor.managed_by(spree_current_user) @@ -716,22 +318,16 @@ Spree::Admin::ReportsController.class_eval do :sales_total => { :name => "Sales Total", :description => "Sales Total For All Orders" }, :users_and_enterprises => { :name => "Users & Enterprises", :description => "Enterprise Ownership & Status" }, :order_cycle_management => {:name => "Order Cycle Management", :description => ''}, + :sales_tax => { :name => "Sales Tax", :description => "Sales Tax For Orders" }, + :xero_invoices => { :name => "Xero Invoices", :description => 'Invoices for import into Xero' }, + :packing => { :name => "Packing Reports", :description => '' }, :sales_tax => { :name => "Sales Tax", :description => "Sales Tax For Orders" } } # Return only reports the user is authorized to view. reports.select { |action| can? action, :report } end - def total_units(line_items) - return " " if line_items.map{ |li| li.variant.unit_value.nil? }.any? - total_units = line_items.sum do |li| - scale_factor = ( li.product.variant_unit == 'weight' ? 1000 : 1 ) - li.quantity * li.variant.unit_value / scale_factor - end - total_units.round(3) - end - def timestamp - Time.now.strftime("%Y%m%d") + Time.zone.now.strftime("%Y%m%d") end end diff --git a/app/controllers/spree/admin/search_controller_decorator.rb b/app/controllers/spree/admin/search_controller_decorator.rb index f268f95820..6c5942934e 100644 --- a/app/controllers/spree/admin/search_controller_decorator.rb +++ b/app/controllers/spree/admin/search_controller_decorator.rb @@ -12,7 +12,14 @@ Spree::Admin::SearchController.class_eval do :bill_address_lastname_start => params[:q] }).result.limit(10) end - render :users + render :users end + + + def users_with_ams + users_without_ams + render json: @users, each_serializer: Api::Admin::UserSerializer + end + alias_method_chain :users, :ams end diff --git a/app/controllers/spree/admin/variants_controller_decorator.rb b/app/controllers/spree/admin/variants_controller_decorator.rb index 2308d2a923..814cfb12f8 100644 --- a/app/controllers/spree/admin/variants_controller_decorator.rb +++ b/app/controllers/spree/admin/variants_controller_decorator.rb @@ -14,9 +14,10 @@ Spree::Admin::VariantsController.class_eval do if params[:distributor_id].present? distributor = Enterprise.find params[:distributor_id] @variants = @variants.in_distributor(distributor) + scoper = OpenFoodNetwork::ScopeVariantToHub.new(distributor) # Perform scoping after all filtering is done. # Filtering could be a problem on scoped variants. - @variants.each { |v| v.scope_to_hub(distributor) } + @variants.each { |v| scoper.scope(v) } end end diff --git a/app/controllers/spree/api/line_items_controller_decorator.rb b/app/controllers/spree/api/line_items_controller_decorator.rb index 35fca864f4..93e4099d2a 100644 --- a/app/controllers/spree/api/line_items_controller_decorator.rb +++ b/app/controllers/spree/api/line_items_controller_decorator.rb @@ -1,8 +1,13 @@ Spree::Api::LineItemsController.class_eval do - after_filter :apply_enterprise_fees, :only => :update + around_filter :apply_enterprise_fees_with_lock, only: :update - def apply_enterprise_fees + private + + def apply_enterprise_fees_with_lock authorize! :read, order - order.update_distribution_charge! + order.with_lock do + yield + order.update_distribution_charge! + end end end diff --git a/app/controllers/spree/checkout_controller_decorator.rb b/app/controllers/spree/checkout_controller_decorator.rb index 061599870f..641e15f640 100644 --- a/app/controllers/spree/checkout_controller_decorator.rb +++ b/app/controllers/spree/checkout_controller_decorator.rb @@ -1,3 +1,5 @@ +require 'open_food_network/last_used_address' + Spree::CheckoutController.class_eval do include CheckoutHelper @@ -19,7 +21,10 @@ Spree::CheckoutController.class_eval do def before_address associate_user - last_used_bill_address, last_used_ship_address = find_last_used_addresses(@order.email) + lua = OpenFoodNetwork::LastUsedAddress.new(@order.email) + last_used_bill_address = lua.last_used_bill_address.andand.clone + last_used_ship_address = lua.last_used_ship_address.andand.clone + preferred_bill_address, preferred_ship_address = spree_current_user.bill_address, spree_current_user.ship_address if spree_current_user.respond_to?(:bill_address) && spree_current_user.respond_to?(:ship_address) @order.bill_address ||= preferred_bill_address || last_used_bill_address || Spree::Address.default @@ -29,14 +34,4 @@ Spree::CheckoutController.class_eval do def after_complete reset_order end - - def find_last_used_addresses(email) - past = Spree::Order.order("id desc").where(:email => email).where("state != 'cart'").limit(8) - if order = past.detect(&:bill_address) - bill_address = order.bill_address.clone if order.bill_address - ship_address = order.ship_address.clone if order.ship_address and order.shipping_method.andand.require_ship_address - end - - [bill_address, ship_address] - end end diff --git a/app/controllers/spree/orders_controller_decorator.rb b/app/controllers/spree/orders_controller_decorator.rb index fd5d3b634e..c6d325d0f8 100644 --- a/app/controllers/spree/orders_controller_decorator.rb +++ b/app/controllers/spree/orders_controller_decorator.rb @@ -23,13 +23,23 @@ Spree::OrdersController.class_eval do end def populate - populator = Spree::OrderPopulator.new(current_order(true), current_currency) - if populator.populate(params.slice(:products, :variants, :quantity), true) - fire_event('spree.cart.add') - fire_event('spree.order.contents_changed') - render json: true, status: 200 - else - render json: false, status: 402 + # Without intervention, the Spree::Adjustment#update_adjustable callback is called many times + # during cart population, for both taxation and enterprise fees. This operation triggers a + # costly Spree::Order#update!, which only needs to be run once. We avoid this by disabling + # callbacks on Spree::Adjustment and then manually invoke Spree::Order#update! on success. + + Spree::Adjustment.without_callbacks do + populator = Spree::OrderPopulator.new(current_order(true), current_currency) + if populator.populate(params.slice(:products, :variants, :quantity), true) + fire_event('spree.cart.add') + fire_event('spree.order.contents_changed') + + current_order.update! + + render json: true, status: 200 + else + render json: false, status: 402 + end end end diff --git a/app/helpers/admin/account_helper.rb b/app/helpers/admin/account_helper.rb new file mode 100644 index 0000000000..0292522b74 --- /dev/null +++ b/app/helpers/admin/account_helper.rb @@ -0,0 +1,14 @@ +module Admin + module AccountHelper + def invoice_description_for(invoice) + month = t(:abbr_month_names, :scope => :date)[invoice.month] + year = invoice.year + star = invoice.order.nil? || invoice.order.completed? ? "" : "*" + "#{month} #{year}#{star}" + end + + def invoice_total_for(invoice) + invoice.order.andand.display_total || Spree::Money.new(0, { :currency => Spree::Config[:currency] }) + end + end +end diff --git a/app/helpers/admin/business_model_configuration_helper.rb b/app/helpers/admin/business_model_configuration_helper.rb new file mode 100644 index 0000000000..dcfebb1969 --- /dev/null +++ b/app/helpers/admin/business_model_configuration_helper.rb @@ -0,0 +1,48 @@ +module Admin + module BusinessModelConfigurationHelper + def monthly_bill_description + plus = monthly_bill_includes_fixed? && monthly_bill_includes_rate? ? " + " : "" + + if fixed_description.empty? && rate_description.empty? + t(:free).upcase + elsif monthly_bill_includes_cap? && monthly_bill_includes_rate? # only care about cap if there is a rate too + "#{fixed_description}#{plus}#{rate_description}{joiner}#{cap_description} #{t(:per_month).upcase}#{tax_description.upcase}" + else + "#{fixed_description}#{plus}#{rate_description} #{t(:per_month).upcase}#{tax_description.upcase}" + end + end + + private + + def fixed_description + fixed_amount = Spree::Money.new(Spree::Config[:account_invoices_monthly_fixed], {currency: Spree::Config[:currency]} ).rounded + monthly_bill_includes_fixed? ? "#{fixed_amount}" : "" + end + + def rate_description + percentage = (Spree::Config[:account_invoices_monthly_rate]*100).round(2) + monthly_bill_includes_rate? ? t(:percentage_of_sales, percentage: "#{percentage}%").upcase : "" + end + + def cap_description + cap_amount = Spree::Money.new(Spree::Config[:account_invoices_monthly_cap], { currency: Spree::Config[:currency] }).rounded + monthly_bill_includes_cap? ? "#{t(:capped_at_cap, cap: cap_amount).upcase}" : "" + end + + def tax_description + Spree::Config[:account_invoices_tax_rate] > 0 ? ", #{t(:plus_tax).upcase}" : "" + end + + def monthly_bill_includes_fixed? + Spree::Config[:account_invoices_monthly_fixed] > 0 + end + + def monthly_bill_includes_rate? + Spree::Config[:account_invoices_monthly_rate] > 0 + end + + def monthly_bill_includes_cap? + Spree::Config[:account_invoices_monthly_cap] > 0 + end + end +end diff --git a/app/helpers/admin/injection_helper.rb b/app/helpers/admin/injection_helper.rb index 961bc60afb..343e15ef29 100644 --- a/app/helpers/admin/injection_helper.rb +++ b/app/helpers/admin/injection_helper.rb @@ -1,5 +1,7 @@ module Admin module InjectionHelper + include BusinessModelConfigurationHelper + def admin_inject_enterprise admin_inject_json_ams "admin.enterprises", "enterprise", @enterprise, Api::Admin::EnterpriseSerializer end @@ -25,12 +27,16 @@ module Admin admin_inject_json_ams_array "admin.shipping_methods", "shippingMethods", @shipping_methods, Api::Admin::IdNameSerializer end - def admin_inject_hubs - admin_inject_json_ams_array "ofn.admin", "hubs", @hubs, Api::Admin::IdNameSerializer + def admin_inject_shops(ngModule='admin.customers') + admin_inject_json_ams_array ngModule, "shops", @shops, Api::Admin::IdNameSerializer end - def admin_inject_producers - admin_inject_json_ams_array "ofn.admin", "producers", @producers, Api::Admin::IdNameSerializer + def admin_inject_hubs(opts={module: 'ofn.admin'}) + admin_inject_json_ams_array opts[:module], "hubs", @hubs, Api::Admin::IdNameSerializer + end + + def admin_inject_producers(opts={module: 'ofn.admin'}) + admin_inject_json_ams_array opts[:module], "producers", @producers, Api::Admin::IdNameSerializer end def admin_inject_enterprise_permissions @@ -43,7 +49,7 @@ module Admin end def admin_inject_hub_permissions - render partial: "admin/json/injection_ams", locals: {ngModule: "ofn.admin", name: "hubPermissions", json: @hub_permissions.to_json} + render partial: "admin/json/injection_ams", locals: {ngModule: "admin.variantOverrides", name: "hubPermissions", json: @hub_permissions.to_json} end def admin_inject_products @@ -63,15 +69,23 @@ module Admin end def admin_inject_variant_overrides - admin_inject_json_ams_array "ofn.admin", "variantOverrides", @variant_overrides, Api::Admin::VariantOverrideSerializer + admin_inject_json_ams_array "admin.variantOverrides", "variantOverrides", @variant_overrides, Api::Admin::VariantOverrideSerializer end def admin_inject_order_cycle_instance - render partial: "admin/json/injection_ams", locals: {ngModule: 'admin.order_cycles', name: 'ocInstance', json: "{coordinator_id: '#{@order_cycle.coordinator.id}'}"} + render partial: "admin/json/injection_ams", locals: {ngModule: 'admin.orderCycles', name: 'ocInstance', json: "{coordinator_id: '#{@order_cycle.coordinator.id}'}"} + end + + def admin_inject_order_cycles + admin_inject_json_ams_array "admin.orders", "orderCycles", @order_cycles, Api::Admin::BasicOrderCycleSerializer, current_user: spree_current_user + end + + def admin_inject_monthly_bill_description + render partial: "admin/json/injection_ams", locals: {ngModule: "admin.enterprises", name: "monthlyBillDescription", json: monthly_bill_description.to_json} end def admin_inject_spree_api_key - render partial: "admin/json/injection_ams", locals: {ngModule: 'ofn.admin', name: 'SpreeApiKey', json: "'#{@spree_api_key.to_s}'"} + render partial: "admin/json/injection_ams", locals: {ngModule: 'admin.indexUtils', name: 'SpreeApiKey', json: "'#{@spree_api_key.to_s}'"} end def admin_inject_json_ams(ngModule, name, data, serializer, opts = {}) diff --git a/app/helpers/checkout_helper.rb b/app/helpers/checkout_helper.rb index c1139b63f9..080db9c0e8 100644 --- a/app/helpers/checkout_helper.rb +++ b/app/helpers/checkout_helper.rb @@ -38,6 +38,10 @@ module CheckoutHelper Spree::Money.new order.total_tax, currency: order.currency end + def display_checkout_total_less_tax(order) + Spree::Money.new order.total - order.total_tax, currency: order.currency + end + def checkout_state_options(source_address) if source_address == :billing address = @order.billing_address diff --git a/app/helpers/discourse_helper.rb b/app/helpers/discourse_helper.rb new file mode 100644 index 0000000000..1d813cf404 --- /dev/null +++ b/app/helpers/discourse_helper.rb @@ -0,0 +1,25 @@ +module DiscourseHelper + def discourse_configured? + discourse_url.present? + end + + def discourse_url + ENV['DISCOURSE_URL'] + end + + def discourse_login_url + discourse_url + '/login' + end + + def discourse_sso_url + discourse_url + '/session/sso_login' + end + + def discourse_url! + discourse_url or raise 'Missing Discourse URL' + end + + def discourse_sso_secret! + ENV['DISCOURSE_SSO_SECRET'] or raise 'Missing SSO secret' + end +end diff --git a/app/helpers/enterprises_helper.rb b/app/helpers/enterprises_helper.rb index 6109842f84..85d1167ab7 100644 --- a/app/helpers/enterprises_helper.rb +++ b/app/helpers/enterprises_helper.rb @@ -48,17 +48,17 @@ module EnterprisesHelper def shop_trial_in_progress?(enterprise) !!enterprise.shop_trial_start_date && - (enterprise.shop_trial_start_date + Enterprise::SHOP_TRIAL_LENGTH.days > Time.now) && + (enterprise.shop_trial_start_date + Enterprise::SHOP_TRIAL_LENGTH.days > Time.zone.now) && %w(own any).include?(enterprise.sells) end def shop_trial_expired?(enterprise) !!enterprise.shop_trial_start_date && - (enterprise.shop_trial_start_date + Enterprise::SHOP_TRIAL_LENGTH.days <= Time.now) && + (enterprise.shop_trial_start_date + Enterprise::SHOP_TRIAL_LENGTH.days <= Time.zone.now) && %w(own any).include?(enterprise.sells) end def remaining_trial_days(enterprise) - distance_of_time_in_words(Time.now, enterprise.shop_trial_start_date + Enterprise::SHOP_TRIAL_LENGTH.days) + distance_of_time_in_words(Time.zone.now, enterprise.shop_trial_start_date + Enterprise::SHOP_TRIAL_LENGTH.days) end end diff --git a/app/helpers/injection_helper.rb b/app/helpers/injection_helper.rb index 37794cef9d..df67261fd7 100644 --- a/app/helpers/injection_helper.rb +++ b/app/helpers/injection_helper.rb @@ -1,6 +1,16 @@ +require 'open_food_network/enterprise_injection_data' + module InjectionHelper def inject_enterprises - inject_json_ams "enterprises", Enterprise.activated.all, Api::EnterpriseSerializer, active_distributors: @active_distributors + inject_json_ams "enterprises", Enterprise.activated.includes(:address).all, Api::EnterpriseSerializer, enterprise_injection_data + end + + def inject_group_enterprises + inject_json_ams "group_enterprises", @group.enterprises.activated.all, Api::EnterpriseSerializer, enterprise_injection_data + end + + def inject_current_hub + inject_json_ams "currentHub", current_distributor, Api::EnterpriseSerializer, enterprise_injection_data end def inject_current_order @@ -53,4 +63,13 @@ module InjectionHelper end render partial: "json/injection_ams", locals: {name: name, json: json} end + + + private + + def enterprise_injection_data + @enterprise_injection_data ||= OpenFoodNetwork::EnterpriseInjectionData.new + {data: @enterprise_injection_data} + end + end diff --git a/app/helpers/markdown_helper.rb b/app/helpers/markdown_helper.rb new file mode 100644 index 0000000000..bd45e31124 --- /dev/null +++ b/app/helpers/markdown_helper.rb @@ -0,0 +1,6 @@ +module MarkdownHelper + def render_markdown(markdown) + md ||= Redcarpet::Markdown.new(Redcarpet::Render::HTML, no_intra_emphasis: true, tables: true, autolink: true, superscript: true) + md.render markdown + end +end diff --git a/app/helpers/spree/admin/base_helper_decorator.rb b/app/helpers/spree/admin/base_helper_decorator.rb index e278626c5c..86e77431ba 100644 --- a/app/helpers/spree/admin/base_helper_decorator.rb +++ b/app/helpers/spree/admin/base_helper_decorator.rb @@ -5,7 +5,7 @@ module Spree def link_to_remove_fields(name, f, options = {}) name = '' if options[:no_text] options[:class] = '' unless options[:class] - options[:class] += 'no-text with-tip' if options[:no_text] + options[:class] += 'no-text' if options[:no_text] url = if f.object.persisted? options[:url] || [:admin, f.object] @@ -15,6 +15,16 @@ module Spree link_to_with_icon('icon-trash', name, url, :class => "remove_fields #{options[:class]}", :data => {:action => 'remove'}, :title => t(:remove)) + f.hidden_field(:_destroy) end + + + def preference_field_tag_with_files(name, value, options) + if options[:type] == :file + file_field_tag name, preference_field_options(options) + else + preference_field_tag_without_files name, value, options + end + end + alias_method_chain :preference_field_tag, :files end end end diff --git a/app/helpers/spree/admin/orders_helper_decorator.rb b/app/helpers/spree/admin/orders_helper_decorator.rb new file mode 100644 index 0000000000..23443cc400 --- /dev/null +++ b/app/helpers/spree/admin/orders_helper_decorator.rb @@ -0,0 +1,25 @@ +module Spree + module Admin + module OrdersHelper + def order_links(order) + links = [] + links << { name: t(:view_order), url: admin_order_path(order), icon: 'icon-eye-open' } unless action_name == "show" + links << { name: t(:edit_order), url: edit_admin_order_path(order), icon: 'icon-edit' } unless action_name == "edit" + if @order.complete? + links << { name: t(:resend_confirmation), url: resend_admin_order_path(order), icon: 'icon-email', method: 'post', confirm: t(:confirm_resend_order_confirmation) } + if @order.distributor.can_invoice? + links << { name: t(:send_invoice), url: invoice_admin_order_path(order), icon: 'icon-email', confirm: t(:confirm_send_invoice) } + else + links << { name: t(:send_invoice), url: "#", icon: 'icon-email', confirm: t(:must_have_valid_business_number, enterprise_name: order.distributor.name) } + end + links << { name: t(:print_invoice), url: print_admin_order_path(order), icon: 'icon-print', target: "_blank" } + end + if @order.ready_to_ship? + links << { name: t(:ship_order), url: fire_admin_order_path(@order, :e => 'ship'), method: 'put', icon: 'icon-truck', confirm: t(:are_you_sure) } + end + links << { name: t(:cancel_order), url: fire_admin_order_path(@order.number, { :e => 'cancel' }), icon: 'icon-trash', confirm: t(:are_you_sure) } if order.can_cancel? + links + end + end + end +end diff --git a/app/helpers/spree/reports_helper.rb b/app/helpers/spree/reports_helper.rb index f4ecdcea6f..bbc184d800 100644 --- a/app/helpers/spree/reports_helper.rb +++ b/app/helpers/spree/reports_helper.rb @@ -18,6 +18,11 @@ module Spree orders.map { |o| o.shipping_method.andand.name }.uniq end + def xero_report_types + [['Summary', 'summary'], + ['Detailed', 'detailed']] + end + def currency_symbol Spree::Money.currency_symbol end diff --git a/app/jobs/finalize_account_invoices.rb b/app/jobs/finalize_account_invoices.rb new file mode 100644 index 0000000000..25b614bebc --- /dev/null +++ b/app/jobs/finalize_account_invoices.rb @@ -0,0 +1,96 @@ +class FinalizeAccountInvoices + attr_reader :year, :month, :start_date, :end_date + + def initialize(year = nil, month = nil) + ref_point = Time.zone.now - 1.month + @year = year || ref_point.year + @month = month || ref_point.month + @start_date = Time.zone.local(@year, @month) + @end_date = Time.zone.local(@year, @month) + 1.month + end + + def before(job) + UpdateBillablePeriods.new(year, month).perform + UpdateAccountInvoices.new(year, month).perform + end + + def perform + return unless settings_are_valid? + + + invoice_orders = AccountInvoice.where(year: year, month: month).map(&:order) + invoice_orders.select{ |order| order.present? && order.completed_at.nil? }.each{ |order| finalize(order) } + end + + def finalize(invoice_order) + # TODO: When we implement per-customer and/or per-user preferences around shipping and payment methods + # we can update these to read from those preferences + invoice_order.payments.create(payment_method_id: Spree::Config.default_accounts_payment_method_id, amount: invoice_order.total) + invoice_order.update_attribute(:shipping_method_id, Spree::Config.default_accounts_shipping_method_id) + while invoice_order.state != "complete" + if invoice_order.errors.any? + Bugsnag.notify(RuntimeError.new("FinalizeInvoiceError"), { + job: "FinalizeAccountInvoices", + error: "Cannot finalize invoice due to errors", + data: { + errors: invoice_order.errors.full_messages + } + }) + break + else + invoice_order.next + end + end + end + + private + + def settings_are_valid? + unless end_date <= Time.zone.now + Bugsnag.notify(RuntimeError.new("InvalidJobSettings"), { + job: "FinalizeAccountInvoices", + error: "end_date is in the future", + data: { + end_date: end_date.in_time_zone.strftime("%F %T"), + now: Time.zone.now.strftime("%F %T") + } + }) + return false + end + + unless @accounts_distributor = Enterprise.find_by_id(Spree::Config.accounts_distributor_id) + Bugsnag.notify(RuntimeError.new("InvalidJobSettings"), { + job: "FinalizeAccountInvoices", + error: "accounts_distributor_id is invalid", + data: { + accounts_distributor_id: Spree::Config.accounts_distributor_id + } + }) + return false + end + + unless @accounts_distributor.payment_methods.find_by_id(Spree::Config.default_accounts_payment_method_id) + Bugsnag.notify(RuntimeError.new("InvalidJobSettings"), { + job: "FinalizeAccountInvoices", + error: "default_accounts_payment_method_id is invalid", + data: { + default_accounts_payment_method_id: Spree::Config.default_accounts_payment_method_id + } + }) + return false + end + + unless @accounts_distributor.shipping_methods.find_by_id(Spree::Config.default_accounts_shipping_method_id) + Bugsnag.notify(RuntimeError.new("InvalidJobSettings"), { + job: "FinalizeAccountInvoices", + error: "default_accounts_shipping_method_id is invalid", + data: { + default_accounts_shipping_method_id: Spree::Config.default_accounts_shipping_method_id + } + }) + return false + end + + true + end +end diff --git a/app/jobs/order_cycle_notification_job.rb b/app/jobs/order_cycle_notification_job.rb new file mode 100644 index 0000000000..b18348813e --- /dev/null +++ b/app/jobs/order_cycle_notification_job.rb @@ -0,0 +1,6 @@ +OrderCycleNotificationJob = Struct.new(:order_cycle_id) do + def perform + order_cycle = OrderCycle.find order_cycle_id + order_cycle.suppliers.each { |supplier| ProducerMailer.order_cycle_report(supplier, order_cycle).deliver } + end +end diff --git a/app/jobs/update_account_invoices.rb b/app/jobs/update_account_invoices.rb new file mode 100644 index 0000000000..16112ef870 --- /dev/null +++ b/app/jobs/update_account_invoices.rb @@ -0,0 +1,108 @@ +class UpdateAccountInvoices + attr_reader :year, :month, :start_date, :end_date + + def initialize(year = nil, month = nil) + ref_point = Time.zone.now - 1.day + @year = year || ref_point.year + @month = month || ref_point.month + @start_date = Time.zone.local(@year, @month) + @end_date = Time.zone.local(@year, @month) + 1.month + @end_date = Time.zone.now.beginning_of_day if start_date == Time.zone.now.beginning_of_month + end + + def before(job) + UpdateBillablePeriods.new(year, month).perform + end + + def perform + return unless settings_are_valid? + + account_invoices = AccountInvoice.where(year: year, month: month) + account_invoices.each { |account_invoice| update(account_invoice) } + end + + def update(account_invoice) + current_adjustments = [] + unless account_invoice.order + account_invoice.order = account_invoice.user.orders.new(distributor_id: Spree::Config[:accounts_distributor_id]) + end + + if account_invoice.order.complete? + Bugsnag.notify(RuntimeError.new("InvoiceAlreadyFinalized"), { + invoice_order: account_invoice.order.as_json + }) + else + billable_periods = account_invoice.billable_periods.order(:enterprise_id, :begins_at).reject{ |bp| bp.bill == 0 } + + if billable_periods.any? + oldest_enterprise = billable_periods.first.enterprise + address = oldest_enterprise.address.dup + first, space, last = (oldest_enterprise.contact || "").partition(' ') + address.update_attributes(phone: oldest_enterprise.phone) if oldest_enterprise.phone.present? + address.update_attributes(firstname: first, lastname: last) if first.present? && last.present? + account_invoice.order.update_attributes(bill_address: address, ship_address: address) + end + + billable_periods.each do |billable_period| + current_adjustments << billable_period.ensure_correct_adjustment_for(account_invoice.order) + end + + account_invoice.save if current_adjustments.any? + + clean_up(account_invoice.order, current_adjustments) + end + end + + def clean_up(invoice_order, current_adjustments) + # Snag and then delete any obsolete adjustments + obsolete_adjustments = invoice_order.adjustments.where('source_type = (?) AND id NOT IN (?)', "BillablePeriod", current_adjustments) + + if obsolete_adjustments.any? + Bugsnag.notify(RuntimeError.new("Obsolete Adjustments"), { + current: current_adjustments.map(&:as_json), + obsolete: obsolete_adjustments.map(&:as_json) + }) + + obsolete_adjustments.destroy_all + end + + if current_adjustments.empty? + if invoice_order.persisted? + Bugsnag.notify(RuntimeError.new("Empty Persisted Invoice"), { + invoice_order: invoice_order.as_json + }) + else + invoice_order.destroy + end + end + end + + private + + def settings_are_valid? + unless end_date <= Time.zone.now + Bugsnag.notify(RuntimeError.new("InvalidJobSettings"), { + job: "UpdateAccountInvoices", + error: "end_date is in the future", + data: { + end_date: end_date.in_time_zone.strftime("%F %T"), + now: Time.zone.now.strftime("%F %T") + } + }) + return false + end + + unless Enterprise.find_by_id(Spree::Config.accounts_distributor_id) + Bugsnag.notify(RuntimeError.new("InvalidJobSettings"), { + job: "UpdateAccountInvoices", + error: "accounts_distributor_id is invalid", + data: { + accounts_distributor_id: Spree::Config.accounts_distributor_id + } + }) + return false + end + + true + end +end diff --git a/app/jobs/update_billable_periods.rb b/app/jobs/update_billable_periods.rb new file mode 100644 index 0000000000..80f19d961a --- /dev/null +++ b/app/jobs/update_billable_periods.rb @@ -0,0 +1,130 @@ +class UpdateBillablePeriods + attr_reader :year, :month, :start_date, :end_date + + def initialize(year = nil, month = nil) + ref_point = Time.zone.now - 1.day + @year = year || ref_point.year + @month = month || ref_point.month + @start_date = Time.zone.local(@year, @month) + @end_date = Time.zone.local(@year, @month) + 1.month + @end_date = Time.zone.now.beginning_of_day if start_date == Time.zone.now.beginning_of_month + end + + def perform + return unless settings_are_valid? + + job_start_time = Time.zone.now + + enterprises = Enterprise.where('created_at < (?)', end_date).select([:id, :name, :owner_id, :sells, :shop_trial_start_date, :created_at]) + + # Cycle through enterprises + enterprises.each do |enterprise| + start_for_enterprise = [start_date, enterprise.created_at].max + end_for_enterprise = [end_date].min # [end_date, enterprise.deleted_at].min + + # Cycle through previous versions of this enterprise + versions = enterprise.versions.where('created_at >= (?) AND created_at < (?)', start_for_enterprise, end_for_enterprise).order(:created_at) + + trial_start = enterprise.shop_trial_start_date + trial_expiry = enterprise.shop_trial_expiry + + versions.each do |version| + begins_at = version.previous.andand.created_at || start_for_enterprise + ends_at = version.created_at + + split_for_trial(version.reify, begins_at, ends_at, trial_start, trial_expiry) + end + + # Update / create billable_period for current start + begins_at = versions.last.andand.created_at || start_for_enterprise + ends_at = end_date + + split_for_trial(enterprise, begins_at, ends_at, trial_start, trial_expiry) + + clean_up_untouched_billable_periods_for(enterprise, job_start_time) + end + end + + def split_for_trial(enterprise, begins_at, ends_at, trial_start, trial_expiry) + trial_start = trial_expiry = begins_at-1.day if trial_start.nil? || trial_expiry.nil? + + # If the trial begins after ends_at, create a bill for the entire period + # Otherwise, create a normal billable_period from the begins_at until the start of the trial + if trial_start > begins_at + update_billable_period(enterprise, begins_at, [ends_at, trial_start].min, false) + end + + # If all or some of the trial occurs between begins_at and ends_at + # Create a trial billable_period from the from begins_at or trial_start, whichever occurs last, until ends_at, or trial_expiry whichever occurs first + if trial_expiry >= begins_at && trial_start <= ends_at + update_billable_period(enterprise, [trial_start, begins_at].max, [ends_at, trial_expiry].min, true) + end + + # If the trial finishes before begins_at, or trial has not been set, create a bill for the entire period + # Otherwise, create a normal billable_period from the end of the trial until ends_at + if trial_expiry < ends_at + update_billable_period(enterprise, [trial_expiry, begins_at].max, ends_at, false) + end + end + + def update_billable_period(enterprise, begins_at, ends_at, trial) + owner_id = enterprise.owner_id + sells = enterprise.sells + orders = Spree::Order.where('distributor_id = (?) AND completed_at >= (?) AND completed_at < (?)', enterprise.id, begins_at, ends_at) + account_invoice = AccountInvoice.find_or_create_by_user_id_and_year_and_month(owner_id, begins_at.year, begins_at.month) + + billable_period = BillablePeriod.where(account_invoice_id: account_invoice.id, begins_at: begins_at, enterprise_id: enterprise.id).first + + unless account_invoice.order.andand.complete? + billable_period ||= BillablePeriod.new(account_invoice_id: account_invoice.id, begins_at: begins_at, enterprise_id: enterprise.id) + billable_period.update_attributes({ + ends_at: ends_at, + sells: sells, + trial: trial, + owner_id: owner_id, + turnover: orders.sum(&:total) + }) + end + + billable_period.touch + end + + def clean_up_untouched_billable_periods_for(enterprise, job_start_time) + # Snag and then delete any BillablePeriods which overlap + obsolete_billable_periods = enterprise.billable_periods.where('ends_at > (?) AND begins_at < (?) AND billable_periods.updated_at < (?)', start_date, end_date, job_start_time) + + if obsolete_billable_periods.any? + current_billable_periods = enterprise.billable_periods.where('ends_at >= (?) AND begins_at <= (?) AND billable_periods.updated_at > (?)', start_date, end_date, job_start_time) + + Delayed::Worker.logger.info "#{enterprise.name} #{start_date.strftime("%F %T")} #{job_start_time.strftime("%F %T")}" + Delayed::Worker.logger.info "#{obsolete_billable_periods.first.updated_at.strftime("%F %T")}" + + Bugsnag.notify(RuntimeError.new("Obsolete BillablePeriods"), { + current: current_billable_periods.map(&:as_json), + obsolete: obsolete_billable_periods.map(&:as_json) + }) + end + + obsolete_billable_periods.includes({ account_invoice: :order}). + where('spree_orders.state <> \'complete\' OR account_invoices.order_id IS NULL'). + each(&:delete) + end + + private + + def settings_are_valid? + unless end_date <= Time.zone.now + Bugsnag.notify(RuntimeError.new("InvalidJobSettings"), { + job: "UpdateBillablePeriods", + error: "end_date is in the future", + data: { + end_date: end_date.in_time_zone.strftime("%F %T"), + now: Time.zone.now.strftime("%F %T") + } + }) + return false + end + + true + end +end diff --git a/app/mailers/enterprise_mailer.rb b/app/mailers/enterprise_mailer.rb index 8ef7e74044..248e31f109 100644 --- a/app/mailers/enterprise_mailer.rb +++ b/app/mailers/enterprise_mailer.rb @@ -11,12 +11,9 @@ class EnterpriseMailer < Spree::BaseMailer def confirmation_instructions(record, token, opts={}) @token = token find_enterprise(record) - opts = { - subject: "Please confirm your email for #{@enterprise.name}", - to: ( @enterprise.unconfirmed_email || @enterprise.email ), - from: from_address, - } - devise_mail(record, :confirmation_instructions, opts) + mail(subject: "Please confirm your email for #{@enterprise.name}", + to: ( @enterprise.unconfirmed_email || @enterprise.email ), + from: from_address) end private diff --git a/app/mailers/producer_mailer.rb b/app/mailers/producer_mailer.rb new file mode 100644 index 0000000000..b9fb52fb41 --- /dev/null +++ b/app/mailers/producer_mailer.rb @@ -0,0 +1,52 @@ +class ProducerMailer < Spree::BaseMailer + + def order_cycle_report(producer, order_cycle) + @producer = producer + @coordinator = order_cycle.coordinator + @order_cycle = order_cycle + @line_items = aggregated_line_items_from(@order_cycle, @producer) + @receival_instructions = @order_cycle.receival_instructions_for @producer + + subject = "[#{Spree::Config.site_name}] Order cycle report for #{producer.name}" + + if has_orders? order_cycle, producer + mail(to: @producer.email, + from: from_address, + subject: subject, + reply_to: @coordinator.email, + cc: @coordinator.email) + end + end + + + private + + def has_orders?(order_cycle, producer) + line_items_from(order_cycle, producer).any? + end + + def aggregated_line_items_from(order_cycle, producer) + aggregate_line_items line_items_from(order_cycle, producer) + end + + def line_items_from(order_cycle, producer) + Spree::LineItem. + joins(:order => :order_cycle, :variant => :product). + where('order_cycles.id = ?', order_cycle). + merge(Spree::Product.in_supplier(producer)). + merge(Spree::Order.complete) + end + + def aggregate_line_items(line_items) + # Arrange the items in a hash to group quantities + line_items.inject({}) do |lis, li| + if lis.key? li.variant + lis[li.variant].quantity += li.quantity + else + lis[li.variant] = li + end + + lis + end + end +end diff --git a/app/mailers/spree/base_mailer_decorator.rb b/app/mailers/spree/base_mailer_decorator.rb index 4f78c1fe1e..339abd8901 100644 --- a/app/mailers/spree/base_mailer_decorator.rb +++ b/app/mailers/spree/base_mailer_decorator.rb @@ -10,4 +10,4 @@ Spree::BaseMailer.class_eval do # This lets us specify assets using relative paths in email templates super.merge(url_options: {host: URI(spree.root_url).host }) end -end \ No newline at end of file +end diff --git a/app/mailers/spree/order_mailer_decorator.rb b/app/mailers/spree/order_mailer_decorator.rb index c6eeb31df8..016b61efa4 100644 --- a/app/mailers/spree/order_mailer_decorator.rb +++ b/app/mailers/spree/order_mailer_decorator.rb @@ -21,4 +21,14 @@ Spree::OrderMailer.class_eval do :from => from_address, :subject => subject) end + + def invoice_email(order, pdf) + find_order(order) # Finds an order instance from an id + attachments["invoice-#{@order.number}.pdf"] = pdf if pdf.present? + subject = "#{Spree::Config[:site_name]} #{t(:invoice)} ##{@order.number}" + mail(:to => @order.email, + :from => from_address, + :subject => subject, + :reply_to => @order.distributor.email) + end end diff --git a/app/models/account_invoice.rb b/app/models/account_invoice.rb new file mode 100644 index 0000000000..6d381565bb --- /dev/null +++ b/app/models/account_invoice.rb @@ -0,0 +1,6 @@ +class AccountInvoice < ActiveRecord::Base + belongs_to :user, class_name: "Spree::User" + belongs_to :order, class_name: "Spree::Order" + attr_accessible :user_id, :order_id, :issued_at, :month, :year + has_many :billable_periods +end diff --git a/app/models/billable_period.rb b/app/models/billable_period.rb new file mode 100644 index 0000000000..d604b5a59c --- /dev/null +++ b/app/models/billable_period.rb @@ -0,0 +1,77 @@ +require 'open_food_network/bill_calculator' + +class BillablePeriod < ActiveRecord::Base + belongs_to :enterprise + belongs_to :owner, class_name: 'Spree::User' + belongs_to :account_invoice + has_one :adjustment, :as => :source, class_name: "Spree::Adjustment" #, :dependent => :destroy + + default_scope where(deleted_at: nil) + + def display_turnover + Spree::Money.new(turnover, {currency: Spree::Config[:currency]}) + end + + def display_bill + Spree::Money.new(bill, {currency: Spree::Config[:currency]}) + end + + def bill + return 0 if trial? + return 0 unless ['own', 'any'].include?(sells) + OpenFoodNetwork::BillCalculator.new(turnover: turnover).bill + end + + def label + enterprise_version = enterprise.version_at(begins_at) + category = enterprise_version.category.to_s.titleize + category += (trial ? " Trial" : "") + + "#{enterprise_version.name} (#{category})" + end + + def adjustment_label + begins = begins_at.in_time_zone.strftime("%d/%m/%y") + ends = ends_at.in_time_zone.strftime("%d/%m/%y") + + "#{label} [#{begins} - #{ends}]" + end + + def delete + self.update_column(:deleted_at, Time.zone.now) + end + + def ensure_correct_adjustment_for(invoice) + if adjustment + # adjustment.originator = enterprise.package + adjustment.adjustable = invoice + adjustment.update_attributes( label: adjustment_label, amount: bill ) + else + self.adjustment = invoice.adjustments.new( adjustment_attrs, :without_protection => true ) + end + + if Spree::Config.account_invoices_tax_rate > 0 + adjustment.set_included_tax! Spree::Config.account_invoices_tax_rate + else + adjustment.set_included_tax! 0 + end + + adjustment + end + + private + + def adjustment_attrs + # We should ultimately have an EnterprisePackage model, which holds all info about shop type, producer, trials, etc. + # It should also implement a calculator that we can use here by specifying the package as the originator of the + # adjustment, meaning that adjustments are created and updated using Spree's existing architecture. + + { label: adjustment_label, + amount: bill, + source: self, + originator: nil, # enterprise.package + mandatory: true, + locked: false + } + end +end diff --git a/app/models/content_configuration.rb b/app/models/content_configuration.rb new file mode 100644 index 0000000000..2357f8dfa3 --- /dev/null +++ b/app/models/content_configuration.rb @@ -0,0 +1,54 @@ +require 'open_food_network/paperclippable' + +class ContentConfiguration < Spree::Preferences::FileConfiguration + include OpenFoodNetwork::Paperclippable + + # Header + preference :logo, :file + preference :logo_mobile, :file + preference :logo_mobile_svg, :file + has_attached_file :logo + has_attached_file :logo_mobile + has_attached_file :logo_mobile_svg + + # Home page + preference :home_hero, :file + preference :home_show_stats, :boolean, default: true + has_attached_file :home_hero + + # Producer sign-up page + preference :producer_signup_pricing_table_html, :text, default: "(TODO: Pricing table)" + preference :producer_signup_case_studies_html, :text, default: "(TODO: Case studies)" + preference :producer_signup_detail_html, :text, default: "(TODO: Detail)" + + # Hubs sign-up page + preference :hub_signup_pricing_table_html, :text, default: "(TODO: Pricing table)" + preference :hub_signup_case_studies_html, :text, default: "(TODO: Case studies)" + preference :hub_signup_detail_html, :text, default: "(TODO: Detail)" + + # Groups sign-up page + preference :group_signup_pricing_table_html, :text, default: "(TODO: Pricing table)" + preference :group_signup_case_studies_html, :text, default: "(TODO: Case studies)" + preference :group_signup_detail_html, :text, default: "(TODO: Detail)" + + # Footer + preference :footer_logo, :file + has_attached_file :footer_logo + preference :footer_facebook_url, :string, default: "https://www.facebook.com/OpenFoodNet" + preference :footer_twitter_url, :string, default: "https://twitter.com/OpenFoodNet" + preference :footer_instagram_url, :string, default: "" + preference :footer_linkedin_url, :string, default: "http://www.linkedin.com/groups/Open-Food-Foundation-4743336" + preference :footer_googleplus_url, :string, default: "" + preference :footer_pinterest_url, :string, default: "" + preference :footer_email, :string, default: "hello@openfoodnetwork.org" + preference :footer_links_md, :text, default: <<-EOS +[Newsletter sign-up](/) + +[News](/) + +[Calendar](/) +EOS + + preference :footer_about_url, :string, default: "http://www.openfoodnetwork.org/ofn-local/open-food-network-australia/" + preference :footer_tos_url, :string, default: "/Terms-of-service.pdf" +end diff --git a/app/models/customer.rb b/app/models/customer.rb index d3fa9e093f..856f7e94d7 100644 --- a/app/models/customer.rb +++ b/app/models/customer.rb @@ -1,10 +1,20 @@ class Customer < ActiveRecord::Base + acts_as_taggable + belongs_to :enterprise belongs_to :user, :class_name => Spree.user_class - validates :code, presence: true, uniqueness: {scope: :enterprise_id} - validates :email, presence: true + validates :code, uniqueness: { scope: :enterprise_id, allow_blank: true, allow_nil: true } + validates :email, presence: true, uniqueness: { scope: :enterprise_id, message: "is associated with an existing customer" } validates :enterprise_id, presence: true scope :of, ->(enterprise) { where(enterprise_id: enterprise) } + + before_create :associate_user + + private + + def associate_user + self.user = user || Spree::User.find_by_email(email) + end end diff --git a/app/models/enterprise.rb b/app/models/enterprise.rb index 067e800e3c..033b4b79d7 100644 --- a/app/models/enterprise.rb +++ b/app/models/enterprise.rb @@ -6,15 +6,19 @@ class Enterprise < ActiveRecord::Base preference :shopfront_message, :text, default: "" preference :shopfront_closed_message, :text, default: "" preference :shopfront_taxon_order, :string, default: "" + preference :shopfront_order_cycle_order, :string, default: "orders_close_at" devise :confirmable, reconfirmable: true, confirmation_keys: [ :id, :email ] handle_asynchronously :send_confirmation_instructions handle_asynchronously :send_on_create_confirmation_instructions + has_paper_trail only: [:owner_id, :sells], on: [:update] self.inheritance_column = nil acts_as_gmappable :process_geocoding => false + has_many :relationships_as_parent, class_name: 'EnterpriseRelationship', foreign_key: 'parent_id', dependent: :destroy + has_many :relationships_as_child, class_name: 'EnterpriseRelationship', foreign_key: 'child_id', dependent: :destroy has_and_belongs_to_many :groups, class_name: 'EnterpriseGroup' has_many :producer_properties, foreign_key: 'producer_id' has_many :properties, through: :producer_properties @@ -30,6 +34,8 @@ class Enterprise < ActiveRecord::Base has_and_belongs_to_many :payment_methods, join_table: 'distributors_payment_methods', class_name: 'Spree::PaymentMethod', foreign_key: 'distributor_id' has_many :distributor_shipping_methods, foreign_key: :distributor_id has_many :shipping_methods, through: :distributor_shipping_methods + has_many :customers + has_many :billable_periods delegate :latitude, :longitude, :city, :state_name, :to => :address @@ -55,6 +61,7 @@ class Enterprise < ActiveRecord::Base validates :name, presence: true + validate :name_is_unique validates :sells, presence: true, inclusion: {in: SELLS} validates :address, presence: true, associated: true validates :email, presence: true @@ -68,6 +75,7 @@ class Enterprise < ActiveRecord::Base before_validation :initialize_permalink, if: lambda { permalink.nil? } before_validation :ensure_owner_is_manager, if: lambda { owner_id_changed? && !owner_id.nil? } + before_validation :ensure_email_set before_validation :set_unused_address_fields after_validation :geocode_address @@ -105,12 +113,12 @@ class Enterprise < ActiveRecord::Base scope :supplying_variant_in, lambda { |variants| joins(:supplied_products => :variants_including_master).where('spree_variants.id IN (?)', variants).select('DISTINCT enterprises.*') } scope :with_supplied_active_products_on_hand, lambda { joins(:supplied_products) - .where('spree_products.deleted_at IS NULL AND spree_products.available_on <= ? AND spree_products.count_on_hand > 0', Time.now) + .where('spree_products.deleted_at IS NULL AND spree_products.available_on <= ? AND spree_products.count_on_hand > 0', Time.zone.now) .uniq } scope :with_distributed_active_products_on_hand, lambda { joins(:distributed_products) - .where('spree_products.deleted_at IS NULL AND spree_products.available_on <= ? AND spree_products.count_on_hand > 0', Time.now) + .where('spree_products.deleted_at IS NULL AND spree_products.available_on <= ? AND spree_products.count_on_hand > 0', Time.zone.now) .uniq } @@ -134,7 +142,7 @@ class Enterprise < ActiveRecord::Base scope :active_distributors, lambda { with_distributed_products_outer.with_order_cycles_as_distributor_outer. - where('(product_distributions.product_id IS NOT NULL AND spree_products.deleted_at IS NULL AND spree_products.available_on <= ? AND spree_products.count_on_hand > 0) OR (order_cycles.id IS NOT NULL AND order_cycles.orders_open_at <= ? AND order_cycles.orders_close_at >= ?)', Time.now, Time.now, Time.now). + where('(product_distributions.product_id IS NOT NULL AND spree_products.deleted_at IS NULL AND spree_products.available_on <= ? AND spree_products.count_on_hand > 0) OR (order_cycles.id IS NOT NULL AND order_cycles.orders_open_at <= ? AND order_cycles.orders_close_at >= ?)', Time.zone.now, Time.zone.now, Time.zone.now). select('DISTINCT enterprises.*') } @@ -178,6 +186,10 @@ class Enterprise < ActiveRecord::Base count(distinct: true) end + def activated? + confirmed_at.present? && sells != 'unspecified' + end + def set_producer_property(property_name, property_value) transaction do property = Spree::Property.where(name: property_name).first_or_create!(presentation: property_name) @@ -212,12 +224,16 @@ class Enterprise < ActiveRecord::Base ", self.id, self.id) end + def relatives_including_self + Enterprise.where(id: relatives.pluck(:id) | [id]) + end + def distributors - self.relatives.is_distributor + self.relatives_including_self.is_distributor end def suppliers - self.relatives.is_primary_producer + self.relatives_including_self.is_primary_producer end def website @@ -316,6 +332,14 @@ class Enterprise < ActiveRecord::Base !confirmed? || pending_reconfirmation? end + def shop_trial_expiry + shop_trial_start_date.andand + Enterprise::SHOP_TRIAL_LENGTH.days + end + + def can_invoice? + abn.present? + end + protected def devise_mailer @@ -324,6 +348,15 @@ class Enterprise < ActiveRecord::Base private + def name_is_unique + dups = Enterprise.where(name: name) + dups = dups.where('id != ?', id) unless new_record? + + if dups.any? + errors.add :name, "has already been taken. If this is your enterprise and you would like to claim ownership, please contact the current manager of this profile at #{dups.first.owner.email}." + end + end + def email_is_known? owner.enterprises.confirmed.map(&:email).include?(email) end @@ -363,6 +396,10 @@ class Enterprise < ActiveRecord::Base users << owner unless users.include?(owner) || owner.admin? end + def ensure_email_set + self.email = owner.email if email.blank? + end + def enforce_ownership_limit unless owner.can_own_more_enterprises? errors.add(:owner, "^#{owner.email} is not permitted to own any more enterprises (limit is #{owner.enterprise_limit}).") diff --git a/app/models/enterprise_group.rb b/app/models/enterprise_group.rb index 986dc07d22..63eb63f0b3 100644 --- a/app/models/enterprise_group.rb +++ b/app/models/enterprise_group.rb @@ -1,6 +1,8 @@ require 'open_food_network/locking' +require 'open_food_network/permalink_generator' class EnterpriseGroup < ActiveRecord::Base + include PermalinkGenerator acts_as_list has_and_belongs_to_many :enterprises @@ -81,25 +83,10 @@ class EnterpriseGroup < ActiveRecord::Base private - def self.find_available_value(existing, requested) - return requested unless existing.include?(requested) - used_indices = existing.map do |p| - p.slice!(/^#{requested}/) - p.match(/^\d+$/).to_s.to_i - end - options = (1..used_indices.length + 1).to_a - used_indices - requested + options.first.to_s - end - - def find_available_permalink(requested) - existing = self.class.where(id: !id).where("permalink LIKE ?", "#{requested}%").pluck(:permalink) - self.class.find_available_value(existing, requested) - end - def sanitize_permalink if permalink.blank? || permalink_changed? requested = permalink.presence || permalink_was.presence || name.presence || 'group' - self.permalink = find_available_permalink(requested.parameterize) + self.permalink = create_unique_permalink(requested.parameterize) end end end diff --git a/app/models/enterprise_relationship.rb b/app/models/enterprise_relationship.rb index fbdef9d52c..8d279b2a20 100644 --- a/app/models/enterprise_relationship.rb +++ b/app/models/enterprise_relationship.rb @@ -25,6 +25,41 @@ class EnterpriseRelationship < ActiveRecord::Base scope :by_name, with_enterprises.order('child_enterprises.name, parent_enterprises.name') + # Load an array of the relatives of each enterprise (ie. any enterprise related to it in + # either direction). This array is split into distributors and producers, and has the format: + # {enterprise_id => {distributors: [id, ...], producers: [id, ...]} } + def self.relatives(activated_only=false) + relationships = EnterpriseRelationship.includes(:child, :parent) + relatives = {} + + Enterprise.is_primary_producer.pluck(:id).each do |enterprise_id| + relatives[enterprise_id] ||= { distributors: Set.new, producers: Set.new } + relatives[enterprise_id][:producers] << enterprise_id + end + Enterprise.is_distributor.pluck(:id).each do |enterprise_id| + relatives[enterprise_id] ||= { distributors: Set.new, producers: Set.new } + relatives[enterprise_id][:distributors] << enterprise_id + end + + relationships.each do |r| + relatives[r.parent_id] ||= {distributors: Set.new, producers: Set.new} + relatives[r.child_id] ||= {distributors: Set.new, producers: Set.new} + + if !activated_only || r.child.activated? + relatives[r.parent_id][:producers] << r.child_id if r.child.is_primary_producer + relatives[r.parent_id][:distributors] << r.child_id if r.child.is_distributor + end + + if !activated_only || r.parent.activated? + relatives[r.child_id][:producers] << r.parent_id if r.parent.is_primary_producer + relatives[r.child_id][:distributors] << r.parent_id if r.parent.is_distributor + end + end + + relatives + end + + def permissions_list=(perms) perms.andand.each { |name| permissions.build name: name } end diff --git a/app/models/exchange.rb b/app/models/exchange.rb index 1b226d269a..137924f7ac 100644 --- a/app/models/exchange.rb +++ b/app/models/exchange.rb @@ -27,7 +27,9 @@ class Exchange < ActiveRecord::Base scope :with_variant, lambda { |variant| joins(:exchange_variants).where('exchange_variants.variant_id = ?', variant) } scope :with_any_variant, lambda { |variants| joins(:exchange_variants).where('exchange_variants.variant_id IN (?)', variants).select('DISTINCT exchanges.*') } scope :with_product, lambda { |product| joins(:exchange_variants).where('exchange_variants.variant_id IN (?)', product.variants_including_master) } - + scope :by_enterprise_name, joins('INNER JOIN enterprises AS sender ON (sender.id = exchanges.sender_id)'). + joins('INNER JOIN enterprises AS receiver ON (receiver.id = exchanges.receiver_id)'). + order("CASE WHEN exchanges.incoming='t' THEN sender.name ELSE receiver.name END") scope :managed_by, lambda { |user| if user.has_spree_role?('admin') diff --git a/app/models/model_set.rb b/app/models/model_set.rb index c9e1456f1a..0c0d156179 100644 --- a/app/models/model_set.rb +++ b/app/models/model_set.rb @@ -16,8 +16,8 @@ class ModelSet end end - def collection_attributes=(attributes) - attributes.each do |k, attributes| + def collection_attributes=(collection_attributes) + collection_attributes.each do |k, attributes| # attributes == {:id => 123, :next_collection_at => '...'} e = @collection.detect { |e| e.id.to_s == attributes[:id].to_s && !e.id.nil? } if e.nil? @@ -41,7 +41,11 @@ class ModelSet end def collection_to_delete - collection.select { |e| @delete_if.andand.call(e.attributes) } + # Remove all elements to be deleted from collection and return them + # Allows us to render @model_set.collection without deleted elements + deleted = [] + collection.delete_if { |e| deleted << e if @delete_if.andand.call(e.attributes) } + deleted end def collection_to_keep @@ -51,5 +55,4 @@ class ModelSet def persisted? false end - end diff --git a/app/models/order_cycle.rb b/app/models/order_cycle.rb index 88e4953edc..03979220d6 100644 --- a/app/models/order_cycle.rb +++ b/app/models/order_cycle.rb @@ -11,24 +11,21 @@ class OrderCycle < ActiveRecord::Base validates_presence_of :name, :coordinator_id - scope :active, lambda { where('order_cycles.orders_open_at <= ? AND order_cycles.orders_close_at >= ?', Time.now, Time.now) } - scope :active_or_complete, lambda { where('order_cycles.orders_open_at <= ?', Time.now) } - scope :inactive, lambda { where('order_cycles.orders_open_at > ? OR order_cycles.orders_close_at < ?', Time.now, Time.now) } - scope :upcoming, lambda { where('order_cycles.orders_open_at > ?', Time.now) } - scope :closed, lambda { where('order_cycles.orders_close_at < ?', Time.now) } - scope :undated, where(orders_open_at: nil, orders_close_at: nil) + scope :active, lambda { where('order_cycles.orders_open_at <= ? AND order_cycles.orders_close_at >= ?', Time.zone.now, Time.zone.now) } + scope :active_or_complete, lambda { where('order_cycles.orders_open_at <= ?', Time.zone.now) } + scope :inactive, lambda { where('order_cycles.orders_open_at > ? OR order_cycles.orders_close_at < ?', Time.zone.now, Time.zone.now) } + scope :upcoming, lambda { where('order_cycles.orders_open_at > ?', Time.zone.now) } + scope :closed, lambda { where('order_cycles.orders_close_at < ?', Time.zone.now).order("order_cycles.orders_close_at DESC") } + scope :undated, where('order_cycles.orders_open_at IS NULL OR orders_close_at IS NULL') scope :soonest_closing, lambda { active.order('order_cycles.orders_close_at ASC') } # TODO This method returns all the closed orders. So maybe we can replace it with :recently_closed. scope :most_recently_closed, lambda { closed.order('order_cycles.orders_close_at DESC') } - scope :recently_closed, -> { - closed. - where("order_cycles.orders_close_at >= ?", 31.days.ago). - order("order_cycles.orders_close_at DESC") } - scope :soonest_opening, lambda { upcoming.order('order_cycles.orders_open_at ASC') } + scope :by_name, order('name') + scope :distributing_product, lambda { |product| joins(:exchanges). merge(Exchange.outgoing). @@ -92,11 +89,25 @@ class OrderCycle < ActiveRecord::Base with_distributor(distributor).soonest_closing.first end - def self.most_recently_closed_for(distributor) with_distributor(distributor).most_recently_closed.first end + # Find the earliest closing times for each distributor in an active order cycle, and return + # them in the format {distributor_id => closing_time, ...} + def self.earliest_closing_times + Hash[ + Exchange. + outgoing. + joins(:order_cycle). + merge(OrderCycle.active). + group('exchanges.receiver_id'). + select('exchanges.receiver_id AS receiver_id, MIN(order_cycles.orders_close_at) AS earliest_close_at'). + map { |ex| [ex.receiver_id, ex.earliest_close_at.to_time] } + ] + end + + def clone! oc = self.dup oc.name = "COPY OF #{oc.name}" @@ -118,7 +129,16 @@ class OrderCycle < ActiveRecord::Base end def variants - self.exchanges.map(&:variants).flatten.uniq.reject(&:deleted?) + Spree::Variant. + joins(:exchanges). + merge(Exchange.in_order_cycle(self)). + not_deleted. + select('DISTINCT spree_variants.*'). + to_a # http://stackoverflow.com/q/15110166 + end + + def supplied_variants + self.exchanges.incoming.map(&:variants).flatten.uniq.reject(&:deleted?) end def distributed_variants @@ -162,26 +182,34 @@ class OrderCycle < ActiveRecord::Base end def undated? - self.orders_open_at.nil? && self.orders_close_at.nil? + self.orders_open_at.nil? || self.orders_close_at.nil? end def upcoming? - self.orders_open_at && Time.now < self.orders_open_at + self.orders_open_at && Time.zone.now < self.orders_open_at end def open? self.orders_open_at && self.orders_close_at && - Time.now > self.orders_open_at && Time.now < self.orders_close_at + Time.zone.now > self.orders_open_at && Time.zone.now < self.orders_close_at end def closed? - self.orders_close_at && Time.now > self.orders_close_at + self.orders_close_at && Time.zone.now > self.orders_close_at end def exchange_for_distributor(distributor) exchanges.outgoing.to_enterprises([distributor]).first end + def exchange_for_supplier(supplier) + exchanges.incoming.from_enterprises([supplier]).first + end + + def receival_instructions_for(supplier) + exchange_for_supplier(supplier).andand.receival_instructions + end + def pickup_time_for(distributor) exchange_for_distributor(distributor).andand.pickup_time || distributor.next_collection_at end @@ -198,6 +226,10 @@ class OrderCycle < ActiveRecord::Base exchanges.supplying_to(order.distributor).with_any_variant(order.variants) end + def coordinated_by?(user) + coordinator.users.include? user + end + private diff --git a/app/models/spree/ability_decorator.rb b/app/models/spree/ability_decorator.rb index 13fe55a852..9bc303116b 100644 --- a/app/models/spree/ability_decorator.rb +++ b/app/models/spree/ability_decorator.rb @@ -66,16 +66,19 @@ class AbilityDecorator def add_enterprise_management_abilities(user) # Spree performs authorize! on (:create, nil) when creating a new order from admin, and also (:search, nil) # when searching for variants to add to the order - can [:create, :search, :bulk_update], nil + can [:create, :search], nil can [:admin, :index], :overview can [:admin, :index, :read, :create, :edit, :update_positions, :destroy], ProducerProperty can [:admin, :index, :create], Enterprise - can [:read, :edit, :update, :bulk_update, :set_sells, :resend_confirmation], Enterprise do |enterprise| + can [:read, :edit, :update, :bulk_update, :resend_confirmation], Enterprise do |enterprise| OpenFoodNetwork::Permissions.new(user).editable_enterprises.include? enterprise end + can [:welcome, :register], Enterprise do |enterprise| + enterprise.owner == user + end can [:manage_payment_methods, :manage_shipping_methods, :manage_enterprise_fees], Enterprise do |enterprise| user.enterprises.include? enterprise end @@ -87,6 +90,13 @@ class AbilityDecorator end can [:admin, :known_users], :search + + can [:admin, :show], :account + + # For printing own account invoice orders + can [:print], Spree::Order do |order| + order.user == user + end end def add_product_management_abilities(user) @@ -101,7 +111,9 @@ class AbilityDecorator OpenFoodNetwork::Permissions.new(user).managed_product_enterprises.include? variant.product.supplier end - can [:admin, :index, :read, :update, :bulk_update], VariantOverride do |vo| + can [:admin, :index, :read, :update, :bulk_update, :bulk_reset], VariantOverride do |vo| + next false unless vo.hub.present? && vo.variant.andand.product.andand.supplier.present? + hub_auth = OpenFoodNetwork::Permissions.new(user). variant_override_hubs. include? vo.hub @@ -120,14 +132,14 @@ class AbilityDecorator can [:admin, :index, :read, :create, :edit], Spree::Classification # Reports page - can [:admin, :index, :customers, :orders_and_distributors, :group_buys, :bulk_coop, :payments, :orders_and_fulfillment, :products_and_inventory, :order_cycle_management], :report + can [:admin, :index, :customers, :orders_and_distributors, :group_buys, :bulk_coop, :payments, :orders_and_fulfillment, :products_and_inventory, :order_cycle_management, :packing], :report end def add_order_cycle_management_abilities(user) can [:admin, :index, :read, :edit, :update], OrderCycle do |order_cycle| OrderCycle.accessible_by(user).include? order_cycle end - can [:bulk_update, :clone, :destroy], OrderCycle do |order_cycle| + can [:bulk_update, :clone, :destroy, :notify_producers], OrderCycle do |order_cycle| user.enterprises.include? order_cycle.coordinator end can [:for_order_cycle], Enterprise @@ -137,15 +149,16 @@ class AbilityDecorator def add_order_management_abilities(user) # Enterprise User can only access orders that they are a distributor for can [:index, :create], Spree::Order - can [:read, :update, :fire, :resend], Spree::Order do |order| + can [:read, :update, :fire, :resend, :invoice, :print], Spree::Order do |order| # We allow editing orders with a nil distributor as this state occurs # during the order creation process from the admin backend - order.distributor.nil? || user.enterprises.include?(order.distributor) + order.distributor.nil? || user.enterprises.include?(order.distributor) || order.order_cycle.andand.coordinated_by?(user) end can [:admin, :bulk_management, :managed], Spree::Order if user.admin? || user.enterprises.any?(&:is_distributor) - can [:admin, :create], Spree::LineItem - can [:destroy], Spree::LineItem do |item| - user.admin? || user.enterprises.include?(order.distributor) || user == order.order_cycle.manager + can [:admin , :for_line_items], Enterprise + can [:admin, :index, :create], Spree::LineItem + can [:destroy, :update], Spree::LineItem do |item| + user.admin? || user.enterprises.include?(order.distributor) || order.order_cycle.andand.coordinated_by?(user) end can [:admin, :index, :read, :create, :edit, :update, :fire], Spree::Payment @@ -158,10 +171,10 @@ class AbilityDecorator true elsif adjustment.adjustable.instance_of? Spree::Order order = adjustment.adjustable - user.enterprises.include?(order.distributor) || user == order.order_cycle.manager + user.enterprises.include?(order.distributor) || order.order_cycle.andand.coordinated_by?(user) elsif adjustment.adjustable.instance_of? Spree::LineItem order = adjustment.adjustable.order - user.enterprises.include?(order.distributor) || user == order.order_cycle.manager + user.enterprises.include?(order.distributor) || order.order_cycle.andand.coordinated_by?(user) end end @@ -183,7 +196,9 @@ class AbilityDecorator end # Reports page - can [:admin, :index, :customers, :group_buys, :bulk_coop, :sales_tax, :payments, :orders_and_distributors, :orders_and_fulfillment, :products_and_inventory, :order_cycle_management], :report + can [:admin, :index, :customers, :group_buys, :bulk_coop, :sales_tax, :payments, :orders_and_distributors, :orders_and_fulfillment, :products_and_inventory, :order_cycle_management, :xero_invoices], :report + + can [:admin, :index, :update], Customer, enterprise_id: Enterprise.managed_by(user).pluck(:id) end diff --git a/app/models/spree/adjustment_decorator.rb b/app/models/spree/adjustment_decorator.rb index 836080183c..0d1bc941f6 100644 --- a/app/models/spree/adjustment_decorator.rb +++ b/app/models/spree/adjustment_decorator.rb @@ -1,9 +1,17 @@ module Spree Adjustment.class_eval do - has_one :metadata, class_name: 'AdjustmentMetadata', dependent: :destroy + # Deletion of metadata is handled in the database. + # So we don't need the option `dependent: :destroy` as long as + # AdjustmentMetadata has no destroy logic itself. + has_one :metadata, class_name: 'AdjustmentMetadata' - scope :enterprise_fee, where(originator_type: 'EnterpriseFee') - scope :included_tax, where(originator_type: 'Spree::TaxRate', adjustable_type: 'Spree::LineItem') + scope :enterprise_fee, where(originator_type: 'EnterpriseFee') + scope :billable_period, where(source_type: 'BillablePeriod') + scope :admin, where(source_type: nil, originator_type: nil) + scope :included_tax, where(originator_type: 'Spree::TaxRate', adjustable_type: 'Spree::LineItem') + + scope :with_tax, where('spree_adjustments.included_tax > 0') + scope :without_tax, where('spree_adjustments.included_tax = 0') attr_accessible :included_tax @@ -15,5 +23,31 @@ module Spree def set_absolute_included_tax!(tax) update_attributes! included_tax: tax.round(2) end + + def display_included_tax + Spree::Money.new(included_tax, { :currency => currency }) + end + + def has_tax? + included_tax > 0 + end + + def display_included_tax + Spree::Money.new(included_tax, { :currency => currency }) + end + + def self.without_callbacks + skip_callback :save, :after, :update_adjustable + skip_callback :destroy, :after, :update_adjustable + + result = yield + + ensure + set_callback :save, :after, :update_adjustable + set_callback :destroy, :after, :update_adjustable + + result + end + end end diff --git a/app/models/spree/app_configuration_decorator.rb b/app/models/spree/app_configuration_decorator.rb index 5ad4a9a1c5..fc7a8171cc 100644 --- a/app/models/spree/app_configuration_decorator.rb +++ b/app/models/spree/app_configuration_decorator.rb @@ -7,4 +7,17 @@ Spree::AppConfiguration.class_eval do # Tax Preferences preference :products_require_tax_category, :boolean, default: false preference :shipping_tax_rate, :decimal, default: 0 + + # Accounts & Billing Preferences + preference :accounts_distributor_id, :integer, default: nil + preference :default_accounts_payment_method_id, :integer, default: nil + preference :default_accounts_shipping_method_id, :integer, default: nil + preference :auto_update_invoices, :boolean, default: false + preference :auto_finalize_invoices, :boolean, default: false + + # Business Model Configuration + preference :account_invoices_monthly_fixed, :decimal, default: 0 + preference :account_invoices_monthly_rate, :decimal, default: 0 + preference :account_invoices_monthly_cap, :decimal, default: 0 + preference :account_invoices_tax_rate, :decimal, default: 0 end diff --git a/app/models/spree/inventory_unit_decorator.rb b/app/models/spree/inventory_unit_decorator.rb index 939a97996f..9868596b41 100644 --- a/app/models/spree/inventory_unit_decorator.rb +++ b/app/models/spree/inventory_unit_decorator.rb @@ -4,9 +4,10 @@ module Spree return [] unless order.completed? #increase inventory to meet initial requirements + scoper = OpenFoodNetwork::ScopeVariantToHub.new(order.distributor) order.line_items.each do |line_item| # Scope variant to hub so that stock levels may be subtracted from VariantOverride. - line_item.variant.scope_to_hub order.distributor + scoper.scope(line_item.variant) increase(order, line_item.variant, line_item.quantity) end diff --git a/app/models/spree/line_item_decorator.rb b/app/models/spree/line_item_decorator.rb index 4eec0bcd2b..9db9ae1269 100644 --- a/app/models/spree/line_item_decorator.rb +++ b/app/models/spree/line_item_decorator.rb @@ -1,6 +1,16 @@ +require 'open_food_network/variant_and_line_item_naming' + Spree::LineItem.class_eval do - attr_accessible :max_quantity, :unit_value - attr_accessible :unit_value, :price, :as => :api + include OpenFoodNetwork::VariantAndLineItemNaming + has_and_belongs_to_many :option_values, join_table: 'spree_option_values_line_items', class_name: 'Spree::OptionValue' + + attr_accessible :max_quantity, :final_weight_volume, :price + attr_accessible :final_weight_volume, :price, :as => :api + + before_save :calculate_final_weight_volume, if: :quantity_changed?, unless: :final_weight_volume_changed? + after_save :update_units + + delegate :unit_description, to: :variant # -- Scopes scope :managed_by, lambda { |user| @@ -24,6 +34,23 @@ Spree::LineItem.class_eval do where('spree_products.supplier_id IN (?)', enterprises) } + scope :with_tax, joins(:adjustments). + where('spree_adjustments.originator_type = ?', 'Spree::TaxRate'). + select('DISTINCT spree_line_items.*') + + # Line items without a Spree::TaxRate-originated adjustment + scope :without_tax, joins("LEFT OUTER JOIN spree_adjustments ON (spree_adjustments.adjustable_id=spree_line_items.id AND spree_adjustments.adjustable_type = 'Spree::LineItem' AND spree_adjustments.originator_type='Spree::TaxRate')"). + where('spree_adjustments.id IS NULL') + + + def has_tax? + adjustments.included_tax.any? + end + + def included_tax + adjustments.included_tax.sum(&:included_tax) + end + def price_with_adjustments # EnterpriseFee#create_locked_adjustment applies adjustments on line items to their parent order, # so line_item.adjustments returns an empty array @@ -44,4 +71,27 @@ Spree::LineItem.class_eval do def display_amount_with_adjustments Spree::Money.new(amount_with_adjustments, { :currency => currency }) end + + def display_included_tax + Spree::Money.new(included_tax, { :currency => currency }) + end + + def display_name + variant.display_name + end + + def unit_value + return 0 if quantity == 0 + (final_weight_volume || 0) / quantity + end + + private + + def calculate_final_weight_volume + if final_weight_volume.present? && quantity_was > 0 + self.final_weight_volume = final_weight_volume * quantity / quantity_was + elsif variant.andand.unit_value.present? + self.final_weight_volume = variant.andand.unit_value * quantity + end + end end diff --git a/app/models/spree/order_decorator.rb b/app/models/spree/order_decorator.rb index cbc5c0086d..8f1c0381b1 100644 --- a/app/models/spree/order_decorator.rb +++ b/app/models/spree/order_decorator.rb @@ -10,11 +10,14 @@ Spree::Order.class_eval do belongs_to :order_cycle belongs_to :distributor, :class_name => 'Enterprise' belongs_to :cart + belongs_to :customer + validates :customer, presence: true, if: :require_customer? validate :products_available_from_new_distribution, :if => lambda { distributor_id_changed? || order_cycle_id_changed? } attr_accessible :order_cycle_id, :distributor_id before_validation :shipping_address_from_distributor + before_validation :associate_customer, unless: :customer_is_valid? checkout_flow do go_to_state :address @@ -99,6 +102,13 @@ Spree::Order.class_eval do end end + def remove_variant(variant) + line_items(:reload) + current_item = find_line_item_by_variant(variant) + current_item.destroy + end + + # Overridden to support max_quantity def add_variant(variant, quantity = 1, max_quantity = nil, currency = nil) line_items(:reload) @@ -127,7 +137,6 @@ Spree::Order.class_eval do else current_item = Spree::LineItem.new(:quantity => quantity, max_quantity: max_quantity) current_item.variant = variant - current_item.unit_value = variant.unit_value if currency current_item.currency = currency unless currency.nil? current_item.price = variant.price_in(currency).amount @@ -154,20 +163,22 @@ Spree::Order.class_eval do end def update_distribution_charge! - EnterpriseFee.clear_all_adjustments_on_order self + with_lock do + EnterpriseFee.clear_all_adjustments_on_order self - line_items.each do |line_item| - if provided_by_order_cycle? line_item - OpenFoodNetwork::EnterpriseFeeCalculator.new.create_line_item_adjustments_for line_item + line_items.each do |line_item| + if provided_by_order_cycle? line_item + OpenFoodNetwork::EnterpriseFeeCalculator.new.create_line_item_adjustments_for line_item - else - pd = product_distribution_for line_item - pd.create_adjustment_for line_item if pd + else + pd = product_distribution_for line_item + pd.create_adjustment_for line_item if pd + end end - end - if order_cycle - OpenFoodNetwork::EnterpriseFeeCalculator.new.create_order_adjustments_for self + if order_cycle + OpenFoodNetwork::EnterpriseFeeCalculator.new.create_order_adjustments_for self + end end end @@ -227,9 +238,16 @@ Spree::Order.class_eval do (adjustments + price_adjustments).sum &:included_tax end + def account_invoice? + distributor_id == Spree::Config.accounts_distributor_id + end + # Overrride of Spree method, that allows us to send separate confirmation emails to user and shop owners + # And separately, to skip sending confirmation email completely for user invoice orders def deliver_order_confirmation_email - Delayed::Job.enqueue ConfirmOrderJob.new(id) + unless account_invoice? + Delayed::Job.enqueue ConfirmOrderJob.new(id) + end end @@ -261,4 +279,24 @@ Spree::Order.class_eval do def product_distribution_for(line_item) line_item.variant.product.product_distribution_for self.distributor end + + def require_customer? + return true unless new_record? or state == 'cart' + end + + def customer_is_valid? + return true unless require_customer? + customer.present? && customer.enterprise_id == distributor_id && customer.email == (user.andand.email || email) + end + + def associate_customer + email_for_customer = user.andand.email || email + existing_customer = Customer.of(distributor).find_by_email(email_for_customer) + if existing_customer + self.customer = existing_customer + else + new_customer = Customer.create(enterprise: distributor, email: email_for_customer, user: user) + self.customer = new_customer + end + end end diff --git a/app/models/spree/order_populator_decorator.rb b/app/models/spree/order_populator_decorator.rb index 3759866236..1ca0f9efc2 100644 --- a/app/models/spree/order_populator_decorator.rb +++ b/app/models/spree/order_populator_decorator.rb @@ -9,31 +9,48 @@ Spree::OrderPopulator.class_eval do errors.add(:base, "That distributor or order cycle can't supply all the products in your cart. Please choose another.") end - if valid? + if valid? @order.with_lock do - @order.empty! if overwrite + variants = read_products_hash(from_hash) + + read_variants_hash(from_hash) - from_hash[:products].each do |product_id, variant_id| - attempt_cart_add(variant_id, from_hash[:quantity]) - end if from_hash[:products] - - from_hash[:variants].each do |variant_id, quantity| - if quantity.is_a?(Hash) - attempt_cart_add(variant_id, quantity[:quantity], quantity[:max_quantity]) - else - attempt_cart_add(variant_id, quantity) + variants.each do |v| + if varies_from_cart(v) + attempt_cart_add(v[:variant_id], v[:quantity], v[:max_quantity]) end - end if from_hash[:variants] + end + + if overwrite + variants_removed(variants).each do |id| + cart_remove(id) + end + end end end valid? end + def read_products_hash(data) + (data[:products] || []).map do |product_id, variant_id| + {variant_id: variant_id, quantity: data[:quantity]} + end + end + + def read_variants_hash(data) + (data[:variants] || []).map do |variant_id, quantity| + if quantity.is_a?(Hash) + {variant_id: variant_id, quantity: quantity[:quantity], max_quantity: quantity[:max_quantity]} + else + {variant_id: variant_id, quantity: quantity} + end + end + end + def attempt_cart_add(variant_id, quantity, max_quantity = nil) quantity = quantity.to_i variant = Spree::Variant.find(variant_id) - variant.scope_to_hub @distributor + OpenFoodNetwork::ScopeVariantToHub.new(@distributor).scope(variant) if quantity > 0 if check_stock_levels(variant, quantity) && check_order_cycle_provided_for(variant) && @@ -43,6 +60,11 @@ Spree::OrderPopulator.class_eval do end end + def cart_remove(variant_id) + variant = Spree::Variant.find(variant_id) + @order.remove_variant(variant) + end + private @@ -54,6 +76,22 @@ Spree::OrderPopulator.class_eval do DistributionChangeValidator.new(@order).can_change_to_distribution?(distributor, order_cycle) end + def varies_from_cart(variant_data) + li = line_item_for_variant_id variant_data[:variant_id] + + li_added = li.nil? && (variant_data[:quantity].to_i > 0 || variant_data[:max_quantity].to_i > 0) + li_quantity_changed = li.present? && li.quantity.to_i != variant_data[:quantity].to_i + li_max_quantity_changed = li.present? && li.max_quantity.to_i != variant_data[:max_quantity].to_i + + li_added || li_quantity_changed || li_max_quantity_changed + end + + def variants_removed(variants_data) + variant_ids_given = variants_data.map { |data| data[:variant_id].to_i } + + (variant_ids_in_cart - variant_ids_given).uniq + end + def check_order_cycle_provided_for(variant) order_cycle_provided = (!order_cycle_required_for(variant) || @order_cycle.present?) errors.add(:base, "Please choose an order cycle for this order.") unless order_cycle_provided @@ -72,4 +110,12 @@ Spree::OrderPopulator.class_eval do def order_cycle_required_for(variant) variant.product.product_distributions.empty? end + + def line_item_for_variant_id(variant_id) + order.find_line_item_by_variant Spree::Variant.find(variant_id) + end + + def variant_ids_in_cart + @order.line_items.pluck :variant_id + end end diff --git a/app/models/spree/preferences/file_configuration.rb b/app/models/spree/preferences/file_configuration.rb new file mode 100644 index 0000000000..fa1838a778 --- /dev/null +++ b/app/models/spree/preferences/file_configuration.rb @@ -0,0 +1,50 @@ +module Spree::Preferences + class FileConfiguration < Configuration + + def self.preference(name, type, *args) + if type == :file + super "#{name}_file_name", :string, *args + super "#{name}_content_type", :string, *args + super "#{name}_file_size", :integer, *args + super "#{name}_updated_at", :string, *args + + else + super name, type, *args + end + end + + + def get_preference(key) + if !has_preference?(key) && has_attachment?(key) + send key + else + super key + end + end + alias :[] :get_preference + + + def preference_type(name) + if has_attachment? name + :file + else + super name + end + end + + + # Spree's Configuration responds to preference methods via method_missing, but doesn't + # override respond_to?, which consequently reports those methods as unavailable. Paperclip + # errors if respond_to? isn't correct, so we override it here. + def respond_to?(method, include_all=false) + name = method.to_s.gsub('=', '') + super(self.class.preference_getter_method(name), include_all) || super(method, include_all) + end + + + def has_attachment?(name) + self.class.respond_to?(:attachment_definitions) && + self.class.attachment_definitions.keys.include?(name.to_sym) + end + end +end diff --git a/app/models/spree/product_decorator.rb b/app/models/spree/product_decorator.rb index c789d81963..b089c9a52b 100644 --- a/app/models/spree/product_decorator.rb +++ b/app/models/spree/product_decorator.rb @@ -1,8 +1,7 @@ -require 'open_food_network/scope_product_to_hub' +require 'open_food_network/permalink_generator' Spree::Product.class_eval do - include OpenFoodNetwork::ProductScopableToHub - + include PermalinkGenerator # We have an after_destroy callback on Spree::ProductOptionType. However, if we # don't specify dependent => destroy on this association, it is not called. See: # https://github.com/rails/rails/issues/7618 @@ -23,6 +22,8 @@ Spree::Product.class_eval do attr_accessible :variant_unit, :variant_unit_scale, :variant_unit_name, :unit_value attr_accessible :inherits_properties, :sku + before_validation :sanitize_permalink + # validates_presence_of :variants, unless: :new_record?, message: "Product must have at least one variant" validates_presence_of :supplier validates :primary_taxon, presence: { message: "^Product Category can't be blank" } @@ -193,7 +194,7 @@ Spree::Product.class_eval do private def set_available_on_to_now - self.available_on ||= Time.now + self.available_on ||= Time.zone.now end def update_units @@ -243,4 +244,11 @@ Spree::Product.class_eval do raise end end + + def sanitize_permalink + if permalink.blank? || permalink_changed? + requested = permalink.presence || permalink_was.presence || name.presence || 'product' + self.permalink = create_unique_permalink(requested.parameterize) + end + end end diff --git a/app/models/spree/shipment_decorator.rb b/app/models/spree/shipment_decorator.rb index 460fae4985..f544d95517 100644 --- a/app/models/spree/shipment_decorator.rb +++ b/app/models/spree/shipment_decorator.rb @@ -11,5 +11,14 @@ module Spree end alias_method_chain :ensure_correct_adjustment, :included_tax + + private + + # NOTE: This is an override of spree's method, needed to allow orders + # without line items (ie. user invoices) to not have inventory units + def require_inventory + return false unless Spree::Config[:track_inventory_levels] && line_items.count > 0 # This line altered + order.completed? && !order.canceled? + end end end diff --git a/app/models/spree/shipping_method_decorator.rb b/app/models/spree/shipping_method_decorator.rb index 4a2cf75b47..b8be603048 100644 --- a/app/models/spree/shipping_method_decorator.rb +++ b/app/models/spree/shipping_method_decorator.rb @@ -25,6 +25,22 @@ Spree::ShippingMethod.class_eval do scope :by_name, order('spree_shipping_methods.name ASC') + + # Return the services (pickup, delivery) that different distributors provide, in the format: + # {distributor_id => {pickup: true, delivery: false}, ...} + def self.services + Hash[ + Spree::ShippingMethod. + joins(:distributor_shipping_methods). + group('distributor_id'). + select("distributor_id"). + select("BOOL_OR(spree_shipping_methods.require_ship_address = 'f') AS pickup"). + select("BOOL_OR(spree_shipping_methods.require_ship_address = 't') AS delivery"). + map { |sm| [sm.distributor_id.to_i, {pickup: sm.pickup == 't', delivery: sm.delivery == 't'}] } + ] + end + + def available_to_order_with_distributor_check?(order, display_on=nil) available_to_order_without_distributor_check?(order, display_on) && self.distributors.include?(order.distributor) diff --git a/app/models/spree/tax_rate_decorator.rb b/app/models/spree/tax_rate_decorator.rb index 42eae86b8c..121d379584 100644 --- a/app/models/spree/tax_rate_decorator.rb +++ b/app/models/spree/tax_rate_decorator.rb @@ -1,20 +1,61 @@ -Spree::TaxRate.class_eval do - class << self - def match_with_sales_tax_registration(order) - return [] if order.distributor && !order.distributor.charges_sales_tax - match_without_sales_tax_registration(order) +module Spree + TaxRate.class_eval do + class << self + def match_with_sales_tax_registration(order) + return [] if order.distributor && !order.distributor.charges_sales_tax + match_without_sales_tax_registration(order) + end + alias_method_chain :match, :sales_tax_registration end - alias_method_chain :match, :sales_tax_registration - end - def adjust_with_included_tax(order) - adjust_without_included_tax(order) + def adjust_with_included_tax(order) + adjust_without_included_tax(order) - order.reload - (order.adjustments.tax + order.price_adjustments).each do |a| - a.set_absolute_included_tax! a.amount + order.reload + (order.adjustments.tax + order.price_adjustments).each do |a| + a.set_absolute_included_tax! a.amount + end + end + alias_method_chain :adjust, :included_tax + + + # Manually apply a TaxRate to a particular amount. TaxRates normally compute against + # LineItems or Orders, so we mock out a line item here to fit the interface + # that our calculator (usually DefaultTax) expects. + def compute_tax(amount) + product = OpenStruct.new tax_category: tax_category + line_item = LineItem.new quantity: 1 + line_item.define_singleton_method(:product) { product } + line_item.define_singleton_method(:price) { amount } + + # Tax on adjustments (represented by the included_tax field) is always inclusive of + # tax. However, there's nothing to stop an admin from setting one up with a tax rate + # that's marked as not inclusive of tax, and that would result in the DefaultTax + # calculator generating a slightly incorrect value. Therefore, we treat the tax + # rate as inclusive of tax for the calculations below, regardless of its original + # setting. + with_tax_included_in_price do + calculator.compute line_item + end + end + + + private + + def with_tax_included_in_price + old_included_in_price = self.included_in_price + + self.included_in_price = true + calculator.calculable.included_in_price = true + + result = yield + + ensure + self.included_in_price = old_included_in_price + calculator.calculable.included_in_price = old_included_in_price + + result end end - alias_method_chain :adjust, :included_tax end diff --git a/app/models/spree/taxon_decorator.rb b/app/models/spree/taxon_decorator.rb index 10ee0b4719..1a26ce73a8 100644 --- a/app/models/spree/taxon_decorator.rb +++ b/app/models/spree/taxon_decorator.rb @@ -9,4 +9,40 @@ Spree::Taxon.class_eval do #fs << Spree::ProductFilters.distributor_filter if Spree::ProductFilters.respond_to? :distributor_filter fs end + + # Find all the taxons of supplied products for each enterprise, indexed by enterprise. + # Format: {enterprise_id => [taxon_id, ...]} + def self.supplied_taxons + taxons = {} + + Spree::Taxon. + joins(:products => :supplier). + select('spree_taxons.*, enterprises.id AS enterprise_id'). + each do |t| + + taxons[t.enterprise_id.to_i] ||= Set.new + taxons[t.enterprise_id.to_i] << t.id + end + + taxons + end + + # Find all the taxons of distributed products for each enterprise, indexed by enterprise. + # Format: {enterprise_id => [taxon_id, ...]} + def self.distributed_taxons + taxons = {} + + Spree::Taxon. + joins(:products). + merge(Spree::Product.with_order_cycles_outer). + where('o_exchanges.incoming = ?', false). + select('spree_taxons.*, o_exchanges.receiver_id AS enterprise_id'). + each do |t| + + taxons[t.enterprise_id.to_i] ||= Set.new + taxons[t.enterprise_id.to_i] << t.id + end + + taxons + end end diff --git a/app/models/spree/user_decorator.rb b/app/models/spree/user_decorator.rb index d8ea312e23..b724a41d1b 100644 --- a/app/models/spree/user_decorator.rb +++ b/app/models/spree/user_decorator.rb @@ -1,14 +1,14 @@ Spree.user_class.class_eval do - if method_defined? :send_reset_password_instructions_with_delay - Bugsnag.notify RuntimeError.new "send_reset_password_instructions already handled asyncronously - double-calling results in infinite job loop" - else - handle_asynchronously :send_reset_password_instructions - end + # handle_asynchronously will define send_reset_password_instructions_with_delay. + # If handle_asynchronously is called twice, we get an infinite job loop. + handle_asynchronously :send_reset_password_instructions unless method_defined? :send_reset_password_instructions_with_delay has_many :enterprise_roles, :dependent => :destroy has_many :enterprises, through: :enterprise_roles has_many :owned_enterprises, class_name: 'Enterprise', foreign_key: :owner_id, inverse_of: :owner has_many :owned_groups, class_name: 'EnterpriseGroup', foreign_key: :owner_id, inverse_of: :owner + has_many :account_invoices + has_many :billable_periods, foreign_key: :owner_id, inverse_of: :owner has_one :cart has_many :customers @@ -19,7 +19,6 @@ Spree.user_class.class_eval do validate :limit_owned_enterprises - def known_users if admin? Spree::User.scoped @@ -50,7 +49,6 @@ Spree.user_class.class_eval do owned_enterprises(:reload).size < enterprise_limit end - private def limit_owned_enterprises diff --git a/app/models/spree/variant_decorator.rb b/app/models/spree/variant_decorator.rb index 72a7fb4dec..33e7f5bffd 100644 --- a/app/models/spree/variant_decorator.rb +++ b/app/models/spree/variant_decorator.rb @@ -1,9 +1,13 @@ -require 'open_food_network/scope_variant_to_hub' require 'open_food_network/enterprise_fee_calculator' -require 'open_food_network/option_value_namer' +require 'open_food_network/variant_and_line_item_naming' Spree::Variant.class_eval do - include OpenFoodNetwork::VariantScopableToHub + # Remove method From Spree, so method from the naming module is used instead + # This file may be double-loaded in delayed job environment, so we check before + # removing the Spree method to prevent error. + remove_method :options_text if instance_methods(false).include? :options_text + include OpenFoodNetwork::VariantAndLineItemNaming + has_many :exchange_variants, dependent: :destroy has_many :exchanges, through: :exchange_variants @@ -22,18 +26,9 @@ Spree::Variant.class_eval do after_save :update_units scope :with_order_cycles_inner, joins(exchanges: :order_cycle) - scope :with_order_cycles_outer, joins('LEFT OUTER JOIN exchange_variants AS o_exchange_variants ON (o_exchange_variants.variant_id = spree_variants.id)'). - joins('LEFT OUTER JOIN exchanges AS o_exchanges ON (o_exchanges.id = o_exchange_variants.exchange_id)'). - joins('LEFT OUTER JOIN order_cycles AS o_order_cycles ON (o_order_cycles.id = o_exchanges.order_cycle_id)') scope :not_deleted, where(deleted_at: nil) scope :in_stock, where('spree_variants.count_on_hand > 0 OR spree_variants.on_demand=?', true) - scope :in_distributor, lambda { |distributor| - with_order_cycles_outer. - where('o_exchanges.incoming = ? AND o_exchanges.receiver_id = ?', false, distributor). - select('DISTINCT spree_variants.*') - } - scope :in_order_cycle, lambda { |order_cycle| with_order_cycles_inner. merge(Exchange.outgoing). @@ -45,6 +40,21 @@ Spree::Variant.class_eval do where('spree_variants.id IN (?)', order_cycle.variants_distributed_by(distributor)) } + # Define sope as class method to allow chaining with other scopes filtering id. + # In Rails 3, merging two scopes on the same column will consider only the last scope. + def self.in_distributor(distributor) + where(id: ExchangeVariant.select(:variant_id). + joins(:exchange). + where('exchanges.incoming = ? AND exchanges.receiver_id = ?', false, distributor) + ) + end + + def self.indexed + Hash[ + scoped.map { |v| [v.id, v] } + ] + end + def price_with_fees(distributor, order_cycle) price + fees_for(distributor, order_cycle) @@ -58,82 +68,22 @@ Spree::Variant.class_eval do OpenFoodNetwork::EnterpriseFeeCalculator.new(distributor, order_cycle).fees_by_type_for self end - - # Copied and modified from Spree::Variant - def options_text - values = self.option_values.joins(:option_type).order("#{Spree::OptionType.table_name}.position asc") - - values.map! &:presentation # This line changed - - values.to_sentence({ :words_connector => ", ", :two_words_connector => ", " }) - end - - def delete_unit_option_values - ovs = self.option_values.where(option_type_id: Spree::Product.all_variant_unit_option_types) - self.option_values.destroy ovs - end - - # Used like "product.name - full_name". If called like this, a product with - # name "Bread" would be displayed as one of these: - # Bread - 1kg # if display_name blank - # Bread - Spelt Sourdough, 1kg # if display_name is "Spelt Sourdough, 1kg" - # Bread - 1kg Spelt Sourdough # if unit_to_display is "1kg Spelt Sourdough" - # Bread - Spelt Sourdough (1kg) # if display_name is "Spelt Sourdough" and unit_to_display is "1kg" - def full_name - return unit_to_display if display_name.blank? - return display_name if display_name.downcase.include? unit_to_display.downcase - return unit_to_display if unit_to_display.downcase.include? display_name.downcase - "#{display_name} (#{unit_to_display})" - end - - def name_to_display - return product.name if display_name.blank? - display_name - end - - def unit_to_display - return options_text if display_as.blank? - display_as - end - - - def update_units - delete_unit_option_values - - option_type = self.product.variant_unit_option_type - if option_type - name = option_value_name - ov = Spree::OptionValue.where(option_type_id: option_type, name: name, presentation: name).first || Spree::OptionValue.create!({option_type: option_type, name: name, presentation: name}, without_protection: true) - option_values << ov - end - end - def delete if product.variants == [self] # Only variant left on product errors.add :product, "must have at least one variant" false else transaction do - self.update_column(:deleted_at, Time.now) + self.update_column(:deleted_at, Time.zone.now) ExchangeVariant.where(variant_id: self).destroy_all self end end end - private def update_weight_from_unit_value - self.weight = unit_value / 1000 if self.product.variant_unit == 'weight' && unit_value.present? - end - - def option_value_name - if display_as.present? - display_as - else - option_value_namer = OpenFoodNetwork::OptionValueNamer.new self - option_value_namer.name - end + self.weight = weight_from_unit_value if self.product.variant_unit == 'weight' && unit_value.present? end end diff --git a/app/models/suburb.rb b/app/models/suburb.rb deleted file mode 100644 index 04aef4db45..0000000000 --- a/app/models/suburb.rb +++ /dev/null @@ -1,9 +0,0 @@ -class Suburb < ActiveRecord::Base - belongs_to :state, :class_name => Spree::State - - delegate :name, to: :state, prefix: true - - scope :matching , ->(term) { - where("lower(name) like ? or cast(postcode as text) like ?", "%#{term.to_s.downcase}%", "%#{term}%") - } -end diff --git a/app/models/variant_override.rb b/app/models/variant_override.rb index 51bb1468f7..21820ce0db 100644 --- a/app/models/variant_override.rb +++ b/app/models/variant_override.rb @@ -3,11 +3,19 @@ class VariantOverride < ActiveRecord::Base belongs_to :variant, class_name: 'Spree::Variant' validates_presence_of :hub_id, :variant_id + # Default stock can be nil, indicating stock should not be reset or zero, meaning reset to zero. Need to ensure this can be set by the user. + validates :default_stock, numericality: { greater_than_or_equal_to: 0 }, allow_nil: true scope :for_hubs, lambda { |hubs| where(hub_id: hubs) } + def self.indexed(hub) + Hash[ + for_hubs(hub).map { |vo| [vo.variant, vo] } + ] + end + def self.price_for(hub, variant) self.for(hub, variant).andand.price end @@ -25,16 +33,40 @@ class VariantOverride < ActiveRecord::Base if vo.nil? Bugsnag.notify RuntimeError.new "Attempting to decrement stock level for a variant without a VariantOverride." - - elsif vo.count_on_hand.blank? - Bugsnag.notify RuntimeError.new "Attempting to decrement stock level on a VariantOverride without a count_on_hand specified." - else - vo.decrement! :count_on_hand, quantity + vo.decrement_stock! quantity end end + def stock_overridden? + count_on_hand.present? + end + + def decrement_stock!(quantity) + if stock_overridden? + decrement! :count_on_hand, quantity + else + Bugsnag.notify RuntimeError.new "Attempting to decrement stock level on a VariantOverride without a count_on_hand specified." + end + end + + def default_stock? + default_stock.present? + end + + def reset_stock! + if resettable + if default_stock? + self.attributes = { count_on_hand: default_stock } + self.save + else + Bugsnag.notify RuntimeError.new "Attempting to reset stock level for a variant with no default stock level." + end + end + self + end + private def self.for(hub, variant) diff --git a/app/models/variant_override_set.rb b/app/models/variant_override_set.rb index 985190095b..730246cebe 100644 --- a/app/models/variant_override_set.rb +++ b/app/models/variant_override_set.rb @@ -1,6 +1,16 @@ class VariantOverrideSet < ModelSet def initialize(collection, attributes={}) - super(VariantOverride, collection, attributes, nil, - proc { |attrs| attrs['price'].blank? && attrs['count_on_hand'].blank? } ) + super(VariantOverride, collection, attributes, nil, proc { |attrs| deletable?(attrs) } ) + end + + private + + def deletable?(attrs) + attrs['price'].blank? && + attrs['count_on_hand'].blank? && + attrs['default_stock'].blank? && + attrs['resettable'].blank? && + attrs['sku'].nil? && + attrs['on_demand'].nil? end end diff --git a/app/overrides/admin/enterprises/show/rename_extended_description.html.haml.deface b/app/overrides/admin/enterprises/show/rename_extended_description.html.haml.deface deleted file mode 100644 index 3571a52472..0000000000 --- a/app/overrides/admin/enterprises/show/rename_extended_description.html.haml.deface +++ /dev/null @@ -1,3 +0,0 @@ -/ replace_contents "[data-hook='long_description']" -%th Profile Info: -%td= @enterprise.long_description.andand.html_safe \ No newline at end of file diff --git a/app/overrides/enterprises/_distributor_details/rich_text.html.haml.deface b/app/overrides/enterprises/_distributor_details/rich_text.html.haml.deface deleted file mode 100644 index 15da8738dc..0000000000 --- a/app/overrides/enterprises/_distributor_details/rich_text.html.haml.deface +++ /dev/null @@ -1,10 +0,0 @@ -/ replace_contents "[data-hook='distributor-details']" -%h2= distributor.name -= distributor.distributor_info.andand.html_safe -.next-collection-at - -# Handle both checkout process and show order page - - order_cycle = current_order_cycle || @order.andand.order_cycle - - if order_cycle - = order_cycle.pickup_time_for(distributor) - - else - = distributor.next_collection_at diff --git a/app/overrides/spree/admin/adjustments/_adjustments_table/add_tax_to_body.html.haml.deface b/app/overrides/spree/admin/adjustments/_adjustments_table/add_tax_to_body.html.haml.deface new file mode 100644 index 0000000000..398d99eaf6 --- /dev/null +++ b/app/overrides/spree/admin/adjustments/_adjustments_table/add_tax_to_body.html.haml.deface @@ -0,0 +1,9 @@ +/ replace_contents "[data-hook='adjustment_row']" + +%td.align-center.created_at= pretty_time(adjustment.created_at) +%td.align-center.label= adjustment.label +%td.align-center.amount= adjustment.display_amount.to_html +%td.align-center.included-tax= adjustment.display_included_tax.to_html +%td.actions + = link_to_edit adjustment, no_text: true + = link_to_delete adjustment, no_text: true \ No newline at end of file diff --git a/app/overrides/spree/admin/adjustments/_adjustments_table/add_tax_to_head.html.haml.deface b/app/overrides/spree/admin/adjustments/_adjustments_table/add_tax_to_head.html.haml.deface new file mode 100644 index 0000000000..6da264865a --- /dev/null +++ b/app/overrides/spree/admin/adjustments/_adjustments_table/add_tax_to_head.html.haml.deface @@ -0,0 +1,8 @@ +/ replace_contents "[data-hook='adjustmment_head']" + +%tr + %th= "#{t('spree.date')}/#{t('spree.time')}" + %th= t(:description) + %th= t(:amount) + %th= t(:included_tax) + %th.actions diff --git a/app/overrides/spree/admin/adjustments/_form/add_tax_rate.html.haml.deface b/app/overrides/spree/admin/adjustments/_form/add_tax_rate.html.haml.deface new file mode 100644 index 0000000000..a37acc1030 --- /dev/null +++ b/app/overrides/spree/admin/adjustments/_form/add_tax_rate.html.haml.deface @@ -0,0 +1,6 @@ +/ replace_contents "[data-hook='admin_adjustment_form_fields']" + +- if @adjustment.new_record? + = render 'new_form', f: f +- else + = render 'edit_form', f: f diff --git a/app/overrides/spree/admin/image_settings/edit/add_image_format.html.haml.deface b/app/overrides/spree/admin/image_settings/edit/add_image_format.html.haml.deface index deb79b5bbf..fe050c6c7c 100644 --- a/app/overrides/spree/admin/image_settings/edit/add_image_format.html.haml.deface +++ b/app/overrides/spree/admin/image_settings/edit/add_image_format.html.haml.deface @@ -3,7 +3,7 @@ - @styles.each_with_index do |(style_name, style_value), index| .field.three.columns = label_tag "attachment_styles[#{style_name}]", style_name - %a.destroy_style.with-tip{:alt => t(:destroy), :href => "#", :title => t(:destroy)} + %a.destroy_style{:alt => t(:destroy), :href => "#", :title => t(:destroy)} %i.icon-trash = text_field_tag "attachment_styles[#{style_name}][]", admin_image_settings_geometry_from_style(style_value), :class => 'fullwidth' %br/ diff --git a/app/overrides/spree/admin/orders/_form/add_distribution_fields.html.haml.deface b/app/overrides/spree/admin/orders/_form/add_distribution_fields.html.haml.deface index a0db4d2564..3fa7d3ea83 100644 --- a/app/overrides/spree/admin/orders/_form/add_distribution_fields.html.haml.deface +++ b/app/overrides/spree/admin/orders/_form/add_distribution_fields.html.haml.deface @@ -1,7 +1,7 @@ / insert_before "[data-hook='admin_order_form_buttons']" %fieldset.no-border-bottom - %legend{align => 'center'} Distribution + %legend{align: 'center'} Distribution - if @order.complete? .alpha.six.columns @@ -18,9 +18,12 @@ - else .alpha.six.columns .field - = f.label :distributor_id - = f.collection_select :distributor_id, Enterprise.is_distributor.managed_by(spree_current_user), :id, :name, include_blank: true + %label{for: "order_distributor_id"} Distributor + %select.fullwidth{id: "order_distributor_id", name: "order[distributor_id]", 'ng-model' => 'distributor_id'} + %option{"ng-repeat" => "shop in shops", "ng-value" => "shop.id", "ng-selected" => "distributor_id == shop.id", "ng-disabled" => "!distributorHasOrderCycles(shop)", "ng-bind" => "shop.name"} + .omega.six.columns - .field - = f.label :order_cycle_id - = f.collection_select :order_cycle_id, OrderCycle.managed_by(spree_current_user), :id, :name, include_blank: true + .field{"ng-show" => "distributor_id"} + %label{for: "order_order_cycle_id"} Order Cycle + %select.select2.fullwidth{id: "order_order_cycle_id", name: "order[order_cycle_id]", 'ng-model' => 'order_cycle_id'} + %option{"ng-repeat" => "oc in orderCycles | filter:validOrderCycle", "ng-value" => "oc.id", "ng-selected" => "order_cycle_id == oc.id", "ng-bind" => "oc.name_and_status"} diff --git a/app/overrides/spree/admin/orders/_form/hide_form_until_distribution.deface b/app/overrides/spree/admin/orders/_form/hide_form_until_distribution.deface new file mode 100644 index 0000000000..7fe5652aae --- /dev/null +++ b/app/overrides/spree/admin/orders/_form/hide_form_until_distribution.deface @@ -0,0 +1,2 @@ +add_to_attributes "table.index, [data-hook='admin_order_form_buttons']" +attributes "ng-show" => "distributionChosen()" diff --git a/app/overrides/spree/admin/orders/_line_item/replace_variant_label.html.haml.deface b/app/overrides/spree/admin/orders/_line_item/replace_variant_label.html.haml.deface new file mode 100644 index 0000000000..6481791112 --- /dev/null +++ b/app/overrides/spree/admin/orders/_line_item/replace_variant_label.html.haml.deface @@ -0,0 +1,3 @@ +/ replace 'code[erb-loud]:contains(\'"(#{f.object.variant.options_text})"\')' + += "(#{f.object.full_name})" diff --git a/app/overrides/spree/admin/orders/edit/add_action_dropdown.html.haml.deface b/app/overrides/spree/admin/orders/edit/add_action_dropdown.html.haml.deface new file mode 100644 index 0000000000..58b13a3b32 --- /dev/null +++ b/app/overrides/spree/admin/orders/edit/add_action_dropdown.html.haml.deface @@ -0,0 +1,6 @@ +/ insert_after "code[erb-loud]:contains('button_link_to t(:resend)')" + +%li.links-dropdown#links-dropdown{ links: order_links(@order).to_json } + +:coffee + angular.bootstrap(document.getElementById("links-dropdown"),['admin.dropdown']) diff --git a/app/overrides/spree/admin/orders/edit/add_ship_button.html.haml.deface b/app/overrides/spree/admin/orders/edit/add_ship_button.html.haml.deface deleted file mode 100644 index 47fbdea70a..0000000000 --- a/app/overrides/spree/admin/orders/edit/add_ship_button.html.haml.deface +++ /dev/null @@ -1,3 +0,0 @@ -/ insert_before "code[erb-loud]:contains('button_link_to t(:resend)')" -- if @order.ready_to_ship? - %li= button_link_to t(:ship), fire_admin_order_url(@order, :e => 'ship'), :method => :put, :data => { :confirm => t(:are_you_sure) } diff --git a/app/overrides/spree/admin/orders/index/add_distributor_and_order_cycle_filter_inputs.html.haml.deface b/app/overrides/spree/admin/orders/index/add_distributor_and_order_cycle_filter_inputs.html.haml.deface index f9e5e54e88..aef08e609e 100644 --- a/app/overrides/spree/admin/orders/index/add_distributor_and_order_cycle_filter_inputs.html.haml.deface +++ b/app/overrides/spree/admin/orders/index/add_distributor_and_order_cycle_filter_inputs.html.haml.deface @@ -2,12 +2,12 @@ .field-block.alpha.eight.columns = label_tag nil, t(:distributors) - = select_tag(:distributor_ids, + = select_tag("q[distributor_id_in]", options_for_select(Enterprise.is_distributor.managed_by(spree_current_user).map {|e| [e.name, e.id]}, params[:distributor_ids]), {class: "select2 fullwidth", multiple: true}) .field-block.alpha.eight.columns = label_tag nil, t(:order_cycles) - = select_tag(:order_cycle_ids, + = select_tag("q[order_cycle_id_in]", options_for_select(OrderCycle.managed_by(spree_current_user).map {|oc| [oc.name, oc.id]}, params[:order_cycle_ids]), {class: "select2 fullwidth", multiple: true}) diff --git a/app/overrides/spree/admin/orders/index/add_special_instructions.html.haml.deface b/app/overrides/spree/admin/orders/index/add_special_instructions.html.haml.deface index 75a2bc4689..1711343f3c 100644 --- a/app/overrides/spree/admin/orders/index/add_special_instructions.html.haml.deface +++ b/app/overrides/spree/admin/orders/index/add_special_instructions.html.haml.deface @@ -2,5 +2,5 @@ - if order.special_instructions.present? %br - %span{class: "icon-warning-sign with-tip", title: order.special_instructions} + %span{class: "icon-warning-sign", "ofn-with-tip" => order.special_instructions} notes diff --git a/app/overrides/spree/admin/orders/index/rearrange_cols.html.haml.deface b/app/overrides/spree/admin/orders/index/rearrange_cols.html.haml.deface index 76bec6c29b..39344b460e 100644 --- a/app/overrides/spree/admin/orders/index/rearrange_cols.html.haml.deface +++ b/app/overrides/spree/admin/orders/index/rearrange_cols.html.haml.deface @@ -1,5 +1,5 @@ -/ replace_contents "table#listing_orders colgroup" +/ replace_contents "table#listing_orders colgroup" -# See also: add_capture_order_shortcut, admin/orders/index/add_distributor_*to_admin_orders %col{style: "width: 10%"} --# There are 8 other columns, but they seem to sort themselves out :) \ No newline at end of file +-# There are 8 other columns, but they seem to sort themselves out :) diff --git a/app/overrides/spree/admin/orders/index/set_ng_app.deface b/app/overrides/spree/admin/orders/index/set_ng_app.deface new file mode 100644 index 0000000000..9ca071be11 --- /dev/null +++ b/app/overrides/spree/admin/orders/index/set_ng_app.deface @@ -0,0 +1,2 @@ +add_to_attributes "table#listing_orders" +attributes "ng-app" => "ofn.admin" diff --git a/app/overrides/spree/admin/orders/show/add_action_dropdown.html.haml.deface b/app/overrides/spree/admin/orders/show/add_action_dropdown.html.haml.deface new file mode 100644 index 0000000000..8c2ed40c26 --- /dev/null +++ b/app/overrides/spree/admin/orders/show/add_action_dropdown.html.haml.deface @@ -0,0 +1,6 @@ +/ insert_after "code[erb-loud]:contains('button_link_to t(:edit)')" + +%li.links-dropdown#links-dropdown{ links: order_links(@order).to_json } + +:coffee + angular.bootstrap(document.getElementById("links-dropdown"),['admin.dropdown']) diff --git a/app/overrides/spree/admin/shared/_configuration_menu/add_accounts_and_billing.html.haml.deface b/app/overrides/spree/admin/shared/_configuration_menu/add_accounts_and_billing.html.haml.deface new file mode 100644 index 0000000000..14f4925206 --- /dev/null +++ b/app/overrides/spree/admin/shared/_configuration_menu/add_accounts_and_billing.html.haml.deface @@ -0,0 +1,4 @@ +// insert_bottom "[data-hook='admin_configurations_sidebar_menu']" + +%li + = link_to 'Accounts & Billing', main_app.edit_admin_accounts_and_billing_settings_path diff --git a/app/overrides/spree/admin/shared/_configuration_menu/add_business_model_configuration.html.haml.deface b/app/overrides/spree/admin/shared/_configuration_menu/add_business_model_configuration.html.haml.deface new file mode 100644 index 0000000000..7d5c311a7b --- /dev/null +++ b/app/overrides/spree/admin/shared/_configuration_menu/add_business_model_configuration.html.haml.deface @@ -0,0 +1,4 @@ +// insert_bottom "[data-hook='admin_configurations_sidebar_menu']" + +%li + = link_to 'Business Model', main_app.edit_admin_business_model_configuration_path diff --git a/app/overrides/spree/admin/shared/_configuration_menu/add_content.html.haml.deface b/app/overrides/spree/admin/shared/_configuration_menu/add_content.html.haml.deface new file mode 100644 index 0000000000..0709cb9451 --- /dev/null +++ b/app/overrides/spree/admin/shared/_configuration_menu/add_content.html.haml.deface @@ -0,0 +1,3 @@ +/ insert_bottom "[data-hook='admin_configurations_sidebar_menu']" + +%li= link_to 'Content', main_app.edit_admin_content_path diff --git a/app/overrides/spree/admin/shared/_order_details/replace_variant_label.html.haml.deface b/app/overrides/spree/admin/shared/_order_details/replace_variant_label.html.haml.deface new file mode 100644 index 0000000000..33f7c95f28 --- /dev/null +++ b/app/overrides/spree/admin/shared/_order_details/replace_variant_label.html.haml.deface @@ -0,0 +1,3 @@ +/ replace 'code[erb-loud]:contains(\'"(" + variant_options(item.variant) + ")"\')' + += "(#{item.full_name})" diff --git a/app/overrides/spree/admin/tax_settings/edit/add_products_require_tax_category.html.haml.deface b/app/overrides/spree/admin/tax_settings/edit/add_products_require_tax_category.html.haml.deface deleted file mode 100644 index 588669f005..0000000000 --- a/app/overrides/spree/admin/tax_settings/edit/add_products_require_tax_category.html.haml.deface +++ /dev/null @@ -1,6 +0,0 @@ -/ insert_before "[data-hook='shipment_vat']" - -%div.field.align-center{ "data-hook" => "products_require_tax_category" } - = hidden_field_tag 'preferences[products_require_tax_category]', '0' - = check_box_tag 'preferences[products_require_tax_category]', '1', Spree::Config[:products_require_tax_category] - = label_tag nil, t(:products_require_tax_category) \ No newline at end of file diff --git a/app/overrides/spree/admin/tax_settings/edit/shipping_tax_rate.html.haml.deface b/app/overrides/spree/admin/tax_settings/edit/shipping_tax_rate.html.haml.deface deleted file mode 100644 index b378ba84a6..0000000000 --- a/app/overrides/spree/admin/tax_settings/edit/shipping_tax_rate.html.haml.deface +++ /dev/null @@ -1,5 +0,0 @@ -/ insert_after "[data-hook='shipment_vat']" - -.field.align-center{ "data-hook" => "shipping_tax_rate" } - = number_field_tag "preferences[shipping_tax_rate]", Spree::Config[:shipping_tax_rate].to_f, in: 0.0..1.0, step: 0.01 - = label_tag nil, t(:shipping_tax_rate) \ No newline at end of file diff --git a/app/overrides/spree/admin/variants/index/replace_options_text.html.haml.deface b/app/overrides/spree/admin/variants/index/replace_options_text.html.haml.deface new file mode 100644 index 0000000000..5b76f19fa6 --- /dev/null +++ b/app/overrides/spree/admin/variants/index/replace_options_text.html.haml.deface @@ -0,0 +1,3 @@ +/ replace "code[erb-loud]:contains('variant.options_text')" + += variant.full_name diff --git a/app/overrides/spree/layouts/admin/add_analytics.html.haml.deface b/app/overrides/spree/layouts/admin/add_analytics.html.haml.deface new file mode 100644 index 0000000000..548439b60f --- /dev/null +++ b/app/overrides/spree/layouts/admin/add_analytics.html.haml.deface @@ -0,0 +1,3 @@ +/ insert_bottom "[data-hook='admin_footer_scripts']" + += render 'shared/analytics' diff --git a/app/overrides/spree/layouts/admin/add_app_wrapper.html.erb.deface b/app/overrides/spree/layouts/admin/add_app_wrapper.html.erb.deface new file mode 100644 index 0000000000..9c5e6fcd80 --- /dev/null +++ b/app/overrides/spree/layouts/admin/add_app_wrapper.html.erb.deface @@ -0,0 +1,5 @@ + + +
> + <%= render_original %> +
diff --git a/app/overrides/spree/layouts/admin/add_customers_admin_tab.html.haml.deface b/app/overrides/spree/layouts/admin/add_customers_admin_tab.html.haml.deface new file mode 100644 index 0000000000..7b7534b23f --- /dev/null +++ b/app/overrides/spree/layouts/admin/add_customers_admin_tab.html.haml.deface @@ -0,0 +1,2 @@ +/ insert_bottom "[data-hook='admin_tabs'], #admin_tabs[data-hook]" += tab :customers, :url => main_app.admin_customers_path diff --git a/app/serializers/api/admin/basic_order_cycle_serializer.rb b/app/serializers/api/admin/basic_order_cycle_serializer.rb index e94795821a..3b3e29f83d 100644 --- a/app/serializers/api/admin/basic_order_cycle_serializer.rb +++ b/app/serializers/api/admin/basic_order_cycle_serializer.rb @@ -1,14 +1,24 @@ class Api::Admin::BasicOrderCycleSerializer < ActiveModel::Serializer - attributes :id, :name, :first_order, :last_order + include OrderCyclesHelper + + attributes :id, :name, :status, :first_order, :last_order has_many :suppliers, serializer: Api::Admin::IdNameSerializer has_many :distributors, serializer: Api::Admin::IdNameSerializer + def status + order_cycle_status_class object + end + def first_order - object.orders_open_at.strftime("%F") + object.orders_open_at.andand.strftime("%F") end def last_order - (object.orders_close_at + 1.day).strftime("%F") + if object.orders_close_at.present? + (object.orders_close_at + 1.day).strftime("%F") + else + nil + end end end diff --git a/app/serializers/api/admin/customer_serializer.rb b/app/serializers/api/admin/customer_serializer.rb new file mode 100644 index 0000000000..3cb9518a9f --- /dev/null +++ b/app/serializers/api/admin/customer_serializer.rb @@ -0,0 +1,11 @@ +class Api::Admin::CustomerSerializer < ActiveModel::Serializer + attributes :id, :email, :enterprise_id, :user_id, :code, :tags, :tag_list + + def tag_list + object.tag_list.join(",") + end + + def tags + object.tag_list.map{ |t| { text: t } } + end +end diff --git a/app/serializers/api/admin/enterprise_serializer.rb b/app/serializers/api/admin/enterprise_serializer.rb index c686aa9f69..8f955bdae2 100644 --- a/app/serializers/api/admin/enterprise_serializer.rb +++ b/app/serializers/api/admin/enterprise_serializer.rb @@ -1,7 +1,7 @@ class Api::Admin::EnterpriseSerializer < ActiveModel::Serializer attributes :name, :id, :is_primary_producer, :is_distributor, :sells, :category, :payment_method_ids, :shipping_method_ids attributes :producer_profile_only, :email, :long_description, :permalink - attributes :preferred_shopfront_message, :preferred_shopfront_closed_message, :preferred_shopfront_taxon_order + attributes :preferred_shopfront_message, :preferred_shopfront_closed_message, :preferred_shopfront_taxon_order, :preferred_shopfront_order_cycle_order attributes :owner, :users has_one :owner, serializer: Api::Admin::UserSerializer diff --git a/app/serializers/api/admin/exchange_serializer.rb b/app/serializers/api/admin/exchange_serializer.rb index 64c2c08cd4..cb49d94fc8 100644 --- a/app/serializers/api/admin/exchange_serializer.rb +++ b/app/serializers/api/admin/exchange_serializer.rb @@ -1,5 +1,5 @@ class Api::Admin::ExchangeSerializer < ActiveModel::Serializer - attributes :id, :sender_id, :receiver_id, :incoming, :variants, :pickup_time, :pickup_instructions + attributes :id, :sender_id, :receiver_id, :incoming, :variants, :receival_instructions, :pickup_time, :pickup_instructions has_many :enterprise_fees, serializer: Api::Admin::BasicEnterpriseFeeSerializer diff --git a/app/serializers/api/admin/for_order_cycle/enterprise_serializer.rb b/app/serializers/api/admin/for_order_cycle/enterprise_serializer.rb index 46afca46c5..9dfa680476 100644 --- a/app/serializers/api/admin/for_order_cycle/enterprise_serializer.rb +++ b/app/serializers/api/admin/for_order_cycle/enterprise_serializer.rb @@ -1,5 +1,17 @@ +require 'open_food_network/enterprise_issue_validator' + class Api::Admin::ForOrderCycle::EnterpriseSerializer < ActiveModel::Serializer attributes :id, :name, :managed, :supplied_products + attributes :issues_summary_supplier, :issues_summary_distributor + attributes :is_primary_producer, :is_distributor, :sells + + def issues_summary_supplier + OpenFoodNetwork::EnterpriseIssueValidator.new(object).issues_summary confirmation_only: true + end + + def issues_summary_distributor + OpenFoodNetwork::EnterpriseIssueValidator.new(object).issues_summary + end def managed Enterprise.managed_by(options[:spree_current_user]).include? object @@ -8,6 +20,6 @@ class Api::Admin::ForOrderCycle::EnterpriseSerializer < ActiveModel::Serializer def supplied_products objects = object.supplied_products.not_deleted serializer = Api::Admin::ForOrderCycle::SuppliedProductSerializer - ActiveModel::ArraySerializer.new(objects, each_serializer: serializer ) + ActiveModel::ArraySerializer.new(objects, each_serializer: serializer) end end diff --git a/app/serializers/api/admin/id_serializer.rb b/app/serializers/api/admin/id_serializer.rb new file mode 100644 index 0000000000..1d7327a167 --- /dev/null +++ b/app/serializers/api/admin/id_serializer.rb @@ -0,0 +1,3 @@ +class Api::Admin::IdSerializer < ActiveModel::Serializer + attributes :id +end diff --git a/app/serializers/api/admin/index_enterprise_serializer.rb b/app/serializers/api/admin/index_enterprise_serializer.rb new file mode 100644 index 0000000000..ca544c97fa --- /dev/null +++ b/app/serializers/api/admin/index_enterprise_serializer.rb @@ -0,0 +1,25 @@ +require 'open_food_network/enterprise_issue_validator' + +class Api::Admin::IndexEnterpriseSerializer < ActiveModel::Serializer + attributes :name, :id, :permalink, :is_primary_producer, :sells, :producer_profile_only, :owned, :edit_path + + attributes :issues, :warnings + + def owned + return true if options[:spree_current_user].admin? + object.owner == options[:spree_current_user] + end + + def edit_path + edit_admin_enterprise_path(object) + end + + def issues + OpenFoodNetwork::EnterpriseIssueValidator.new(object).issues + end + + def warnings + OpenFoodNetwork::EnterpriseIssueValidator.new(object).warnings + end + +end diff --git a/app/serializers/api/admin/line_item_serializer.rb b/app/serializers/api/admin/line_item_serializer.rb index 21fde91145..5feeddc43a 100644 --- a/app/serializers/api/admin/line_item_serializer.rb +++ b/app/serializers/api/admin/line_item_serializer.rb @@ -1,8 +1,10 @@ class Api::Admin::LineItemSerializer < ActiveModel::Serializer - attributes :id, :quantity, :max_quantity, :supplier, :price, :unit_value, :units_product, :units_variant + attributes :id, :quantity, :max_quantity, :price, :supplier, :final_weight_volume, :units_product, :units_variant + + has_one :order, serializer: Api::Admin::IdSerializer def supplier - Api::Admin::IdNameSerializer.new(object.product.supplier).serializable_hash + { id: object.product.supplier_id } end def units_product @@ -13,7 +15,12 @@ class Api::Admin::LineItemSerializer < ActiveModel::Serializer Api::Admin::UnitsVariantSerializer.new(object.variant).serializable_hash end - def unit_value - object.unit_value.to_f + def final_weight_volume + object.final_weight_volume.to_f + end + + def max_quantity + return object.quantity unless object.max_quantity.present? && object.max_quantity > object.quantity + object.max_quantity end end diff --git a/app/serializers/api/admin/order_cycle_serializer.rb b/app/serializers/api/admin/order_cycle_serializer.rb index d305612790..53b1a10c04 100644 --- a/app/serializers/api/admin/order_cycle_serializer.rb +++ b/app/serializers/api/admin/order_cycle_serializer.rb @@ -1,3 +1,5 @@ +require 'open_food_network/order_cycle_permissions' + class Api::Admin::OrderCycleSerializer < ActiveModel::Serializer attributes :id, :name, :orders_open_at, :orders_close_at, :coordinator_id, :exchanges attributes :editable_variants_for_incoming_exchanges, :editable_variants_for_outgoing_exchanges @@ -19,7 +21,7 @@ class Api::Admin::OrderCycleSerializer < ActiveModel::Serializer end def exchanges - scoped_exchanges = OpenFoodNetwork::OrderCyclePermissions.new(options[:current_user], object).visible_exchanges.order('id ASC') + scoped_exchanges = OpenFoodNetwork::OrderCyclePermissions.new(options[:current_user], object).visible_exchanges.by_enterprise_name ActiveModel::ArraySerializer.new(scoped_exchanges, {each_serializer: Api::Admin::ExchangeSerializer, current_user: options[:current_user] }) end diff --git a/app/serializers/api/admin/order_serializer.rb b/app/serializers/api/admin/order_serializer.rb index 2277551989..6f22ba1e94 100644 --- a/app/serializers/api/admin/order_serializer.rb +++ b/app/serializers/api/admin/order_serializer.rb @@ -1,8 +1,8 @@ class Api::Admin::OrderSerializer < ActiveModel::Serializer - attributes :id, :number, :full_name, :email, :phone, :completed_at, :line_items + attributes :id, :number, :full_name, :email, :phone, :completed_at - has_one :distributor, serializer: Api::Admin::IdNameSerializer - has_one :order_cycle, serializer: Api::Admin::BasicOrderCycleSerializer + has_one :distributor, serializer: Api::Admin::IdSerializer + has_one :order_cycle, serializer: Api::Admin::IdSerializer def full_name object.billing_address.nil? ? "" : ( object.billing_address.full_name || "" ) @@ -19,13 +19,4 @@ class Api::Admin::OrderSerializer < ActiveModel::Serializer def completed_at object.completed_at.blank? ? "" : object.completed_at.strftime("%F %T") end - - def line_items - # we used to have a scope here, but we are at the point where a user which can edit an order - # should be able to edit all of the line_items as well, making the scope redundant - ActiveModel::ArraySerializer.new( - object.line_items.order('id ASC'), - {each_serializer: Api::Admin::LineItemSerializer} - ) - end end diff --git a/app/serializers/api/admin/user_serializer.rb b/app/serializers/api/admin/user_serializer.rb index 501cd75674..c080a8fd6a 100644 --- a/app/serializers/api/admin/user_serializer.rb +++ b/app/serializers/api/admin/user_serializer.rb @@ -1,3 +1,18 @@ +require 'open_food_network/last_used_address' + class Api::Admin::UserSerializer < ActiveModel::Serializer attributes :id, :email + + has_one :ship_address, serializer: Api::AddressSerializer + has_one :bill_address, serializer: Api::AddressSerializer + + def ship_address + object.ship_address || + OpenFoodNetwork::LastUsedAddress.new(object.email).last_used_ship_address + end + + def bill_address + object.bill_address || + OpenFoodNetwork::LastUsedAddress.new(object.email).last_used_bill_address + end end diff --git a/app/serializers/api/admin/variant_override_serializer.rb b/app/serializers/api/admin/variant_override_serializer.rb index ebe76a1049..c1e6e0038c 100644 --- a/app/serializers/api/admin/variant_override_serializer.rb +++ b/app/serializers/api/admin/variant_override_serializer.rb @@ -1,3 +1,3 @@ class Api::Admin::VariantOverrideSerializer < ActiveModel::Serializer - attributes :id, :hub_id, :variant_id, :price, :count_on_hand + attributes :id, :hub_id, :variant_id, :sku, :price, :count_on_hand, :on_demand, :default_stock, :resettable end diff --git a/app/serializers/api/admin/variant_serializer.rb b/app/serializers/api/admin/variant_serializer.rb index 510f7af333..66acfe8ece 100644 --- a/app/serializers/api/admin/variant_serializer.rb +++ b/app/serializers/api/admin/variant_serializer.rb @@ -1,5 +1,5 @@ class Api::Admin::VariantSerializer < ActiveModel::Serializer - attributes :id, :options_text, :unit_value, :unit_description, :unit_to_display, :on_demand, :display_as, :display_name, :name_to_display + attributes :id, :options_text, :unit_value, :unit_description, :unit_to_display, :on_demand, :display_as, :display_name, :name_to_display, :sku attributes :on_hand, :price has_many :variant_overrides diff --git a/app/serializers/api/enterprise_serializer.rb b/app/serializers/api/enterprise_serializer.rb index 532887ae01..3210c1d2af 100644 --- a/app/serializers/api/enterprise_serializer.rb +++ b/app/serializers/api/enterprise_serializer.rb @@ -1,4 +1,7 @@ class Api::EnterpriseSerializer < ActiveModel::Serializer + # We reference this here because otherwise the serializer complains about its absence + Api::IdSerializer + def serializable_hash cached_serializer_hash.merge uncached_serializer_hash end @@ -6,11 +9,11 @@ class Api::EnterpriseSerializer < ActiveModel::Serializer private def cached_serializer_hash - Api::CachedEnterpriseSerializer.new(object, @options).serializable_hash + Api::CachedEnterpriseSerializer.new(object, @options).serializable_hash || {} end def uncached_serializer_hash - Api::UncachedEnterpriseSerializer.new(object, @options).serializable_hash + Api::UncachedEnterpriseSerializer.new(object, @options).serializable_hash || {} end end @@ -18,41 +21,54 @@ class Api::UncachedEnterpriseSerializer < ActiveModel::Serializer attributes :orders_close_at, :active def orders_close_at - OrderCycle.first_closing_for(object).andand.orders_close_at + options[:data].earliest_closing_times[object.id] end def active - @options[:active_distributors].andand.include? object + options[:data].active_distributors.andand.include? object end - - end class Api::CachedEnterpriseSerializer < ActiveModel::Serializer cached - delegate :cache_key, to: :object + #delegate :cache_key, to: :object + + def cache_key + object.andand.cache_key + end + attributes :name, :id, :description, :latitude, :longitude, :long_description, :website, :instagram, :linkedin, :twitter, :facebook, :is_primary_producer, :is_distributor, :phone, :visible, - :email, :hash, :logo, :promo_image, :path, :pickup, :delivery, + :email_address, :hash, :logo, :promo_image, :path, :pickup, :delivery, :icon, :icon_font, :producer_icon_font, :category, :producers, :hubs - has_many :distributed_taxons, key: :taxons, serializer: Api::IdSerializer - has_many :supplied_taxons, serializer: Api::IdSerializer + attributes :taxons, :supplied_taxons has_one :address, serializer: Api::AddressSerializer + + def taxons + ids_to_objs options[:data].distributed_taxons[object.id] + end + + def supplied_taxons + ids_to_objs options[:data].supplied_taxons[object.id] + end + def pickup - object.shipping_methods.where(:require_ship_address => false).present? + services = options[:data].shipping_method_services[object.id] + services ? services[:pickup] : false end def delivery - object.shipping_methods.where(:require_ship_address => true).present? + services = options[:data].shipping_method_services[object.id] + services ? services[:delivery] : false end - def email - object.email.to_s.reverse + def email_address + object.email_address.to_s.reverse end def hash @@ -60,11 +76,11 @@ class Api::CachedEnterpriseSerializer < ActiveModel::Serializer end def logo - object.logo(:medium) if object.logo.exists? + object.logo(:medium) if object.logo? end def promo_image - object.promo_image(:large) if object.promo_image.exists? + object.promo_image(:large) if object.promo_image? end def path @@ -72,11 +88,13 @@ class Api::CachedEnterpriseSerializer < ActiveModel::Serializer end def producers - ActiveModel::ArraySerializer.new(object.suppliers.activated, {each_serializer: Api::IdSerializer}) + relatives = options[:data].relatives[object.id] + ids_to_objs(relatives.andand[:producers]) end def hubs - ActiveModel::ArraySerializer.new(object.distributors.activated, {each_serializer: Api::IdSerializer}) + relatives = options[:data].relatives[object.id] + ids_to_objs(relatives.andand[:distributors]) end # Map svg icons. @@ -116,4 +134,12 @@ class Api::CachedEnterpriseSerializer < ActiveModel::Serializer } icon_fonts[object.category] end + + + private + + def ids_to_objs(ids) + return [] if ids.blank? + ids.map { |id| {id: id} } + end end diff --git a/app/serializers/api/line_item_serializer.rb b/app/serializers/api/line_item_serializer.rb index d791febdfc..35d3f9c540 100644 --- a/app/serializers/api/line_item_serializer.rb +++ b/app/serializers/api/line_item_serializer.rb @@ -1,5 +1,5 @@ class Api::LineItemSerializer < ActiveModel::Serializer - attributes :id, :quantity, :price + attributes :id, :quantity, :max_quantity, :price has_one :variant, serializer: Api::VariantSerializer end diff --git a/app/serializers/api/product_serializer.rb b/app/serializers/api/product_serializer.rb index 0de794796b..5a1d1b5c86 100644 --- a/app/serializers/api/product_serializer.rb +++ b/app/serializers/api/product_serializer.rb @@ -22,7 +22,12 @@ class Api::UncachedProductSerializer < ActiveModel::Serializer attributes :price def price - object.master.price_with_fees(options[:current_distributor], options[:current_order_cycle]) + if options[:enterprise_fee_calculator] + object.master.price + options[:enterprise_fee_calculator].indexed_fees_for(object.master) + else + object.master.price_with_fees(options[:current_distributor], options[:current_order_cycle]) + end + end end @@ -30,29 +35,29 @@ class Api::CachedProductSerializer < ActiveModel::Serializer #cached #delegate :cache_key, to: :object - attributes :id, :name, :permalink, :count_on_hand, :on_demand, :group_buy, - :notes, :description, :properties_with_values + attributes :id, :name, :permalink, :count_on_hand + attributes :on_demand, :group_buy, :notes, :description + attributes :properties_with_values has_many :variants, serializer: Api::VariantSerializer - has_many :taxons, serializer: Api::IdSerializer - has_many :images, serializer: Api::ImageSerializer - - has_one :supplier, serializer: Api::IdSerializer - has_one :primary_taxon, serializer: Api::TaxonSerializer has_one :master, serializer: Api::VariantSerializer + has_one :primary_taxon, serializer: Api::TaxonSerializer + has_many :taxons, serializer: Api::IdSerializer + + has_many :images, serializer: Api::ImageSerializer + has_one :supplier, serializer: Api::IdSerializer + def properties_with_values object.properties_including_inherited end def variants - # We use the in_stock? method here instead of the in_stock scope because we need to - # look up the stock as overridden by VariantOverrides, and the scope method is not affected - # by them. - - object.variants. - for_distribution(options[:current_order_cycle], options[:current_distributor]). - each { |v| v.scope_to_hub options[:current_distributor] }. - select(&:in_stock?) + options[:variants][object.id] || [] end + + def master + options[:master_variants][object.id].andand.first + end + end diff --git a/app/serializers/api/variant_serializer.rb b/app/serializers/api/variant_serializer.rb index f995fa4345..6908eaf84f 100644 --- a/app/serializers/api/variant_serializer.rb +++ b/app/serializers/api/variant_serializer.rb @@ -1,21 +1,25 @@ class Api::VariantSerializer < ActiveModel::Serializer - attributes :id, :is_master, :count_on_hand, :name_to_display, :unit_to_display, + attributes :id, :is_master, :count_on_hand, :name_to_display, :unit_to_display, :options_text, :on_demand, :price, :fees, :price_with_fees, :product_name - def price_with_fees - object.price_with_fees(options[:current_distributor], options[:current_order_cycle]) - end - def price object.price end def fees - object.fees_by_type_for(options[:current_distributor], options[:current_order_cycle]) + options[:enterprise_fee_calculator].andand.indexed_fees_by_type_for(object) || + object.fees_by_type_for(options[:current_distributor], options[:current_order_cycle]) + end + + def price_with_fees + if options[:enterprise_fee_calculator] + object.price + options[:enterprise_fee_calculator].indexed_fees_for(object) + else + object.price_with_fees(options[:current_distributor], options[:current_order_cycle]) + end end def product_name object.product.name end - end diff --git a/app/views/admin/account/show.html.haml b/app/views/admin/account/show.html.haml new file mode 100644 index 0000000000..808402ed16 --- /dev/null +++ b/app/views/admin/account/show.html.haml @@ -0,0 +1,72 @@ + +- content_for :page_title do + = t(:account) + + +- if @invoices.empty? + %h4= t(:no_invoices_to_display) + +- @invoices.order('year DESC, month DESC').each do |invoice| + .row.invoice_title + .two.columns.alpha + %h4= invoice_description_for(invoice) + .two.columns.text-right + %h5 + - if invoice.order.andand.complete? + %a{ href: print_admin_order_url(invoice.order), :target => "_blank"} + %i.icon-print + = t(:print) + - else +   + .ten.columns +   + .two.columns.omega.text-right + %h4.balance= invoice_total_for(invoice) + %table.invoice_summary + %col{ width: '25%' } + %col{ width: '62.5%' } + %col{ width: '12.5%' } + %thead + %th Date + %th= t(:description) + %th= t(:charge) + - if order = invoice.order + - invoice.billable_periods.select{ |bp| bp.adjustment.andand.amount.andand > 0}.each do |billable_period| + %tr + %td.text-center= "#{billable_period.begins_at.strftime("%d/%m/%Y")}" + %td= billable_period.label + -# Using amount from the actual adjustment on the order here so that we avoid recalculating the bill + -# at a future date with different settings to those used at the time the invoice was finalized + %td.text-right= billable_period.adjustment.display_amount + - order.adjustments.where('source_type <> (?)', "BillablePeriod").reject{ |a| a.amount == 0 }.each do |adjustment| + %tr + %td.text-center   + %td= adjustment.label + %td.text-right= adjustment.display_amount + %tr.total + %td.text-center   + %td= t(:total).upcase + %td.text-right= invoice_total_for(invoice) + + +-# - if @enterprises.empty? +-# %h4 No enterprises to display +-# +-# - @enterprises.each do |enterprise| +-# %h2= enterprise.name +-# %table +-# %thead +-# %th Begins +-# %th Ends +-# %th Sells +-# %th Trial? +-# %th Turnover +-# %th Bill +-# - enterprise.billable_periods.each do |billable_period| +-# %tr +-# %td= billable_period.begins_at.in_time_zone.strftime("%F %T") +-# %td= billable_period.ends_at.in_time_zone.strftime("%F %T") +-# %td= billable_period.sells +-# %td= billable_period.trial? +-# %td= billable_period.display_turnover +-# %td= billable_period.display_bill diff --git a/app/views/admin/accounts_and_billing_settings/_method_settings.html.haml b/app/views/admin/accounts_and_billing_settings/_method_settings.html.haml new file mode 100644 index 0000000000..17037d47e9 --- /dev/null +++ b/app/views/admin/accounts_and_billing_settings/_method_settings.html.haml @@ -0,0 +1,10 @@ +.row + .six.columns.alpha + .field + = label :settings, :default_accounts_payment_method_id, t(:default_accounts_payment_method) + = collection_select(:settings, :default_accounts_payment_method_id, @payment_methods, :id, :name, { include_blank: true, selected: Spree::Config.default_accounts_payment_method_id}, { class: "select2 fullwidth" }) + + .six.columns.omega + .field + = label :settings, :default_accounts_shipping_method_id, t(:default_accounts_shipping_method) + = collection_select(:settings, :default_accounts_shipping_method_id, @shipping_methods, :id, :name, { include_blank: true, selected: Spree::Config.default_accounts_shipping_method_id}, { class: "select2 fullwidth" }) diff --git a/app/views/admin/accounts_and_billing_settings/edit.html.haml b/app/views/admin/accounts_and_billing_settings/edit.html.haml new file mode 100644 index 0000000000..71fac1fd28 --- /dev/null +++ b/app/views/admin/accounts_and_billing_settings/edit.html.haml @@ -0,0 +1,88 @@ += render :partial => 'spree/admin/shared/configuration_menu' + +- content_for :page_title do + = t(:accounts_and_billing_settings) + += render 'spree/shared/error_messages', target: @settings + +-# - month_options = (0...12).map { |i| Time.zone.now.beginning_of_month - i.months }.map{ |t| [t.strftime("%b %Y"), t.strftime("%b %Y %z")]} + +%fieldset.no-border-bottom + %legend Settings + = form_for @settings, as: :settings, url: main_app.admin_accounts_and_billing_settings_path, :method => :put do |f| + .row{ ng: { app: 'admin.accounts_and_billing_settings' } } + .twelve.columns.alpha.omega + .field + = f.label :accounts_distributor_id, t(:accounts_administration_distributor) + = f.collection_select(:accounts_distributor_id, @distributors, :id, :name, { include_blank: true }, { class: "select2 fullwidth", 'watch-value-as' => "enterprise_id"}) + + = f.hidden_field :default_accounts_payment_method_id, value: '' + = f.hidden_field :default_accounts_shipping_method_id, value: '' + %div{ 'method-settings-for' => 'enterprise_id' } + + .row + .six.columns.alpha + %fieldset.no-border-bottom + %legend Update Invoices + = f.check_box :auto_update_invoices + = f.label :auto_update_invoices, "Auto-update invoices nightly at 1:00am" + + .six.columns.omega + %fieldset.no-border-bottom + %legend Finalise Invoices + = f.check_box :auto_finalize_invoices + = f.label :auto_finalize_invoices, "Auto-finalise invoices monthly on the 2nd at 1:30am" + + .row + .twelve.columns.alpha.omega.form-buttons{"data-hook" => "buttons"} + = button t(:update), 'icon-refresh', value: "update" + +%fieldset.no-border-bottom + %legend Manually Run Tasks + .row + .six.columns.alpha.step.text-center + .form-buttons{"data-hook" => "buttons"} + =link_to_with_icon "icon-undo", "Update User Invoices", + main_app.start_job_admin_accounts_and_billing_settings_path(job: { name: "update_account_invoices" }), + class: "button fullwidth" + + %br + + - if @update_account_invoices_job + %p.text-center + - if @update_account_invoices_job.run_at < Time.zone.now + %strong In Progress + %br + Started at: + - else + %strong Queued + %br + Scheduled for: + = @update_account_invoices_job.run_at + - else + %p.explanation + Use this button to immediately update invoices for the month to date for each enterprise user in the system. This task can be set up to run automatically every night. + + + .six.columns.omega.step.text-center + .form-buttons{"data-hook" => "buttons"} + =link_to_with_icon "icon-ok-sign", "Finalise User Invoices", + main_app.start_job_admin_accounts_and_billing_settings_path(job: { name: "finalize_account_invoices" }), + class: "button fullwidth" + + %br + + - if @finalize_account_invoices_job + %p.text-center + - if @finalize_account_invoices_job.run_at < Time.zone.now + %strong In Progress + %br + Started at: + - else + %strong Queued + %br + Scheduled for: + = @finalize_account_invoices_job.run_at + - else + %p.explanation + Use this button to finalize all invoices in the system for the previous calendar month. This task can be set up to run automatically once a month. diff --git a/app/views/admin/business_model_configuration/edit.html.haml b/app/views/admin/business_model_configuration/edit.html.haml new file mode 100644 index 0000000000..89345178f9 --- /dev/null +++ b/app/views/admin/business_model_configuration/edit.html.haml @@ -0,0 +1,87 @@ += render :partial => 'spree/admin/shared/configuration_menu' + +- content_for :app_wrapper_attrs do + = "ng-app='admin.businessModelConfiguration'" + +- content_for :page_title do + %h1.page-title= t(:business_model_configuration) + %a{ 'ofn-with-tip' => "Configure the rate at which shops will be charged each month for use of the Open Food Network." } What's this? + += render 'spree/shared/error_messages', target: @settings + +.row{ ng: { controller: "BusinessModelConfigCtrl" } } + .five.columns.omega + %fieldset.no-border-bottom + %legend=t(:bill_calculation_settings) + %p + Adjust the amount that enterprises will be billed each month for use of the OFN. + %br + = form_for @settings, as: :settings, url: main_app.admin_business_model_configuration_path, :method => :put do |f| + .row + .three.columns.alpha + = f.label :account_invoices_monthly_fixed, t(:fixed_monthly_charge) + %span.icon-question-sign{'ofn-with-tip' => "A fixed monthly charge for ALL enterprises who are set up as a shop, regardless of how much produce they sell."} + .two.columns.omega + .input-symbol.before + %span= Spree::Money.currency_symbol + = f.number_field :account_invoices_monthly_fixed, min: 0.0, class: "fullwidth", 'watch-value-as' => 'fixed' + .row + .three.columns.alpha + = f.label :account_invoices_monthly_rate, t(:percentage_of_turnover) + %span.icon-question-sign{'ofn-with-tip' => "When greater than zero, this rate (0.0 - 1.0) will be applied to the total turnover of each shop and added to any fixed charges (to the left) to calculate the monthly bill."} + .two.columns.omega + = f.number_field :account_invoices_monthly_rate, min: 0.0, max: 1.0, step: 0.01, class: "fullwidth", 'watch-value-as' => 'rate' + .row + .three.columns.alpha + = f.label :account_invoices_monthly_cap, t(:monthly_cap_excl_tax) + %span.icon-question-sign{'ofn-with-tip' => "When greater than zero, this value will be used as a cap on the amount that shops will be charged each month."} + .two.columns.omega + .input-symbol.before + %span= Spree::Money.currency_symbol + = f.number_field :account_invoices_monthly_cap, min: 0.0, class: "fullwidth", 'watch-value-as' => 'cap' + .row + .three.columns.alpha + = f.label :account_invoices_tax_rate, t(:tax_rate) + %span.icon-question-sign{'ofn-with-tip' => "Tax rate that applies to the the monthly bill that enterprises are charged for using the system."} + .two.columns.omega + = f.number_field :account_invoices_tax_rate, min: 0.0, max: 1.0, step: 0.01, class: "fullwidth", 'watch-value-as' => 'taxRate' + + .row + .five.columns.alpha.omega.form-buttons{"data-hook" => "buttons"} + = button t(:update), 'icon-refresh', value: "update" + + .two.columns +   + + .five.columns.alpha + %fieldset.no-border-bottom + %legend=t(:example_bill_calculator) + %p + Alter the example turnover to visualise the effect of the settings to the left. + %br + .row + .three.columns.alpha + = label_tag :turnover, t(:example_monthly_turnover) + %span.icon-question-sign{'ofn-with-tip' => "An example monthly turnover for an enterprise which will be used to generate calculate an example monthly bill below."} + .two.columns.omega + .input-symbol.before + %span= Spree::Money.currency_symbol + %input.fullwidth{ id: 'turnover', type: "number", ng: { model: 'turnover' } } + .row + .three.columns.alpha + = label_tag :cap_reached, t(:cap_reached?) + %span.icon-question-sign{'ofn-with-tip' => "Whether the cap (specified to the left) has been reached, given the settings and the turnover provided."} + .two.columns.omega + %input.fullwidth{ id: 'cap_reached', type: "text", readonly: true, ng: { value: 'capReached()' } } + .row + .three.columns.alpha + = label_tag :included_tax, t(:included_tax) + %span.icon-question-sign{'ofn-with-tip' => "The total tax included in the example monthly bill, given the settings and the turnover provided."} + .two.columns.omega + %input.fullwidth{ id: 'included_tax', type: "text", readonly: true, ng: { value: 'includedTax() | currency' } } + .row + .three.columns.alpha + = label_tag :total_incl_tax, t(:total_monthly_bill_incl_tax) + %span.icon-question-sign{'ofn-with-tip' => "The example total monthly bill with tax included, given the settings and the turnover provided."} + .two.columns.omega + %input.fullwidth{ id: 'total_incl_tax', type: "text", readonly: true, ng: { value: 'total() | currency' } } diff --git a/app/views/admin/contents/_fieldset.html.haml b/app/views/admin/contents/_fieldset.html.haml new file mode 100644 index 0000000000..709f3d5ec8 --- /dev/null +++ b/app/views/admin/contents/_fieldset.html.haml @@ -0,0 +1,8 @@ +%fieldset.no-border-bottom + %legend{align: "center"}= name + - preferences.each do |key| + - type = ContentConfig.preference_type(key) + .field + = label_tag(key, t(key) + ': ') + tag(:br) if type != :boolean + = preference_field_tag(key, ContentConfig[key], :type => type) + = label_tag(key, t(key)) + tag(:br) if type == :boolean diff --git a/app/views/admin/contents/edit.html.haml b/app/views/admin/contents/edit.html.haml new file mode 100644 index 0000000000..82751abebc --- /dev/null +++ b/app/views/admin/contents/edit.html.haml @@ -0,0 +1,15 @@ += render 'spree/admin/shared/configuration_menu' + +- content_for :page_title do + Content + + += form_tag main_app.admin_content_path, method: :put, multipart: true do + #preferences + - @preference_sections.each do |preference_section| + = render 'fieldset', name: preference_section[:name], preferences: preference_section[:preferences] + + .form-buttons.filter-actions.actions{"data-hook" => "buttons"} + = button t(:update), 'icon-refresh' + %span.or= t(:or) + = link_to_with_icon 'icon-remove', t(:cancel), main_app.edit_admin_content_path, class: 'button' diff --git a/app/views/admin/customers/index.html.haml b/app/views/admin/customers/index.html.haml new file mode 100644 index 0000000000..5647821bf5 --- /dev/null +++ b/app/views/admin/customers/index.html.haml @@ -0,0 +1,60 @@ +- content_for :page_title do + %h1.page-title Customers + += admin_inject_shops + +%div{ ng: { app: 'admin.customers', controller: 'customersCtrl' } } + .row{ ng: { hide: "loaded() && filteredCustomers.length > 0" } } + .five.columns.alpha + %h3 Please select a Hub: + .four.columns + %select.select2.fullwidth#shop_id{ 'ng-model' => 'shop.id', name: 'shop_id', 'ng-options' => 'shop.id as shop.name for shop in shops' } + .seven.columns.omega   + + .row{ 'ng-hide' => '!loaded() || filteredCustomers.length == 0' } + .controls.sixteen.columns.alpha.omega + .five.columns.alpha + %input.fullwidth{ :type => "text", :id => 'quick_search', 'ng-model' => 'quickSearch', :placeholder => 'Quick Search' } + .five.columns   + -# =render 'admin/shared/bulk_actions_dropdown' + .three.columns   + = render 'admin/shared/columns_dropdown' + .row{ 'ng-if' => 'shop && !loaded()' } + .sixteen.columns.alpha#loading + %img.spinner{ src: "/assets/spinning-circles.svg" } + %h1 LOADING CUSTOMERS + .row{ :class => "sixteen columns alpha", 'ng-show' => 'loaded() && filteredCustomers.length == 0'} + %h1#no_results No customers found. + + + .row{ ng: { show: "loaded() && filteredCustomers.length > 0" } } + %form{ name: "customers" } + %table.index#customers + %col.email{ width: "20%"} + %col.code{ width: "20%"} + %col.tags{ width: "50%"} + %col.actions{ width: "10%"} + %thead + %tr{ ng: { controller: "ColumnsCtrl" } } + -# %th.bulk + -# %input{ :type => "checkbox", :name => 'toggle_bulk', 'ng-click' => 'toggleAllCheckboxes()', 'ng-checked' => "allBoxesChecked()" } + %th.email{ 'ng-show' => 'columns.email.visible' } + %a{ :href => '', 'ng-click' => "predicate = 'customer.email'; reverse = !reverse" } Email + %th.code{ 'ng-show' => 'columns.code.visible' } + %a{ :href => '', 'ng-click' => "predicate = 'customer.code'; reverse = !reverse" } Code + %th.tags{ 'ng-show' => 'columns.tags.visible' } Tags + %th.actions + Ask?  + %input{ :type => 'checkbox', 'ng-model' => "confirmDelete" } + %tr.customer{ 'ng-repeat' => "customer in filteredCustomers = ( customers | filter:quickSearch | orderBy:predicate:reverse )", 'ng-class-even' => "'even'", 'ng-class-odd' => "'odd'", :id => "c_{{customer.id}}" } + -# %td.bulk + -# %input{ :type => "checkbox", :name => 'bulk', 'ng-model' => 'customer.checked' } + %td.email{ 'ng-show' => 'columns.email.visible' } {{ customer.email }} + %td.code{ 'ng-show' => 'columns.code.visible' } + %input{ :type => 'text', :name => 'code', :id => 'code', 'ng-model' => 'customer.code', 'obj-for-update' => "customer", "attr-for-update" => "code" } + %td.tags{ 'ng-show' => 'columns.tags.visible' } + .tag_watcher{ 'obj-for-update' => "customer", "attr-for-update" => "tag_list"} + %tags_with_translation{ object: 'customer' } + %td.actions + %a{ 'ng-click' => "deleteCustomer(customer)", :class => "delete-customer icon-trash no-text" } + %input{ :type => "button", 'value' => 'Update', 'ng-click' => 'submitAll()' } diff --git a/app/views/admin/enterprise_groups/_form_images.html.haml b/app/views/admin/enterprise_groups/_form_images.html.haml index 49169851c3..1bfffa86d2 100644 --- a/app/views/admin/enterprise_groups/_form_images.html.haml +++ b/app/views/admin/enterprise_groups/_form_images.html.haml @@ -2,16 +2,16 @@ %legend Images .row .alpha.three.columns - = f.label :logo, class: 'with-tip', 'data-powertip' => 'This is the logo' - .with-tip{'data-powertip' => 'This is the logo'} + = f.label :logo, 'ofn-with-tip' => 'This is the logo for the group' + %div{'ofn-with-tip' => 'This is the logo for the group'} %a What's this? .omega.eight.columns = image_tag @object.logo.url if @object.logo.present? = f.file_field :logo .row .alpha.three.columns - = f.label :promo_image, class: 'with-tip', 'data-powertip' => 'This image is displayed at the top of the Group profile' - .with-tip{'data-powertip' => 'This image is displayed at the top of the Group profile'} + = f.label :promo_image, 'ofn-with-tip' => 'This image is displayed at the top of the Group profile' + %div{'ofn-with-tip' => 'This image is displayed at the top of the Group profile'} %a What's this? .omega.eight.columns = image_tag @object.promo_image.url if @object.promo_image.present? diff --git a/app/views/admin/enterprise_groups/_form_users.html.haml b/app/views/admin/enterprise_groups/_form_users.html.haml index 0a8a5dd635..c4f57da48a 100644 --- a/app/views/admin/enterprise_groups/_form_users.html.haml +++ b/app/views/admin/enterprise_groups/_form_users.html.haml @@ -3,7 +3,7 @@ .row .three.columns.alpha =f.label :owner_id, 'Owner' - .with-tip{'data-powertip' => "The primary user responsible for this group."} + %div{'ofn-with-tip' => "The primary user responsible for this group."} %a What's this? .eight.columns.omega - if spree_current_user.admin? diff --git a/app/views/admin/enterprise_relationships/_enterprise_relationship.html.haml b/app/views/admin/enterprise_relationships/_enterprise_relationship.html.haml index 45e79b1877..996bc487d8 100644 --- a/app/views/admin/enterprise_relationships/_enterprise_relationship.html.haml +++ b/app/views/admin/enterprise_relationships/_enterprise_relationship.html.haml @@ -1,10 +1,10 @@ -%tr{"ng-repeat" => "enterprise_relationship in EnterpriseRelationships.enterprise_relationships | filter:query"} +%tr{"ng-repeat" => "enterprise_relationship in EnterpriseRelationships.enterprise_relationships | keywords:query"} %td {{ enterprise_relationship.parent_name }} %td permits %td {{ enterprise_relationship.child_name }} %td %ul %li{"ng-repeat" => "permission in enterprise_relationship.permissions"} - {{ EnterpriseRelationships.permission_presentation(permission.name) }} + to {{ EnterpriseRelationships.permission_presentation(permission.name) }} %td.actions %a.delete-enterprise-relationship.icon-trash.no-text{'ng-click' => 'delete(enterprise_relationship)'} diff --git a/app/views/admin/enterprise_relationships/_form.html.haml b/app/views/admin/enterprise_relationships/_form.html.haml index 9d031a57c5..433f9f4ca0 100644 --- a/app/views/admin/enterprise_relationships/_form.html.haml +++ b/app/views/admin/enterprise_relationships/_form.html.haml @@ -7,10 +7,13 @@ %td %select{name: "enterprise_relationship_child_id", "ng-model" => "child_id", "ng-options" => "e.id as e.name for e in Enterprises.all_enterprises"} %td + %label + %input{type: "checkbox", ng: {checked: "allPermissionsChecked()", click: "checkAllPermissions()"}} + Everything %div{"ng-repeat" => "permission in EnterpriseRelationships.all_permissions"} %label %input{type: "checkbox", "ng-model" => "permissions[permission]"} - {{ EnterpriseRelationships.permission_presentation(permission) }} + to {{ EnterpriseRelationships.permission_presentation(permission) }} %td.actions %input{type: "button", value: "Create", "ng-click" => "create()"} .errors {{ EnterpriseRelationships.create_errors }} diff --git a/app/views/admin/enterprise_relationships/_search_input.html.haml b/app/views/admin/enterprise_relationships/_search_input.html.haml new file mode 100644 index 0000000000..b8bcbc62c6 --- /dev/null +++ b/app/views/admin/enterprise_relationships/_search_input.html.haml @@ -0,0 +1,5 @@ +%input.search{"ng-model" => "query", "placeholder" => "Search"} + +%label{ng: {repeat: "permission in EnterpriseRelationships.all_permissions"}} + %input{type: "checkbox", ng: {click: "$parent.query = toggleKeyword($parent.query, permission)"}} + {{ EnterpriseRelationships.permission_presentation(permission) }} diff --git a/app/views/admin/enterprise_relationships/index.html.haml b/app/views/admin/enterprise_relationships/index.html.haml index 0807a37825..40fbdbc415 100644 --- a/app/views/admin/enterprise_relationships/index.html.haml +++ b/app/views/admin/enterprise_relationships/index.html.haml @@ -6,7 +6,7 @@ %div{"ng-app" => "ofn.admin", "ng-controller" => "AdminEnterpriseRelationshipsCtrl"} = render 'data' - %input.search{"ng-model" => "query", "placeholder" => "Search"} + = render 'search_input' %table#enterprise-relationships %tbody diff --git a/app/views/admin/enterprises/_actions.html.haml b/app/views/admin/enterprises/_actions.html.haml index ec29607747..5bcfc7a512 100644 --- a/app/views/admin/enterprises/_actions.html.haml +++ b/app/views/admin/enterprises/_actions.html.haml @@ -15,18 +15,18 @@ = link_to_with_icon 'icon-chevron-right', 'Payment Methods', spree.admin_payment_methods_path(enterprise_id: enterprise.id) (#{enterprise.payment_methods.count}) - if enterprise.payment_methods.count == 0 - %span.icon-exclamation-sign.with-tip{"data-powertip" => "This enterprise has no payment methods", style: "font-size: 16px;color: #DA5354"} + %span.icon-exclamation-sign{"ofn-with-tip" => "This enterprise has no payment methods", style: "font-size: 16px;color: #DA5354"} %br/ - if can?(:admin, Spree::ShippingMethod) && can?(:manage_shipping_methods, enterprise) = link_to_with_icon 'icon-plane', 'Shipping Methods', spree.admin_shipping_methods_path(enterprise_id: enterprise.id) (#{enterprise.shipping_methods.count}) - if enterprise.shipping_methods.count == 0 - %span.icon-exclamation-sign.with-tip{"data-powertip" => "This enterprise has shipping methods", style: "font-size: 16px;color: #DA5354"} + %span.icon-exclamation-sign{"ofn-with-tip" => "This enterprise has shipping methods", style: "font-size: 16px;color: #DA5354"} %br/ - if can?(:admin, EnterpriseFee) && can?(:manage_enterprise_fees, enterprise) = link_to_with_icon 'icon-money', 'Enterprise Fees', main_app.admin_enterprise_fees_path(enterprise_id: enterprise.id) (#{enterprise.enterprise_fees.count}) - if enterprise.enterprise_fees.count == 0 - %span.icon-warning-sign.with-tip{"data-powertip" => "This enterprise has no fees", style: "font-size: 16px;color: orange"} + %span.icon-warning-sign{"ofn-with-tip" => "This enterprise has no fees", style: "font-size: 16px;color: orange"} diff --git a/app/views/admin/enterprises/_admin_index.html.haml b/app/views/admin/enterprises/_admin_index.html.haml new file mode 100644 index 0000000000..2359f45f78 --- /dev/null +++ b/app/views/admin/enterprises/_admin_index.html.haml @@ -0,0 +1,44 @@ +-# For purposes of debugging bulk_update. See Admin/Enterprises#bulk_update. +- if flash[:action] + %p= flash[:action] + += form_for @enterprise_set, url: main_app.bulk_update_admin_enterprises_path, html: {"ng-app" => "admin.enterprises"} do |f| + %table#listing_enterprises.index + %colgroup + %col{style: "width: 25%;"}/ + %col{style: "width: 15%;"}/ + %col{style: "width: 5%;"}/ + - if spree_current_user.admin? + %col{style: "width: 12%;"}/ + - if spree_current_user.admin? + %col{style: "width: 18%;"}/ + %col{style: "width: 25%;"}/ + %thead + %tr{"data-hook" => "enterprises_header"} + %th Name + %th Role + - if spree_current_user.admin? + %th Sells + %th Visible? + - if spree_current_user.admin? + %th Owner + %th + %tbody + = f.fields_for :collection do |enterprise_form| + - enterprise = enterprise_form.object + %tr{class: "enterprise-#{enterprise.id}"} + %td= link_to enterprise.name, main_app.edit_admin_enterprise_path(enterprise) + %td + = enterprise_form.check_box :is_primary_producer + Producer + - if spree_current_user.admin? + %td= enterprise_form.select :sells, Enterprise::SELLS, {}, class: 'select2 fullwidth' + %td= enterprise_form.check_box :visible + - if spree_current_user.admin? + %td= enterprise_form.select :owner_id, enterprise.users.map{ |e| [ e.email, e.id ] }, {}, class: "select2 fullwidth" + %td{"data-hook" => "admin_users_index_row_actions"} + = render 'actions', enterprise: enterprise + - if @enterprises.empty? + %tr + %td{colspan: "4"}= t(:none) + = f.submit 'Update' diff --git a/app/views/admin/enterprises/_change_type_form.html.haml b/app/views/admin/enterprises/_change_type_form.html.haml new file mode 100644 index 0000000000..6474af1344 --- /dev/null +++ b/app/views/admin/enterprises/_change_type_form.html.haml @@ -0,0 +1,86 @@ += admin_inject_enterprise += admin_inject_monthly_bill_description + += form_for @enterprise, url: main_app.register_admin_enterprise_path(@enterprise), + html: { name: "change_type", id: "change_type", novalidate: true, "ng-app" => "admin.enterprises", "ng-controller"=> 'changeTypeFormCtrl' } do |change_type_form| + -# Have to use hidden:'true' on this input rather than type:'hidden' as the latter seems to break ngPattern and therefore validation + %input{ hidden: "true", name: "sells", ng: { required: true, pattern: "/^(none|own|any)$/", model: 'sells', value: "sells"} } + + .row + .options.sixteen.columns.alpha + - if @enterprise.is_primary_producer + .basic_producer.option.one-third.column.alpha + %a.full-width.button.selector{ ng: { click: "sells='none'", class: "{selected: sells=='none'}" } } + .top + %h3 Producer Profile + %p Connect through OFN + .bottom ALWAYS FREE + %p.description + Add your products to Open Food Network, allowing hubs to stock your products in their stores. + + .producer_shop.option.one-third.column + %a.full-width.button.selector{ ng: { click: "sells='own'", class: "{selected: sells=='own'}" } } + .top + %h3 Producer Shop + %p Sell your own produce + .bottom + %monthly-pricing-description{ joiner: "newline" } + + %p.description + Sell your products directly to customers through your very own Open Food Network shopfront. + %br + %br + A Producer Shop is for your produce only, if you want to sell produce grown/produced off site, select 'Producer Hub'. + + .full_hub.option.one-third.column.omega + %a.full-width.button.selector{ ng: { click: "sells='any'", class: "{selected: sells=='any'}" } } + .top + %h3 Producer Hub + %p Sell produce from self and others + .bottom + %monthly-pricing-description{ joiner: "newline" } + + %p.description + Your enterprise is the backbone of your local food system. You can sell your own produce as well as produce aggregated from other enterprises through your shopfront on the Open Food Network. + + -# %p.description + -# Test out having your own shopfront with full access to all Shopfront features for 30 days. + -# %br + -# %br + -# At the end of your trial, there is a one-off $200 fee to fully activate your account. Then you will be billed for 2% of your actual transactions, capped at $50 a month (so if you don’t sell anything you don’t pay anything, but you never pay more than $50 a month). + + - else + .shop_profile.option.one-third.column.alpha + %a.full-width.button.selector{ ng: { click: "sells='none'", class: "{selected: sells=='none'}" } } + .top + %h3 Profile Only + %p Get a listing + .bottom ALWAYS FREE + %p.description + People can find and contact you on the Open Food Network. Your enterprise will be visible on the map, and will be searchable in listings. + + .full_hub.option.one-third.column + %a.full-width.button.selector{ ng: { click: "sells='any'", class: "{selected: sells=='any'}" } } + .top + %h3 Hub Shop + %p Sell produce from others + .bottom + %monthly-pricing-description{ joiner: "newline" } + + %p.description + Your enterprise is the backbone of your local food system. You aggregate produce from other enterprises and can sell it through your shop on the Open Food Network. + + + .row + .sixteen.columns.alpha + %span.error{ ng: { show: "(change_type.sells.$error.required || change_type.sells.$error.pattern) && submitted" } } + Please choose one of the options above. + - if @enterprise.sells == 'unspecified' && @enterprise.shop_trial_start_date.nil? + %input.button.big{ type: 'submit', value: 'Start 30 Day Trial', ng: { click: "submit(change_type)", show: "sells=='own' || sells=='any'" } } + %input.button.big{ type: 'submit', value: 'Select and continue', ng: { click: "submit(change_type)", hide: "sells=='own' || sells=='any'" } } + - elsif @enterprise.sells == 'unspecified' + %input.button.big{ type: 'submit', value: 'Select and continue', ng: { click: "submit(change_type)" } } + - else + %input.button.big{ type: 'submit', value: 'Change now', ng: { click: "submit(change_type)" } } + %br   + %hr diff --git a/app/views/admin/enterprises/_enterprise_user_index.html.haml b/app/views/admin/enterprises/_enterprise_user_index.html.haml new file mode 100644 index 0000000000..1fb35e595c --- /dev/null +++ b/app/views/admin/enterprises/_enterprise_user_index.html.haml @@ -0,0 +1,48 @@ +%div{ ng: { app: 'admin.enterprises', controller: 'enterprisesCtrl' } } + .row{ 'ng-hide' => '!loaded' } + .controls{ :class => "sixteen columns alpha", :style => "margin-bottom: 15px;" } + .four.columns.alpha + %input{ :class => "fullwidth", :type => "text", :id => 'quick_search', 'ng-model' => 'quickSearch', :placeholder => 'Search By Name' } + .six.columns   + -# = render 'admin/shared/bulk_actions_dropdown' + .three.columns   + = render 'admin/shared/columns_dropdown' + .row{ 'ng-if' => '!loaded' } + .sixteen.columns.alpha#loading + %img.spinner{ src: "/assets/spinning-circles.svg" } + %h1 LOADING ENTERPRISES + .row{ :class => "sixteen columns alpha", 'ng-show' => 'loaded && filteredEnterprises.length == 0'} + %h1#no_results No enterprises found. + + + .row{ ng: { show: "loaded && filteredEnterprises.length > 0" }, bindonce: true } + %table.index#enterprises + %col.name{ width: "28%", ng: { show: 'columns.name.visible' } } + %col.producer{ width: "18%", ng: { show: 'columns.producer.visible' }} + %col.package{ width: "18%", ng: { show: 'columns.package.visible' }} + %col.status{ width: "18%", ng: { show: 'columns.status.visible' }} + %col.manage{ width: "18%", ng: { show: 'columns.manage.visible' }} + %thead + %tr{ ng: { controller: "ColumnsCtrl" } } + %th.name{ ng: { show: 'columns.name.visible' } } + Name + %th.producer{ ng: { show: 'columns.producer.visible' } } Producer? + %th.package{ ng: { show: 'columns.package.visible' } } Package + %th.status{ ng: { show: 'columns.status.visible' } } Status + %th.manage{ ng: { show: 'columns.manage.visible' } } Manage + %tbody{ :id => "e_{{enterprise.id}}", ng: { repeat: "enterprise in filteredEnterprises = ( allEnterprises | filter:{ name: quickSearch } )", controller: 'EnterpriseIndexRowCtrl' } } + %tr.enterprise.panel-toggle-row{ object: "enterprise", ng: { class: { even: "'even'", odd: "'odd'"} } } + %td.name{ ng: { show: 'columns.name.visible' } } + %span{ bo: { bind: "enterprise.name" } } + %td.producer.panel-toggle.text-center{ ng: { show: 'columns.producer.visible', class: "{error: producerError}" }, name: "producer" } + %h5{ ng: { bind: "producer" } } + %td.package.panel-toggle.text-center{ ng: { show: 'columns.package.visible', class: "{error: packageError}" }, name: "package" } + %h5{ ng: { bind: "package" } } + %td.status.panel-toggle.text-center{ ng: { show: 'columns.status.visible' }, name: "status" } + %i.icon-status{ bo: { class: "status" } } + %td.manage{ ng: { show: 'columns.manage.visible' } } + %a.button.fullwidth{ bo: { href: 'enterprise.edit_path' } } + Manage + %i.icon-arrow-right + + %tr.panel-row{ object: "enterprise", panels: "{producer: 'enterprise_producer', package: 'enterprise_package', status: 'enterprise_status'}" } diff --git a/app/views/admin/enterprises/_new_form.html.haml b/app/views/admin/enterprises/_new_form.html.haml index 7aa35417df..ba8fce56ae 100644 --- a/app/views/admin/enterprises/_new_form.html.haml +++ b/app/views/admin/enterprises/_new_form.html.haml @@ -6,11 +6,11 @@ = f.text_field :name, { placeholder: "eg. Professor Plum's Biodynamic Truffles", class: "fullwidth" } - if spree_current_user.admin? - .row{ ng: { app: "admin.users" } } + .row .three.columns.alpha =f.label :owner_id, 'Owner' %span.required * - .with-tip{'data-powertip' => "The primary user responsible for this enterprise."} + %div{'ofn-with-tip' => "The primary user responsible for this enterprise."} %a What's this? .nine.columns.omega - owner_email = @enterprise.andand.owner.andand.email || "" @@ -18,7 +18,7 @@ .row .three.columns.alpha %label Primary Producer? - .with-tip{'data-powertip' => "Select 'Producer' if you are a primary producer of food."} + %div{'ofn-with-tip' => "Select 'Producer' if you are a primary producer of food."} %a What's this? .five.columns.omega = f.check_box :is_primary_producer, 'ng-model' => 'Enterprise.is_primary_producer' @@ -29,7 +29,7 @@ .alpha.eleven.columns .three.columns.alpha = f.label :sells, 'Sells' - .with-tip{'data-powertip' => "None - enterprise does not sell to customers directly.
Own - Enterprise sells own products to customers.
Any - Enterprise can sell own or other enterprises products.
"} + %div{'ofn-with-tip' => "None - enterprise does not sell to customers directly.
Own - Enterprise sells own products to customers.
Any - Enterprise can sell own or other enterprises products.
"} %a What's this? .two.columns = f.radio_button :sells, "none", 'ng-model' => 'Enterprise.sells' @@ -52,14 +52,9 @@ = f.text_field :contact, { placeholder: "eg. Gustav Plum"} .row .alpha.three.columns - = f.label :email - %span.required * + = f.label :email_address .omega.nine.columns - = f.text_field :email, { placeholder: "eg. gustav@truffles.com", "ng-model" => "Enterprise.email" } - .alert-box - %i.icon-info-sign - If we don't recognise this email address we'll send you a confirmation email to make sure it belongs to you. You'll need to use the link in the email we send to fully activate your new enterprise. - %a.close{ href: "" } × + = f.text_field :email_address, { placeholder: "eg. gustav@truffles.com", "ng-model" => "Enterprise.email_address" } .row .alpha.three.columns = f.label :phone diff --git a/app/views/admin/enterprises/form/_contact.html.haml b/app/views/admin/enterprises/form/_contact.html.haml index df28b6a921..28bbd6014b 100644 --- a/app/views/admin/enterprises/form/_contact.html.haml +++ b/app/views/admin/enterprises/form/_contact.html.haml @@ -1,11 +1,3 @@ --if @enterprise.pending_any_confirmation? - .alert-box - - email = @enterprise.confirmed? ? @enterprise.unconfirmed_email : @enterprise.email - Email confirmation is pending. - We've sent a confirmation email to - %strong= "#{email}." - = link_to('Resend', main_app.enterprise_confirmation_path(enterprise: { id: @enterprise.id, email: email } ), method: :post) - %a.close{ href: "#" } × .row .alpha.three.columns = f.label :contact, 'Name' @@ -13,15 +5,9 @@ = f.text_field :contact, { placeholder: "eg. Gustav Plum"} .row .alpha.three.columns - = f.label :email - %span.required * + = f.label :email_address .omega.eight.columns - = f.text_field :email, { placeholder: "eg. gustav@truffles.com", "ng-model" => "Enterprise.email" } -.row{ ng: { hide: "pristineEmail == null || pristineEmail == Enterprise.email"} } - .alpha.three.columns -   - .omega.eight.columns - Note: A new email address may need to be confirmed prior to use + = f.text_field :email_address, { placeholder: "eg. gustav@truffles.com" } .row .alpha.three.columns = f.label :phone diff --git a/app/views/admin/enterprises/form/_images.html.haml b/app/views/admin/enterprises/form/_images.html.haml index ac960ccc1e..c03be2caa3 100644 --- a/app/views/admin/enterprises/form/_images.html.haml +++ b/app/views/admin/enterprises/form/_images.html.haml @@ -8,7 +8,7 @@ = f.file_field :logo .row .alpha.three.columns - = f.label :promo_image, class: 'with-tip', 'data-powertip' => 'This image is displayed in "About Us"' + = f.label :promo_image, 'ofn-with-tip' => 'This image is displayed in "About Us"' %br/ %span{ style: 'font-weight:bold' } PLEASE NOTE: Any promo image uploaded here will be cropped to 1200 x 260. @@ -16,4 +16,4 @@ .omega.eight.columns = image_tag @object.promo_image(:large) if @object.promo_image.present? - = f.file_field :promo_image \ No newline at end of file + = f.file_field :promo_image diff --git a/app/views/admin/enterprises/form/_primary_details.html.haml b/app/views/admin/enterprises/form/_primary_details.html.haml index 96f4674912..437a61e866 100644 --- a/app/views/admin/enterprises/form/_primary_details.html.haml +++ b/app/views/admin/enterprises/form/_primary_details.html.haml @@ -10,7 +10,7 @@ .alpha.eleven.columns .three.columns.alpha = f.label :group_ids, 'Groups' - .with-tip{'data-powertip' => "Select any groups or regions that you are a member of. This will help customers find your enterprise."} + %div{'ofn-with-tip' => "Select any groups or regions that you are a member of. This will help customers find your enterprise."} %a What's this? .eight.columns.omega = f.collection_select :group_ids, @groups, :id, :name, {}, class: "select2 fullwidth", multiple: true, placeholder: "Start typing to search available groups..." @@ -18,7 +18,7 @@ .row .three.columns.alpha %label Primary Producer - .with-tip{'data-powertip' => "Select 'Producer' if you are a primary producer of food."} + %div{'ofn-with-tip' => "Select 'Producer' if you are a primary producer of food."} %a What's this? .five.columns.omega = f.check_box :is_primary_producer, 'ng-model' => 'Enterprise.is_primary_producer' @@ -29,7 +29,7 @@ .alpha.eleven.columns .three.columns.alpha = f.label :sells, 'Sells' - .with-tip{'data-powertip' => "None - enterprise does not sell to customers directly.
Own - Enterprise sells own products to customers.
Any - Enterprise can sell own or other enterprises products.
"} + %div{'ofn-with-tip' => "None - enterprise does not sell to customers directly.
Own - Enterprise sells own products to customers.
Any - Enterprise can sell own or other enterprises products.
"} %a What's this? .two.columns = f.radio_button :sells, "none", 'ng-model' => 'Enterprise.sells' @@ -46,7 +46,7 @@ .row .three.columns.alpha %label Visible in search? - .with-tip{'data-powertip' => "Determines whether this enterprise will be visible to customers when searching the site."} + %div{'ofn-with-tip' => "Determines whether this enterprise will be visible to customers when searching the site."} %a What's this? .two.columns = f.radio_button :visible, true @@ -60,7 +60,7 @@ .row{ ng: { show: "Enterprise.sells == 'own' || Enterprise.sells == 'any'" } } .three.columns.alpha = f.label :permalink, 'Permalink (no spaces)' - .with-tip{'data-powertip' => "This permalink is used to create the url to your shop: #{spree.root_url}your-shop-name/shop"} + %div{'ofn-with-tip' => "This permalink is used to create the url to your shop: #{spree.root_url}your-shop-name/shop"} %a What's this? .six.columns = f.text_field :permalink, { 'ng-model' => "Enterprise.permalink", placeholder: "eg. your-shop-name", 'ng-model-options' => "{ updateOn: 'default blur', debounce: {'default': 300, 'blur': 0} }" } @@ -72,7 +72,7 @@ .row{ ng: { show: "Enterprise.sells == 'own' || Enterprise.sells == 'any'" } } .three.columns.alpha %label Link to shop front - .with-tip{'data-powertip' => "A direct link to your shopfront on the Open Food Network."} + %div{'ofn-with-tip' => "A direct link to your shopfront on the Open Food Network."} %a What's this? .eight.columns.omega = surround spree.root_url, "/shop" do diff --git a/app/views/admin/enterprises/form/_shop_preferences.html.haml b/app/views/admin/enterprises/form/_shop_preferences.html.haml index f528bb35f0..3085727f85 100644 --- a/app/views/admin/enterprises/form/_shop_preferences.html.haml +++ b/app/views/admin/enterprises/form/_shop_preferences.html.haml @@ -22,3 +22,14 @@ (top to bottom) .eight.columns.omega %textarea.fullwidth{ id: 'enterprise_preferred_shopfront_taxon_order', name: 'enterprise[preferred_shopfront_taxon_order]', rows: 6, 'ng-model' => 'Enterprise.preferred_shopfront_taxon_order', 'ofn-taxon-autocomplete' => '', 'multiple-selection' => 'true', placeholder: 'Category' } + +.row + .alpha.eleven.columns + .three.columns.alpha + = f.label "enterprise_preferred_shopfront_order_cycle_order", t(:sort_order_cycles_on_shopfront_by) + .three.columns + = radio_button :enterprise, :preferred_shopfront_order_cycle_order, :orders_open_at, { 'ng-model' => 'Enterprise.preferred_shopfront_order_cycle_order' } + = label :enterprise, :preferred_shopfront_order_cycle_order_orders_open_at, "Open Date" + .five.columns.omega + = radio_button :enterprise, :preferred_shopfront_order_cycle_order, :orders_close_at, { 'ng-model' => 'Enterprise.preferred_shopfront_order_cycle_order' } + = label :enterprise, :preferred_shopfront_order_cycle_order_orders_close_at, "Close Date" diff --git a/app/views/admin/enterprises/form/_users.html.haml b/app/views/admin/enterprises/form/_users.html.haml index d13723795d..42e25fad0a 100644 --- a/app/views/admin/enterprises/form/_users.html.haml +++ b/app/views/admin/enterprises/form/_users.html.haml @@ -1,12 +1,21 @@ - owner_email = @enterprise.andand.owner.andand.email || "" - full_permissions = (spree_current_user.admin? || spree_current_user == @enterprise.andand.owner) +-if @enterprise.pending_any_confirmation? + .alert-box + - email = @enterprise.confirmed? ? @enterprise.unconfirmed_email : @enterprise.email + Email confirmation is pending. + We've sent a confirmation email to + %strong= "#{email}." + = link_to('Resend', main_app.enterprise_confirmation_path(enterprise: { id: @enterprise.id, email: email } ), method: :post) + %a.close{ href: "#" } × + .row .three.columns.alpha =f.label :owner_id, 'Owner' - if full_permissions %span.required * - .with-tip{'data-powertip' => "The primary user responsible for this enterprise."} + %div{'ofn-with-tip' => "The primary user responsible for this enterprise."} %a What's this? .eight.columns.omega - if full_permissions @@ -14,12 +23,30 @@ - else = owner_email +.row + .three.columns.alpha + = f.label :email, 'Notifications' + - if full_permissions + %span.required * + .with-tip{'data-powertip' => "Notifications about orders will be send to this email address."} + %a What's this? + .eight.columns.omega + - if full_permissions + = f.text_field :email, { placeholder: "eg. gustav@truffles.com", "ng-model" => "Enterprise.email" } + - else + = @enterprise.email +.row{ ng: { hide: "pristineEmail == null || pristineEmail == Enterprise.email"} } + .alpha.three.columns +   + .omega.eight.columns + Note: A new email address may need to be confirmed prior to use + .row .three.columns.alpha =f.label :user_ids, 'Managers' - if full_permissions %span.required * - .with-tip{'data-powertip' => "The other users with permission to manage this enterprise."} + %div{'ofn-with-tip' => "The other users with permission to manage this enterprise."} %a What's this? .eight.columns.omega - if full_permissions diff --git a/app/views/admin/enterprises/index.html.haml b/app/views/admin/enterprises/index.html.haml index 8269cd3699..62bc881728 100644 --- a/app/views/admin/enterprises/index.html.haml +++ b/app/views/admin/enterprises/index.html.haml @@ -2,55 +2,18 @@ Enterprises - content_for :page_actions do + = render 'admin/shared/user_guide_link' + - if spree_current_user.can_own_more_enterprises? %li#new_product_link = button_link_to "New Enterprise", main_app.new_admin_enterprise_path, :icon => 'icon-plus', :id => 'admin_new_enterprise_link' += admin_inject_monthly_bill_description = render 'admin/shared/enterprises_sub_menu' = render :partial => 'spree/shared/error_messages', :locals => { :target => @enterprise_set } --# For purposes of debugging bulk_update. See Admin/Enterprises#bulk_update. -- if flash[:action] - %p= flash[:action] - -= form_for @enterprise_set, url: main_app.bulk_update_admin_enterprises_path do |f| - %table#listing_enterprises.index - %colgroup - %col{style: "width: 25%;"}/ - %col{style: "width: 15%;"}/ - %col{style: "width: 5%;"}/ - - if spree_current_user.admin? - %col{style: "width: 12%;"}/ - - if spree_current_user.admin? - %col{style: "width: 18%;"}/ - %col{style: "width: 25%;"}/ - %thead - %tr{"data-hook" => "enterprises_header"} - %th Name - %th Role - - if spree_current_user.admin? - %th Sells - %th Visible? - - if spree_current_user.admin? - %th Owner - %th - %tbody - = f.fields_for :collection do |enterprise_form| - - enterprise = enterprise_form.object - %tr{class: "enterprise-#{enterprise.id}"} - %td= link_to enterprise.name, main_app.edit_admin_enterprise_path(enterprise) - %td - = enterprise_form.check_box :is_primary_producer - Producer - - if spree_current_user.admin? - %td= enterprise_form.select :sells, Enterprise::SELLS, {}, class: 'select2 fullwidth' - %td= enterprise_form.check_box :visible - - if spree_current_user.admin? - %td= enterprise_form.select :owner_id, enterprise.users.map{ |e| [ e.email, e.id ] }, {}, class: "select2 fullwidth" - %td{"data-hook" => "admin_users_index_row_actions"} - = render 'actions', enterprise: enterprise - - if @enterprises.empty? - %tr - %td{colspan: "4"}= t(:none) - = f.submit 'Update' +- if spree_current_user.admin? + = render 'admin_index' +- else + = render 'enterprise_user_index' diff --git a/app/views/admin/enterprises/new.html.haml b/app/views/admin/enterprises/new.html.haml index 8f21067941..4413acfc2c 100644 --- a/app/views/admin/enterprises/new.html.haml +++ b/app/views/admin/enterprises/new.html.haml @@ -11,5 +11,5 @@ = form_for [main_app, :admin, @enterprise], html: { "nav-check" => '', "nav-callback" => '' } do |f| .row - .twelve.columns.fullwidth_inputs + .twelve.columns.fullwidth_inputs{ ng: { app: "admin.users" } } = render 'new_form', f: f diff --git a/app/views/spree/admin/overview/welcome.html.haml b/app/views/admin/enterprises/welcome.html.haml similarity index 100% rename from app/views/spree/admin/overview/welcome.html.haml rename to app/views/admin/enterprises/welcome.html.haml diff --git a/app/views/admin/order_cycles/_add_exchange_form.html.haml b/app/views/admin/order_cycles/_add_exchange_form.html.haml new file mode 100644 index 0000000000..f9f64ab631 --- /dev/null +++ b/app/views/admin/order_cycles/_add_exchange_form.html.haml @@ -0,0 +1,6 @@ +%select{id: "new_#{type}_id", name: "new_#{type}_id", 'ng-model' => "new_#{type}_id"} + %option{"ng-repeat" => "enterprise in #{type}_enterprises|filter:OrderCycle.novel#{type.capitalize}|orderBy:'name'", "value" => "{{ enterprise.id }}", "ng-disabled" => "enterprise.issues_summary_#{type}"} + {{ enterprise.name }} + = "{{ enterprise.issues_summary_#{type} ? '('+enterprise.issues_summary_#{type}+')' : '' }}" + += f.submit "Add #{type}", 'ng-click' => "add#{type.capitalize}($event)", 'ng-disabled' => "!new_#{type}_id || !OrderCycle.novel#{type.capitalize}(new_#{type}_id)" diff --git a/app/views/admin/order_cycles/_exchange_distributed_products_form.html.haml b/app/views/admin/order_cycles/_exchange_distributed_products_form.html.haml index 1616561a8d..78e64fc2d0 100644 --- a/app/views/admin/order_cycles/_exchange_distributed_products_form.html.haml +++ b/app/views/admin/order_cycles/_exchange_distributed_products_form.html.haml @@ -1,22 +1,23 @@ -%td{:colspan => 3} +%td{:colspan => 4} .exchange-select-all-variants %label = check_box_tag 'order_cycle_outgoing_exchange_{{ $parent.$index }}_select_all_variants', 1, 1, 'ng-model' => 'exchange.select_all_variants', 'ng-change' => 'setExchangeVariants(exchange, incomingExchangeVariantsFor(exchange.enterprise_id), exchange.select_all_variants)', 'id' => 'order_cycle_outgoing_exchange_{{ $parent.$index }}_select_all_variants' Select all - -# Scope product list based on permissions the current user has to view variants in this exchange - .exchange-product{'ng-repeat' => 'product in supplied_products | filter:productSuppliedToOrderCycle | visibleProducts:exchange:order_cycle.visible_variants_for_outgoing_exchanges' } - .exchange-product-details - .supplier {{ product.supplier_name }} - %label - = check_box_tag 'order_cycle_outgoing_exchange_{{ $parent.$index }}_variants_{{ product.master_id }}', 1, 1, 'ng-hide' => 'product.variants.length > 0', 'ng-model' => 'exchange.variants[product.master_id]', 'id' => 'order_cycle_outgoing_exchange_{{ $parent.$index }}_variants_{{ product.master_id }}', - 'ng-disabled' => 'product.variants.length > 0 || !order_cycle.editable_variants_for_outgoing_exchanges.hasOwnProperty(exchange.enterprise_id) || order_cycle.editable_variants_for_outgoing_exchanges[exchange.enterprise_id].indexOf(product.master_id) < 0' - %img{'ng-src' => '{{ product.image_url }}'} - {{ product.name }} + .exchange-products + -# Scope product list based on permissions the current user has to view variants in this exchange + .exchange-product{'ng-repeat' => 'product in supplied_products | filter:productSuppliedToOrderCycle | visibleProducts:exchange:order_cycle.visible_variants_for_outgoing_exchanges | orderBy:"name"' } + .exchange-product-details + %label + = check_box_tag 'order_cycle_outgoing_exchange_{{ $parent.$index }}_variants_{{ product.master_id }}', 1, 1, 'ng-hide' => 'product.variants.length > 0', 'ng-model' => 'exchange.variants[product.master_id]', 'id' => 'order_cycle_outgoing_exchange_{{ $parent.$index }}_variants_{{ product.master_id }}', + 'ng-disabled' => 'product.variants.length > 0 || !order_cycle.editable_variants_for_outgoing_exchanges.hasOwnProperty(exchange.enterprise_id) || order_cycle.editable_variants_for_outgoing_exchanges[exchange.enterprise_id].indexOf(product.master_id) < 0' + %img{'ng-src' => '{{ product.image_url }}'} + .name {{ product.name }} + .supplier {{ product.supplier_name }} - -# if we ever need to filter variants within a product using visibility permissions, we can use this filter: visibleVariants:exchange:order_cycle.visible_variants_for_outgoing_exchanges - .exchange-product-variant{'ng-repeat' => 'variant in product.variants | filter:variantSuppliedToOrderCycle'} - %label - = check_box_tag 'order_cycle_outgoing_exchange_{{ $parent.$parent.$index }}_variants_{{ variant.id }}', 1, 1, 'ng-model' => 'exchange.variants[variant.id]', 'id' => 'order_cycle_outgoing_exchange_{{ $parent.$parent.$index }}_variants_{{ variant.id }}', - 'ng-disabled' => '!order_cycle.editable_variants_for_outgoing_exchanges.hasOwnProperty(exchange.enterprise_id) || order_cycle.editable_variants_for_outgoing_exchanges[exchange.enterprise_id].indexOf(variant.id) < 0' - {{ variant.label }} + -# if we ever need to filter variants within a product using visibility permissions, we can use this filter: visibleVariants:exchange:order_cycle.visible_variants_for_outgoing_exchanges + .exchange-product-variant{'ng-repeat' => 'variant in product.variants | filter:variantSuppliedToOrderCycle'} + %label + = check_box_tag 'order_cycle_outgoing_exchange_{{ $parent.$parent.$index }}_variants_{{ variant.id }}', 1, 1, 'ng-model' => 'exchange.variants[variant.id]', 'id' => 'order_cycle_outgoing_exchange_{{ $parent.$parent.$index }}_variants_{{ variant.id }}', + 'ng-disabled' => '!order_cycle.editable_variants_for_outgoing_exchanges.hasOwnProperty(exchange.enterprise_id) || order_cycle.editable_variants_for_outgoing_exchanges[exchange.enterprise_id].indexOf(variant.id) < 0' + {{ variant.label }} diff --git a/app/views/admin/order_cycles/_exchange_form.html.haml b/app/views/admin/order_cycles/_exchange_form.html.haml index 7f0fb983f9..ed3e43fd6b 100644 --- a/app/views/admin/order_cycles/_exchange_form.html.haml +++ b/app/views/admin/order_cycles/_exchange_form.html.haml @@ -7,6 +7,9 @@ - else {{ (incomingExchangeVariantsFor(exchange.enterprise_id)).length }} selected +- if type == 'supplier' + %td.receival-details + = text_field_tag 'order_cycle_incoming_exchange_{{ $index }}_receival_instructions', '', 'id' => 'order_cycle_incoming_exchange_{{ $index }}_receival_instructions', 'placeholder' => 'Receival instructions', 'ng-model' => 'exchange.receival_instructions' - if type == 'distributor' %td.collection-details = text_field_tag 'order_cycle_outgoing_exchange_{{ $index }}_pickup_time', '', 'id' => 'order_cycle_outgoing_exchange_{{ $index }}_pickup_time', 'placeholder' => 'Ready for (ie. Date / Time)', 'ng-model' => 'exchange.pickup_time', 'ng-disabled' => '!enterprises[exchange.enterprise_id].managed && !order_cycle.viewing_as_coordinator' diff --git a/app/views/admin/order_cycles/_exchange_supplied_products_form.html.haml b/app/views/admin/order_cycles/_exchange_supplied_products_form.html.haml index 869a862a1b..0fc5a7e760 100644 --- a/app/views/admin/order_cycles/_exchange_supplied_products_form.html.haml +++ b/app/views/admin/order_cycles/_exchange_supplied_products_form.html.haml @@ -1,31 +1,32 @@ / TODO: Unify this with exchange_distributed_products_form -%td{:colspan => 3} +%td{:colspan => 4} .exchange-select-all-variants %label = check_box_tag 'order_cycle_incoming_exchange_{{ $parent.$index }}_select_all_variants', 1, 1, 'ng-model' => 'exchange.select_all_variants', 'ng-change' => 'setExchangeVariants(exchange, suppliedVariants(exchange.enterprise_id), exchange.select_all_variants)', 'id' => 'order_cycle_incoming_exchange_{{ $parent.$index }}_select_all_variants' Select all - -# No need to scope product list based on permissions, because if an incoming exchange is visible, - -# then all of the variants within it should be visible. May change in the future? - .exchange-product{'ng-repeat' => 'product in enterprises[exchange.enterprise_id].supplied_products'} + .exchange-products + -# No need to scope product list based on permissions, because if an incoming exchange is visible, + -# then all of the variants within it should be visible. May change in the future? + .exchange-product{'ng-repeat' => 'product in enterprises[exchange.enterprise_id].supplied_products'} - .exchange-product-details - %label - = check_box_tag 'order_cycle_incoming_exchange_{{ $parent.$index }}_variants_{{ product.master_id }}', 1, 1, 'ng-hide' => 'product.variants.length > 0', 'ng-model' => 'exchange.variants[product.master_id]', 'ofn-sync-distributions' => '{{ product.master_id }}', 'id' => 'order_cycle_incoming_exchange_{{ $parent.$index }}_variants_{{ product.master_id }}', - 'ng-disabled' => 'product.variants.length > 0 || !order_cycle.editable_variants_for_incoming_exchanges.hasOwnProperty(exchange.enterprise_id) || order_cycle.editable_variants_for_incoming_exchanges[exchange.enterprise_id].indexOf(product.master_id) < 0' - %img{'ng-src' => '{{ product.image_url }}'} - {{ product.name }} + .exchange-product-details + %label + = check_box_tag 'order_cycle_incoming_exchange_{{ $parent.$index }}_variants_{{ product.master_id }}', 1, 1, 'ng-hide' => 'product.variants.length > 0', 'ng-model' => 'exchange.variants[product.master_id]', 'ofn-sync-distributions' => '{{ product.master_id }}', 'id' => 'order_cycle_incoming_exchange_{{ $parent.$index }}_variants_{{ product.master_id }}', + 'ng-disabled' => 'product.variants.length > 0 || !order_cycle.editable_variants_for_incoming_exchanges.hasOwnProperty(exchange.enterprise_id) || order_cycle.editable_variants_for_incoming_exchanges[exchange.enterprise_id].indexOf(product.master_id) < 0' + %img{'ng-src' => '{{ product.image_url }}'} + {{ product.name }} - -# When the master variant is in the order cycle but the product has variants, we want to - -# be able to remove the master variant, since it serves no purpose. Display a checkbox to do so. - .exchange-product-variant{'ng-show' => 'exchange.variants[product.master_id] && product.variants.length > 0'} - %label - = check_box_tag 'order_cycle_incoming_exchange_{{ $parent.$index }}_variants_{{ product.master_id }}', 1, 1, 'ng-model' => 'exchange.variants[product.master_id]', 'ofn-sync-distributions' => '{{ product.master_id }}', 'id' => 'order_cycle_incoming_exchange_{{ $parent.$index }}_variants_{{ product.master_id }}', - 'ng-disabled' => '!order_cycle.editable_variants_for_incoming_exchanges.hasOwnProperty(exchange.enterprise_id) || order_cycle.editable_variants_for_incoming_exchanges[exchange.enterprise_id].indexOf(product.master_id) < 0' - Obsolete master + -# When the master variant is in the order cycle but the product has variants, we want to + -# be able to remove the master variant, since it serves no purpose. Display a checkbox to do so. + .exchange-product-variant{'ng-show' => 'exchange.variants[product.master_id] && product.variants.length > 0'} + %label + = check_box_tag 'order_cycle_incoming_exchange_{{ $parent.$index }}_variants_{{ product.master_id }}', 1, 1, 'ng-model' => 'exchange.variants[product.master_id]', 'ofn-sync-distributions' => '{{ product.master_id }}', 'id' => 'order_cycle_incoming_exchange_{{ $parent.$index }}_variants_{{ product.master_id }}', + 'ng-disabled' => '!order_cycle.editable_variants_for_incoming_exchanges.hasOwnProperty(exchange.enterprise_id) || order_cycle.editable_variants_for_incoming_exchanges[exchange.enterprise_id].indexOf(product.master_id) < 0' + Obsolete master - .exchange-product-variant{'ng-repeat' => 'variant in product.variants'} - %label - = check_box_tag 'order_cycle_incoming_exchange_{{ $parent.$parent.$index }}_variants_{{ variant.id }}', 1, 1, 'ng-model' => 'exchange.variants[variant.id]', 'ofn-sync-distributions' => '{{ variant.id }}', 'id' => 'order_cycle_incoming_exchange_{{ $parent.$parent.$index }}_variants_{{ variant.id }}', - 'ng-disabled' => '!order_cycle.editable_variants_for_incoming_exchanges.hasOwnProperty(exchange.enterprise_id) || order_cycle.editable_variants_for_incoming_exchanges[exchange.enterprise_id].indexOf(variant.id) < 0' - {{ variant.label }} + .exchange-product-variant{'ng-repeat' => 'variant in product.variants'} + %label + = check_box_tag 'order_cycle_incoming_exchange_{{ $parent.$parent.$index }}_variants_{{ variant.id }}', 1, 1, 'ng-model' => 'exchange.variants[variant.id]', 'ofn-sync-distributions' => '{{ variant.id }}', 'id' => 'order_cycle_incoming_exchange_{{ $parent.$parent.$index }}_variants_{{ variant.id }}', + 'ng-disabled' => '!order_cycle.editable_variants_for_incoming_exchanges.hasOwnProperty(exchange.enterprise_id) || order_cycle.editable_variants_for_incoming_exchanges[exchange.enterprise_id].indexOf(variant.id) < 0' + {{ variant.label }} diff --git a/app/views/admin/order_cycles/_form.html.haml b/app/views/admin/order_cycles/_form.html.haml index a5f7ec1518..c447d35d58 100644 --- a/app/views/admin/order_cycles/_form.html.haml +++ b/app/views/admin/order_cycles/_form.html.haml @@ -8,7 +8,13 @@ %thead %tr %th Supplier - %th Products + %th + Products + = surround '(', ')' do + %a{href: '#', 'ng-click' => "OrderCycle.toggleAllProducts('incoming')"} + %span{'ng-show' => "OrderCycle.showProducts['incoming']"} Collapse all + %span{'ng-hide' => "OrderCycle.showProducts['incoming']"} Expand all + %th Receival details %th Fees %th.actions %tbody{'ng-repeat' => 'exchange in order_cycle.incoming_exchanges'} @@ -18,16 +24,19 @@ = render 'exchange_supplied_products_form' - if Enterprise.managed_by(spree_current_user).include? @order_cycle.coordinator - = select_tag :new_supplier_id, options_for_select(permitted_producer_enterprise_options_for(@order_cycle)), {'ng-model' => 'new_supplier_id'} - - = f.submit 'Add supplier', 'ng-click' => 'addSupplier($event)' + = render 'add_exchange_form', f: f, type: 'supplier' %h2 Outgoing %table.exchanges %thead %tr %th Distributor - %th Products + %th + Products + = surround '(', ')' do + %a{href: '#', 'ng-click' => "OrderCycle.toggleAllProducts('outgoing')"} + %span{'ng-show' => "OrderCycle.showProducts['outgoing']"} Collapse all + %span{'ng-hide' => "OrderCycle.showProducts['outgoing']"} Expand all %th Pickup / Delivery details %th Fees %th.actions @@ -38,15 +47,19 @@ = render 'exchange_distributed_products_form' - if Enterprise.managed_by(spree_current_user).include? @order_cycle.coordinator - = select_tag :new_distributor_id, options_for_select(permitted_hub_enterprise_options_for(@order_cycle)), {'ng-model' => 'new_distributor_id'} - = f.submit 'Add distributor', 'ng-click' => 'addDistributor($event)' + = render 'add_exchange_form', f: f, type: 'distributor' .actions - = f.submit @order_cycle.new_record? ? 'Create' : 'Update', 'ng-disabled' => '!loaded()' + - if @order_cycle.new_record? + = f.submit 'Create', 'ng-click' => "submit('#{main_app.admin_order_cycles_path}')", 'ng-disabled' => '!loaded()' + - else + = f.submit 'Update', 'ng-click' => "submit(null)", 'ng-disabled' => '!loaded()' + = f.submit 'Update and Close', 'ng-click' => "submit('#{main_app.admin_order_cycles_path}')", 'ng-disabled' => '!loaded()' %span{'ng-show' => 'loaded()'} or = link_to 'Cancel', main_app.admin_order_cycles_path %span{'ng-hide' => 'loaded()'} Loading... + = render 'spree/admin/shared/status_message' - unless Rails.env.production? diff --git a/app/views/admin/order_cycles/_row.html.haml b/app/views/admin/order_cycles/_row.html.haml index 2594d5c9f3..d75a97bbb5 100644 --- a/app/views/admin/order_cycles/_row.html.haml +++ b/app/views/admin/order_cycles/_row.html.haml @@ -11,7 +11,7 @@ - suppliers = order_cycle.suppliers.merge(OpenFoodNetwork::OrderCyclePermissions.new(spree_current_user, order_cycle).visible_enterprises) - supplier_list = suppliers.map(&:name).sort.join ', ' - if suppliers.count > 3 - %span.with-tip{'data-powertip' => supplier_list} + %span{'ofn-with-tip' => supplier_list} = suppliers.count suppliers - else @@ -21,7 +21,7 @@ - distributors = order_cycle.distributors.merge(OpenFoodNetwork::OrderCyclePermissions.new(spree_current_user, order_cycle).visible_enterprises) - distributor_list = distributors.map(&:name).sort.join ', ' - if distributors.count > 3 - %span.with-tip{'data-powertip' => distributor_list} + %span{'ofn-with-tip' => distributor_list} = distributors.count distributors - else diff --git a/app/views/admin/order_cycles/_simple_form.html.haml b/app/views/admin/order_cycles/_simple_form.html.haml index 9bffb9753e..4dc64a012b 100644 --- a/app/views/admin/order_cycles/_simple_form.html.haml +++ b/app/views/admin/order_cycles/_simple_form.html.haml @@ -21,9 +21,14 @@ = render 'coordinator_fees', f: f .actions - = f.submit @order_cycle.new_record? ? 'Create' : 'Update', 'ng-disabled' => '!loaded()' + - if @order_cycle.new_record? + = f.submit 'Create', 'ng-click' => "submit('#{main_app.admin_order_cycles_path}')", 'ng-disabled' => '!loaded()' + - else + = f.submit 'Update', 'ng-click' => "submit(null)", 'ng-disabled' => '!loaded()' + = f.submit 'Update and Close', 'ng-click' => "submit('#{main_app.admin_order_cycles_path}')", 'ng-disabled' => '!loaded()' + %span{'ng-show' => 'loaded()'} or = link_to 'Cancel', main_app.admin_order_cycles_path %span{'ng-hide' => 'loaded()'} Loading... - + = render 'spree/admin/shared/status_message' diff --git a/app/views/admin/order_cycles/edit.html.haml b/app/views/admin/order_cycles/edit.html.haml index 2f17ecf85b..12dc6238ae 100644 --- a/app/views/admin/order_cycles/edit.html.haml +++ b/app/views/admin/order_cycles/edit.html.haml @@ -1,8 +1,14 @@ +- if can? :notify_producers, @order_cycle + = content_for :page_actions do + %li + = button_to "Notify producers", main_app.notify_producers_admin_order_cycle_path, :id => 'admin_notify_producers', :confirm => 'Are you sure?' + + %h1 Edit Order Cycle -- ng_controller = order_cycles_simple_form ? 'AdminSimpleEditOrderCycleCtrl' : 'AdminEditOrderCycleCtrl' -= form_for [main_app, :admin, @order_cycle], :url => '', :html => {:class => 'ng order_cycle', 'ng-app' => 'admin.order_cycles', 'ng-controller' => ng_controller, 'ng-submit' => 'submit($event)'} do |f| +- ng_controller = order_cycles_simple_form ? 'AdminSimpleEditOrderCycleCtrl' : 'AdminEditOrderCycleCtrl' += form_for [main_app, :admin, @order_cycle], :url => '', :html => {:class => 'ng order_cycle', 'ng-app' => 'admin.orderCycles', 'ng-controller' => ng_controller} do |f| - if order_cycles_simple_form = render 'simple_form', f: f - else diff --git a/app/views/admin/order_cycles/index.html.haml b/app/views/admin/order_cycles/index.html.haml index e39fdde12a..8f062ff7db 100644 --- a/app/views/admin/order_cycles/index.html.haml +++ b/app/views/admin/order_cycles/index.html.haml @@ -11,7 +11,7 @@ %li = button_link_to "Show more", main_app.admin_order_cycles_path(params: { show_more: true }) -= form_for @order_cycle_set, :url => main_app.bulk_update_admin_order_cycles_path do |f| += form_for @order_cycle_set, url: main_app.bulk_update_admin_order_cycles_path, html: {"ng-app" => "admin.orderCycles"} do |f| %table.index#listing_order_cycles %colgroup %col diff --git a/app/views/admin/order_cycles/new.html.haml b/app/views/admin/order_cycles/new.html.haml index 770eac0269..460f2e08ca 100644 --- a/app/views/admin/order_cycles/new.html.haml +++ b/app/views/admin/order_cycles/new.html.haml @@ -3,7 +3,7 @@ - ng_controller = order_cycles_simple_form ? 'AdminSimpleCreateOrderCycleCtrl' : 'AdminCreateOrderCycleCtrl' = admin_inject_order_cycle_instance -= form_for [main_app, :admin, @order_cycle], :url => '', :html => {:class => 'ng order_cycle', 'ng-app' => 'admin.order_cycles', 'ng-controller' => ng_controller, 'ng-submit' => 'submit($event)'} do |f| += form_for [main_app, :admin, @order_cycle], :url => '', :html => {:class => 'ng order_cycle', 'ng-app' => 'admin.orderCycles', 'ng-controller' => ng_controller} do |f| - if order_cycles_simple_form = render 'simple_form', f: f - else diff --git a/app/views/admin/shared/_bulk_actions_dropdown.html.haml b/app/views/admin/shared/_bulk_actions_dropdown.html.haml new file mode 100644 index 0000000000..912fe6662a --- /dev/null +++ b/app/views/admin/shared/_bulk_actions_dropdown.html.haml @@ -0,0 +1,7 @@ +.three.columns + .ofn-drop-down#bulk-actions-dropdown{ 'ng-controller' => "DropDownCtrl" } + %span.icon-check   Actions + %span{ 'ng-class' => "expanded && 'icon-caret-up' || !expanded && 'icon-caret-down'" } + %div.menu{ 'ng-show' => "expanded" } + .three.columns.alpha.menu_item{ 'ng-repeat' => "action in bulkActions", 'ng-click' => "$eval(action.callback)(filteredLineItems)", 'ofn-close-on-click' => true } + %span.three.columns.omega {{action.name }} diff --git a/app/views/admin/shared/_columns_dropdown.html.haml b/app/views/admin/shared/_columns_dropdown.html.haml new file mode 100644 index 0000000000..b16d388c1d --- /dev/null +++ b/app/views/admin/shared/_columns_dropdown.html.haml @@ -0,0 +1,8 @@ +%div.three.columns.omega + %div.ofn-drop-down.right#columns-dropdown{ 'ng-controller' => "DropDownCtrl" } + %span{ :class => 'icon-reorder' }   Columns + %span{ 'ng-class' => "expanded && 'icon-caret-up' || !expanded && 'icon-caret-down'" } + %div.menu{ 'ng-show' => "expanded" } + %div.menu_item.three.columns.alpha.omega{ 'ng-repeat' => "column in columns", 'ofn-toggle-column' => true } + %span.one.column.alpha.text-center {{ column.visible && "✓" || !column.visible && " " }} + %span.two.columns.omega {{column.name }} diff --git a/app/views/admin/shared/_enterprises_sub_menu.html.haml b/app/views/admin/shared/_enterprises_sub_menu.html.haml index 31366091ef..3088b7e73e 100644 --- a/app/views/admin/shared/_enterprises_sub_menu.html.haml +++ b/app/views/admin/shared/_enterprises_sub_menu.html.haml @@ -1,4 +1,4 @@ = content_for :sub_menu do %ul#sub_nav.inline-menu{"data-hook" => "admin_enterprise_sub_tabs"} = tab :enterprises, url: main_app.admin_enterprises_path - = tab :relationships, url: main_app.admin_enterprise_relationships_path, match_path: '/enterprise_relationships' + = tab :enterprise_relationships, url: main_app.admin_enterprise_relationships_path diff --git a/app/views/admin/shared/_user_guide_link.html.haml b/app/views/admin/shared/_user_guide_link.html.haml new file mode 100644 index 0000000000..bfae6002e7 --- /dev/null +++ b/app/views/admin/shared/_user_guide_link.html.haml @@ -0,0 +1 @@ += button_link_to "User Guide", "http://www.openfoodnetwork.org/platform/user-guide/", :icon => 'icon-external-link', :id => 'user_guide_link', target: '_blank' diff --git a/app/views/admin/variant_overrides/_actions.html.haml b/app/views/admin/variant_overrides/_actions.html.haml deleted file mode 100644 index 0ae6f8b96b..0000000000 --- a/app/views/admin/variant_overrides/_actions.html.haml +++ /dev/null @@ -1,4 +0,0 @@ -.row - %input.four.columns.alpha{type: 'button', value: 'Save Changes', 'ng-click' => 'update()'} - .twelve.columns.omega - = render 'spree/admin/shared/status_message' diff --git a/app/views/admin/variant_overrides/_data.html.haml b/app/views/admin/variant_overrides/_data.html.haml index 3b5f7f125c..64a7619ea7 100644 --- a/app/views/admin/variant_overrides/_data.html.haml +++ b/app/views/admin/variant_overrides/_data.html.haml @@ -1,5 +1,5 @@ = admin_inject_spree_api_key -= admin_inject_hubs += admin_inject_hubs module: 'admin.variantOverrides' = admin_inject_hub_permissions -= admin_inject_producers += admin_inject_producers module: 'admin.variantOverrides' = admin_inject_variant_overrides diff --git a/app/views/admin/variant_overrides/_filters.html.haml b/app/views/admin/variant_overrides/_filters.html.haml new file mode 100644 index 0000000000..d5f8bb9714 --- /dev/null +++ b/app/views/admin/variant_overrides/_filters.html.haml @@ -0,0 +1,26 @@ +.filters.sixteen.columns.alpha + .filter.four.columns.alpha + %label{ :for => 'query', ng: {class: '{disabled: !hub.id}'} }Quick Search + %br + %input.fullwidth{ :type => "text", :id => 'query', ng: { model: 'query', disabled: '!hub.id'} } + .two.columns   + .filter_select.four.columns + %label{ :for => 'hub_id', ng: { bind: 'hub_id ? "Shop" : "Select a shop"' } } + %br + %select.select2.fullwidth#hub_id{ 'ng-model' => 'hub_id', name: 'hub_id', ng: { options: 'hub.id as hub.name for (id, hub) in hubs', change: 'selectHub()' } } + .filter_select.four.columns + %label{ :for => 'producer_filter', ng: {class: '{disabled: !hub.id}'} }Producer + %br + %input.ofn-select2.fullwidth{ :id => 'producer_filter', type: 'number', data: 'producers', blank: "{id: 0, name: 'All'}", ng: { model: 'producerFilter', disabled: '!hub.id' } } + -# .filter_select{ :class => "three columns" } + -# %label{ :for => 'distributor_filter' }Hub + -# %br + -# %select{ :class => "three columns alpha", :id => 'distributor_filter', 'select2-min-search' => 5, 'ng-model' => 'distributorFilter', 'ng-options' => 'd.id as d.name for d in distributors'} + -# .filter_select{ :class => "three columns" } + -# %label{ :for => 'order_cycle_filter' }Order Cycle + -# %br + -# %select{ :class => "three columns alpha", :id => 'order_cycle_filter', 'select2-min-search' => 5, 'ng-model' => 'orderCycleFilter', 'ng-options' => 'oc.id as oc.name for oc in orderCycles', 'confirm-change' => "confirmRefresh()", 'ng-change' => 'refreshData()'} + .filter_clear.two.columns.omega + %label{ :for => 'clear_all_filters' } + %br + %input.red.fullwidth{ :type => 'button', :id => 'clear_all_filters', :value => "Clear All", ng: { click: "resetSelectFilters()", disabled: '!hub.id'} } diff --git a/app/views/admin/variant_overrides/_hub_choice.html.haml b/app/views/admin/variant_overrides/_hub_choice.html.haml deleted file mode 100644 index aa0f7ab738..0000000000 --- a/app/views/admin/variant_overrides/_hub_choice.html.haml +++ /dev/null @@ -1,7 +0,0 @@ -.row - .two.columns.alpha - Hub - .four.columns - %select.select2.fullwidth#hub_id{ 'ng-model' => 'hub_id', name: 'hub_id', 'ng-options' => 'hub.id as hub.name for hub in hubs' } - .ten.columns.omega - %input{ type: 'button', value: 'Go', 'ng-click' => 'selectHub()' } diff --git a/app/views/admin/variant_overrides/_products.html.haml b/app/views/admin/variant_overrides/_products.html.haml index cf11e8ac5d..ed3354de9a 100644 --- a/app/views/admin/variant_overrides/_products.html.haml +++ b/app/views/admin/variant_overrides/_products.html.haml @@ -1,10 +1,23 @@ -%table.index.bulk{ng: {show: 'hub'}} +%table.index.bulk{ ng: {show: 'hub'}} + %col.producer{ width: "20%", ng: { show: 'columns.producer.visible' } } + %col.product{ width: "20%", ng: { show: 'columns.product.visible' } } + %col.sku{ width: "20%", ng: { show: 'columns.sku.visible' } } + %col.price{ width: "10%", ng: { show: 'columns.price.visible' } } + %col.on_hand{ width: "10%", ng: { show: 'columns.on_hand.visible' } } + %col.on_demand{ width: "10%", ng: { show: 'columns.on_demand.visible' } } + %col.reset{ width: "1%", ng: { show: 'columns.reset.visible' } } + %col.reset{ width: "15%", ng: { show: 'columns.reset.visible' } } + %col.inheritance{ width: "5%", ng: { show: 'columns.inheritance.visible' } } %thead - %tr - %th Producer - %th Product - %th Price - %th On hand - %tbody{ng: {repeat: 'product in products | hubPermissions:hubPermissions:hub.id'}} + %tr{ ng: { controller: "ColumnsCtrl" } } + %th.producer{ ng: { show: 'columns.producer.visible' } } Producer + %th.product{ ng: { show: 'columns.product.visible' } } Product + %th.sku{ ng: { show: 'columns.sku.visible' } } SKU + %th.price{ ng: { show: 'columns.price.visible' } } Price + %th.on_hand{ ng: { show: 'columns.on_hand.visible' } } On hand + %th.on_demand{ ng: { show: 'columns.on_demand.visible' } } On Demand? + %th.reset{ colspan: 2, ng: { show: 'columns.reset.visible' } } Enable Stock Level Reset? + %th.inheritance{ ng: { show: 'columns.inheritance.visible' } } Inherit? + %tbody{bindonce: true, ng: {repeat: 'product in products | hubPermissions:hubPermissions:hub.id | attrFilter:{producer_id:producerFilter} | filter:query' } } = render 'admin/variant_overrides/products_product' = render 'admin/variant_overrides/products_variants' diff --git a/app/views/admin/variant_overrides/_products_product.html.haml b/app/views/admin/variant_overrides/_products_product.html.haml index 52551e8e0b..b7cb11041b 100644 --- a/app/views/admin/variant_overrides/_products_product.html.haml +++ b/app/views/admin/variant_overrides/_products_product.html.haml @@ -1,5 +1,9 @@ %tr.product.even - %td {{ producers[product.producer_id].name }} - %td {{ product.name }} - %td - %td + %td.producer{ ng: { show: 'columns.producer.visible' }, bo: { bind: 'producersByID[product.producer_id].name'} } + %td.product{ ng: { show: 'columns.product.visible' }, bo: { bind: 'product.name'} } + %td.sku{ ng: { show: 'columns.sku.visible' } } + %td.price{ ng: { show: 'columns.price.visible' } } + %td.on_hand{ ng: { show: 'columns.on_hand.visible' } } + %td.on_demand{ ng: { show: 'columns.on_demand.visible' } } + %td.reset{ colspan: 2, ng: { show: 'columns.reset.visible' } } + %td.inheritance{ ng: { show: 'columns.inheritance.visible' } } diff --git a/app/views/admin/variant_overrides/_products_variants.html.haml b/app/views/admin/variant_overrides/_products_variants.html.haml index bd48ea343c..87ec1709e4 100644 --- a/app/views/admin/variant_overrides/_products_variants.html.haml +++ b/app/views/admin/variant_overrides/_products_variants.html.haml @@ -1,10 +1,19 @@ -%tr.variant{ng: {repeat: 'variant in product.variants'}} - %td - %td - {{ variant.display_name }} - .variant-override-unit {{ variant.unit_to_display }} - %td +%tr.variant{ id: "v_{{variant.id}}", ng: {repeat: 'variant in product.variants'}} + %td.producer{ ng: { show: 'columns.producer.visible' } } + %td.product{ ng: { show: 'columns.product.visible' } } + %span{ bo: { bind: 'variant.display_name || ""'} } + .variant-override-unit{ bo: { bind: 'variant.unit_to_display'} } + %td.sku{ ng: { show: 'columns.sku.visible' } } + %input{name: 'variant-overrides-{{ variant.id }}-sku', type: 'text', ng: {model: 'variantOverrides[hub.id][variant.id].sku'}, placeholder: '{{ variant.sku }}', 'ofn-track-variant-override' => 'sku'} + %td.price{ ng: { show: 'columns.price.visible' } } %input{name: 'variant-overrides-{{ variant.id }}-price', type: 'text', ng: {model: 'variantOverrides[hub.id][variant.id].price'}, placeholder: '{{ variant.price }}', 'ofn-track-variant-override' => 'price'} - - %td - %input{name: 'variant-overrides-{{ variant.id }}-count-on-hand', type: 'text', ng: {model: 'variantOverrides[hub.id][variant.id].count_on_hand'}, placeholder: '{{ variant.on_hand }}', 'ofn-track-variant-override' => 'price'} + %td.on_hand{ ng: { show: 'columns.on_hand.visible' } } + %input{name: 'variant-overrides-{{ variant.id }}-count_on_hand', type: 'text', ng: {model: 'variantOverrides[hub.id][variant.id].count_on_hand'}, placeholder: '{{ variant.on_hand }}', 'ofn-track-variant-override' => 'count_on_hand'} + %td.on_demand{ ng: { show: 'columns.on_demand.visible' } } + %input.field{ :type => 'checkbox', name: 'variant-overrides-{{ variant.id }}-on_demand', ng: { model: 'variantOverrides[hub.id][variant.id].on_demand' }, 'ofn-track-variant-override' => 'on_demand' } + %td.reset{ ng: { show: 'columns.reset.visible' } } + %input{name: 'variant-overrides-{{ variant.id }}-resettable', type: 'checkbox', ng: {model: 'variantOverrides[hub.id][variant.id].resettable'}, placeholder: '{{ variant.resettable }}', 'ofn-track-variant-override' => 'resettable'} + %td.reset{ ng: { show: 'columns.reset.visible' } } + %input{name: 'variant-overrides-{{ variant.id }}-default_stock', type: 'text', ng: {model: 'variantOverrides[hub.id][variant.id].default_stock'}, placeholder: '{{ variant.default_stock ? variant.default_stock : "Default stock"}}', 'ofn-track-variant-override' => 'default_stock'} + %td.inheritance{ ng: { show: 'columns.inheritance.visible' } } + %input.field{ :type => 'checkbox', name: 'variant-overrides-{{ variant.id }}-inherit', ng: { model: 'inherit' }, 'track-inheritance' => true } diff --git a/app/views/admin/variant_overrides/index.html.haml b/app/views/admin/variant_overrides/index.html.haml index 8d7fc4e0b1..9027445e62 100644 --- a/app/views/admin/variant_overrides/index.html.haml +++ b/app/views/admin/variant_overrides/index.html.haml @@ -1,11 +1,14 @@ = render 'admin/variant_overrides/header' = render 'admin/variant_overrides/data' -%div{ ng: { app: 'ofn.admin', controller: 'AdminVariantOverridesCtrl', init: 'initialise()' } } - = render 'admin/variant_overrides/hub_choice' +%div{ ng: { app: 'admin.variantOverrides', controller: 'AdminVariantOverridesCtrl', init: 'initialise()' } } + = render 'admin/variant_overrides/filters' + %hr.divider.sixteen.columns.alpha.omega{ ng: { show: 'hub' } } + .controls.sixteen.columns.alpha.omega{ ng: { show: 'hub' } } + %input.four.columns.alpha{ type: 'button', value: 'Reset Stock to Defaults', 'ng-click' => 'resetStock()' } + %div.nine.columns.alpha   + = render 'admin/shared/columns_dropdown' - %div{ng: {show: 'hub'}} - %h2 {{ hub.name }} - = render 'admin/variant_overrides/actions' - - = render 'admin/variant_overrides/products' + %form{ name: 'variant_overrides_form' } + %save-bar{ save: "update()", form: "variant_overrides_form" } + = render 'admin/variant_overrides/products' diff --git a/app/views/checkout/_accordion_heading.html.haml b/app/views/checkout/_accordion_heading.html.haml index c7872ce109..443897b2b5 100644 --- a/app/views/checkout/_accordion_heading.html.haml +++ b/app/views/checkout/_accordion_heading.html.haml @@ -7,9 +7,11 @@ .small-4.medium-3.columns.text-right %span.accordion-up %em - %small Hide + %small + = t :checkout_hide %i.ofn-i_053-point-up %span.accordion-down %em - %small Expand + %small + = t :checkout_expand %i.ofn-i_052-point-down diff --git a/app/views/checkout/_authentication.html.haml b/app/views/checkout/_authentication.html.haml index 3326ddb0fe..2120ba6549 100644 --- a/app/views/checkout/_authentication.html.haml +++ b/app/views/checkout/_authentication.html.haml @@ -1,11 +1,14 @@ %section{"ng-show" => "!enabled"} .row .small-12.columns.text-center{"ng-controller" => "AuthenticationCtrl"} - %h3.pad-top Ok, ready to checkout? + %h3.pad-top + = t :checkout_headline .row.pad-top .small-5.columns.text-center{"ng-controller" => "AuthenticationCtrl"} - %button.primary.expand{"ng-click" => "open()"} Log in + %button.primary.expand{"ng-click" => "open()"} + = t :label_login .small-2.columns.text-center - %p.pad-top -OR- + %p.pad-top= "-#{t :action_or}-" .small-5.columns.text-center - %button.neutral-btn.dark.expand{"ng-click" => "enabled = true"} Checkout as guest + %button.neutral-btn.dark.expand{"ng-click" => "enabled = true"} + = t :checkout_as_guest diff --git a/app/views/checkout/_billing.html.haml b/app/views/checkout/_billing.html.haml index 854f359b0c..7ea4d57da1 100644 --- a/app/views/checkout/_billing.html.haml +++ b/app/views/checkout/_billing.html.haml @@ -7,7 +7,7 @@ %i.ofn-i_009-close %label.label.round.success.right %i.ofn-i_051-check-big - Billing info + = t :checkout_billing %accordion-group{"is-open" => "accordion.billing", "ng-class" => "{valid: billing.$valid, open: accordion.billing}"} @@ -16,23 +16,24 @@ = f.fields_for :bill_address, @order.bill_address do |ba| .row .small-12.columns - = validated_input "Address", "order.bill_address.address1", "ofn-focus" => "accordion['billing']" + = validated_input t(:address), "order.bill_address.address1", "ofn-focus" => "accordion['billing']" .row .small-12.columns - = validated_input "Address (contd.)", "order.bill_address.address2", required: false + = validated_input t(:address2), "order.bill_address.address2", required: false .row .small-6.columns - = validated_input "City", "order.bill_address.city" + = validated_input t(:city), "order.bill_address.city" .small-6.columns - = validated_select "State", "order.bill_address.state_id", checkout_state_options(:billing) + = validated_select t(:state), "order.bill_address.state_id", checkout_state_options(:billing) .row .small-6.columns - = validated_input "Postcode", "order.bill_address.zipcode" + = validated_input t(:postcode), "order.bill_address.zipcode" .small-6.columns.right - = validated_select "Country", "order.bill_address.country_id", checkout_country_options + = validated_select t(:country), "order.bill_address.country_id", checkout_country_options .row .small-12.columns.text-right - %button.primary{"ng-disabled" => "billing.$invalid", "ng-click" => "next($event)"} Next + %button.primary{"ng-disabled" => "billing.$invalid", "ng-click" => "next($event)"} + = t :next diff --git a/app/views/checkout/_details.html.haml b/app/views/checkout/_details.html.haml index 646a3312e0..788996be37 100644 --- a/app/views/checkout/_details.html.haml +++ b/app/views/checkout/_details.html.haml @@ -7,7 +7,7 @@ %i.ofn-i_009-close %label.label.round.success.right %i.ofn-i_051-check-big - Your details + = t :checkout_details %accordion-group{"is-open" => "accordion.details", "ng-class" => "{valid: details.$valid, open: accordion.details}"} @@ -15,17 +15,18 @@ .row .small-6.columns - = validated_input "First Name", "order.bill_address.firstname" + = validated_input t(:first_name), "order.bill_address.firstname" .small-6.columns - = validated_input "Last Name", "order.bill_address.lastname" + = validated_input t(:last_name), "order.bill_address.lastname" .row .small-6.columns - = validated_input 'Email', 'order.email', type: :email, "ofn-focus" => "accordion['details']" + = validated_input t(:email), 'order.email', type: :email, "ofn-focus" => "accordion['details']" .small-6.columns - = validated_input 'Phone', 'order.bill_address.phone' + = validated_input t(:phone), 'order.bill_address.phone' .row .small-12.columns.text-right - %button.primary{"ng-disabled" => "details.$invalid", "ng-click" => "next($event)"} Next + %button.primary{"ng-disabled" => "details.$invalid", "ng-click" => "next($event)"} + = t :next diff --git a/app/views/checkout/_form.html.haml b/app/views/checkout/_form.html.haml index 1944dfffe1..65ed90b069 100644 --- a/app/views/checkout/_form.html.haml +++ b/app/views/checkout/_form.html.haml @@ -13,5 +13,5 @@ = render "checkout/payment", f: f %p %button.button.primary{type: :submit} - Place order now + = t :checkout_send / {{ checkout.$valid }} diff --git a/app/views/checkout/_payment.html.haml b/app/views/checkout/_payment.html.haml index cd3091efcd..c11d7cd45e 100644 --- a/app/views/checkout/_payment.html.haml +++ b/app/views/checkout/_payment.html.haml @@ -7,7 +7,7 @@ %i.ofn-i_009-close %label.label.round.success.right %i.ofn-i_051-check-big - Payment + = t :checkout_payment %accordion-group{"is-open" => "accordion.payment", "ng-class" => "{valid: payment.$valid, open: accordion.payment}"} diff --git a/app/views/checkout/_shipping.html.haml b/app/views/checkout/_shipping.html.haml index e769376107..8bdc855b7a 100644 --- a/app/views/checkout/_shipping.html.haml +++ b/app/views/checkout/_shipping.html.haml @@ -7,7 +7,7 @@ %i.ofn-i_009-close %label.label.round.success.right %i.ofn-i_051-check-big - Shipping info + = t :checkout_shipping %accordion-group{"is-open" => "accordion.shipping", "ng-class" => "{valid: shipping.$valid, open: accordion.shipping}"} @@ -22,7 +22,7 @@ "ng-model" => "order.shipping_method_id"} {{ method.name }} %em.light{"ng-show" => "!method.price || method.price == 0"} - (Free) + = "(#{t(:checkout_method_free)})" %em.light{"ng-hide" => "!method.price || method.price == 0"} ({{ method.price | localizeCurrency }}) @@ -31,14 +31,15 @@ %label{"ng-if" => "Checkout.requireShipAddress()"} %input{type: :checkbox, "ng-model" => "Checkout.ship_address_same_as_billing"} - Shipping address same as billing address? + = t :checkout_address_same .small-12.columns.medium-6.columns.large-6.columns #distributor_address.panel{"ng-show" => "Checkout.shippingMethod().description"} %span{ style: "white-space: pre-wrap;" }{{ Checkout.shippingMethod().description }} %br/ %br/ - = 'Ready for:' if @order.order_cycle.pickup_time_for(@order.distributor) + - if @order.order_cycle.pickup_time_for(@order.distributor) + = t :checkout_ready_for = @order.order_cycle.pickup_time_for(@order.distributor) = f.fields_for :ship_address, @order.ship_address do |sa| @@ -46,8 +47,9 @@ .row .small-12.columns - = f.text_area :special_instructions, label: "Any comments or special instructions?", size: "60x4", "ng-model" => "order.special_instructions" + = f.text_area :special_instructions, label: t(:checkout_instructions), size: "60x4", "ng-model" => "order.special_instructions" .row .small-12.columns.text-right - %button.primary{"ng-disabled" => "shipping.$invalid", "ng-click" => "next($event)"} Next + %button.primary{"ng-disabled" => "shipping.$invalid", "ng-click" => "next($event)"} + = t :next diff --git a/app/views/checkout/_summary.html.haml b/app/views/checkout/_summary.html.haml index 1d24d1dcf2..3a4f6a4ea1 100644 --- a/app/views/checkout/_summary.html.haml +++ b/app/views/checkout/_summary.html.haml @@ -1,10 +1,12 @@ %orderdetails = form_for current_order, url: "#", html: {"ng-submit" => "purchase($event, checkout)"} do |f| %fieldset - %legend Your order + %legend + = t :checkout_your_order %table %tr - %th Cart total + %th + = t :checkout_cart_total %td.cart-total.text-right= display_checkout_subtotal(@order) - checkout_adjustments_for(current_order, exclude: [:shipping, :line_item]).reject{ |a| a.amount == 0 }.each do |adjustment| @@ -13,15 +15,16 @@ %td.text-right= adjustment.display_amount.to_html %tr - %th Shipping + %th + = t :checkout_shipping_price %td.shipping.text-right {{ Checkout.shippingPrice() | localizeCurrency }} %tr - %th Total + %th + = t :checkout_total_price %td.total.text-right {{ Checkout.cartTotal() | localizeCurrency }} //= f.submit "Purchase", class: "button", "ofn-focus" => "accordion['payment']" %a.button.secondary{href: cart_url} %i.ofn-i_008-caret-left - Back to Cart - + = t :checkout_back_to_cart diff --git a/app/views/checkout/edit.html.haml b/app/views/checkout/edit.html.haml index 2f30c93ba5..f0bef536db 100644 --- a/app/views/checkout/edit.html.haml +++ b/app/views/checkout/edit.html.haml @@ -1,11 +1,15 @@ +- content_for(:title) do + = t :checkout_title + = inject_enterprises -.darkswarm +.darkswarm.footer-pad - content_for :order_cycle_form do - %closing Checkout now + %closing + = t :checkout_now %p - Order ready for + = t :checkout_order_ready %strong = pickup_time current_order_cycle @@ -23,4 +27,3 @@ = render partial: "shared/footer" - diff --git a/app/views/enterprise_mailer/confirmation_instructions.html.haml b/app/views/enterprise_mailer/confirmation_instructions.html.haml index e957b70b1c..c1b288f6ef 100644 --- a/app/views/enterprise_mailer/confirmation_instructions.html.haml +++ b/app/views/enterprise_mailer/confirmation_instructions.html.haml @@ -1,20 +1,18 @@ %h3 - = "Hi, #{@resource.contact}!" + = t :email_confirmation_greeting, contact: @enterprise.contact %p.lead - = "Please confirm your email address for " - %strong - = "#{@resource.name}." + = t :email_confirmation_profile_created, name: @enterprise.name %p   %p.callout - Click the link below to confirm your email and to activate your enterprise. This link can be used only once: + = t :email_confirmation_click_link %br %strong - = link_to 'Confirm this email address »', confirmation_url(@resource, :confirmation_token => @resource.confirmation_token) + = link_to t(:email_confirmation_link_label), confirmation_url(@enterprise, :confirmation_token => @enterprise.confirmation_token) %p   %p - = "We're so excited that you're joining the #{ Spree::Config[:site_name] }! Don't hestitate to get in touch if you have any questions." + = t :email_confirmation_help_html, link: link_to(t(:email_confirmation_userguide), 'http://www.openfoodnetwork.org/platform/user-guide/'), sitename: Spree::Config[:site_name] = render 'shared/mailers/signoff' diff --git a/app/views/enterprise_mailer/welcome.html.haml b/app/views/enterprise_mailer/welcome.html.haml index 5c69caae77..97b350de45 100644 --- a/app/views/enterprise_mailer/welcome.html.haml +++ b/app/views/enterprise_mailer/welcome.html.haml @@ -1,67 +1,23 @@ %h3 - = "Welcome, #{@enterprise.contact}!" + = "#{t(:email_welcome)}, #{@enterprise.contact}!" %p.lead - Congratulations, + = t :email_confirmed %strong - %strong= @enterprise.name - = "is now part of #{ Spree::Config.site_name }!" -/ Heading Panel + = @enterprise.name + = "#{t(:email_registered)} #{ Spree::Config.site_name }!" + %p - Please find below all the details for viewing and editing your enterprise on - %strong= "#{ Spree::Config.site_name }." - We suggest keeping this email and information somewhere safe. Logging in with the account details below will allow complete access to your products and services. + = t :email_userguide_html, link: link_to('Open Food Network User Guide', 'http://www.openfoodnetwork.org/platform/user-guide/') --#%p   - --# %p.callout --# %strong --# Your enterprise details --# %table{:width => "100%"} --# %tr --# %td{:align => "right"} --# %strong --# Shop URL --# %td   --# %td --# %a{:href => "#{ main_app.enterprise_shop_url(@enterprise) }", :target => "_blank"} --# = main_app.enterprise_shop_url(@enterprise) --# %tr --# %td   --# %tr --# %td{:align => "right"} --# %strong --# Email --# %td   --# %td --# %a{:href => "mailto:#{ @enterprise.email }", :target => "_blank"} --# = @enterprise.email - -%p   %p - Log into - %strong= "#{ Spree::Config.site_name } Admin" - in order to edit your enterprise details such as website and social media links, or to start adding products to your enterprise! + = t :email_admin_html, link: link_to('Admin Panel', spree.admin_url) -%p.callout - %strong - OFN Admin -%table{ :width => "100%"} - %tr - %td{:align => "right"} - %strong - Admin - %td   - %td - %a{:href => "#{ spree.admin_url }", :target => "_blank"} - = spree.admin_url - -%p   -/ /Heading Panel %p - We're so pleased to have you as a valued member of - %strong= "#{Spree::Config.site_name}!" - Don't hestitate to get in touch if you have any questions. + = t :email_community_html, link: link_to('Join the community.', 'http://community.openfoodnetwork.org/') + +%p + = t :email_help = render 'shared/mailers/signoff' -= render 'shared/mailers/social_and_contact' \ No newline at end of file += render 'shared/mailers/social_and_contact' diff --git a/app/views/enterprises/_distributor_details.html.haml b/app/views/enterprises/_distributor_details.html.haml deleted file mode 100644 index a3f070b451..0000000000 --- a/app/views/enterprises/_distributor_details.html.haml +++ /dev/null @@ -1,24 +0,0 @@ -.distributor-details{'data-hook' => 'distributor-details'} - %h2= distributor.name - %p - %strong Address: - %br/ - = render 'spree/shared/address', :address => distributor.address - %p - %strong Next collection time: - %br/ - = distributor.next_collection_at - %p - %strong Regular collection times: - %br/ - = distributor.pickup_times - %p - %strong Contact: - %br/ - = distributor.contact - %br/ - = "Phone: #{distributor.phone}" - %br/ - = "Email: #{distributor.email}" - %p= distributor.description - %p= link_to distributor.website, distributor.website if distributor.website diff --git a/app/views/enterprises/distributors.html.haml b/app/views/enterprises/distributors.html.haml deleted file mode 100644 index 941b65237b..0000000000 --- a/app/views/enterprises/distributors.html.haml +++ /dev/null @@ -1,12 +0,0 @@ -- content_for :sidebar do - %div{'data-hook' => "homepage_sidebar_navigation"} - = render 'spree/sidebar' - - -%h1 Distributors - -= cms_page_content(:content, Cms::Page.find_by_full_path('/enterprises/distributors')) - -%ul.enterprises - - @distributors.each do |distributor| - %li= link_to distributor.name, distributor diff --git a/app/views/enterprises/distributors.js.erb b/app/views/enterprises/distributors.js.erb deleted file mode 100644 index aed01164da..0000000000 --- a/app/views/enterprises/distributors.js.erb +++ /dev/null @@ -1 +0,0 @@ -distributors = <%= @distributor_details.to_json.html_safe %>; \ No newline at end of file diff --git a/app/views/enterprises/index.html.haml b/app/views/enterprises/index.html.haml deleted file mode 100644 index a1e07dfc04..0000000000 --- a/app/views/enterprises/index.html.haml +++ /dev/null @@ -1,12 +0,0 @@ -- content_for :sidebar do - %div{'data-hook' => "homepage_sidebar_navigation"} - = render 'spree/sidebar' - - -%h1 Enterprises - -= cms_page_content(:content, Cms::Page.find_by_full_path('/enterprises')) - -%ul.enterprises - - @enterprises.each do |enterprise| - %li= link_to enterprise.name, enterprise diff --git a/app/views/enterprises/shop.html.haml b/app/views/enterprises/shop.html.haml index 89ce1a30ac..f2063494b4 100644 --- a/app/views/enterprises/shop.html.haml +++ b/app/views/enterprises/shop.html.haml @@ -1,21 +1,29 @@ +- content_for(:title) do + = current_distributor.name +- content_for(:description) do + = current_distributor.description +- content_for(:image) do + = current_distributor.logo.url + = inject_enterprises %shop.darkswarm - content_for :order_cycle_form do - %div{"ng-controller" => "OrderCycleChangeCtrl"} + %div{"ng-controller" => "OrderCycleChangeCtrl", "ng-cloak" => true} %closing{"ng-if" => "OrderCycle.selected()"} - Next order closing + = t :enterprises_next_closing %strong {{ OrderCycle.orders_close_at() | date_in_words }} - %span Ready for + %span + = t :enterprises_ready_for / Will this label should be a variable to reflect 'Ready for pickup / delivery' as appropriate %select.avenir#order_cycle_id{"ng-model" => "order_cycle.order_cycle_id", - "ng-change" => "changeOrderCycle()", + "ofn-change-order-cycle" => true, "ng-options" => "oc.id as oc.time for oc in #{@order_cycles.map {|oc| {time: pickup_time(oc), id: oc.id}}.to_json}", - "popover-placement" => "left", "popover" => "Choose when you want your order:", "popover-trigger" => "openTrigger"} + "popover-placement" => "left", "popover" => t(:enterprises_choose), "popover-trigger" => "openTrigger"} diff --git a/app/views/enterprises/shop_front.html.haml b/app/views/enterprises/shop_front.html.haml deleted file mode 100644 index 79364d368a..0000000000 --- a/app/views/enterprises/shop_front.html.haml +++ /dev/null @@ -1,15 +0,0 @@ -.row - .large-12.columns - %h2= @enterprise.name -.row - .large-12.columns= @enterprise.long_description.andand.html_safe - -.row - .large-12.columns - .products - - @products.each_slice(4).to_a.each do |products_row| - .row - - products_row.each do |product| - .large-4.columns.centered - .clearfix= link_to small_image(product), product - = link_to product.name, product diff --git a/app/views/enterprises/show.html.haml b/app/views/enterprises/show.html.haml deleted file mode 100644 index 8e5035dbcc..0000000000 --- a/app/views/enterprises/show.html.haml +++ /dev/null @@ -1,18 +0,0 @@ -- if @enterprise != current_distributor - %h2= @enterprise.name - -.enterprise-description= @enterprise.long_description.andand.html_safe - -- if current_distributor - = render :template => 'spree/products/index' - -- else - %h3 Hubs that distribute our products - %p.hint Select a hub to start shopping: - - %ul#supplier-distributors - - if @distributors.delete @enterprise - %li= link_to "Buy direct from the farm", enterprise_shop_path(@enterprise), {class: distributor_link_class(@enterprise)} - - - @distributors.each do |distributor| - %li= render partial: "shared/distributor", object: distributor diff --git a/app/views/enterprises/suppliers.html.haml b/app/views/enterprises/suppliers.html.haml deleted file mode 100644 index c67a7bc987..0000000000 --- a/app/views/enterprises/suppliers.html.haml +++ /dev/null @@ -1,12 +0,0 @@ -- content_for :sidebar do - %div{'data-hook' => "homepage_sidebar_navigation"} - = render 'spree/sidebar' - - -%h1 Suppliers - -= cms_page_content(:content, Cms::Page.find_by_full_path('/enterprises/suppliers')) - -%ul.enterprises - - @suppliers.each do |supplier| - %li= link_to supplier.name, supplier diff --git a/app/views/groups/_contact.html.haml b/app/views/groups/_contact.html.haml index 915c608d15..320da464bd 100644 --- a/app/views/groups/_contact.html.haml +++ b/app/views/groups/_contact.html.haml @@ -1,7 +1,8 @@ %div.contact-container{bindonce: true} - if @group.email.present? || @group.website.present? || @group.phone.present? %div.modal-centered - %p.modal-header Contact + %p.modal-header + = t :groups_contact_web - if @group.phone.present? %p %a{tel: @group.phone} @@ -9,16 +10,17 @@ - if @group.email.present? %p =link_to_service "", @group.email.reverse, mailto: true do - Email us + = t :groups_contact_email - if @group.website.present? %p =link_to_service "http://", @group.website do - Visit our website + = t :groups_contact_website %div{bindonce: true} - if @group.facebook.present? || @group.twitter.present? || @group.linkedin.present? || @group.instagram.present? %div.modal-centered.pad-top - %p.modal-header Follow + %p.modal-header + = t :groups_contact_web .follow-icons{bindonce: true} =link_to_service "http://twitter.com/", @group.twitter do %i.ofn-i_041-twitter @@ -32,7 +34,8 @@ %div{bindonce: true} - if @group.address1.present? || @group.city.present? %div.modal-centered.pad-top - %p.modal-header Address + %p.modal-header + = t :groups_contact_web %p = @group.address1 - if @group.address2.present? diff --git a/app/views/groups/_hub_filters.html.haml b/app/views/groups/_hub_filters.html.haml new file mode 100644 index 0000000000..71924d5e85 --- /dev/null +++ b/app/views/groups/_hub_filters.html.haml @@ -0,0 +1,21 @@ +.row + = render partial: 'shared/components/filter_controls' + = render partial: 'shared/components/show_profiles' + +.row.animate-show{"ng-show" => "filtersActive"} + .small-12.columns + .row.filter-box + .small-12.large-9.columns + %h5.tdhead + .light + = t :hubs_filter_by + = t :hubs_filter_type + %filter-selector.small-block-grid-2.medium-block-grid-4.large-block-grid-5{"selector-set" => "filterSelectors", objects: "group_hubs | searchEnterprises:query | shipping:shippingTypes | showHubProfiles:show_profiles | taxonsOf", "active-selectors" => "activeTaxons"} + .small-12.large-3.columns + %h5.tdhead + .light + = t :hubs_filter_by + = t :hubs_filter_delivery + %shipping-type-selector + += render partial: 'shared/components/filter_box' diff --git a/app/views/groups/index.html.haml b/app/views/groups/index.html.haml index 710039c7fb..afb19e1b91 100644 --- a/app/views/groups/index.html.haml +++ b/app/views/groups/index.html.haml @@ -1,20 +1,28 @@ -= inject_enterprises +- content_for(:title) do + = t :groups_title + += inject_enterprises :javascript angular.module('Darkswarm').value('groups', #{render partial: "json/groups", object: @groups}) -#groups.pad-top{"ng-controller" => "GroupsCtrl"} +#groups.pad-top.footer-pad{"ng-controller" => "GroupsCtrl"} + .row + .small-12.medium-6.medium-offset-3.columns.text-center + %h1 + = t :groups_headline + %p.text + = t :groups_text #active-table-search.row.pad-top .small-12.columns - %h1 Groups / regions %p - %input.animate-show{type: :text, + %input{type: :text, "ng-model" => "query", - placeholder: "Search name or keyword", + placeholder: t(:groups_search), "ng-debounce" => "150", "ofn-disable-enter" => true} - .group{"ng-repeat" => "group in groups = (Groups.groups | groups:query | orderBy:order)", + .group.animate-repeat{"ng-repeat" => "group in groups = (Groups.groups | groups:query | orderBy:order)", name: "group{{group.id}}", id: "group{{group.id}}"} .row.pad-top{bindonce: true} @@ -36,6 +44,6 @@ .group{"ng-show" => "groups.length == 0"} .row.pad-top - No groups found + = t :groups_no_groups = render partial: "shared/footer" diff --git a/app/views/groups/show.html.haml b/app/views/groups/show.html.haml index 1bc965dfb3..74588176b9 100644 --- a/app/views/groups/show.html.haml +++ b/app/views/groups/show.html.haml @@ -1,12 +1,19 @@ +- content_for(:title) do + = @group.name +- content_for(:description) do + = @group.description +- content_for(:image) do + = @group.logo.url + -# inject all enterprises as "enterprises" -# it could be more efficient to inject only the enterprises that are related to the group = inject_enterprises -# inject enterprises in this group --# further hubs and producers of these enterprises can't be resoleved within this small subset -= inject_json_ams "group_enterprises", @group.enterprises, Api::EnterpriseSerializer, active_distributors: @active_distributors +-# further hubs and producers of these enterprises can't be resolved within this small subset += inject_group_enterprises -#group-page.row.pad-top{"ng-controller" => "GroupPageCtrl"} +#group-page.row.pad-top.footer-pad{"ng-controller" => "GroupPageCtrl"} .small-12.columns.pad-top %header .row @@ -27,7 +34,7 @@ .small-12.medium-12.large-9.columns %div{"ng-controller" => "TabsCtrl"} %tabset - %tab{heading: 'Map', + %tab{heading: t(:label_map), active: "active(\'\')", select: "select(\'\')"} .map-container @@ -36,28 +43,29 @@ %markers{models: "mapMarkers", fit: "true", coords: "'self'", icon: "'icon'", click: "'reveal'"} - %tab{heading: 'About us', + %tab{heading: t(:groups_about), active: "active(\'about\')", select: "select(\'about\')"} - %h1 About Us + %h1 + = t :groups_about %p!= @group.long_description - %tab{heading: 'Our producers', + %tab{heading: t(:groups_producers), active: "active(\'producers\')", select: "select(\'producers\')"} .producers{"ng-controller" => "GroupEnterprisesCtrl"} .row .small-12.columns - %h1 Our Producers + %h1 + = t :groups_producers = render partial: "shared/components/enterprise_search" - -# TODO: find out why this is not working - -#= render partial: "producers/filters" + = render partial: "producers/filters" .row{bindonce: true} .small-12.columns .active_table %producer.active_table_node.row.animate-repeat{id: "{{producer.path}}", - "ng-repeat" => "producer in filteredEnterprises = (group_producers | visible | searchEnterprises:query | taxons:activeTaxons)", + "ng-repeat" => "producer in filteredEnterprises = (group_producers | searchEnterprises:query | taxons:activeTaxons)", "ng-controller" => "GroupEnterpriseNodeCtrl", "ng-class" => "{'closed' : !open(), 'open' : open(), 'inactive' : !producer.active}", id: "{{producer.hash}}"} @@ -68,26 +76,23 @@ = render partial: 'shared/components/enterprise_no_results' - %tab{heading: 'Our hubs', + %tab{heading: t(:groups_hubs), active: "active(\'hubs\')", select: "select(\'hubs\')"} .hubs{"ng-controller" => "GroupEnterprisesCtrl"} .row .small-12.columns - %h1 Our Hubs + %h1 + = t :groups_hubs = render partial: "shared/components/enterprise_search" - -# TODO: find out why this is not working - -#= render partial: "home/filters" - .small-12.medium-6.columns - %span   - = render partial: 'shared/components/show_profiles' + = render partial: "hub_filters" .row{bindonce: true} .small-12.columns .active_table %hub.active_table_node.row.animate-repeat{id: "{{hub.hash}}", - "ng-repeat" => "hub in filteredEnterprises = (group_hubs | visible | searchEnterprises:query | taxons:activeTaxons | shipping:shippingTypes | showHubProfiles:show_profiles | orderBy:['-active', '+orders_close_at'])", + "ng-repeat" => "hub in filteredEnterprises = (group_hubs | searchEnterprises:query | taxons:activeTaxons | shipping:shippingTypes | showHubProfiles:show_profiles | orderBy:['-active', '+orders_close_at'])", "ng-class" => "{'is_profile' : hub.category == 'hub_profile', 'closed' : !open(), 'open' : open(), 'inactive' : !hub.active, 'current' : current()}", "ng-controller" => "GroupEnterpriseNodeCtrl"} .small-12.columns @@ -95,7 +100,7 @@ = render partial: 'home/fat' = render partial: 'shared/components/enterprise_no_results' - + .small-12.medium-12.large-3.columns = render partial: 'contact' @@ -104,13 +109,13 @@ .small-12.columns.text-center.small %hr %p.text-small - = "Copyright #{Date.today.year} #{@group.name}" + = "Copyright #{Date.current.year} #{@group.name}" %h2 - =link_to_service "https://www.facebook.com/", @group.facebook, title: 'Follow us on Facebook' do + =link_to_service "https://www.facebook.com/", @group.facebook, title: t(:groups_contact_facebook) do %i.ofn-i_044-facebook - =link_to_service "", @group.email.reverse, title:'Email us', mailto: true do + =link_to_service "", @group.email.reverse, title: t(:groups_contact_email), mailto: true do %i.ofn-i_050-mail-circle - =link_to_service "http://", @group.website, title: 'Visit our website' do + =link_to_service "http://", @group.website, title: t(:groups_contact_website) do %i.ofn-i_049-web %p   diff --git a/app/views/groups/signup.html.haml b/app/views/groups/signup.html.haml new file mode 100644 index 0000000000..22aae2b330 --- /dev/null +++ b/app/views/groups/signup.html.haml @@ -0,0 +1,77 @@ +- content_for(:title) do + = t :groups_signup_title + +#panes + #shops-signup.pane + .row.header + .small-12.medium-12.columns.text-center + %h2 + = t :groups_signup_headline + .row.content + .small-12.medium-6.medium-offset-3.columns.text-center + %p.text-big + = t :groups_signup_intro + %br + %a.button.transparent{href: "hello@openfoodnetwork.org?subject=I'd%20like%20to%20talk%20to%20you%20about%20groups%20on%20the%20Open%20Food%20Network".reverse, target: '_blank', mailto: true} + = t :groups_signup_email + + .groups-details.pane + .row + .small-12.medium-8.medium-offset-2.columns + %h3.text-center + = t :groups_signup_motivation1 + %p.text-big + = t :groups_signup_motivation2 + %p.text-big + = t :groups_signup_motivation3 + %br + %h3.text-center + = t :groups_signup_motivation4 + %p.text-big + = t :groups_signup_motivation5 + %p.text-big + = t :groups_signup_motivation6 + %br + %h3.text-center + = t :groups_signup_motivation7 + %p.text-big + = t :groups_signup_motivation8 + %p.text-big + = t :groups_signup_motivation9 + .pane + .row + .small-12.medium-10.medium-offset-1.columns.text-center + %h2 + = t :groups_signup_pricing + -# %p.text-big + -# / If there is a time-sensitive offer you can write it here, e.g. + -# Time-sensitive offer goes here! + %br + = ContentConfig.group_signup_pricing_table_html.html_safe + + #shops-case-studies + .row + .small-12.medium-10.medium-offset-1.columns + %h2.text-center + = t :groups_signup_studies + %br + = ContentConfig.group_signup_case_studies_html.html_safe + + .pane#cta + .row + .small-12.medium-6.medium-offset-3.columns.text-center + %h2 + = t :groups_signup_contact + %p.text-big + = t :groups_signup_contact_text + %a.button.transparent{href: "hello@openfoodnetwork.org?subject=I'd%20like%20to%20talk%20to%20you%20about%20groups%20on%20the%20Open%20Food%20Network".reverse, target: '_blank', mailto: true} + = t :groups_signup_email + + #hub-details.pane.footer-pad + .row + .small-12.medium-10.medium-offset-1.columns + %h2.text-center + = t :groups_signup_detail + = ContentConfig.group_signup_detail_html.html_safe + += render partial: "shared/footer" diff --git a/app/views/home/_beta.en-GB.html.haml b/app/views/home/_beta.en-GB.html.haml deleted file mode 100644 index 2badc7c826..0000000000 --- a/app/views/home/_beta.en-GB.html.haml +++ /dev/null @@ -1,13 +0,0 @@ -#beta.pane - - .row - .small-12.columns.text-center - %h2 S'cuse us - %h5 while we get (more) awesome - %p Open Food Network UK is a new service that’s being piloted right now! - %p Want to help? Or find out when OFN is coming to you? - %strong We’d love to hear from you: - %p - %a{href: "hello@openfoodnetwork.org".reverse, target: '_blank', mailto: true} Food buyers - | - %a{href: "hello@openfoodnetwork.org".reverse, target: '_blank', mailto: true} Food producers & farmers diff --git a/app/views/home/_beta.html.haml b/app/views/home/_beta.html.haml deleted file mode 100644 index af72211862..0000000000 --- a/app/views/home/_beta.html.haml +++ /dev/null @@ -1,13 +0,0 @@ -#beta.pane - - .row - .small-12.columns.text-center - %h2 S'cuse us - %h5 while we get (more) awesome - %p Open Food Network (beta) is a new service that’s being built right now! Our food producers are currently based around Melbourne and Victoria, and we hope to expand OFN nationally very soon. - %p Want to help? Or find out when OFN is coming to you? - %strong We’d love to hear from you: - %p - %a{href: "hello@openfoodnetwork.org".reverse, target: '_blank', mailto: true} Food buyers - | - %a{href: "hello@openfoodnetwork.org".reverse, target: '_blank', mailto: true} Food producers & farmers \ No newline at end of file diff --git a/app/views/home/_brandstory.html.haml b/app/views/home/_brandstory.html.haml new file mode 100644 index 0000000000..25b8687742 --- /dev/null +++ b/app/views/home/_brandstory.html.haml @@ -0,0 +1,26 @@ +#brand-story.pane + .row + .small-12.medium-8.medium-offset-2.columns.text-center + %h2 + = t :brandstory_headline + %p + = t :brandstory_intro + + #brand-story-text.hide-show.slideable + %p + = t :brandstory_part1 + %p + = t :brandstory_part2 + %p + = t :brandstory_part3 + %p + = t :brandstory_part4 + %p + %strong + = t :brandstory_part5_strong + %p + = t :brandstory_part6 + + %a.text-vbig{"slide-toggle" => "#brand-story-text", "ng-click" => "toggleBrandStory()"} + %i.ofn-i_005-caret-down{"ng-hide" => "brandStoryExpanded"} + %i.ofn-i_006-caret-up{ "ng-show" => "brandStoryExpanded"} diff --git a/app/views/home/_cta.html.haml b/app/views/home/_cta.html.haml new file mode 100644 index 0000000000..692ac06361 --- /dev/null +++ b/app/views/home/_cta.html.haml @@ -0,0 +1,8 @@ +#cta.pane + .row + .small-12.columns.text-center + %h2 + = t :cta_headline + %br + %a.button.transparent{href: "/shops"} + = t :cta_label diff --git a/app/views/home/_fat.html.haml b/app/views/home/_fat.html.haml index a5543fa2a3..b96b13885e 100644 --- a/app/views/home/_fat.html.haml +++ b/app/views/home/_fat.html.haml @@ -1,26 +1,29 @@ -.row.active_table_row{"ng-show" => "open()", "ng-click" => "toggle($event)", "ng-class" => "{'open' : !ofn-i_032-closed-sign()}"} +.row.active_table_row{"ng-show" => "open()", "ng-click" => "toggle($event)", "ng-class" => "{'open' : !ofn-i_032-closed-sign()}", bindonce: true} .columns.small-12.medium-6.large-5.fat %div{"bo-if" => "hub.taxons"} - %label Shop for + %label + = t :hubs_buy .trans-sentence %span.fat-taxons{"ng-repeat" => "taxon in hub.taxons"} %render-svg{path: "{{taxon.icon}}"} - %span{"bo-text" => "taxon.name"} + %span{"bo-text" => "taxon.name"} %div.show-for-medium-up{"bo-if" => "hub.taxons.length==0"}   .columns.small-12.medium-3.large-2.fat %div{"bo-if" => "hub.pickup || hub.delivery"} - %label Delivery options + %label + = t :hubs_delivery_options %ul.small-block-grid-2.medium-block-grid-1.large-block-grid-1 - %li.pickup{"bo-if" => "hub.pickup"} + %li.pickup{"bo-if" => "hub.pickup"} %i.ofn-i_038-takeaway - Pickup - %li.delivery{"bo-if" => "hub.delivery"} + = t :hubs_pickup + %li.delivery{"bo-if" => "hub.delivery"} %i.ofn-i_039-delivery - Delivery + = t :hubs_delivery .columns.small-12.medium-3.large-5.fat %div{"bo-if" => "hub.producers"} - %label Our producers + %label + = t :hubs_producers %ul.small-block-grid-2.medium-block-grid-1.large-block-grid-2{"ng-class" => "{'show-more-producers' : toggleMoreProducers}", "class" => "producers-list"} %li{"ng-repeat" => "enterprise in hub.producers | limitTo:7"} %enterprise-modal @@ -31,9 +34,9 @@ .more + %span{"bo-text" => "hub.producers.length-7"} - More + = t :label_more .less - Show less + = t :label_less %li{"ng-repeat" => "enterprise in hub.producers.slice(7,hub.producers.length)", "class" => "additional-producer"} %enterprise-modal %i.ofn-i_036-producers diff --git a/app/views/home/_filters.html.haml b/app/views/home/_filters.html.haml index 6b339c13c9..bfd13fdf54 100644 --- a/app/views/home/_filters.html.haml +++ b/app/views/home/_filters.html.haml @@ -1,22 +1,22 @@ .row - -# = render partial: 'shared/components/filter_controls' - .small-12.medium-6.columns   + = render partial: 'shared/components/filter_controls' + -# .small-12.medium-6.columns   = render partial: 'shared/components/show_profiles' --# .row.animate-show{"ng-show" => "filtersActive"} --# .small-12.columns --# .row.filter-box --# .small-12.large-9.columns --# %h5.tdhead --# .light Filter by --# Type --# %ul.small-block-grid-2.medium-block-grid-4.large-block-grid-5 --# %filter-selector{objects: "Enterprises.hubs | searchEnterprises:query | taxonsOf", "active-selectors" => "activeTaxons"} --# .small-12.large-3.columns --# %h5.tdhead --# .light Filter by --# Delivery --# %ul.small-block-grid-2.medium-block-grid-4.large-block-grid-2 --# %shipping-type-selector{results: "shippingTypes"} --# --# = render partial: 'shared/components/filter_box' +.row.animate-show{"ng-show" => "filtersActive"} + .small-12.columns + .row.filter-box + .small-12.large-9.columns + %h5.tdhead + .light + = t :hubs_filter_by + = t :hubs_filter_type + %filter-selector.small-block-grid-2.medium-block-grid-4.large-block-grid-5{ "selector-set" => "filterSelectors", objects: "visibleMatches | visible | taxonsOf", "active-selectors" => "activeTaxons" } + .small-12.large-3.columns + %h5.tdhead + .light + = t :hubs_filter_by + = t :hubs_filter_delivery + %shipping-type-selector + += render partial: 'shared/components/filter_box' diff --git a/app/views/home/_groups.html.haml b/app/views/home/_groups.html.haml deleted file mode 100644 index 5d76dd1e2d..0000000000 --- a/app/views/home/_groups.html.haml +++ /dev/null @@ -1,10 +0,0 @@ -#groups.pane - - .row - .small-12.columns.text-center - %h2 Groups / Regions - %h5 See all the groups & regions on the Open Food Network - %p - %a.neutral-btn.light{href: "/groups"} - %i.ofn-i_035-groups - View groups & regions \ No newline at end of file diff --git a/app/views/home/_hubs.html.haml b/app/views/home/_hubs.html.haml index 1e5151469e..fea1fc0a7d 100644 --- a/app/views/home/_hubs.html.haml +++ b/app/views/home/_hubs.html.haml @@ -1,22 +1,29 @@ -= inject_enterprises -#hubs.hubs{"ng-controller" => "EnterprisesCtrl"} += inject_enterprises + +#hubs.hubs{"ng-controller" => "EnterprisesCtrl", "ng-cloak" => true} .row .small-12.columns - %h1 Shop in your local area + %h1{"scroll-after-load" => (spree_current_user ? true : nil)} + = t :hubs_intro - = render partial: "shared/components/enterprise_search" - = render partial: "home/filters" + = render "shared/components/enterprise_search" + = render "home/filters" - .row{bindonce: true} + .row .small-12.columns - .active_table - %hub.active_table_node.row.animate-repeat{"ng-repeat" => "hub in filteredEnterprises = (Enterprises.hubs | visible | searchEnterprises:query | taxons:activeTaxons | shipping:shippingTypes | showHubProfiles:show_profiles | orderBy:['-active', '+orders_close_at'])", - "ng-class" => "{'is_profile' : hub.category == 'hub_profile', 'closed' : !open(), 'open' : open(), 'inactive' : !hub.active, 'current' : current()}", - "scroll-after-load" => true, - "ng-controller" => "HubNodeCtrl", - id: "{{hub.hash}}"} - .small-12.columns - = render partial: 'home/skinny' - = render partial: 'home/fat' + .name-matches{"ng-show" => "nameMatchesFiltered.length > 0"} + %h2 + = t :hubs_matches + = render "home/hubs_table", enterprises: "nameMatches" - = render partial: 'shared/components/enterprise_no_results' + .distance-matches{"ng-if" => "nameMatchesFiltered.length == 0 || distanceMatchesShown"} + %h2{"ng-show" => "nameMatchesFiltered.length > 0 || query.length > 0"} + = t :hubs_matches + %span{"ng-show" => "nameMatchesFiltered.length > 0"} {{ nameMatchesFiltered[0].name }}... + %span{"ng-hide" => "nameMatchesFiltered.length > 0"} {{ query }}... + + = render "home/hubs_table", enterprises: "distanceMatches" + + .show-distance-matches{"ng-show" => "nameMatchesFiltered.length > 0 && !distanceMatchesShown"} + %a{href: "", "ng-click" => "showDistanceMatches()"} + = t :hubs_distance_filter, location: "{{ nameMatchesFiltered[0].name }}" diff --git a/app/views/home/_hubs_table.html.haml b/app/views/home/_hubs_table.html.haml new file mode 100644 index 0000000000..8842079f56 --- /dev/null +++ b/app/views/home/_hubs_table.html.haml @@ -0,0 +1,10 @@ +.active_table + %hub.active_table_node.row{"ng-repeat" => "hub in #{enterprises}Filtered = (#{enterprises} | visible | taxons:activeTaxons | shipping:shippingTypes | showHubProfiles:show_profiles | orderBy:['-active', '+distance', '+orders_close_at'])", + "ng-class" => "{'is_profile' : hub.category == 'hub_profile', 'closed' : !open(), 'open' : open(), 'inactive' : !hub.active, 'current' : current()}", + "ng-controller" => "HubNodeCtrl", + id: "{{hub.hash}}"} + .small-12.columns + = render 'home/skinny' + = render 'home/fat' + + = render 'shared/components/enterprise_no_results', enterprises: "#{enterprises}Filtered" diff --git a/app/views/home/_login.html.haml b/app/views/home/_login.html.haml index 31fafd05d9..12b261b8f5 100644 --- a/app/views/home/_login.html.haml +++ b/app/views/home/_login.html.haml @@ -1,10 +1,11 @@ .row .large-12.large-centered.columns - %h2 Login + %h2 + = t :label_login = form_for Spree::User.new, :remote => true, :html => {'data-type' => :json}, :as => :spree_user, :url => spree.spree_user_session_path do |f| #password-credentials #login-error-alert.alert-box.alert.hide - Invalid email or password + = t :login_invalid %p = f.label :email, t(:email) = f.email_field :email, :class => 'title', :tabindex => 1, :id => "login_spree_user_email" diff --git a/app/views/home/_map.html.haml b/app/views/home/_map.html.haml deleted file mode 100644 index 5a89dd181e..0000000000 --- a/app/views/home/_map.html.haml +++ /dev/null @@ -1,10 +0,0 @@ -#map.pane - - .row - .small-12.columns.text-center - %h2 Map - %h5 of all our food hubs and producers - %p - %a.neutral-btn.light{href: "/map"} - %i.ofn-i_037-map - View map \ No newline at end of file diff --git a/app/views/home/_producer-register.html.haml b/app/views/home/_producer-register.html.haml deleted file mode 100644 index 62845bd9ea..0000000000 --- a/app/views/home/_producer-register.html.haml +++ /dev/null @@ -1,75 +0,0 @@ -#producers.pane - - .row - .small-12.columns.text-center - %h2 - = t :producers - %h5 Want to join the Open Food Network? - %br - %a.neutral-btn.turquoise{href: "/register"} - Register now - %i.ofn-i_007-caret-right - / .row - / .small-12.medium-4.columns.text-center - / %ul.pricing-table - / %li.title Profile - / %li.price Always free - / %li.description Help people find and contact you on OFN - / %li.bullet-item - / %i.ofn-i_019-map-pin - / Pin on OFN Map - / %li.bullet-item - / %i.ofn-i_044-facebook - / Share your contact and social info - / %li.cta-button - / %a.neutral-btn.turquoise{:href => "/register"} - / Register now - / %i.ofn-i_007-caret-right - - / .small-12.medium-4.columns.text-center - / %ul.pricing-table - / %li.title Supplier - / %li.price Always free - / %li.description Sell your products through existing OFN shopfronts - / %li.bullet-item - / %i.ofn-i_019-map-pin - / Pin on OFN Map - / %li.bullet-item - / %i.ofn-i_044-facebook - / Share your contact and social info - / %li.bullet-item - / %i.ofn-i_067-shop - / Create and manage products - / %li.cta-button - / %a.neutral-btn.turquoise{:href => "/register"} - / Register now - / %i.ofn-i_007-caret-right - - / .small-12.medium-4.columns.text-center - / %ul.pricing-table - / %li.title Shopfront - / %li.price $200 setup fee - / %li.description + Sliding monthly fee of $5-$50/month - / %li.bullet-item - / %i.ofn-i_019-map-pin - / Pin on OFN Map - / %li.bullet-item - / %i.ofn-i_044-facebook - / Share your contact and social info - / %li.bullet-item - / %i.ofn-i_067-shop - / Create and manage products - / %li.bullet-item - / %i.ofn-i_051-check-big - / Create and manage order cycles - / %li.bullet-item - / %i.ofn-i_051-check-big - / Sell your products on Shopfront - / %li.cta-button - / %a.neutral-btn.turquoise{:href => "/register"} - / Register now - / %i.ofn-i_007-caret-right - - - - \ No newline at end of file diff --git a/app/views/home/_producers.html.haml b/app/views/home/_producers.html.haml deleted file mode 100644 index 3d4fcfbd68..0000000000 --- a/app/views/home/_producers.html.haml +++ /dev/null @@ -1,10 +0,0 @@ -#producers.pane - - .row - .small-12.columns.text-center - %h2 Producers - %h5 Looking for a specific producer or farmer? - %p - %a.neutral-btn.turquoise{href: "/producers"} - %i.ofn-i_036-producers - View all producers \ No newline at end of file diff --git a/app/views/home/_signup.html.haml b/app/views/home/_signup.html.haml index 33eaff94d0..4eddb76c96 100644 --- a/app/views/home/_signup.html.haml +++ b/app/views/home/_signup.html.haml @@ -1,6 +1,7 @@ .row .large-12.large-centered.columns - %h2 Sign Up + %h2 + = t :label_signup = form_for Spree::User.new, :as => :spree_user, :url => spree.spree_user_registration_path(@spree_user) do |f| %p = f.label :email, t(:email) diff --git a/app/views/home/_skinny.html.haml b/app/views/home/_skinny.html.haml index 9cf7a01c77..85e200fb94 100644 --- a/app/views/home/_skinny.html.haml +++ b/app/views/home/_skinny.html.haml @@ -1,7 +1,6 @@ .row.active_table_row{"ng-if" => "hub.is_distributor", "ng-click" => "toggle($event)", "ng-class" => "{'closed' : !open(), 'is_distributor' : producer.is_distributor}", bindonce: true} - - .columns.small-12.medium-6.large-5.skinny-head - %a.hub{"bo-href" => "hub.path", "ng-class" => "{primary: hub.active, secondary: !hub.active}", "ofn-empties-cart" => "hub"} + .columns.small-12.medium-5.large-5.skinny-head + %a.hub{"bo-href" => "hub.path", "ng-class" => "{primary: hub.active, secondary: !hub.active}", "ofn-change-hub" => "hub", "data-is-link" => "true"} %i{bo: {class: "hub.icon_font"}} %span.margin-top.hub-name-listing{"bo-bind" => "hub.name | truncate:40"} @@ -9,23 +8,29 @@ %span.margin-top{"bo-text" => "hub.address.city"} .columns.small-2.medium-1.large-1 %span.margin-top{"bo-bind" => "hub.address.state_name | uppercase"} + %span.margin-top{"ng-if" => "hub.distance != null && hub.distance > 0"} ({{ hub.distance / 1000 | number:0 }} km) - .columns.small-6.medium-3.large-4.text-right{"bo-if" => "hub.active"} - %a.hub.open_closed{"bo-href" => "hub.path", "ng-class" => "{primary: hub.active, secondary: !hub.active}", "ofn-empties-cart" => "hub"} + .columns.small-4.medium-3.large-3.text-right{"bo-if" => "hub.active"} + %a.hub.open_closed{"bo-href" => "hub.path", "ng-class" => "{primary: hub.active, secondary: !hub.active}", "ofn-change-hub" => "hub"} %i.ofn-i_033-open-sign %span.margin-top{ bo: { if: "current()" } } - %em Shopping here + %em= t :hubs_shopping_here %span.margin-top{ bo: { if: "!current()" } } %span{"bo-bind" => "hub.orders_close_at | sensible_timeframe"} - .columns.small-6.medium-3.large-4.text-right{"bo-if" => "!hub.active"} - %a.hub.open_closed{"bo-href" => "hub.path", "ng-class" => "{primary: hub.active, secondary: !hub.active}", "ofn-empties-cart" => "hub"} + .columns.small-4.medium-3.large-3.text-right{"bo-if" => "!hub.active"} + %a.hub.open_closed{"bo-href" => "hub.path", "ng-class" => "{primary: hub.active, secondary: !hub.active}", "ofn-change-hub" => "hub"} %i.ofn-i_032-closed-sign %span.margin-top{ bo: { if: "current()" } } - %em Shopping here - %span.margin-top{ bo: { if: "!current()" } } Orders closed + %em= t :hubs_shopping_here + %span.margin-top{ bo: { if: "!current()" } } + = t :hubs_orders_closed -.row.active_table_row{"ng-if" => "!hub.is_distributor", "ng-class" => "closed"} + .columns.small-2.medium-1.large-1.text-right + %span.margin-top + %i{"ng-class" => "{'ofn-i_005-caret-down' : !open(), 'ofn-i_006-caret-up' : open()}"} + +.row.active_table_row{"ng-if" => "!hub.is_distributor", "ng-class" => "closed", bindonce: true} .columns.small-12.medium-6.large-5.skinny-head %a.hub{"ng-click" => "openModal(hub)", "ng-class" => "{primary: hub.active, secondary: !hub.active}"} %i{ng: {class: "hub.icon_font"}} @@ -38,4 +43,4 @@ .columns.small-6.medium-3.large-4.text-right %span.margin-top{ bo: { if: "!current()" } } - %em Profile only + %em= t :hubs_profile_only diff --git a/app/views/home/_stats.html.haml b/app/views/home/_stats.html.haml new file mode 100644 index 0000000000..baef4f0fbd --- /dev/null +++ b/app/views/home/_stats.html.haml @@ -0,0 +1,24 @@ +#stats.pane + .row.header + .small-12.medium-8.medium-offset-2.columns.text-center + %h2 + = t :stats_headline + + .row.content + - if ContentConfig.home_show_stats + .small-12.medium-3.columns.text-center + %h4 + %strong= number_with_delimiter @num_producers + = t :stats_producers + .small-12.medium-3.columns.text-center + %h4 + %strong= number_with_delimiter @num_distributors + = t :stats_shops + .small-12.medium-3.columns.text-center + %h4 + %strong= number_with_delimiter @num_users + = t :stats_shoppers + .small-12.medium-3.columns.text-center + %h4 + %strong= number_with_delimiter @num_orders + = t :stats_orders diff --git a/app/views/home/_system.html.haml b/app/views/home/_system.html.haml new file mode 100644 index 0000000000..fdfd9660dc --- /dev/null +++ b/app/views/home/_system.html.haml @@ -0,0 +1,30 @@ +#system.pane + .row + .small-12.medium-12.large-8.large-offset-2.columns.text-center + %h2 + = t :system_headline + .row + .small-12.medium-4.columns.text-left + .home-icon-box + %a.search{href: "/shops"} + .home-icon-box-bottom + %h4 + = t :system_step1 + %p.text-normal + = t :system_step1_text + .small-12.medium-4.columns.text-left + .home-icon-box + %a.shop{href: "/shops"} + .home-icon-box-bottom + %h4 + = t :system_step2 + %p.text-normal + = t :system_step2_text + .small-12.medium-4.columns.text-left + .home-icon-box + %a.pick-up-delivery{href: "/shops"} + .home-icon-box-bottom + %h4 + = t :system_step3 + %p.text-normal + = t :system_step3_text diff --git a/app/views/home/about_us.html.haml b/app/views/home/about_us.html.haml deleted file mode 100644 index 79e4a657a7..0000000000 --- a/app/views/home/about_us.html.haml +++ /dev/null @@ -1,8 +0,0 @@ -.row - .large-12.columns - %h2 What is open food network - %p - Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor incididunt ut labore et dolore magna - aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. - Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint - occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum \ No newline at end of file diff --git a/app/views/home/index.html.haml b/app/views/home/index.html.haml index a15442e794..cfb3015179 100644 --- a/app/views/home/index.html.haml +++ b/app/views/home/index.html.haml @@ -1,24 +1,25 @@ -#tagline - .row - .small-12.text-center.columns - %h1 - %img{src: "/assets/open-food-network-beta.png", srcset: "/assets/open-food-network-beta.svg", width: "550", height: "134", title: "Open Food Network (beta)"} - %h2 An open marketplace that makes it easy to find, buy, sell and move sustainable local food. - - %ofn-modal{title: "Learn more", "ng-cloak" => true} - = render partial: "modals/learn_more" +:css + #tagline:before { background-image: url("#{ContentConfig.home_hero.url}") } -.ng-cloak - = render partial: "home/hubs" - / = render partial: "home/map" +%div{"ng-controller" => "HomeCtrl"} + = render partial: "shared/menu/alert" - / = render partial: "home/producers" + #tagline + .row + .small-12.text-center.columns + %h1 + / TODO: Rohan - logo asset & width is content manageable: + %img{src: "/assets/logo-white-notext.png", width: "250", title: Spree::Config.site_name} + %br/ + %a.button.transparent{href: "/shops"} + = t :home_shop - / = render partial: "home/groups" -= render partial: "home/producer-register" + #panes + = render partial: "home/brandstory" + = render partial: "home/system" + = render partial: "home/cta" + = render partial: "home/stats" -= render partial: "home/beta" - -= render partial: "shared/footer" + = render partial: "shared/footer" diff --git a/app/views/json/_current_hub.rabl b/app/views/json/_current_hub.rabl deleted file mode 100644 index 103baf9fb3..0000000000 --- a/app/views/json/_current_hub.rabl +++ /dev/null @@ -1,6 +0,0 @@ -object current_distributor -extends 'json/partials/enterprise' - -child suppliers: :producers do - attributes :id -end diff --git a/app/views/json/_flash.rabl b/app/views/json/_flash.rabl index dce07f849a..2900b2b948 100644 --- a/app/views/json/_flash.rabl +++ b/app/views/json/_flash.rabl @@ -1,2 +1,2 @@ -object OpenStruct.new(flash) +object OpenStruct.new(flash.to_hash) attributes :info, :success, :error diff --git a/app/views/json/_groups.rabl b/app/views/json/_groups.rabl index bc50586a62..adf0e57709 100644 --- a/app/views/json/_groups.rabl +++ b/app/views/json/_groups.rabl @@ -6,11 +6,11 @@ child enterprises: :enterprises do end node :logo do |group| - group.logo(:medium) if group.logo.exists? + group.logo(:medium) if group.logo? end node :promo_image do |group| - group.promo_image(:large) if group.promo_image.exists? + group.promo_image(:large) if group.promo_image? end node :state do |group| diff --git a/app/views/json/partials/_enterprise.rabl b/app/views/json/partials/_enterprise.rabl index b8800e22ae..ca99b9d14c 100644 --- a/app/views/json/partials/_enterprise.rabl +++ b/app/views/json/partials/_enterprise.rabl @@ -1,7 +1,7 @@ attributes :name, :id, :description, :latitude, :longitude, :long_description, :website, :instagram, :linkedin, :twitter, :facebook, :is_primary_producer, :is_distributor, :phone -node :email do |enterprise| - enterprise.email.to_s.reverse +node :email_address do |enterprise| + enterprise.email_address.to_s.reverse end child :address do @@ -13,11 +13,11 @@ node :hash do |enterprise| end node :logo do |enterprise| - enterprise.logo(:medium) if enterprise.logo.exists? + enterprise.logo(:medium) if enterprise.logo? end node :promo_image do |enterprise| - enterprise.promo_image(:large) if enterprise.promo_image.exists? + enterprise.promo_image(:large) if enterprise.promo_image? end node :icon do |e| diff --git a/app/views/layouts/_become_distributor.html.haml b/app/views/layouts/_become_distributor.html.haml deleted file mode 100644 index 6d67ee9306..0000000000 --- a/app/views/layouts/_become_distributor.html.haml +++ /dev/null @@ -1,17 +0,0 @@ -#become-distributor.reveal-modal - .row - .small-12.columns - %h2 Become our distributor - %p - Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor incididunt ut labore et dolore magna - aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. - Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint - occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum - - .row - .small-12.columns - = form_tag do - = text_area_tag :email_body, "", :input_html => { :rows => 10 } - = submit_tag "Submit", class: "button" - - = link_to "×".html_safe, "#", class: "close-reveal-modal" \ No newline at end of file diff --git a/app/views/layouts/_become_farmer.html.haml b/app/views/layouts/_become_farmer.html.haml deleted file mode 100644 index aacabea7ff..0000000000 --- a/app/views/layouts/_become_farmer.html.haml +++ /dev/null @@ -1,17 +0,0 @@ -#become-farmer.reveal-modal - .row - .small-12.columns - %h2 Become our farmer - %p - Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor incididunt ut labore et dolore magna - aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. - Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint - occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum - - .row - .small-12.columns - = form_tag do - = text_area_tag :email_body, "", :input_html => { :rows => 10 } - = submit_tag "Submit", class: "button" - - = link_to "×".html_safe, "#", class: "close-reveal-modal" diff --git a/app/views/layouts/darkswarm.html.haml b/app/views/layouts/darkswarm.html.haml index a627ba4896..c758e4836e 100644 --- a/app/views/layouts/darkswarm.html.haml +++ b/app/views/layouts/darkswarm.html.haml @@ -2,17 +2,19 @@ %head %meta{charset: 'utf-8'}/ %meta{name: 'viewport', content: "width=device-width,initial-scale=1.0"}/ - - %title= content_for?(:title) ? yield(:title) : 'Welcome to Open Food Network' + %meta{property: "og:title", content: content_for?(:title) ? yield(:title) : t(:title)} + %meta{property: "og:description", content: content_for?(:description) ? yield(:description) : t(:site_meta_description)} + %meta{property: "og:image", content: content_for?(:image) ? yield(:image) : ContentConfig.logo.url} + %title= content_for?(:title) ? "#{yield(:title)} - #{t(:title)}".html_safe : "#{t(:welcome_to)} #{t(:title)}" - if Rails.env.production? = favicon_link_tag - else = favicon_link_tag "/favicon-staging.ico" - %link{href: "https://fonts.googleapis.com/css?family=Open+Sans:400,700", rel: "stylesheet", type: "text/css"}/ + %link{href: "https://fonts.googleapis.com/css?family=Roboto:400,300italic,400italic,300,700,700italic|Oswald:300,400,700", rel: "stylesheet", type: "text/css"} = yield :scripts - %script{src: "//maps.googleapis.com/maps/api/js?libraries=places&sensor=false"} - = stylesheet_link_tag "darkswarm/all" + %script{src: "//maps.googleapis.com/maps/api/js?libraries=places,geometry"} + = split_stylesheet_link_tag "darkswarm/all" = javascript_include_tag "darkswarm/all" @@ -24,7 +26,7 @@ = render partial: "shared/ie_warning" = javascript_include_tag "iehack" - = inject_json "currentHub", "current_hub" + = inject_current_hub = inject_json "user", "current_user" = inject_json "railsFlash", "flash" = inject_taxons @@ -34,7 +36,7 @@ .off-canvas-wrap{offcanvas: true} .inner-wrap - = render partial: "shared/menu/menu" + = render "shared/menu/menu" %section{ role: "main" } = yield diff --git a/app/views/layouts/mailer.html.haml b/app/views/layouts/mailer.html.haml index 9042c91213..4f9b3d78c1 100644 --- a/app/views/layouts/mailer.html.haml +++ b/app/views/layouts/mailer.html.haml @@ -4,7 +4,7 @@ %meta{:content => "width=device-width", :name => "viewport" }/ %meta{:content => "text/html; charset=UTF-8", "http-equiv" => "Content-Type"}/ %title - Open Food Network + = Spree::Config[:site_name] = stylesheet_link_tag 'mail/all' %body{:bgcolor => "#FFFFFF" } %table.head-wrap{:bgcolor => "#f2f2f2"} @@ -15,10 +15,10 @@ %table{:bgcolor => "#f2f2f2"} %tr %td - %img{:src => "#{ asset_path 'open-food-network-beta-black.png' }", :width => "200", :height => "49"}/ + %img{src: ContentConfig.footer_logo.url, width: "144", height: "50"}/ %td{:align => "right"} %h6.collapse - Open Food Network + = Spree::Config[:site_name] %td %table.body-wrap @@ -43,9 +43,9 @@ %td{:align => "center"} %p %a{:href => "#{ URI.join(spree.root_url, "Terms-of-service.pdf").to_s }", :target => "_blank"} - Terms of service + = t :terms_of_service | %a{:href => "#{ spree.root_url }"} - Open Food Network + = Spree::Config[:site_name] / | Unsubscribe - %td \ No newline at end of file + %td diff --git a/app/views/layouts/registration.html.haml b/app/views/layouts/registration.html.haml index d6122cdc3b..78966acabc 100644 --- a/app/views/layouts/registration.html.haml +++ b/app/views/layouts/registration.html.haml @@ -3,15 +3,15 @@ %meta{charset: 'utf-8'}/ %meta{name: 'viewport', content: "width=device-width,initial-scale=1.0"}/ - %title= content_for?(:title) ? yield(:title) : 'Welcome to Open Food Network' + %title= content_for?(:title) ? "#{yield(:title)} - #{Spree::Config[:site_name]}" : "#{t(:welcome_to)} #{Spree::Config[:site_name]}" - if Rails.env.production? = favicon_link_tag - else = favicon_link_tag "/favicon-staging.ico" - %link{href: "https://fonts.googleapis.com/css?family=Open+Sans:400,700", rel: "stylesheet", type: "text/css"}/ + %link{href: "https://fonts.googleapis.com/css?family=Roboto:400,300italic,400italic,300,700,700italic|Oswald:300,400,700", rel: "stylesheet", type: "text/css"} = yield :scripts - %script{src: "//maps.googleapis.com/maps/api/js?libraries=places&sensor=false"} + %script{src: "//maps.googleapis.com/maps/api/js?libraries=places"} = stylesheet_link_tag "darkswarm/all" = javascript_include_tag "darkswarm/all" @@ -19,7 +19,7 @@ = render "layouts/bugherd_script" = csrf_meta_tags - %body.off-canvas{"ng-app" => "Darkswarm", style: 'background-image: url("/assets/home/ofn_bg_1.jpg")' } + %body.off-canvas{"ng-app" => "Darkswarm", style: 'background-image: url("/assets/tile-wide.png")' } / [if lte IE 8] = render partial: "shared/ie_warning" = javascript_include_tag "iehack" diff --git a/app/views/map/index.html.haml b/app/views/map/index.html.haml index ce4052be57..a4d012282b 100644 --- a/app/views/map/index.html.haml +++ b/app/views/map/index.html.haml @@ -1,8 +1,11 @@ -= inject_enterprises +- content_for(:title) do + = t :label_map + += inject_enterprises .map-container{"fill-vertical" => true} %map{"ng-controller" => "MapCtrl"} %google-map{options: "map.additional_options", center: "map.center", zoom: "map.zoom", styles: "map.styles", draggable: "true"} %map-search - %markers{models: "OfnMap.enterprises", fit: "true", - coords: "'self'", icon: "'icon'", click: "'reveal'"} + %markers{models: "OfnMap.enterprises", fit: "true", + coords: "'self'", icon: "'icon'", click: "'reveal'"} diff --git a/app/views/modals/_food_hub.html.haml b/app/views/modals/_food_hub.html.haml index ba0864b284..233512b5f0 100644 --- a/app/views/modals/_food_hub.html.haml +++ b/app/views/modals/_food_hub.html.haml @@ -1,8 +1,11 @@ %h2 %i.ofn-i_040-hub> - Food Hubs -%h5 Our food hubs are the point of contact between you and the people who make your food! -%p You can search for a convenient hub by location or name. Some hubs have multiple points where you can pick-up your purchases, and some will also provide delivery options. Each food hub is a sales point with independent business operations and logistics - so variations between hubs are to be expected. -%p You can only shop at one food hub at a time. + = t :modal_hubs +%h5 + = t :modal_hubs_abstract +%p + = t :modal_hubs_content1 +%p + = t :modal_hubs_content2 %a.close-reveal-modal{"ng-click" => "$close()"} %i.ofn-i_009-close diff --git a/app/views/modals/_groups.html.haml b/app/views/modals/_groups.html.haml index 43be03bcc9..4a6f092a03 100644 --- a/app/views/modals/_groups.html.haml +++ b/app/views/modals/_groups.html.haml @@ -1,7 +1,9 @@ %h2 %i.ofn-i_035-groups - Groups / Regions -%p These are the organisations and relationships between hubs which make up the Open Food Network. -%p Some groups are clustered by location or council, others by non-geographic similarities. + = t :modal_groups +%p + = t :modal_groups +%p + = t :modal_groups %a.close-reveal-modal{"ng-click" => "$close()"} - %i.ofn-i_009-close \ No newline at end of file + %i.ofn-i_009-close diff --git a/app/views/modals/_learn_more.html.haml b/app/views/modals/_learn_more.html.haml index 58a1bf260d..3a7330aee3 100644 --- a/app/views/modals/_learn_more.html.haml +++ b/app/views/modals/_learn_more.html.haml @@ -1,10 +1,17 @@ -%h2 How it works -%h5 Shop the Open Food Network -%p Search for a food hub near you to start shopping! You can expand each food hub to see what kinds of goodies are available, and click through to start shopping. (You can only shop one food hub at a time.) -%h5 Pick-ups, delivery & shipping costs -%p Some food hubs deliver to your door, while others require you to pick-up your purchases. You can see which options are available on the homepage, and select which you'd like at the shopping and check-out pages. Delivery will cost more, and pricing differs from hub-to-hub. Each food hub is a sales point with independent business operations and logisitics - so variations between hubs are to be expected. -%h5 Learn more -%p If you want to learn more about the Open Food Network, how it works, and get involved, check out: +%h2 + = t :modal_how +%h5 + = t :modal_how_shop +%p + = t :modal_how_shop_explained +%h5 + = t :modal_how_pickup +%p + = t :modal_how_pickup_explained +%h5 + = t :modal_how +%p + = t :modal_how %a.button.neutral-btn.dark{:href => "http://www.openfoodnetwork.org" , :target => "_blank" } Open Food Network %a.close-reveal-modal{"ng-click" => "$close()"} %i.ofn-i_009-close diff --git a/app/views/modals/_producers.html.haml b/app/views/modals/_producers.html.haml index 069ec9e3b6..6e87e0d5c4 100644 --- a/app/views/modals/_producers.html.haml +++ b/app/views/modals/_producers.html.haml @@ -1,6 +1,7 @@ %h2 %i.ofn-i_036-producers - Producers -%p Our producers make all the delicious food you can shop for on the Open Food Network. + = t :modal_producers +%p + = t :modal_producers_explained %a.close-reveal-modal{"ng-click" => "$close()"} - %i.ofn-i_009-close \ No newline at end of file + %i.ofn-i_009-close diff --git a/app/views/open_food_network/cart/_show.html.haml b/app/views/open_food_network/cart/_show.html.haml index 5a633cf05b..5cebd3f40c 100644 --- a/app/views/open_food_network/cart/_show.html.haml +++ b/app/views/open_food_network/cart/_show.html.haml @@ -1,6 +1,6 @@ / %script = Spree.api_key = raw(try_spree_current_user.try(:spree_api_key).to_s.inspect) -Current cart for: += t :ofn_cart_headline = spree_current_user.andand.email %div{ 'ng-app' => 'store', 'ng-controller' => 'CartCtrl', 'ng-init' => "loadCart(#{spree_current_user.andand.cart.andand.id});" } {{cart}} {{state}} @@ -8,20 +8,27 @@ Current cart for: %br %ul %li(ng-repeat="order in cart.orders") - %strong Distributor: + %strong + = t :ofn_cart_distributor {{order.distributor}} - %strong Order cycle: + %strong + = t :ofn_cart_oc {{order.order_cycle.andand.name}} - %strong From: + %strong + = t :ofn_cart_from {{order.order_cycle.andand.orders_open_at}} - %strong To: + %strong + = t :ofn_cart_to {{order.order_cycle.andand.orders_close_at}} %ul %li(ng-repeat="line_item in order.line_items") - %strong Product: + %strong + = t :ofn_cart_product {{line_item.name}} - %strong Quantity: + %strong + = t :ofn_cart_quatity {{line_item.quantity}} - %button Buy me + %button + = t :ofn_cart_send %br diff --git a/app/views/order_cycles/_choice.html.haml b/app/views/order_cycles/_choice.html.haml index 59effe0d0f..7b1cfeb444 100644 --- a/app/views/order_cycles/_choice.html.haml +++ b/app/views/order_cycles/_choice.html.haml @@ -3,13 +3,16 @@ #distribution-choice - if current_distributor.present? %p - %strong Hub: + %strong + = t :ocs_choice_distributor = current_distributor.name - if current_order_cycle.present? %p - %strong Order Cycle: + %strong + = t :ocs_choice_oc = current_order_cycle.name - if current_distributor.nil? && current_order_cycle.nil? - %p You have not yet picked where you will get your order from. + %p + = t :ocs_choice_text diff --git a/app/views/order_cycles/_orders_closed.html.haml b/app/views/order_cycles/_orders_closed.html.haml index ea3d221622..782c680add 100644 --- a/app/views/order_cycles/_orders_closed.html.haml +++ b/app/views/order_cycles/_orders_closed.html.haml @@ -1,19 +1,17 @@ .columns.two= image_tag 'pickup.png' .columns.nine - %h2 Orders are currently closed for this hub + %h2 + = t :ocs_closed_headline %p - if most_recently_closed = OrderCycle.most_recently_closed_for(@enterprise) - The last cycle closed - = distance_of_time_in_words_to_now most_recently_closed.orders_close_at - ago. - Please contact your hub directly to see if they accept late orders, or wait until the next cycle opens. + = t :ocs_closed_time, time: distance_of_time_in_words_to_now(most_recently_closed.orders_close_at) + = t :ocs_closed_contact - if next_oc = OrderCycle.first_opening_for(@enterprise) %h4 - The next order cycle opens in - = distance_of_time_in_words_to_now next_oc.orders_open_at + = t :ocs_closed_opens, time: distance_of_time_in_words_to_now(next_oc.orders_open_at) %p - = "Email: #{current_distributor.email}" if current_distributor.email + = t(:ocs_closed_email, email: current_distributor.email_address) if current_distributor.email_address %br/ - = "Phone: #{current_distributor.phone}" if current_distributor.phone + = t(:ocs_closed_phone, phone: current_distributor.phone) if current_distributor.phone diff --git a/app/views/order_cycles/_orders_current.html.haml b/app/views/order_cycles/_orders_current.html.haml index a424e8ca6c..185011690d 100644 --- a/app/views/order_cycles/_orders_current.html.haml +++ b/app/views/order_cycles/_orders_current.html.haml @@ -1,10 +1,12 @@ .columns.six - %h1= "Your order will be ready on #{pickup_time}" + %h1 + = t :ocs_pickup_time, pickup_time: pickup_time %i - = link_to 'Change Collection Date', spree.clear_orders_path, :id => 'reset_order_cycle' - (This will reset your cart) + = link_to t(:ocs_change_date), spree.clear_orders_path, :id => 'reset_order_cycle' + = t :ocs_change_date_notice .columns.five .row - %strong ORDERS CLOSE + %strong + = t :ocs_close_time .countdown-panel %h1= distance_of_time_in_words_to_now(current_order_cycle.orders_close_at) diff --git a/app/views/order_cycles/_orders_open.html.haml b/app/views/order_cycles/_orders_open.html.haml index 8647a85d33..66ee9509e6 100644 --- a/app/views/order_cycles/_orders_open.html.haml +++ b/app/views/order_cycles/_orders_open.html.haml @@ -1,10 +1,12 @@ .columns.two= image_tag 'pickup.png' .columns.six - %h2 When do you want your order? - %p No products are displayed until you select a date. + %h2 + = t :ocs_when_headline + %p + = t :ocs_when_text .columns.three = form_for current_order(true), :html => {:id => 'order_cycle_select'} do |f| = f.hidden_field :distributor_id, :value => @enterprise.id .order-cycles - = f.select :order_cycle_id, order_cycle_options, {include_blank: 'Closing On'} - = hidden_field_tag :commit, 'Choose Order Cycle' + = f.select :order_cycle_id, order_cycle_options, {include_blank: t(:ocs_when_closing)} + = hidden_field_tag :commit, t(:ocs_when_choose) diff --git a/app/views/order_cycles/_selection.html.haml b/app/views/order_cycles/_selection.html.haml index fd4b2737e8..949766a162 100644 --- a/app/views/order_cycles/_selection.html.haml +++ b/app/views/order_cycles/_selection.html.haml @@ -11,5 +11,5 @@ = render partial: "order_cycles/orders_open" %p - %strong= link_to "List View", shop_path + %strong= link_to t(:ocs_list), shop_path diff --git a/app/views/producer_mailer/order_cycle_report.text.haml b/app/views/producer_mailer/order_cycle_report.text.haml new file mode 100644 index 0000000000..93748395ce --- /dev/null +++ b/app/views/producer_mailer/order_cycle_report.text.haml @@ -0,0 +1,22 @@ +Dear #{@producer.name}, +\ +We now have all the consumer orders for the next food drop. +\ +- if @receival_instructions + Stock pickup/delivery instructions: + = @receival_instructions + +\ +Orders summary +================ +\ +Here is a summary of the orders for your products: +\ +- @line_items.each_pair do |variant, line_item| + #{variant.sku} - #{raw(variant.product.supplier.name)} - #{raw(variant.product_and_full_name)} (QTY: #{line_item.quantity}) @ #{line_item.single_money} = #{line_item.display_amount} +\ +Thanks and best wishes, +#{@coordinator.name} +#{@coordinator.address.address1}, #{@coordinator.address.city}, #{@coordinator.address.zipcode} +#{@coordinator.phone} +#{@coordinator.email} diff --git a/app/views/producers/_fat.html.haml b/app/views/producers/_fat.html.haml index 99099a31f8..9faa239c02 100644 --- a/app/views/producers/_fat.html.haml +++ b/app/views/producers/_fat.html.haml @@ -1,58 +1,62 @@ .row.active_table_row{"ng-if" => "open()", "ng-click" => "toggle($event)", "ng-class" => "{'open' : !ofn-i_032-closed-sign()}"} - + .columns.small-12.medium-7.large-7.fat / Will add in long description available once clean up HTML formatting producer.long_description %div{"bo-if" => "producer.description"} - %label About us + %label + = t :producers_about %img.right.show-for-medium-up{"bo-src" => "producer.logo" } %p.text-small{ "bo-text" => "producer.description"} %div.show-for-medium-up{"bo-if" => "producer.description.length==0"} %label   .columns.small-12.medium-5.large-5.fat - + %div{"bo-if" => "producer.supplied_taxons"} - %label Shop for + %label + = t :producers_buy %p.trans-sentence %span.fat-taxons{"ng-repeat" => "taxon in producer.supplied_taxons"} %render-svg{path: "{{taxon.icon}}"} %span{"bo-text" => "taxon.name"} - + %div.show-for-medium-up{"ng-if" => "producer.supplied_taxons.length==0"}   - %div{"bo-if" => "producer.email || producer.website || producer.phone"} - %label Contact - - %p.word-wrap{"bo-if" => "producer.phone"} - Call - %span{"bo-text" => "producer.phone"} + %div{"bo-if" => "producer.email_address || producer.website || producer.phone"} + %label + = t :producers_contact - %p.word-wrap{"bo-if" => "producer.email"} - %a{"bo-href" => "producer.email | stripUrl", target: "_blank", mailto: true} - %span.email{"bo-bind" => "producer.email | stripUrl"} + %p.word-wrap{"bo-if" => "producer.phone"} + = t :producers_contact_phone + %span{"bo-text" => "producer.phone"} + + %p.word-wrap{"bo-if" => "producer.email_address"} + %a{"bo-href" => "producer.email_address | stripUrl", target: "_blank", mailto: true} + %span.email{"bo-bind" => "producer.email_address | stripUrl"} %p.word-wrap{"bo-if" => "producer.website"} %a{"bo-href-i" => "http://{{producer.website | stripUrl}}", target: "_blank" } %span{"bo-bind" => "producer.website | stripUrl"} %div{"bo-if" => "producer.twitter || producer.facebook || producer.linkedin || producer.instagram"} - %label Follow + %label + = t :producers_social .follow-icons{bindonce: true} - %span{"bo-if" => "producer.twitter"} + %span{"bo-if" => "producer.twitter"} %a{"bo-href-i" => "http://twitter.com/{{producer.twitter}}", target: "_blank"} %i.ofn-i_041-twitter - + %span{"bo-if" => "producer.facebook"} %a{"bo-href-i" => "http://{{producer.facebook | stripUrl}}", target: "_blank"} %i.ofn-i_044-facebook - + %span{"bo-if" => "producer.linkedin"} %a{"bo-href-i" => "http://{{producer.linkedin | stripUrl}}", target: "_blank"} %i.ofn-i_042-linkedin - + %span{"bo-if" => "producer.instagram"} - %a{"bo-href-i" => "http://instagram.com/{{producer.instagram}}", target: "_blank"} + %a{"bo-href-i" => "http://instagram.com/{{producer.instagram}}", target: "_blank"} %i.ofn-i_043-instagram .row.active_table_row.pad-top{"ng-if" => "open()", "bo-if" => "producer.hubs"} @@ -60,19 +64,16 @@ .row .columns.small-12.fat %div{"bo-if" => "producer.name"} - %label - Shop for - %span.turquoise{"bo-text" => "producer.name"} - products at: + %label + = t :producers_buy_at_html, {enterprise: ''.html_safe} %div.show-for-medium-up{"bo-if" => "!producer.name"}   .row.cta-container .columns.small-12 - %a.cta-hub{"ng-repeat" => "hub in producer.hubs | orderBy:'-active'", - "bo-href" => "hub.path", "ofn-empties-cart" => "hub", + %a.cta-hub{"ng-repeat" => "hub in producer.hubs | visible | orderBy:'-active'", + "bo-href" => "hub.path", "ofn-change-hub" => "hub", "bo-class" => "{primary: hub.active, secondary: !hub.active}"} %i.ofn-i_033-open-sign{"bo-if" => "hub.active"} %i.ofn-i_032-closed-sign{"bo-if" => "!hub.active"} .hub-name{"bo-text" => "hub.name"} .button-address{"bo-bind" => "[hub.address.city, hub.address.state_name] | printArray"} - diff --git a/app/views/producers/_filters.html.haml b/app/views/producers/_filters.html.haml index ec23c05b92..97cc204cab 100644 --- a/app/views/producers/_filters.html.haml +++ b/app/views/producers/_filters.html.haml @@ -1,15 +1,15 @@ --# .row --# = render partial: 'shared/components/filter_controls' --# .small-12.medium-6.columns.text-right --#   --# --# .row.animate-show{"ng-show" => "filtersActive"} --# .small-12.columns --# .row.filter-box --# .small-12.columns --# %h5.tdhead --# .light Filter by --# Type --# %ul.small-block-grid-2.medium-block-grid-4.large-block-grid-6 --# %filter-selector{objects: "Enterprises.producers | searchEnterprises:query | taxonsOf", "active-selectors" => "activeTaxons"} --# = render partial: 'shared/components/filter_box' +.row + = render partial: 'shared/components/filter_controls' + .small-12.medium-6.columns.text-right +   + +.row.animate-show{"ng-show" => "filtersActive"} + .small-12.columns + .row.filter-box + .small-12.columns + %h5.tdhead + .light + = t :producers_filter + = t :producers_filter_type + %filter-selector.small-block-grid-2.medium-block-grid-4.large-block-grid-6{"selector-set" => "filterSelectors", objects: "producers_to_filter | searchEnterprises:query | taxonsOf", "active-selectors" => "activeTaxons"} + = render partial: 'shared/components/filter_box' diff --git a/app/views/producers/index.html.haml b/app/views/producers/index.html.haml index 0e46795701..e842568cb3 100644 --- a/app/views/producers/index.html.haml +++ b/app/views/producers/index.html.haml @@ -1,8 +1,13 @@ -= inject_enterprises -.producers.pad-top{"ng-controller" => "EnterprisesCtrl"} +- content_for(:title) do + = t :producers_title + += inject_enterprises + +.producers{"ng-controller" => "EnterprisesCtrl", "ng-cloak" => true} .row .small-12.columns.pad-top - %h1 Find local producers + %h1 + = t :producers_headline = render partial: "shared/components/enterprise_search" = render partial: "producers/filters" @@ -11,7 +16,6 @@ .small-12.columns .active_table %producer.active_table_node.row.animate-repeat{id: "{{producer.path}}", - "scroll-after-load" => true, "ng-repeat" => "producer in filteredEnterprises = (Enterprises.producers | visible | searchEnterprises:query | taxons:activeTaxons)", "ng-controller" => "ProducerNodeCtrl", "ng-class" => "{'closed' : !open(), 'open' : open(), 'inactive' : !producer.active}", diff --git a/app/views/producers/signup.html.haml b/app/views/producers/signup.html.haml new file mode 100644 index 0000000000..0afead9415 --- /dev/null +++ b/app/views/producers/signup.html.haml @@ -0,0 +1,52 @@ +- content_for(:title) do + = t :producers_signup_title + +#panes + #producer-signup.pane + .row.header + .small-12.medium-12.columns.text-center + %h2 + = t :producers_signup_headline + .row.content + .small-12.medium-6.medium-offset-3.columns.text-center + %p.text-big + = t :producers_signup_motivation + %br + %a.button.transparent{href: "/register"} + = t :producers_signup_send + .pane + .row + .small-12.medium-10.medium-offset-1.columns.text-center + %h2 + = t :producers_signup_enterprise + -# %p.text-big + -# If there is a time-sensitive offer you can write it here, e.g. + -# Sign up before 30th June for an extra month free! + %br + = ContentConfig.producer_signup_pricing_table_html.html_safe + + #producer-case-studies + .row + .small-12.medium-10.medium-offset-1.columns + %h2.text-center + = t :producers_signup_studies + %br + = ContentConfig.producer_signup_case_studies_html.html_safe + + .pane#cta + .row + .small-12.medium-6.medium-offset-3.columns.text-center + %h2 + = t :producers_signup_cta_headline + %p.text-big Start with a free profile, and expand when you're ready! + %a.button.transparent{href: "/register"} + = t :producers_signup_cta_action + + #producer-details.pane.footer-pad + .row + .small-12.medium-10.medium-offset-1.columns + %h2.text-center + = t :producers_signup_detail + = ContentConfig.producer_signup_detail_html.html_safe + += render partial: "shared/footer" diff --git a/app/views/products/_list.html.haml b/app/views/products/_list.html.haml index 3770387175..d9fa7cbfbd 100644 --- a/app/views/products/_list.html.haml +++ b/app/views/products/_list.html.haml @@ -1,11 +1,17 @@ %table#product-list %thead - %th Item - %th Description - %th Variant - %th Quantity - %th Available? - %th Price + %th + = t :products_item + %th + = t :products_description + %th + = t :products_variant + %th + = t :products_quantity + %th + = t :products_available + %th + = t :products_price - list.each do |product| %tr diff --git a/app/views/registration/index.html.haml b/app/views/registration/index.html.haml index d110e98657..fdeef18534 100644 --- a/app/views/registration/index.html.haml +++ b/app/views/registration/index.html.haml @@ -1,3 +1,6 @@ +- content_for(:title) do + = t :register_title + = inject_spree_api_key = inject_available_countries = inject_enterprise_attributes diff --git a/app/views/shared/_account_sidebar.html.haml b/app/views/shared/_account_sidebar.html.haml deleted file mode 100644 index e572979eb1..0000000000 --- a/app/views/shared/_account_sidebar.html.haml +++ /dev/null @@ -1,20 +0,0 @@ --##account{"ng-controller" => "AccountSidebarCtrl"} - -#.row - -#.panel - -#%p - -#%strong= link_to "Manage my account", account_path - -#- if enterprise_user? - -#%strong= link_to "Enterprise admin", admin_path - -#- if order = last_completed_order - -#%dl - -#%dt Current Hub: - -#%dd= link_to current_distributor.name, main_app.shop_path - -#%br - -#%dt Last hub: - -#%dd - -#- if order.distributor != current_distributor - -#= link_to "#{order.distributor.name}".html_safe, "", - -#{class: distributor_link_class(order.distributor), - -#"ng-click" => "emptyCart('#{main_app.enterprise_shop_path(order.distributor)}', $event)"} - -#- else - -#= order.distributor.name diff --git a/app/views/shared/_analytics.html.haml b/app/views/shared/_analytics.html.haml index ee9ba69923..16ad08ff5f 100644 --- a/app/views/shared/_analytics.html.haml +++ b/app/views/shared/_analytics.html.haml @@ -1,8 +1,9 @@ -:javascript - (function(i,s,o,g,r,a,m){i['GoogleAnalyticsObject']=r;i[r]=i[r]||function(){ - (i[r].q=i[r].q||[]).push(arguments)},i[r].l=1*new Date();a=s.createElement(o), - m=s.getElementsByTagName(o)[0];a.async=1;a.src=g;m.parentNode.insertBefore(a,m) - })(window,document,'script','//www.google-analytics.com/analytics.js','ga'); +- if Rails.env.production? + :javascript + (function(i,s,o,g,r,a,m){i['GoogleAnalyticsObject']=r;i[r]=i[r]||function(){ + (i[r].q=i[r].q||[]).push(arguments)},i[r].l=1*new Date();a=s.createElement(o), + m=s.getElementsByTagName(o)[0];a.async=1;a.src=g;m.parentNode.insertBefore(a,m) + })(window,document,'script','//www.google-analytics.com/analytics.js','ga'); - ga('create', 'UA-62912229-1', 'auto'); - ga('send', 'pageview'); + ga('create', 'UA-62912229-1', 'auto'); + ga('send', 'pageview'); diff --git a/app/views/shared/_case_study.html.haml b/app/views/shared/_case_study.html.haml new file mode 100644 index 0000000000..67c389301e --- /dev/null +++ b/app/views/shared/_case_study.html.haml @@ -0,0 +1,7 @@ +.case-study + %img.case-study-img{src: img_src, width: "100", height: "100", title: title} + %h4= title + %p.text-small= description + %a{href: link, target: "_blank"} + %strong + = t :label_more diff --git a/app/views/shared/_footer.html.haml b/app/views/shared/_footer.html.haml index e06ae0f0aa..fd7f5faf4e 100644 --- a/app/views/shared/_footer.html.haml +++ b/app/views/shared/_footer.html.haml @@ -1,60 +1,158 @@ %footer - .row.landing-page-row - .small-12.medium-4.columns.text-left - %h4 Email us - %p - %a{href: "hello@openfoodnetwork.org".reverse, target: '_blank', mailto: true} - %span.email - = "hello@openfoodnetwork.org".reverse - %h4 Follow us - %p - %a{title:'Follow us on Facebook', href: 'https://www.facebook.com/OpenFoodNet', target: '_blank'} - %i.ofn-i_044-facebook - Facebook    - %a{title:'Follow us on Twitter', href: 'https://twitter.com/OpenFoodNet', target: '_blank'} - %i.ofn-i_041-twitter - Twitter    - %a{title:'Join our group on LinkedIn', href: 'http://www.linkedin.com/groups/Open-Food-Foundation-4743336', target: '_blank'} - %i.ofn-i_042-linkedin - LinkedIn - .small-12.medium-3.columns.text-left - %h4 Getting around - %ul.bullet-list - %li - %a{href: "/shop"} Shop - %li - %a{href: "/map"} Map - %li - %a{href: "/producers"} Producers - %li - %a{href: "/groups"} Groups - .small-12.medium-2.columns.text-left - %h4 Producers - %p - = t :producers_join - %p - %a{href: "/register"} Register now - .small-12.medium-3.columns.text-left - %h4 About us - %p OFN is a network of independent online food stores that connect farmers and food hubs with individuals and local businesses. It gives farmers and food hubs an easier and fairer way to distribute their food. - .row.landing-page-row - .small-12.columns.text-center.pad-top - %hr - %h5.pad-top - %a{title: 'Open Food Network', href:'http://www.openfoodnetwork.org', target: '_blank' } openfoodnetwork.org - %br - © Copyright - = Date.today.year - Open Food Foundation - %p - %small - %a{href:"https://creativecommons.org/licenses/by-sa/3.0/", target: "_blank" } Creative Commons Attribution-ShareAlike 3.0 Unported (CC BY-SA 3.0) - %p - %small - %a{href:"/Terms-of-service.pdf", target: "_blank" } Site terms & conditions - | - %a{href:"https://github.com/openfoodfoundation/openfoodnetwork", target: "_blank" } Open Source & developer info on GitHub + .footer-global + .row + .small-12.columns.text-center + .logo + %img{src: "/assets/logo-white-notext.png", width: "120px"} + .row + .small-12.medium-8.medium-offset-2.columns.text-center + .alert-box + %a.big-alert{href: "http://www.openfoodnetwork.org", target: "_blank"} + %h6 + = t :alert_selling_on_ofn +   + %strong + = t :alert_start_here + %i.ofn-i_054-point-right + .row + .small-12.medium-4.medium-offset-2.columns.text-center + %h6 + = t :footer_global_headline + %p + %a{href: "http://www.openfoodnetwork.org", target: "_blank"} + = t :footer_global_home + %span | + %a{href: "http://www.openfoodnetwork.org/news/", target: "_blank"} + = t :footer_global_news + %span | + %a{href: "http://www.openfoodnetwork.org/about/history-team/", target: "_blank"} + = t :footer_global_about + %span | + %a{href: "http://www.openfoodnetwork.org/contact/", target: "_blank"} + = t :footer_global_contact - // To be added when Guy's pretty landing page is up: - //| - //%a{href:'' } Developers + + .small-12.medium-4.columns.text-center + %h6 + = t :footer_sites_headline + %p + %a{href: "http://dev.openfoodnetwork.org", target: "_blank"} + = t :footer_sites_developer + %span | + %a{href: "http://community.openfoodnetwork.org", target: "_blank"} + = t :footer_sites_community + %span | + %a{href: "http://www.openfoodnetwork.org/platform/user-guide/", target: "_blank"} + = t :footer_sites_userguide + + .medium-2.columns.text-center + / Placeholder + + .footer-local + .row + .small-12.medium-2.medium-offset-2.columns.text-center + %p.secure-icon + %i.ofn-i_017-locked + .small-12.medium-6.columns.text-center + %p.text-big.secure-text + = t :footer_secure + %p.secure-text + = t :footer_secure_text + .small-12.medium-2.columns + + .row + .small-12.medium-8.medium-offset-2.columns.text-center + %hr.hr-light + %br + + .row + .small-6.medium-3.medium-offset-2.columns.text-left + // This is the instance-managed set of links: + %h4 + = t :footer_contact_headline + %p.social-icons + - if ContentConfig.footer_facebook_url.present? + %a{href: ContentConfig.footer_facebook_url} + %i.ofn-i_044-facebook + - if ContentConfig.footer_twitter_url.present? + %a{href: ContentConfig.footer_twitter_url} + %i.ofn-i_041-twitter + - if ContentConfig.footer_instagram_url.present? + %a{href: ContentConfig.footer_instagram_url} + %i.ofn-i_043-instagram + - if ContentConfig.footer_linkedin_url.present? + %a{href: ContentConfig.footer_linkedin_url} + %i.ofn-i_042-linkedin + - if ContentConfig.footer_googleplus_url.present? + %a{href: ContentConfig.footer_googleplus_url} + %i.ofn-i_046-g + - if ContentConfig.footer_pinterest_url.present? + %a{href: ContentConfig.footer_pinterest_url} + %i.ofn-i_045-pintrest + - if ContentConfig.footer_email.present? + %p + %a{href: ContentConfig.footer_email.reverse, mailto: true, target: '_blank'} + = t :footer_contact_email + = render_markdown(ContentConfig.footer_links_md).html_safe + + + .small-6.medium-3.columns.text-left + %h4 + = t :footer_nav_headline + %p + %a{href: "/shops"} + = t :label_shops + %p + %a{href: "/map"} + = t :label_map + %p + %a{href: "/producers"} + = t :label_producers + %p + %a{href: "/groups"} + = t :label_groups + %p + %a{href: ContentConfig.footer_about_url} + = t :label_about + + .small-12.medium-2.columns.text-left + %h4 + = t :footer_join_headline + %p + %a{href: "/producers/signup"} + = t :footer_join_producers + %p + %a{href: "/shops/signup"} + = t :footer_join_hubs + %p + %a{href: "/groups/signup"} + = t :footer_join_groups + %p + %a{href: "http://www.openfoodnetwork.org/platform/food-system-partners/", target: "_blank"} + = t :footer_join_partners + + .medium-2.columns.text-center + / Placeholder + + .row + .small-12.medium-8.medium-offset-2.columns.text-center + %hr.hr-light + %br + + .row + .small-12.medium-3.medium-offset-2.columns.text-left + %a{href: root_path} + %img{src: ContentConfig.footer_logo.url, width: "220"} + .small-12.medium-5.columns.text-left + %p.text-small + = t :footer_legal_call + %a{href: ContentConfig.footer_tos_url} + = t :footer_legal_tos + | + = t :footer_legal_visit + %a{href:"https://github.com/openfoodfoundation/openfoodnetwork", target: "_blank"} GitHub + %p.text-small + = t :footer_legal_text_html, {content_license: link_to('CC BY-SA 3.0', 'https://creativecommons.org/licenses/by-sa/3.0/'), code_license: link_to('AGPL 3', 'https://tldrlegal.com/license/gnu-affero-general-public-license-v3-(agpl-3.0)')} + + .medium-2.columns.text-center + / Placeholder diff --git a/app/views/shared/_ie_warning.html.haml b/app/views/shared/_ie_warning.html.haml index b5cd980ad6..2239fa7450 100644 --- a/app/views/shared/_ie_warning.html.haml +++ b/app/views/shared/_ie_warning.html.haml @@ -3,23 +3,29 @@ .small-4.large-2.columns %i.ofn-i_012-warning .small-8.large-10.columns - %h3 Your browser is out of date :-( - %p For the best Open Food Network experience, we strongly recommend upgrading your browser: + %h3 + = t :ie_warning_headline + %p + = t :ie_warning_text .row .small-4.columns.browserbtn %a.browserlogo{href: "https://www.google.com/intl/en_au/chrome/browser/", target: "_blank"} %img{src: "assets/browser-logos/chrome.png"} - %a{href: "https://www.google.com/intl/en_au/chrome/browser/", target: "_blank"} Download Chrome + %a{href: "https://www.google.com/intl/en_au/chrome/browser/", target: "_blank"} + = t :ie_warning_chrome .small-4.columns.browserbtn %a.browserlogo{href: "http://www.mozilla.org/en-US/firefox/new/", target: "_blank"} %img{src: "assets/browser-logos/firefox.png"} - %a{href: "http://www.mozilla.org/en-US/firefox/new/", target: "_blank"} Download Firefox + %a{href: "http://www.mozilla.org/en-US/firefox/new/", target: "_blank"} + = t :ie_warning_firefox .small-4.columns.browserbtn %a.browserlogo{href: "http://windows.microsoft.com/en-AU/internet-explorer/download-ie", target: "_blank"} %img{src: "assets/browser-logos/internet-explorer.png"} - %a{href: "http://windows.microsoft.com/en-AU/internet-explorer/download-ie", target: "_blank"} Upgrade Internet Explorer + %a{href: "http://windows.microsoft.com/en-AU/internet-explorer/download-ie", target: "_blank"} + = t :ie_warning_ie .row.ie-msg .small-12.large-12.columns .text-center - %em Can't upgrade your browser? Try Open Food Network on your smartphone :-) + %em + = t :ie_warning_other %a#closeie.close{href: "#"} × diff --git a/app/views/shared/_login.html.haml b/app/views/shared/_login.html.haml index c46d0f424f..5c4d579944 100644 --- a/app/views/shared/_login.html.haml +++ b/app/views/shared/_login.html.haml @@ -1,12 +1,12 @@ - if spree_current_user.nil? - %li#login-link= link_to "Login", "#login", id: "sidebarLoginButton", class: "sidebar-button" + %li#login-link= link_to t(:label_login), "#login", id: "sidebarLoginButton", class: "sidebar-button" %li#login-name.hide %li.divider - %li#sign-up-link= link_to "Sign Up", "#signup", id: "sidebarSignUpButton", class: "sidebar-button" + %li#sign-up-link= link_to t(:label_signup), "#signup", id: "sidebarSignUpButton", class: "sidebar-button" %li#sign-out-link.hide= link_to "Sign Out", "/logout" - else - %li#login-link.hide= link_to "Login", "#sidebar", id: "sidebarLoginButton", class: "sidebar-button" + %li#login-link.hide= link_to t(:label_login), "#sidebar", id: "sidebarLoginButton", class: "sidebar-button" %li#login-name= link_to "#{spree_current_user.email}", "#" %li.divider - %li#sign-up-link.hide= link_to "Sign Up", "#" - %li#sign-out-link= link_to "Sign Out", "/logout" + %li#sign-up-link.hide= link_to t(:label_signup), "#" + %li#sign-out-link= link_to t(:label_logout), "/logout" diff --git a/app/views/shared/_sidebar.html.haml b/app/views/shared/_sidebar.html.haml deleted file mode 100644 index 8fdcf7c2b5..0000000000 --- a/app/views/shared/_sidebar.html.haml +++ /dev/null @@ -1,12 +0,0 @@ --#%aside#sidebar.right-off-canvas-menu{ role: "complementary", "ng-controller" => "SidebarCtrl", --#"ng-class" => "{'active' : Sidebar.active()}"} - - -#- if spree_current_user.nil? - -#%tabset - -#= render partial: "shared/login_sidebar" - -#= render partial: "shared/signup_sidebar" - -#= render partial: "shared/forgot_sidebar" - -#- else - -#= render partial: "shared/account_sidebar" - - -#= yield :sidebar diff --git a/app/views/shared/_signed_in.html.haml b/app/views/shared/_signed_in.html.haml index 69f0e61dbd..0af52d6d94 100644 --- a/app/views/shared/_signed_in.html.haml +++ b/app/views/shared/_signed_in.html.haml @@ -1,3 +1,9 @@ +- if discourse_configured? + %li + %a{href: main_app.discourse_login_path, target: '_blank'} + %span.nav-primary + = t 'label_notices' + %li.has-dropdown.not-click %a{href: "#"} @@ -9,17 +15,15 @@ %li %a{href: spree.admin_path, target:'_blank'} %i.ofn-i_021-tools - Administration + = t 'label_administration' %li %a{href: spree.account_path} %i.ofn-i_015-user - Account + = t 'label_account' = "(" + spree_current_user.email + ")" %li - %a{title: 'Log Out', href:'/logout' } + %a{title: t('label_logout'), href:'/logout' } %i.ofn-i_018-unlocked - Log out - - \ No newline at end of file + = t 'label_logout' diff --git a/app/views/shared/_signed_in_offcanvas.html.haml b/app/views/shared/_signed_in_offcanvas.html.haml index 97b3e1ebca..f34a03068f 100644 --- a/app/views/shared/_signed_in_offcanvas.html.haml +++ b/app/views/shared/_signed_in_offcanvas.html.haml @@ -1,16 +1,22 @@ +- if discourse_configured? + %li.li-menu + %a{href: main_app.discourse_login_path, target: '_blank'} + %span.nav-primary + %i.ofn-i_025-notepad + = t 'label_notices' + - if admin_user? or enterprise_user? %li %a{href: spree.admin_path, target:'_blank'} %i.ofn-i_021-tools - Admin + = t 'label_admin' %li %a{href: spree.account_path} %i.ofn-i_015-user - Account - / = spree_current_user.email + = t 'label_account' %li - %a{title: 'Log Out', href:'/logout' } + %a{title: t('label_logout'), href:'/logout' } %i.ofn-i_018-unlocked - Log out \ No newline at end of file + = t 'label_logout' diff --git a/app/views/shared/_signed_out.html.haml b/app/views/shared/_signed_out.html.haml index 237baa6bd8..1a7cbcc920 100644 --- a/app/views/shared/_signed_out.html.haml +++ b/app/views/shared/_signed_out.html.haml @@ -1,5 +1,5 @@ %li#login-link{"ng-controller" => "AuthenticationCtrl"} %a{"ng-click" => "open()"} %i.ofn-i_017-locked - %span Log in - + %span + = t 'label_login' diff --git a/app/views/shared/components/_enterprise_no_results.html.haml b/app/views/shared/components/_enterprise_no_results.html.haml index 2e6edd0875..97fa20c8d0 100644 --- a/app/views/shared/components/_enterprise_no_results.html.haml +++ b/app/views/shared/components/_enterprise_no_results.html.haml @@ -1,5 +1,4 @@ -%producer.row{"ng-show" => "filteredEnterprises.length == 0"} +- enterprises ||= 'filteredEnterprises' +%producer.row{"ng-show" => "#{enterprises}.length == 0"} %p.no-results - Sorry, no results found for - %strong {{query}}. - Try another search? + = t :search_no_results_html, query: "{{query}}".html_safe diff --git a/app/views/shared/components/_enterprise_search.html.haml b/app/views/shared/components/_enterprise_search.html.haml index 62dcec3ea6..4bb7ca446b 100644 --- a/app/views/shared/components/_enterprise_search.html.haml +++ b/app/views/shared/components/_enterprise_search.html.haml @@ -3,5 +3,5 @@ %input{type: :text, "ng-model" => "query", placeholder: t('search_by_name'), - "ng-debounce" => "150", + "ng-debounce" => "500", "ofn-disable-enter" => true} diff --git a/app/views/shared/components/_filter_box.html.haml b/app/views/shared/components/_filter_box.html.haml index efa8b582f2..564edd4150 100644 --- a/app/views/shared/components/_filter_box.html.haml +++ b/app/views/shared/components/_filter_box.html.haml @@ -1,5 +1,5 @@ -.row.filter-box.clear-filters.animate-show{"ng-show" => "filtersActive && totalActive() > 0"} +.row.filter-box.clear-filters.animate-show{"ng-show" => "filtersActive && filterSelectors.totalActive() > 0"} .small-12.columns - %a.button.secondary.small.expand{"ng-click" => "clearAll()"} + %a.button.secondary.small.expand{"ng-click" => "filterSelectors.clearAll()"} %i.ofn-i_009-close - Clear all filters + = t :components_filters_clearfilters diff --git a/app/views/shared/components/_filter_box_shopfront.html.haml b/app/views/shared/components/_filter_box_shopfront.html.haml deleted file mode 100644 index 7a2aa23296..0000000000 --- a/app/views/shared/components/_filter_box_shopfront.html.haml +++ /dev/null @@ -1,4 +0,0 @@ -%span.animate-show{"ng-show" => "filtersActive && totalActive() > 0"} - %a.button.secondary.tiny{"ng-click" => "clearAll()"} - %i.ofn-i_009-close - Clear all filters diff --git a/app/views/shared/components/_filter_controls.html.haml b/app/views/shared/components/_filter_controls.html.haml index e15d2de48d..d3141a3ea0 100644 --- a/app/views/shared/components/_filter_controls.html.haml +++ b/app/views/shared/components/_filter_controls.html.haml @@ -1,9 +1,9 @@ .small-12.medium-6.columns %a.button.success.tiny.filterbtn{"ng-click" => "filtersActive = !filtersActive", - "ng-show" => "FilterSelectorsService.selectors.length > 0"} - {{ filterText(filtersActive) }} + "ng-show" => "filterSelectors.selectors.length > 0"} + {{ filterSelectors.filterText(filtersActive) }} %i.ofn-i_005-caret-down{"ng-show" => "!filtersActive"} %i.ofn-i_006-caret-up{"ng-show" => "filtersActive"} - %a.button.secondary.tiny.filterbtn.disabled{"ng-show" => "FilterSelectorsService.selectors.length == 0"} - No filters + %a.button.secondary.tiny.filterbtn.disabled{"ng-show" => "filterSelectors.selectors.length == 0"} + = t :components_filters_nofilters diff --git a/app/views/shared/components/_filter_controls_shopfront.html.haml b/app/views/shared/components/_filter_controls_shopfront.html.haml deleted file mode 100644 index b89d55c452..0000000000 --- a/app/views/shared/components/_filter_controls_shopfront.html.haml +++ /dev/null @@ -1,8 +0,0 @@ -%a.button.success.tiny.filterbtn{"ng-click" => "filtersActive = !filtersActive", -"ng-show" => "FilterSelectorsService.selectors.length > 0"} - {{ filterText(filtersActive) }} - %i.ofn-i_005-caret-down{"ng-show" => "!filtersActive"} - %i.ofn-i_006-caret-up{"ng-show" => "filtersActive"} - -%a.button.secondary.tiny.filterbtn.disabled{"ng-show" => "FilterSelectorsService.selectors.length == 0"} - No filters diff --git a/app/views/shared/components/_show_profiles.html.haml b/app/views/shared/components/_show_profiles.html.haml index 638cc47c27..84a55ae405 100644 --- a/app/views/shared/components/_show_profiles.html.haml +++ b/app/views/shared/components/_show_profiles.html.haml @@ -1,7 +1,7 @@ .small-12.medium-6.columns.text-right .profile-checkbox - %button.button.secondary.tiny.help-btn.ng-scope{:popover => "Profiles do not have a shopfront on the Open Food Network, but may have their own physical or online shop elsewhere", "popover-placement" => "left"}>< + %button.button.secondary.tiny.help-btn.ng-scope{:popover => t(:components_profiles_popover, sitename: Spree::Config[:site_name]), "popover-placement" => "left"}>< %i.ofn-i_013-help %label %input{"ng-model" => "show_profiles", type: "checkbox", name: "profile"} - Show profiles + = t :components_profiles_show diff --git a/app/views/shared/mailers/_signoff.html.haml b/app/views/shared/mailers/_signoff.html.haml index 6343ac85ae..7b10fffe97 100644 --- a/app/views/shared/mailers/_signoff.html.haml +++ b/app/views/shared/mailers/_signoff.html.haml @@ -1,8 +1,8 @@ %p   %p - Cheers, + = t :email_signoff %br - = "#{ Spree::Config[:site_name] } Team" + = t :email_signature, sitename: Spree::Config[:site_name] -%p   \ No newline at end of file +%p   diff --git a/app/views/shared/mailers/_social_and_contact.html.haml b/app/views/shared/mailers/_social_and_contact.html.haml index 81244778d6..4f5222f77d 100644 --- a/app/views/shared/mailers/_social_and_contact.html.haml +++ b/app/views/shared/mailers/_social_and_contact.html.haml @@ -6,21 +6,24 @@ %tr %td %h5 - Connect with Us: - %p - %a.soc-btn.fb{:href => "https://www.facebook.com/OpenFoodNet", :target => "_blank"} - Facebook - %a.soc-btn.tw{:href => "https://twitter.com/OpenFoodNet", :target => "_blank"} - Twitter - %a.soc-btn.li{:href => "http://www.linkedin.com/groups/Open-Food-Foundation-4743336", :target => "_blank"} - LinkedIn + = t :email_social + %p.social-icons + - if ContentConfig.footer_facebook_url.present? + %a.soc-btn.fb{href: ContentConfig.footer_facebook_url} + Facebook + - if ContentConfig.footer_twitter_url.present? + %a.soc-btn.tw{href: ContentConfig.footer_twitter_url} + Twitter + - if ContentConfig.footer_linkedin_url.present? + %a.soc-btn.li{href: ContentConfig.footer_linkedin_url} + LinkedIn %table.column{:align => "left"} %tr %td - %h5 - Email us: - %p + - if ContentConfig.footer_email.present? + %h5 + = t :email_contact %strong - %a{:href => "mailto:hello@openfoodnetwork.org"} - hello@openfoodnetwork.org + %a{href: ContentConfig.footer_email.reverse, mailto: true, target: '_blank'} + #{ContentConfig.footer_email} %span.clear diff --git a/app/views/shared/menu/_alert.html.haml b/app/views/shared/menu/_alert.html.haml new file mode 100644 index 0000000000..b6ee3acfb1 --- /dev/null +++ b/app/views/shared/menu/_alert.html.haml @@ -0,0 +1,10 @@ +.text-center.page-alert.fixed{ "ofn-page-alert" => true } + .alert-box + %a.alert-cta{href: registration_path, target: "_blank"} + %h6 + = t 'alert_selling_on_ofn' +   + %strong + = t 'alert_start_here' + %i.ofn-i_054-point-right + %a.close{ ng: { click: "close()" } } × diff --git a/app/views/shared/menu/_cart.html.haml b/app/views/shared/menu/_cart.html.haml index f8287c8f61..f67ff2ffe1 100644 --- a/app/views/shared/menu/_cart.html.haml +++ b/app/views/shared/menu/_cart.html.haml @@ -1,15 +1,21 @@ -%span.cart-span{"ng-controller" => "CartCtrl", "ng-class" => "{ dirty: Cart.dirty || Cart.empty() }"} +%span.cart-span{"ng-controller" => "CartCtrl", "ng-class" => "{ dirty: Cart.dirty || Cart.empty(), 'pure-dirty': Cart.dirty }"} %a#cart.icon{cart: true} %span.nav-branded %i.ofn-i_027-shopping-cart %span {{ Cart.total_item_count() }} - items + = t 'items' .joyride-tip-guide{"ng-class" => "{ in: open }", "ng-show" => "open"} %span.joyride-nub.top .joyride-content-wrapper - %h5 Your shopping cart + %h5 + = t 'cart_headline' + .buttons.text-right + %a.button.secondary.tiny.add_to_cart{ href: cart_path, type: :submit, "ng-disabled" => "Cart.dirty || Cart.empty()", "ng-class" => "{ dirty: Cart.dirty }" } + = "{{ Cart.dirty ? '#{t(:cart_updating)}' : (Cart.empty() ? '#{t(:cart_empty)}' : '#{t(:cart_edit)}' ) }}" + %a.button.primary.tiny{href: checkout_path, "ng-disabled" => "Cart.dirty || Cart.empty()"} + = t 'checkout' %table %tr.product-cart{"ng-repeat" => "line_item in Cart.line_items_present()", "ng-controller" => "LineItemCtrl", "id" => "cart-variant-{{ line_item.variant.id }}"} @@ -32,11 +38,14 @@ %table{"ng-show" => "Cart.line_items_present().length > 0"} %tr.total-cart %td - %em Total: + %em + = t 'total' + \: %td.text-right %strong {{ Cart.total() | localizeCurrency }} .buttons.text-right %a.button.secondary.tiny.add_to_cart{ href: cart_path, type: :submit, "ng-disabled" => "Cart.dirty || Cart.empty()", "ng-class" => "{ dirty: Cart.dirty }" } - {{ Cart.dirty ? 'Updating cart...' : (Cart.empty() ? 'Cart empty' : 'Edit your cart' ) }} - %a.button.primary.tiny{href: checkout_path, "ng-disabled" => "Cart.dirty || Cart.empty()"} Checkout now + = "{{ Cart.dirty ? '#{t(:cart_updating)}' : (Cart.empty() ? '#{t(:cart_empty)}' : '#{t(:cart_edit)}' ) }}" + %a.button.primary.tiny{href: checkout_path, "ng-disabled" => "Cart.dirty || Cart.empty()"} + = t 'checkout' diff --git a/app/views/shared/menu/_large_menu.html.haml b/app/views/shared/menu/_large_menu.html.haml index a12d0030e0..05209dff75 100644 --- a/app/views/shared/menu/_large_menu.html.haml +++ b/app/views/shared/menu/_large_menu.html.haml @@ -1,43 +1,40 @@ %nav.top-bar.show-for-large-up{'data-topbar' => true} %section.top-bar-section - %ul.left{} + %ul.left %li.ofn-logo - %a{href: root_path} - %img{src: "/assets/open-food-network-beta.png", srcset: "/assets/open-food-network-beta.svg", width: "110", height: "26"} - %li.divider - - if current_page? root_path - %li - %a{"ofn-scroll-to" => "hubs"} - %span.nav-primary Hubs - - else - %li - %a{href: root_path + "#/#hubs"} - %span.nav-primary Hubs - %li.divider + %a{href: root_path} + %img{src: ContentConfig.logo.url, width: "250", height: "51"} + %ul.center + %li + %a{href: main_app.shops_path} + %span.nav-primary + = t 'label_shops' %li %a{href: main_app.map_path} - %span.nav-primary Map - %li.divider + %span.nav-primary + = t 'label_map' %li %a{href: main_app.producers_path} - %span.nav-primary Producers - %li.divider + %span.nav-primary + = t 'label_producers' %li %a{href: main_app.groups_path} - %span.nav-primary Groups - %li.divider - %section.top-bar-section + %span.nav-primary + = t 'label_groups' + %li + %a{href: ContentConfig.footer_about_url} + %span.nav-primary + = t 'label_about' %ul.right - %li.divider - if spree_current_user.nil? = render 'shared/signed_out' - else = render 'shared/signed_in' - %li.divider %li.current_hub{"ng-controller" => "CurrentHubCtrl", "ng-show" => "CurrentHub.hub.id", "ng-cloak" => true} %a{href: main_app.shop_path} - %em Shopping @ - %span.nav-primary.nav-branded {{ CurrentHub.hub.name }} - %li.divider + %em + = t 'label_shopping' + @ + %span.nav-primary.nav-branded {{ CurrentHub.hub.name | truncate:25 }} %li.cart{"ng-cloak" => true} = render partial: "shared/menu/cart" diff --git a/app/views/shared/menu/_mobile_menu.html.haml b/app/views/shared/menu/_mobile_menu.html.haml index 5ed57917fd..71fd8ea86f 100644 --- a/app/views/shared/menu/_mobile_menu.html.haml +++ b/app/views/shared/menu/_mobile_menu.html.haml @@ -2,7 +2,13 @@ %section.left %a.left-off-canvas-toggle.menu-icon %span - %section.right + + %section.left + .ofn-logo + %a{href: root_path} + %img{src: ContentConfig.logo_mobile.url, srcset: ContentConfig.logo_mobile_svg.url, width: "75", height: "26"} + + %section.right{"ng-cloak" => true} .cart = render partial: "shared/menu/cart" %a{href: main_app.shop_path} @@ -11,36 +17,39 @@ %aside.left-off-canvas-menu.show-for-medium-down %ul.off-canvas-list %li.ofn-logo - %a{href: root_path} - %img{src: "/assets/open-food-network-beta.png", srcset: "/assets/open-food-network-beta.svg", width: "110", height: "26"} - - - if current_page? root_path - %li.li-menu + %a{href: root_path} + %img{src: ContentConfig.logo_mobile.url, srcset: ContentConfig.logo_mobile_svg.url, width: "75", height: "26"} + %li.li-menu + - if current_page? main_app.shops_path %a{"ofn-scroll-to" => "hubs"} - %span.nav-primary - %i.ofn-i_040-hub - Hubs - - else - %li.li-menu - %a{href: root_path + "#/#hubs"} - %span.nav-primary - %i.ofn-i_040-hub - Hubs + %span.nav-primary + %i.ofn-i_019-map-pin + = t 'label_shops' + - else + %a{href: main_app.shops_path} + %span.nav-primary + %i.ofn-i_019-map-pin + = t 'label_shops' %li.li-menu %a{href: main_app.map_path} - %span.nav-primary + %span.nav-primary %i.ofn-i_037-map - Map + = t 'label_map' %li.li-menu %a{href: main_app.producers_path} - %span.nav-primary + %span.nav-primary %i.ofn-i_036-producers - Producers + = t 'label_producers' %li.li-menu %a{href: main_app.groups_path} - %span.nav-primary + %span.nav-primary %i.ofn-i_035-groups - Groups + = t 'label_groups' + %li.li-menu + %a{href: ContentConfig.footer_about_url} + %span.nav-primary + %i.ofn-i_013-help + = t 'label_about' %li - if spree_current_user.nil? diff --git a/app/views/shared/unauthorized.html.haml b/app/views/shared/unauthorized.html.haml index fcf3f3bf27..988fc5e369 100644 --- a/app/views/shared/unauthorized.html.haml +++ b/app/views/shared/unauthorized.html.haml @@ -1 +1 @@ -Unauthorized += t :unauthorized diff --git a/app/views/shop/products/_filters.html.haml b/app/views/shop/products/_filters.html.haml index 786cd48662..7efaba6cc9 100644 --- a/app/views/shop/products/_filters.html.haml +++ b/app/views/shop/products/_filters.html.haml @@ -1,5 +1,5 @@ .filter-shopfront.taxon-selectors.text-right - %single-line-selectors{ objects: "Products.products | products:query | properties: activeProperties | taxonsOf", "active-selectors" => "activeTaxons"} + %single-line-selectors{ selectors: "taxonSelectors", objects: "Products.products | products:query | properties: activeProperties | taxonsOf", "active-selectors" => "activeTaxons"} .filter-shopfront.property-selectors.text-right - %single-line-selectors{ objects: "Products.products | products:query | taxons:activeTaxons | propertiesOf", "active-selectors" => "activeProperties"} + %single-line-selectors{ selectors: "propertySelectors", objects: "Products.products | products:query | taxons:activeTaxons | propertiesOf", "active-selectors" => "activeProperties"} diff --git a/app/views/shop/products/_form.html.haml b/app/views/shop/products/_form.html.haml index 0df4f022aa..18868e5c77 100644 --- a/app/views/shop/products/_form.html.haml +++ b/app/views/shop/products/_form.html.haml @@ -1,57 +1,56 @@ -%products.small-12.columns{"ng-controller" => "ProductsCtrl", "ng-show" => "order_cycle.order_cycle_id != null", -"infinite-scroll" => "incrementLimit()", "infinite-scroll-distance" => "1"} +.footer-pad.small-12.columns + %products{"ng-controller" => "ProductsCtrl", "ng-show" => "order_cycle.order_cycle_id != null", "ng-cloak" => true, + "infinite-scroll" => "incrementLimit()", "infinite-scroll-distance" => "1"} - // TODO: Needs an ng-show to slide content down - .row.animate-slide{ "ng-show" => "query || appliedPropertiesList() || appliedTaxonsList()" } - .small-12.columns - .alert-box.search-alert.ng-scope - %a.right{"ng-click" => "clearAll()"} - Clear all - %i.ofn-i_009-close - %span.filter-label - Showing: - %span.applied-properties - {{ appliedPropertiesList() }} - %span.applied-taxons - {{ appliedTaxonsList() }} - %span{ ng: { hide: "!query"} } - %span{ "ng-show" => "appliedPropertiesList() || appliedTaxonsList()" } - with - %span.applied-search "{{ query }}" - .row - .small-12.medium-6.large-5.columns - %input#search.text{"ng-model" => "query", - placeholder: "Search by product or producer", - "ng-debounce" => "100", - "ofn-disable-enter" => true} + // TODO: Needs an ng-show to slide content down + .row.animate-slide{ "ng-show" => "query || appliedPropertiesList() || appliedTaxonsList()" } + .small-12.columns + .alert-box.search-alert.ng-scope + %a.right{"ng-click" => "clearAll()"} + = t :products_clear_all + %i.ofn-i_009-close + %span.filter-label + = t :products_showing + %span.applied-properties + {{ appliedPropertiesList() }} + %span.applied-taxons + {{ appliedTaxonsList() }} + %span{ ng: { hide: "!query"} } + %span{ "ng-show" => "appliedPropertiesList() || appliedTaxonsList()" } + = t :products_with + %span.applied-search "{{ query }}" + .row + .small-12.medium-6.large-5.columns + %input#search.text{"ng-model" => "query", + placeholder: t(:products_search), + "ng-debounce" => "100", + "ofn-disable-enter" => true} - .small-12.medium-6.large-6.large-offset-1.columns - = render partial: "shop/products/filters" + .small-12.medium-6.large-6.large-offset-1.columns + = render partial: "shop/products/filters" - %div.pad-top{bindonce: true} - %product.animate-repeat{"ng-controller" => "ProductNodeCtrl", - "ng-repeat" => "product in filteredProducts = (Products.products | products:query | taxons:activeTaxons | properties: activeProperties) track by product.id ", "id" => "product-{{ product.id }}"} - = render partial: "shop/products/summary" - %shop-variant{variant: 'product.master', "bo-if" => "!product.hasVariants", "id" => "variant-{{ product.master.id }}"} - %shop-variant{variant: 'variant', "ng-repeat" => "variant in product.variants track by variant.id", "id" => "variant-{{ variant.id }}"} + %div.pad-top{bindonce: true} + %product.animate-repeat{"ng-controller" => "ProductNodeCtrl", + "ng-repeat" => "product in filteredProducts = (Products.products | products:query | taxons:activeTaxons | properties: activeProperties) track by product.id ", "id" => "product-{{ product.id }}"} + = render partial: "shop/products/summary" + %shop-variant{variant: 'product.master', "bo-if" => "!product.hasVariants", "id" => "variant-{{ product.master.id }}"} + %shop-variant{variant: 'variant', "ng-repeat" => "variant in product.variants track by variant.id", "id" => "variant-{{ variant.id }}"} - %product{"ng-show" => "Products.loading"} - .row.summary - .small-12.columns.text-center - Loading products... - .row - .small-12.columns.text-center - %img.spinner{ src: "/assets/spinning-circles.svg" } + %product{"ng-show" => "Products.loading"} + .row.summary + .small-12.columns.text-center + = t :products_loading + .row + .small-12.columns.text-center + %img.spinner{ src: "/assets/spinning-circles.svg" } - %div{"ng-show" => "filteredProducts.length == 0 && !Products.loading"} - .row.summary - .small-12.columns - %p.no-results - Sorry, no results found for - %strong {{query}}. - Try another search? - .row - .small-12.columns - %form{action: cart_path} - %i.ofn-i_011-spinner.cart-spinner{"ng-show" => "Cart.dirty"} - %input.small.button.primary.right.add_to_cart{type: :submit, value: "{{ Cart.dirty ? 'Updating cart...' : (Cart.empty() ? 'Cart empty' : 'Edit your cart' ) }}", "ng-disabled" => "Cart.dirty || Cart.empty()", "ng-class" => "{ dirty: Cart.dirty }" } + %div{"ng-show" => "filteredProducts.length == 0 && !Products.loading"} + .row.summary + .small-12.columns + %p.no-results + = t :search_no_results_html, query: "{{query}}".html_safe + .row + .small-12.columns + %form{action: cart_path} + %i.ofn-i_011-spinner.cart-spinner{"ng-show" => "Cart.dirty"} + %input.small.button.primary.right.add_to_cart{type: :submit, value: "{{ Cart.dirty ? '#{t(:products_updating_cart)}' : (Cart.empty() ? '#{t(:products_cart_empty)}' : '#{t(:products_edit_cart)}' ) }}", "ng-disabled" => "Cart.dirty || Cart.empty()", "ng-class" => "{ dirty: Cart.dirty }" } diff --git a/app/views/shop/products/_summary.html.haml b/app/views/shop/products/_summary.html.haml index dec398f80f..6d36ac81ce 100644 --- a/app/views/shop/products/_summary.html.haml +++ b/app/views/shop/products/_summary.html.haml @@ -10,7 +10,8 @@ %span{"bo-text" => "product.name"} %i.ofn-i_057-expand %small - %em from + %em + = t :products_from %span %enterprise-modal %i.ofn-i_036-producers{"bo-text" => "enterprise.name"} diff --git a/app/views/shopping_shared/_contact.html.haml b/app/views/shopping_shared/_contact.html.haml index 6069d453a2..100d64d958 100644 --- a/app/views/shopping_shared/_contact.html.haml +++ b/app/views/shopping_shared/_contact.html.haml @@ -4,7 +4,8 @@ .small-12.large-4.columns - if current_distributor.address.address1 || current_distributor.address.address2 || current_distributor.address.city || current_distributor.address.state || current_distributor.address.zipcode %div.center - .header Address + .header + = t :shopping_contact_address %strong=current_distributor.name %p = current_distributor.address.address1 @@ -17,23 +18,25 @@ = current_distributor.address.zipcode .small-12.large-4.columns - - if current_distributor.website || current_distributor.email + - if current_distributor.website || current_distributor.email_address %div.center - .header Contact + .header + = t :shopping_contact_web %p - unless current_distributor.website.blank? %a{href: "http://#{current_distributor.website}", target: "_blank" } = current_distributor.website %br - - unless current_distributor.email.blank? - %a{href: current_distributor.email.reverse, mailto: true} + - unless current_distributor.email_address.blank? + %a{href: current_distributor.email_address.reverse, mailto: true} %span.email - = current_distributor.email.reverse + = current_distributor.email_address.reverse .small-12.large-4.columns - if current_distributor.twitter.present? || current_distributor.facebook.present? || current_distributor.linkedin.present? || current_distributor.instagram.present? %div.center - .header Follow + .header + = t :shopping_contact_social %div.follow-icons - unless current_distributor.twitter.blank? %span diff --git a/app/views/shopping_shared/_details.html.haml b/app/views/shopping_shared/_details.html.haml index 247f1142b9..d060c130ca 100644 --- a/app/views/shopping_shared/_details.html.haml +++ b/app/views/shopping_shared/_details.html.haml @@ -2,7 +2,7 @@ %distributor.details.row .small-12.medium-6.large-6.columns #distributor_title - - if current_distributor.logo.exists? + - if current_distributor.logo? %img.left{src: current_distributor.logo.url(:thumb)} %h3 = current_distributor.name diff --git a/app/views/shopping_shared/_groups.html.haml b/app/views/shopping_shared/_groups.html.haml index d94d61359c..6278f44c77 100644 --- a/app/views/shopping_shared/_groups.html.haml +++ b/app/views/shopping_shared/_groups.html.haml @@ -5,7 +5,7 @@ - if current_distributor.groups.length > 0 %h5 =current_distributor.name - is part of: + = t :shopping_groups_part_of %ul.bullet-list - for group in current_distributor.groups %li diff --git a/app/views/shopping_shared/_last_order_cycle.html.haml b/app/views/shopping_shared/_last_order_cycle.html.haml index 353529e60a..ca474fadd6 100644 --- a/app/views/shopping_shared/_last_order_cycle.html.haml +++ b/app/views/shopping_shared/_last_order_cycle.html.haml @@ -1,4 +1,2 @@ - if most_recently_closed = OrderCycle.most_recently_closed_for(@distributor) - The last cycle closed - = distance_of_time_in_words_to_now most_recently_closed.orders_close_at - ago + = t :shopping_oc_last_closed, distance_of_time: distance_of_time_in_words_to_now(most_recently_closed.orders_close_at) diff --git a/app/views/shopping_shared/_next_order_cycle.html.haml b/app/views/shopping_shared/_next_order_cycle.html.haml index f4b3e5172f..04f63fdcad 100644 --- a/app/views/shopping_shared/_next_order_cycle.html.haml +++ b/app/views/shopping_shared/_next_order_cycle.html.haml @@ -1,3 +1,2 @@ - if next_oc = OrderCycle.first_opening_for(@distributor) - The next cycle opens in - = distance_of_time_in_words_to_now next_oc.orders_open_at + = t :shopping_oc_next_open, distance_of_time: distance_of_time_in_words_to_now(next_oc.orders_open_at) diff --git a/app/views/shopping_shared/_order_cycles.html.haml b/app/views/shopping_shared/_order_cycles.html.haml index cfc77091d3..cd5ead30f3 100644 --- a/app/views/shopping_shared/_order_cycles.html.haml +++ b/app/views/shopping_shared/_order_cycles.html.haml @@ -5,8 +5,9 @@ - if @order_cycles and @order_cycles.empty? %h4.text-right %i.ofn-i_032-closed-sign - Orders are closed - %p.text-right Please wait until the next cycle opens (or contact us directly to see if we can accept any late orders) + = t :shopping_oc_closed + %p.text-right + = t :shopping_oc_closed_description .text-right %small %em diff --git a/app/views/shopping_shared/_producers.html.haml b/app/views/shopping_shared/_producers.html.haml index d1ece47329..3c9e9982db 100644 --- a/app/views/shopping_shared/_producers.html.haml +++ b/app/views/shopping_shared/_producers.html.haml @@ -2,7 +2,8 @@ .panel .row .small-12.columns - %h5 {{CurrentHub.hub.name}}'s producers: + %h5 + = t :shopping_producers_of_hub, hub: '{{CurrentHub.hub.name}}' %ul.small-block-grid-2.large-block-grid-4 %li{"ng-repeat" => "enterprise in CurrentHub.hub.producers"} %enterprise-modal diff --git a/app/views/shopping_shared/_tabs.html.haml b/app/views/shopping_shared/_tabs.html.haml index 3641e0cbd5..8d0d9e021d 100644 --- a/app/views/shopping_shared/_tabs.html.haml +++ b/app/views/shopping_shared/_tabs.html.haml @@ -1,11 +1,11 @@ -#tabs{"ng-controller" => "TabsCtrl"} +#tabs{"ng-controller" => "TabsCtrl", "ng-cloak" => true} .row %tabset -# Build all tabs. - - for name, heading_cols in { about: ["About #{current_distributor.name}", 6], - producers: ["Producers",2], - contact: ["Contact",2], - groups: ["Groups",2]} + - for name, heading_cols in { about: [t(:shopping_tabs_about, distributor: current_distributor.name), 6], + producers: [t(:label_producers),2], + contact: [t(:shopping_tabs_contact),2], + groups: [t(:label_groups),2]} -# tabs take tab path in 'active' and 'select' functions defined in TabsCtrl. - heading, cols = heading_cols %tab.columns{heading: heading, diff --git a/app/views/shops/index.html.haml b/app/views/shops/index.html.haml new file mode 100644 index 0000000000..1b3cf547a8 --- /dev/null +++ b/app/views/shops/index.html.haml @@ -0,0 +1,14 @@ +- content_for(:title) do + = t :shops_title + +#panes + #shops.pane + .row + .small-12.medium-6.medium-offset-3.columns.text-center + %h2 + = t :shops_headline + %p.text-big + = t :shops_text + += render partial: "home/hubs" += render partial: "shared/footer" diff --git a/app/views/shops/signup.html.haml b/app/views/shops/signup.html.haml new file mode 100644 index 0000000000..fd8dc3a0da --- /dev/null +++ b/app/views/shops/signup.html.haml @@ -0,0 +1,55 @@ +- content_for(:title) do + = t :shops_signup_title + +#panes + #shops-signup.pane + .row.header + .small-12.medium-12.columns.text-center + %h2 + = t :shops_signup_headline + .row.content + .small-12.medium-6.medium-offset-3.columns.text-center + %p.text-big + = t :shops_signup_motivation + %br + %a.button.transparent{href: "/register"} + = t :shops_signup_action + .pane + .row + .small-12.medium-10.medium-offset-1.columns.text-center + %h2 + = t :shops_signup_pricing + -# %p.text-big + -# If there is a time-sensitive offer you can write it here, e.g. + -# Sign up before 30th June for an extra month free! + %br + = ContentConfig.hub_signup_pricing_table_html.html_safe + + #shops-case-studies + .row + .small-12.medium-10.medium-offset-1.columns + %h2.text-center + = t :shops_signup_stories + %br + = ContentConfig.hub_signup_case_studies_html.html_safe + + .pane#cta + .row + .small-12.medium-6.medium-offset-3.columns.text-center + %h2 + = t :shops_signup_help + %p.text-big + = t :shops_signup_help_text + %br + %a.button.transparent{href: "/register"} + = t :shops_signup_action + + #hub-details.pane.footer-pad + .row + .small-12.medium-10.medium-offset-1.columns + %h2.text-center + = t :shops_signup_detail + = ContentConfig.hub_signup_detail_html.html_safe + + += render partial: "shared/footer" diff --git a/app/views/spree/admin/adjustments/_edit_form.html.haml b/app/views/spree/admin/adjustments/_edit_form.html.haml new file mode 100644 index 0000000000..5ca71ff9f7 --- /dev/null +++ b/app/views/spree/admin/adjustments/_edit_form.html.haml @@ -0,0 +1,25 @@ +.row + .alpha.four.columns + = f.field_container :amount do + = f.label :amount, raw(t(:amount) + content_tag(:span, " *", :class => "required")) + = text_field :adjustment, :amount, :class => 'fullwidth' + = f.error_message_on :amount + + .four.columns + = f.field_container :included_tax do + = f.label :included_tax, t(:included_tax) + = text_field :adjustment, :included_tax, disabled: true, class: 'fullwidth' + = f.error_message_on :included_tax + + .omega.four.columns + = f.field_container :tax_rate_id do + = f.label :tax_rate_id, t(:tax_rate) + = select_tag :tax_rate_id, options_from_collection_for_select(Spree::TaxRate.all, :id, :name, @tax_rate_id), prompt: t(:remove_tax), class: 'select2 fullwidth' + = f.error_message_on :tax_rate_id + +.row + .alpha.omega.twelve.columns + = f.field_container :label do + = f.label :label, raw(t(:description) + content_tag(:span, " *", :class => "required")) + = text_field :adjustment, :label, :class => 'fullwidth' + = f.error_message_on :label diff --git a/app/views/spree/admin/adjustments/_new_form.html.haml b/app/views/spree/admin/adjustments/_new_form.html.haml new file mode 100644 index 0000000000..a3e831aa8d --- /dev/null +++ b/app/views/spree/admin/adjustments/_new_form.html.haml @@ -0,0 +1,19 @@ +.row + .alpha.three.columns + = f.field_container :amount do + = f.label :amount, raw(t(:amount) + content_tag(:span, " *", :class => "required")) + = text_field :adjustment, :amount, :class => 'fullwidth' + = f.error_message_on :amount + + .omega.three.columns + = f.field_container :tax_rate_id do + = f.label :tax_rate_id, t(:tax_rate) + = select_tag :tax_rate_id, options_from_collection_for_select(Spree::TaxRate.all, :id, :name), prompt: t(:none), class: 'select2 fullwidth' + = f.error_message_on :tax_rate_id + +.row + .alpha.omega.twelve.columns + = f.field_container :label do + = f.label :label, raw(t(:description) + content_tag(:span, " *", :class => "required")) + = text_field :adjustment, :label, :class => 'fullwidth' + = f.error_message_on :label diff --git a/app/views/spree/admin/line_items/create.js.erb b/app/views/spree/admin/line_items/create.js.erb new file mode 100644 index 0000000000..f1644d2203 --- /dev/null +++ b/app/views/spree/admin/line_items/create.js.erb @@ -0,0 +1,6 @@ +// The admin order form contains angular directives, so it needs to be +// compiled before insertion into the DOM +var scope = angular.element("#order-form-wrapper").scope(); +$("#order-form-wrapper").html(scope.$compile('<%= escape_javascript(render :partial => "spree/admin/orders/form") %>')(scope)); +scope.$apply(); +$('select.select2').select2({allowClear: true}); diff --git a/app/views/spree/admin/orders/_invoice_table.html.haml b/app/views/spree/admin/orders/_invoice_table.html.haml new file mode 100644 index 0000000000..4bd9917eb0 --- /dev/null +++ b/app/views/spree/admin/orders/_invoice_table.html.haml @@ -0,0 +1,50 @@ +%table.order-summary{:width => "100%"} + %thead + %tr + %th{:align => "left"} + %h4= t(:invoice_column_item) + %th{:align => "right", :width => "15%"} + %h4= t(:invoice_column_qty) + %th{:align => "right", :width => "15%"} + %h4= @order.total_tax > 0 ? t(:invoice_column_tax) : "" + %th{:align => "right", :width => "15%"} + %h4= t(:invoice_column_price) + %tbody + - @order.line_items.sort_by{ |li| li.product.name }.each do |item| + %tr + %td + = render 'spree/shared/line_item_name', line_item: item + %td{:align => "right"} + = item.quantity + %td{:align => "right"} + = item.included_tax > 0 ? item.display_included_tax : "" + %td{:align => "right"} + = item.display_amount_with_adjustments + - checkout_adjustments_for(@order, exclude: [:line_item]).reject{ |a| a.amount == 0 }.reverse_each do |adjustment| + %tr + %td + %strong= "#{raw(adjustment.label)}" + %td{:align => "right"} + 1 + %td{:align => "right"} + = adjustment.included_tax > 0 ? adjustment.display_included_tax : "" + %td{:align => "right"} + = adjustment.display_amount + %tfoot + %tr + %td{:align => "right", :colspan => "2"} + %strong GST Total: + %td{:align => "right", :colspan => "2"} + %strong= display_checkout_tax_total(@order) + %tr + %td{:align => "right", :colspan => "2"} + %strong Total (Excl. GST): + %td{:align => "right", :colspan => "2"} + %strong= display_checkout_total_less_tax(@order) + %tr + %td{:align => "right", :colspan => "2"} + %strong Total (Incl. GST): + %td{:align => "right", :colspan => "2"} + %strong= @order.display_total +%p +   diff --git a/app/views/spree/admin/orders/bulk_management.html.haml b/app/views/spree/admin/orders/bulk_management.html.haml index caf18c6cd3..46e5caf191 100644 --- a/app/views/spree/admin/orders/bulk_management.html.haml +++ b/app/views/spree/admin/orders/bulk_management.html.haml @@ -1,42 +1,44 @@ +- content_for :app_wrapper_attrs do + = "ng-app='admin.lineItems'" + - content_for :page_title do %h1.page-title Bulk Order Management - %a.with-tip{ 'data-powertip' => "Use this page to alter product quantities across multiple orders. Products may also be removed from orders entirely, if required." } What's this? + %a{ 'ofn-with-tip' => "Use this page to alter product quantities across multiple orders. Products may also be removed from orders entirely, if required." } What's this? = render :partial => 'spree/admin/shared/order_sub_menu' -=admin_inject_spree_api_key - -%div{ ng: { app: 'ofn.admin', controller: 'AdminOrderMgmtCtrl', init: 'initialise()' } } - %div{ 'ng-show' => '!spree_api_key_ok' } - {{ api_error_msg }} +%div{ ng: { controller: 'LineItemsCtrl' } } + %save-bar{ save: "submit()", form: "bulk_order_form" } .filters{ :class => "sixteen columns alpha" } .date_filter{ :class => "two columns alpha" } %label{ :for => 'start_date_filter' }Start Date %br - %input{ :class => "two columns alpha", :type => "text", :id => 'start_date_filter', 'ng-model' => 'startDate', 'datepicker' => "startDate", 'ofn-confirm-model-change' => "startDate" } + %input{ :class => "two columns alpha", :type => "text", :id => 'start_date_filter', 'ng-model' => 'startDate', 'datepicker' => "startDate", 'confirm-change' => "confirmRefresh()", 'ng-change' => 'refreshData()' } .date_filter{ :class => "two columns" } %label{ :for => 'end_date_filter' }End Date %br - %input{ :class => "two columns alpha", :type => "text", :id => 'end_date_filter', 'ng-model' => 'endDate', 'datepicker' => "endDate", 'ofn-confirm-model-change' => "endDate" } + %input{ :class => "two columns alpha", :type => "text", :id => 'end_date_filter', 'ng-model' => 'endDate', 'datepicker' => "endDate", 'confirm-change' => "confirmRefresh()", 'ng-change' => 'refreshData()' } .one.column   .filter_select{ :class => "three columns" } %label{ :for => 'supplier_filter' }Producer %br - %select{ :class => "three columns alpha", :id => 'supplier_filter', 'ofn-select2-min-search' => 5, 'ng-model' => 'supplierFilter', 'ng-options' => 's.id as s.name for s in suppliers' } + %select{ :class => "three columns alpha", :id => 'supplier_filter', 'select2-min-search' => 5, 'ng-model' => 'supplierFilter', 'ng-options' => 's.id as s.name for s in suppliers' } .filter_select{ :class => "three columns" } %label{ :for => 'distributor_filter' }Hub %br - %select{ :class => "three columns alpha", :id => 'distributor_filter', 'ofn-select2-min-search' => 5, 'ng-model' => 'distributorFilter', 'ng-options' => 'd.id as d.name for d in distributors'} + %select{ :class => "three columns alpha", :id => 'distributor_filter', 'select2-min-search' => 5, 'ng-model' => 'distributorFilter', 'ng-options' => 'd.id as d.name for d in distributors'} .filter_select{ :class => "three columns" } %label{ :for => 'order_cycle_filter' }Order Cycle %br - %select{ :class => "three columns alpha", :id => 'order_cycle_filter', 'ofn-select2-min-search' => 5, 'ng-model' => 'orderCycleFilter', 'ng-options' => 'oc.id as oc.name for oc in orderCycles'} + %select{ :class => "three columns alpha", :id => 'order_cycle_filter', 'select2-min-search' => 5, 'ng-model' => 'orderCycleFilter', 'ng-options' => 'oc.id as oc.name for oc in orderCycles', 'confirm-change' => "confirmRefresh()", 'ng-change' => 'refreshData()'} .filter_clear{ :class => "two columns omega" } %label{ :for => 'clear_all_filters' } %br - %input.fullwidth{ :type => 'button', :id => 'clear_all_filters', :value => "Clear All", 'ng-click' => "resetSelectFilters()" } - %hr{ :class => "sixteen columns alpha", 'ng-show' => 'unitsVariantSelected()' } - %div#group_buy_calculation{ :class => "sixteen columns alpha", 'ng-show' => 'unitsVariantSelected()' } + %input.red.fullwidth{ :type => 'button', :id => 'clear_all_filters', :value => "Clear All", 'ng-click' => "resetSelectFilters()" } + + %hr.divider.sixteen.columns.alpha.omega{ ng: { show: 'unitsVariantSelected()' } } + + %div.sixteen.columns.alpha.omega#group_buy_calculation{ ng: { show: 'unitsVariantSelected()' } } %div.shared_resource{ :class => "four columns alpha" } %span{ :class => 'three columns alpha' } %input{ type: 'checkbox', :id => 'shared_resource', 'ng-model' => 'sharedResource'} @@ -70,40 +72,31 @@ %span.two.columns Max Fulfilled Units %span.two.columns {{ fulfilled(sumMaxUnitValues()) }} .one.column.omega   - %div{ :class => "eight columns alpha", 'ng-hide' => 'allUnitValuesPresent()' } + %div{ :class => "eight columns alpha", 'ng-hide' => 'allFinalWeightVolumesPresent()' } %span{ :class => "eight columns alpha", style: 'color:red' } WARNING: Some variants do not have a unit value - %hr{ :class => "sixteen columns alpha", :style => "margin-bottom: 15px" } - %div{ 'ng-hide' => 'loading || lineItems.length == 0' } - .controls{ :class => "sixteen columns alpha", :style => "margin-bottom: 15px;" } - %div{ :class => "three columns alpha" } - %input{ :class => "fullwidth", :type => "text", :id => 'quick_search', 'ng-model' => 'quickSearch', :placeholder => 'Quick Search' } - %div{ :class => "three columns" } - %div.ofn_drop_down{ 'ng-controller' => "DropDownCtrl", :id => "bulk_actions_dropdown", 'ofn-drop-down' => true } - %span{ :class => 'icon-check' }   Actions - %span{ 'ng-class' => "expanded && 'icon-caret-up' || !expanded && 'icon-caret-down'" } - %div.menu{ 'ng-show' => "expanded" } - %div.menu_item{ :class => "three columns alpha", 'ng-repeat' => "action in bulkActions", 'ng-click' => "selectedBulkAction.callback(filteredLineItems)", 'ofn-close-on-click' => true } - %span{ :class => 'three columns omega' } {{action.name }} - %div{ :class => "seven columns" }   - %div{ :class => "three columns omega" } - %div.ofn_drop_down{ 'ng-controller' => "DropDownCtrl", :id => "columns_dropdown", 'ofn-drop-down' => true, :style => 'float:right;' } - %span{ :class => 'icon-reorder' }   Columns - %span{ 'ng-class' => "expanded && 'icon-caret-up' || !expanded && 'icon-caret-down'" } - %div.menu{ 'ng-show' => "expanded" } - %div.menu_item{ :class => "three columns alpha", 'ng-repeat' => "column in columns", 'ofn-toggle-column' => true } - %span{ :class => 'one column alpha', :style => 'text-align: center'} {{ column.visible && "✓" || !column.visible && " " }} - %span{ :class => 'two columns omega' } {{column.name }} - %div.sixteen.columns.alpha#loading{ 'ng-if' => 'loading' } + + %hr.divider.sixteen.columns.alpha.omega + + .controls.sixteen.columns.alpha.omega{ ng: { hide: 'RequestMonitor.loading || lineItems.length == 0' } } + %div.three.columns.alpha + %input.fullwidth{ :type => "text", :id => 'quick_search', 'ng-model' => 'quickSearch', :placeholder => 'Quick Search' } + = render 'admin/shared/bulk_actions_dropdown' + %div.seven.columns   + = render 'admin/shared/columns_dropdown' + + %div.sixteen.columns.alpha#loading{ 'ng-if' => 'RequestMonitor.loading' } %img.spinner{ src: "/assets/spinning-circles.svg" } %h1 LOADING ORDERS - %div{ :class => "sixteen columns alpha", 'ng-show' => '!loading && filteredLineItems.length == 0'} + + %div{ :class => "sixteen columns alpha", 'ng-show' => '!RequestMonitor.loading && filteredLineItems.length == 0'} %h1#no_results No orders found. - %div{ 'ng-hide' => 'loading || filteredLineItems.length == 0' } - %form{ 'ng-model' => "bulk_order_form" } + + %div{ 'ng-hide' => 'RequestMonitor.loading || filteredLineItems.length == 0' } + %form{ name: 'bulk_order_form' } %table.index#listing_orders.bulk{ :class => "sixteen columns alpha" } %thead - %tr + %tr{ ng: { controller: "ColumnsCtrl" } } %th.bulk %input{ :type => "checkbox", :name => 'toggle_bulk', 'ng-click' => 'toggleAllCheckboxes()', 'ng-checked' => "allBoxesChecked()" } %th.order_no{ 'ng-show' => 'columns.order_no.visible' } @@ -126,34 +119,37 @@ %a{ :href => '', 'ng-click' => "predicate = 'units_variant.full_name'; reverse = !reverse" } Product: Unit %th.quantity{ 'ng-show' => 'columns.quantity.visible' } Quantity %th.max{ 'ng-show' => 'columns.max.visible' } Max - %th.unit_value{ 'ng-show' => 'columns.unit_value.visible' } Weight/Volume + %th.final_weight_volume{ 'ng-show' => 'columns.final_weight_volume.visible' } Weight/Volume %th.price{ 'ng-show' => 'columns.price.visible' } Price %th.actions %th.actions Ask?  %input{ :type => 'checkbox', 'ng-model' => "confirmDelete" } - %tr.line_item{ 'ng-repeat' => "line_item in filteredLineItems = ( lineItems | filter:quickSearch | selectFilter:supplierFilter:distributorFilter:orderCycleFilter | variantFilter:selectedUnitsProduct:selectedUnitsVariant:sharedResource | orderBy:predicate:reverse )", 'ng-class-even' => "'even'", 'ng-class-odd' => "'odd'", :id => "li_{{line_item.id}}" } - %td.bulk - %input{ :type => "checkbox", :name => 'bulk', 'ng-model' => 'line_item.checked' } - %td.order_no{ 'ng-show' => 'columns.order_no.visible' } {{ line_item.order.number }} - %td.full_name{ 'ng-show' => 'columns.full_name.visible' } {{ line_item.order.full_name }} - %td.email{ 'ng-show' => 'columns.email.visible' } {{ line_item.order.email }} - %td.phone{ 'ng-show' => 'columns.phone.visible' } {{ line_item.order.phone }} - %td.date{ 'ng-show' => 'columns.order_date.visible' } {{ line_item.order.completed_at }} - %td.producer{ 'ng-show' => 'columns.producer.visible' } {{ line_item.supplier.name }} - %td.order_cycle{ 'ng-show' => 'columns.order_cycle.visible' } {{ line_item.order.order_cycle.name }} - %td.hub{ 'ng-show' => 'columns.hub.visible' } {{ line_item.order.distributor.name }} - %td.variant{ 'ng-show' => 'columns.variant.visible' } - %a{ :href => '#', 'ng-click' => "setSelectedUnitsVariant(line_item.units_product,line_item.units_variant)" } {{ line_item.units_variant.full_name }} - %td.quantity{ 'ng-show' => 'columns.quantity.visible' } - %input{ :type => 'number', :name => 'quantity', 'ng-model' => "line_item.quantity", 'ofn-line-item-upd-attr' => "quantity" } - %td.max{ 'ng-show' => 'columns.max.visible' } {{ line_item.max_quantity }} - %td.unit_value{ 'ng-show' => 'columns.unit_value.visible' } - %input{ :type => 'number', :name => 'unit_value', :id => 'unit_value', 'ng-model' => "line_item.unit_value", 'ng-readonly' => "unitValueLessThanZero(line_item)", 'ng-change' => "weightAdjustedPrice(line_item, {{ line_item.unit_value }})", 'ofn-line-item-upd-attr' => "unit_value" } - %td.price{ 'ng-show' => 'columns.price.visible' } - %input{ :type => 'text', :name => 'price', :id => 'price', :value => '{{ line_item.price | currency }}', 'ng-model' => "line_item.price", 'ng-readonly' => "true", 'ofn-line-item-upd-attr' => "price" } - %td.actions - %a{ :class => "edit-order icon-edit no-text", 'ofn-confirm-link-path' => "/admin/orders/{{line_item.order.number}}/edit" } - %td.actions - %a{ 'ng-click' => "deleteLineItem(line_item)", :class => "delete-line-item icon-trash no-text" } - %input{ :type => "button", 'value' => 'Update', 'ng-click' => 'pendingChanges.submitAll()' } + + %tr.line_item{ 'ng-repeat' => "line_item in filteredLineItems = ( lineItems | filter:quickSearch | selectFilter:supplierFilter:distributorFilter:orderCycleFilter | variantFilter:selectedUnitsProduct:selectedUnitsVariant:sharedResource | orderBy:predicate:reverse )", 'ng-class-even' => "'even'", 'ng-class-odd' => "'odd'", :id => "li_{{line_item.id}}" } + %td.bulk + %input{ :type => "checkbox", :name => 'bulk', 'ng-model' => 'line_item.checked', 'ignore-dirty' => true } + %td.order_no{ 'ng-show' => 'columns.order_no.visible' } {{ line_item.order.number }} + %td.full_name{ 'ng-show' => 'columns.full_name.visible' } {{ line_item.order.full_name }} + %td.email{ 'ng-show' => 'columns.email.visible' } {{ line_item.order.email }} + %td.phone{ 'ng-show' => 'columns.phone.visible' } {{ line_item.order.phone }} + %td.date{ 'ng-show' => 'columns.order_date.visible' } {{ line_item.order.completed_at }} + %td.producer{ 'ng-show' => 'columns.producer.visible' } {{ line_item.supplier.name }} + %td.order_cycle{ 'ng-show' => 'columns.order_cycle.visible' } {{ line_item.order.order_cycle.name }} + %td.hub{ 'ng-show' => 'columns.hub.visible' } {{ line_item.order.distributor.name }} + %td.variant{ 'ng-show' => 'columns.variant.visible' } + %a{ :href => '#', 'ng-click' => "setSelectedUnitsVariant(line_item.units_product,line_item.units_variant)" } {{ line_item.units_variant.full_name }} + %td.quantity{ 'ng-show' => 'columns.quantity.visible' } + %input.show-dirty{ :type => 'number', :name => 'quantity', :id => 'quantity', ng: { model: "line_item.quantity", change: "updateOnQuantity(line_item)", required: "true", class: '{"update-error": line_item.errors.quantity}' }, min: 1, step: 1 } + %span.error{ ng: { bind: 'line_item.errors.quantity' } } + %td.max{ 'ng-show' => 'columns.max.visible' } {{ line_item.max_quantity }} + %td.final_weight_volume{ 'ng-show' => 'columns.final_weight_volume.visible' } + %input.show-dirty{ :type => 'number', :name => 'final_weight_volume', :id => 'final_weight_volume', ng: { model: "line_item.final_weight_volume", readonly: "unitValueLessThanZero(line_item)", change: "weightAdjustedPrice(line_item)", required: "true", class: '{"update-error": line_item.errors.final_weight_volume}' }, min: 0, 'ng-pattern' => '/[1-9]+/' } + %span.error{ ng: { bind: 'line_item.errors.final_weight_volume' } } + %td.price{ 'ng-show' => 'columns.price.visible' } + %input.show-dirty{ :type => 'text', :name => 'price', :id => 'price', :ng => { value: 'line_item.price * line_item.quantity | currency', readonly: "true", class: '{"update-error": line_item.errors.price}' } } + %span.error{ ng: { bind: 'line_item.errors.price' } } + %td.actions + %a{ href: "/admin/orders/{{line_item.order.number}}/edit", :class => "edit-order icon-edit no-text", 'confirm-link-click' => 'confirmRefresh()' } + %td.actions + %a{ 'ng-click' => "deleteLineItem(line_item)", :class => "delete-line-item icon-trash no-text" } diff --git a/app/views/spree/admin/orders/edit.html.haml b/app/views/spree/admin/orders/edit.html.haml new file mode 100644 index 0000000000..4daddc2a2f --- /dev/null +++ b/app/views/spree/admin/orders/edit.html.haml @@ -0,0 +1,24 @@ += csrf_meta_tags + +- content_for :page_actions do + %li= event_links + %li= button_link_to t(:resend), resend_admin_order_url(@order), method: :post, icon: 'icon-email' + %li= button_link_to t(:back_to_orders_list), admin_orders_path, icon: 'icon-arrow-left' + += admin_inject_shops 'admin.orders' += admin_inject_order_cycles + += render 'spree/admin/shared/order_tabs', current: 'Order Details' + +%div{"data-hook" => "admin_order_edit_header"} + = render 'spree/shared/error_messages', target: @order + +%div{"ng-app" => "admin.orders", "ng-controller" => "ordersCtrl", "ofn-distributor-id" => @order.distributor_id, "ofn-order-cycle-id" => @order.order_cycle_id} + = render 'add_product' + + %div{"data-hook" => "admin_order_edit_form"} + #order-form-wrapper + = render 'form', order: @order + +- content_for :head do + = javascript_tag 'var expand_variants = true;' diff --git a/app/views/spree/admin/orders/invoice.html.haml b/app/views/spree/admin/orders/invoice.html.haml new file mode 100644 index 0000000000..0913ee27db --- /dev/null +++ b/app/views/spree/admin/orders/invoice.html.haml @@ -0,0 +1,52 @@ += wicked_pdf_stylesheet_link_tag "mail/all" + + +%table{:width => "100%"} + %tbody + %tr{ valign: "top" } + %td{ :align => "left", colspan: 3 } + %h6= "Issued on: #{Time.zone.now.strftime("%F")}" + %tr{ valign: "top" } + %td{ :align => "left" } + %h4 + = "TAX INVOICE: " + = "#{@order.number}" + %td{width: "10%" } +   + %td{ :align => "right" } + %h4= @order.order_cycle.andand.name + %tr{ valign: "top" } + %td{ :align => "left" } + %strong= "From: #{@order.distributor.name}" + - if @order.distributor.abn.present? + %br + = "ABN: #{@order.distributor.abn}" + %br + = @order.distributor.address.full_address + %br + = @order.distributor.email + %td{width: "10%" } +   + %td{ :align => "right" } + %strong= "To: #{@order.ship_address.full_name}" + - if @order.customer.code.present? + %br + = "Code: #{@order.customer.code}" + %br + = @order.ship_address.full_address + %br + = "#{@order.customer.email}," + = "#{@order.bill_address.phone}" + += render 'spree/admin/orders/invoice_table' + +- if @order.special_instructions.present? + %p.callout + %strong + = t :customer_instructions + %p + %em= @order.special_instructions + %p +   + += render 'spree/order_mailer/payment' diff --git a/app/views/spree/admin/orders/new.html.haml b/app/views/spree/admin/orders/new.html.haml new file mode 100644 index 0000000000..3c5466df14 --- /dev/null +++ b/app/views/spree/admin/orders/new.html.haml @@ -0,0 +1,27 @@ +- content_for :page_title do + = t(:new) + +- content_for :page_actions do + %li= button_link_to t(:back_to_orders_list), spree.admin_orders_path, :icon => 'icon-arrow-left' + += admin_inject_shops 'admin.orders' += admin_inject_order_cycles + += render 'spree/admin/shared/order_tabs', :current => 'Order Details' + += csrf_meta_tags + +%div{"data-hook" => "admin_order_new_header"} + = render 'spree/shared/error_messages', :target => @order + +%div{"ng-app" => "admin.orders", "ng-controller" => "ordersCtrl"} + %div{"ng-show" => "distributionChosen()"} + = render 'add_product' + + - unless @order.line_items.any? + %div{"data-hook" => "admin_order_new_form"} + #order-form-wrapper + = render 'form' + +- content_for :head do + = javascript_tag 'var expand_variants = true;' diff --git a/app/views/spree/admin/overview/_change_type_form.html.haml b/app/views/spree/admin/overview/_change_type_form.html.haml deleted file mode 100644 index 530d870307..0000000000 --- a/app/views/spree/admin/overview/_change_type_form.html.haml +++ /dev/null @@ -1,60 +0,0 @@ -= admin_inject_enterprise - -= form_for @enterprise, url: main_app.set_sells_admin_enterprise_path(@enterprise), - html: { name: "change_type", id: "change_type", novalidate: true, "ng-app" => "admin.enterprises", "ng-controller"=> 'changeTypeFormCtrl' } do |change_type_form| - -# Have to use hidden:'true' on this input rather than type:'hidden' as the latter seems to break ngPattern and therefore validation - %input{ hidden: "true", name: "sells", ng: { required: true, pattern: "/^(none|own)$/", model: 'sells', value: "sells"} } - - .row - .options.sixteen.columns.alpha - - if @enterprise.is_primary_producer - %input{ type: 'checkbox', hidden: true, name: "producer_profile_only", ng: { required: true, model: 'producer_profile_only', value: "producer_profile_only"} } - .basic_producer.option.one-third.column.alpha - %a.full-width.button.selector{ ng: { click: "sells='none';producer_profile_only=true;", class: "{selected: sells=='none' && producer_profile_only==true}" } } - .top - %h3 Producer Profile - %p Connect through OFN - .bottom ALWAYS FREE - %p.description - You want to use Open Food Network as a place for people to find and contact you. - - .producer_shop.option.one-third.column - %a.full-width.button.selector{ ng: { click: "sells='none';producer_profile_only=false;", class: "{selected: sells=='none' && producer_profile_only==false}" } } - .top - %h3 Sell products - %p As a supplier - .bottom ALWAYS FREE - %p.description - Add your products to Open Food Network, allowing customers to see your product range, and allowing you to act as a supplier to other shopfronts. - - .full_hub.option.one-third.column.omega.disabled - %a.full-width.button.selector{ ng: { click: "sells='own';producer_profile_only=false;", class: "{selected: sells=='own' && producer_profile_only==false}" } } - .top - %h3 Sell products - %p Through an OFN shopfront - .bottom 30 DAY TRIAL - %p.description - Test out having your own shopfront with full access to all Shopfront features for 30 days. - %br - %br - At the end of your trial, there is a one-off $200 fee to fully activate your account. Then you will be billed for 2% of your actual transactions, capped at $50 a month (so if you don’t sell anything you don’t pay anything, but you never pay more than $50 a month). - - - else - .shop_profile.option.one-third.column.alpha - %a.full-width.button.selector{ ng: { click: "sells='none'", class: "{selected: sells=='none'}" } } - .top - %h3 Shop Profile - %p Get a listing - .bottom ALWAYS FREE - %p.description - You want to use OFN as a place for people to find and contact you. - .row - .sixteen.columns.alpha - %span.error{ ng: { show: "(change_type.sells.$error.required || change_type.sells.$error.pattern) && submitted" } } - Please choose one of the options above. - - if @enterprise.sells == 'unspecified' - %input.button.big{ type: 'submit', value: 'Select and continue', ng: { click: "submit(change_type)" } } - - else - %input.button.big{ type: 'submit', value: 'Change now', ng: { click: "submit(change_type)" } } - %br   - %hr diff --git a/app/views/spree/admin/overview/_enterprises.html.haml b/app/views/spree/admin/overview/_enterprises.html.haml index 757755c718..fbf19fbd43 100644 --- a/app/views/spree/admin/overview/_enterprises.html.haml +++ b/app/views/spree/admin/overview/_enterprises.html.haml @@ -1,4 +1,4 @@ -%div.dashboard_item.sixteen.columns.alpha#enterprises{ 'ng-app' => 'ofn.admin', 'ng-controller' => "enterprisesDashboardCtrl" } +%div.dashboard_item.sixteen.columns.alpha#enterprises{ 'ng-controller' => "enterprisesDashboardCtrl" } = render 'enterprises_header' - if @enterprises.empty? diff --git a/app/views/spree/admin/overview/_enterprises_header.html.haml b/app/views/spree/admin/overview/_enterprises_header.html.haml index fcc7d269f2..d198e8b549 100644 --- a/app/views/spree/admin/overview/_enterprises_header.html.haml +++ b/app/views/spree/admin/overview/_enterprises_header.html.haml @@ -5,4 +5,4 @@ %a.three.columns.omega.icon-plus.button.blue.white-bottom{ href: "#{main_app.new_admin_enterprise_path}" } CREATE NEW - else - %a.with-tip{ title: "Enterprises are Producers and/or Hubs and are the basic unit of organisation within the Open Food Network." } What's this? + %a{ "ofn-with-tip" => "Enterprises are Producers and/or Hubs and are the basic unit of organisation within the Open Food Network." } What's this? diff --git a/app/views/spree/admin/overview/_enterprises_hubs_tab.html.haml b/app/views/spree/admin/overview/_enterprises_hubs_tab.html.haml index cb177f9fb3..78c0e2427b 100644 --- a/app/views/spree/admin/overview/_enterprises_hubs_tab.html.haml +++ b/app/views/spree/admin/overview/_enterprises_hubs_tab.html.haml @@ -16,27 +16,27 @@ - if can? :admin, Spree::PaymentMethod - payment_method_count = enterprise.payment_methods.count - if payment_method_count > 0 - %span.icon-ok-sign.with-tip{ title: "#{pluralize payment_method_count, 'payment method'}" } + %span.icon-ok-sign{ 'ofn-with-tip' => "#{pluralize payment_method_count, 'payment method'}" } - else - %span.icon-remove-sign.with-tip{ title: "#{enterprise.name} has no payment methods" } + %span.icon-remove-sign{ 'ofn-with-tip' => "#{enterprise.name} has no payment methods" } - else   %span.symbol.three.columns.centered - if can? :admin, Spree::ShippingMethod - shipping_method_count = enterprise.shipping_methods.count - if shipping_method_count > 0 - %span.icon-ok-sign.with-tip{ title: "#{pluralize shipping_method_count, 'shipping method'}" } + %span.icon-ok-sign{ 'ofn-with-tip' => "#{pluralize shipping_method_count, 'shipping method'}" } - else - %span.icon-remove-sign.with-tip{ title: "#{enterprise.name} has no shipping methods" } + %span.icon-remove-sign{ 'ofn-with-tip' => "#{enterprise.name} has no shipping methods" } - else   %span.symbol.three.columns.centered - if can? :admin, EnterpriseFee - fee_count = enterprise.enterprise_fees.count - if fee_count > 0 - %span.icon-ok-sign.with-tip{ title: "#{pluralize fee_count, 'fee'}" } + %span.icon-ok-sign{ 'ofn-with-tip' => "#{pluralize fee_count, 'fee'}" } - else - %span.icon-warning-sign.with-tip{ title: "#{enterprise.name} has no enterprise fees" } + %span.icon-warning-sign{ 'ofn-with-tip' => "#{enterprise.name} has no enterprise fees" } - else   %span.two.columns.omega.right diff --git a/app/views/spree/admin/overview/_order_cycles.html.haml b/app/views/spree/admin/overview/_order_cycles.html.haml index c1b7f90276..34b82c1f87 100644 --- a/app/views/spree/admin/overview/_order_cycles.html.haml +++ b/app/views/spree/admin/overview/_order_cycles.html.haml @@ -5,7 +5,7 @@ %a.three.columns.omega.icon-plus.button.blue{ href: "#{main_app.new_admin_order_cycle_path}" } CREATE NEW - else - %a.with-tip{ title: "Order cycles determine when and where your products are available to customers." } What's this? + %a{ "ofn-with-tip" => "Order cycles determine when and where your products are available to customers." } What's this? %div.seven.columns.alpha.list - if @order_cycle_count > 0 %div.seven.columns.alpha.list-item @@ -23,4 +23,4 @@ %span.icon-warning-sign %a.seven.columns.alpha.button.bottom.orange{ href: "#{main_app.admin_order_cycles_path}" } MANAGE ORDER CYCLES - %span.icon-arrow-right \ No newline at end of file + %span.icon-arrow-right diff --git a/app/views/spree/admin/overview/_products.html.haml b/app/views/spree/admin/overview/_products.html.haml index 988e779398..24b60250c3 100644 --- a/app/views/spree/admin/overview/_products.html.haml +++ b/app/views/spree/admin/overview/_products.html.haml @@ -5,7 +5,7 @@ %a.three.columns.omega.icon-plus.button.blue{ href: "#{new_admin_product_path}" } CREATE NEW - else - %a.with-tip{ title: "The products that you sell through the Open Food Network." } What's this? + %a{ "ofn-with-tip" => "The products that you sell through the Open Food Network." } What's this? %div.seven.columns.alpha.list - if @product_count > 0 %div.seven.columns.alpha.list-item @@ -23,4 +23,4 @@ %span.icon-remove-sign %a.seven.columns.alpha.button.bottom.red{ href: "#{new_admin_product_path}" } CREATE A NEW PRODUCT - %span.icon-arrow-right \ No newline at end of file + %span.icon-arrow-right diff --git a/app/views/spree/admin/overview/multi_enterprise_dashboard.html.haml b/app/views/spree/admin/overview/multi_enterprise_dashboard.html.haml index f230d7e44e..511718d3f4 100644 --- a/app/views/spree/admin/overview/multi_enterprise_dashboard.html.haml +++ b/app/views/spree/admin/overview/multi_enterprise_dashboard.html.haml @@ -1,24 +1,29 @@ -%h1{ :style => 'margin-bottom: 30px'} Dashboard +- content_for :page_actions do + = render 'admin/shared/user_guide_link' -- if @enterprises.unconfirmed.any? - = render partial: "unconfirmed" +%div{ 'ng-app' => 'ofn.admin' } + %h1{ :style => 'margin-bottom: 30px' } Dashboard - %hr + - if @enterprises.unconfirmed.any? -- if @enterprises.empty? + = render partial: "unconfirmed" - = render partial: "enterprises" + %hr -- else + - if @enterprises.empty? - - if can? :admin, Spree::Product - = render partial: "products" + = render partial: "enterprises" - %div.two.columns -   + - else - - if can? :admin, OrderCycle - = render partial: "order_cycles" + - if can? :admin, Spree::Product + = render partial: "products" - = render partial: "enterprises" \ No newline at end of file + %div.two.columns +   + + - if can? :admin, OrderCycle + = render partial: "order_cycles" + + = render partial: "enterprises" diff --git a/app/views/spree/admin/overview/single_enterprise_dashboard.html.haml b/app/views/spree/admin/overview/single_enterprise_dashboard.html.haml index f8cb1fb5d9..3998f736de 100644 --- a/app/views/spree/admin/overview/single_enterprise_dashboard.html.haml +++ b/app/views/spree/admin/overview/single_enterprise_dashboard.html.haml @@ -5,24 +5,26 @@ = "(#{enterprise_type_name(@enterprise)})" - content_for :page_actions do + = render 'admin/shared/user_guide_link' + :javascript function toggleType(){ - if( $('#type_selection').is(":visible") ){ + if( $('#package_selection').is(":visible") ){ $('button#toggle_type i').switchClass("icon-chevron-up","icon-chevron-down") } else { $('button#toggle_type i').switchClass("icon-chevron-down","icon-chevron-up") } - $("#type_selection").slideToggle() + $("#package_selection").slideToggle() } - #type_button + #package_button %button#toggle_type{ onClick: 'toggleType()' } - Change type + Change Package %i.icon-chevron-down -#type_selection{ hidden: true } - = render partial: "change_type_form" +#package_selection{ hidden: true } + = render partial: "/admin/enterprises/change_type_form" - if @enterprise.confirmed_at.nil? @@ -94,7 +96,3 @@ %a.button.bottom{href: main_app.admin_order_cycles_path} Manage order cycles %span.icon-arrow-right - - - - diff --git a/app/views/spree/admin/products/_group_buy_form.html.haml b/app/views/spree/admin/products/_group_buy_form.html.haml index 6669563d15..8f1de2a884 100644 --- a/app/views/spree/admin/products/_group_buy_form.html.haml +++ b/app/views/spree/admin/products/_group_buy_form.html.haml @@ -9,6 +9,6 @@ = f.label :group_buy_0, 'No' %br.clear = f.field_container :group_buy_unit_size do - = f.label :group_buy_unit_size + = f.label :group_buy_unit_size, "Bulk unit size" %br = f.text_field :group_buy_unit_size diff --git a/app/views/spree/admin/products/_supplier_and_group_buy_for_new.html.haml b/app/views/spree/admin/products/_supplier_and_group_buy_for_new.html.haml deleted file mode 100644 index d17a45fa6c..0000000000 --- a/app/views/spree/admin/products/_supplier_and_group_buy_for_new.html.haml +++ /dev/null @@ -1,20 +0,0 @@ -.row - .alpha.six.columns - = f.field_container :supplier do - = f.label :supplier - = f.collection_select(:supplier_id, Enterprise.is_primary_producer.managed_by(spree_current_user).by_name, :id, :name, {:include_blank => true}, {:class => "select2 fullwidth"}) - = f.error_message_on :supplier - .four.columns - = f.field_container :group_buy do - = f.label :group_buy, 'Group buy?' - %br - .alpha.two.columns - = f.radio_button :group_buy, '1', :checked => f.object.group_buy - = f.label :group_buy_1, 'Yes' - .omega.two.columns - = f.radio_button :group_buy, '0', :checked => !f.object.group_buy - = f.label :group_buy_0, 'No' - .omega.six.columns - = f.field_container :group_buy_unit_size do - = f.label :group_buy_unit_size - = f.text_field :group_buy_unit_size, :class => "fullwidth" diff --git a/app/views/spree/admin/products/bulk_edit.html.haml b/app/views/spree/admin/products/bulk_edit.html.haml index 22b0a195da..235544cb8a 100644 --- a/app/views/spree/admin/products/bulk_edit.html.haml +++ b/app/views/spree/admin/products/bulk_edit.html.haml @@ -4,7 +4,7 @@ %div{ ng: { app: 'ofn.admin', controller: 'AdminProductEditCtrl', init: 'initialise()' } } = render 'spree/admin/products/bulk_edit/filters' - %hr.sixteen.columns.alpha + %hr.divider.sixteen.columns.alpha.omega = render 'spree/admin/products/bulk_edit/actions' = render 'spree/admin/products/bulk_edit/indicators' = render 'spree/admin/products/bulk_edit/products' diff --git a/app/views/spree/admin/products/bulk_edit/_actions.html.haml b/app/views/spree/admin/products/bulk_edit/_actions.html.haml index 337f1d469b..7ea29bdb4c 100644 --- a/app/views/spree/admin/products/bulk_edit/_actions.html.haml +++ b/app/views/spree/admin/products/bulk_edit/_actions.html.haml @@ -1,13 +1,6 @@ -%div.sixteen.columns.alpha{ 'ng-hide' => 'loading || products.length == 0', style: "margin-bottom: 10px" } - %div.four.columns.alpha +.controls.sixteen.columns.alpha{ 'ng-hide' => 'loading || products.length == 0' } + .four.columns.alpha %input.four.columns.alpha{ :type => 'button', :value => 'Save Changes', 'ng-click' => 'submitProducts()'} - %div.nine.columns + .nine.columns = render 'spree/admin/shared/status_message' - %div.three.columns.omega - %div.ofn_drop_down.three.columns.omega{ 'ng-controller' => "DropDownCtrl", :id => "columns_dropdown", 'ofn-drop-down' => true, :style => 'float:right;' } - %span{ :class => 'icon-reorder' }   Columns - %span{ 'ng-class' => "expanded && 'icon-caret-up' || !expanded && 'icon-caret-down'" } - %div.menu{ 'ng-show' => "expanded" } - %div.menu_item{ :class => "three columns alpha", 'ng-repeat' => "column in columns", 'ofn-toggle-column' => true } - %span{ :class => 'one column alpha', :style => 'text-align: center'} {{ column.visible && "✓" || !column.visible && " " }} - %span{ :class => 'two columns omega' } {{column.name }} + = render 'admin/shared/columns_dropdown' diff --git a/app/views/spree/admin/products/bulk_edit/_filters.html.haml b/app/views/spree/admin/products/bulk_edit/_filters.html.haml index beee7d0a6a..3e676ede6d 100644 --- a/app/views/spree/admin/products/bulk_edit/_filters.html.haml +++ b/app/views/spree/admin/products/bulk_edit/_filters.html.haml @@ -1,18 +1,18 @@ -%div.sixteen.columns.alpha - %div.quick_search{ :class => "four columns alpha" } +.filters.sixteen.columns.alpha.omega + .quick_search.four.columns.alpha %label{ :for => 'quick_filter' } %br - %input.search{ :class => "four columns alpha", 'ng-model' => 'query', :name => "quick_filter", :type => 'text', 'placeholder' => 'Quick Search' } - .filter_select{ :class => "four columns" } + %input.quick-search.fullwidth{ 'ng-model' => 'query', :name => "quick_filter", :type => 'text', 'placeholder' => 'Quick Search' } + .filter_select.four.columns %label{ :for => 'producer_filter' }Producer %br - %select{ :class => "four columns alpha", :id => 'producer_filter', 'ofn-select2-min-search' => 5, 'ng-model' => 'producerFilter', 'ng-options' => 'producer.id as producer.name for producer in filterProducers' } - .filter_select{ :class => "four columns" } + %select.fullwidth{ :id => 'producer_filter', 'ofn-select2-min-search' => 5, 'ng-model' => 'producerFilter', 'ng-options' => 'producer.id as producer.name for producer in filterProducers' } + .filter_select.four.columns %label{ :for => 'category_filter' }Category %br - %select{ :class => "four columns alpha", :id => 'category_filter', 'ofn-select2-min-search' => 5, 'ng-model' => 'categoryFilter', 'ng-options' => 'taxon.id as taxon.name for taxon in filterTaxons'} + %select.fullwidth{ :id => 'category_filter', 'ofn-select2-min-search' => 5, 'ng-model' => 'categoryFilter', 'ng-options' => 'taxon.id as taxon.name for taxon in filterTaxons'} %div{ :class => "one column" }   - .filter_clear{ :class => "three columns omega" } + .filter_clear.three.columns.omega %label{ :for => 'clear_all_filters' } %br - %input.fullwidth{ :type => 'button', :id => 'clear_all_filters', :value => "Clear Filters", 'ng-click' => "resetSelectFilters()" } + %input.fullwidth.red{ :type => 'button', :id => 'clear_all_filters', :value => "Clear Filters", 'ng-click' => "resetSelectFilters()" } diff --git a/app/views/spree/admin/products/bulk_edit/_products_head.html.haml b/app/views/spree/admin/products/bulk_edit/_products_head.html.haml index 6e22aef8ff..7767b8de89 100644 --- a/app/views/spree/admin/products/bulk_edit/_products_head.html.haml +++ b/app/views/spree/admin/products/bulk_edit/_products_head.html.haml @@ -17,8 +17,10 @@ %col.actions %thead - %tr + %tr{ ng: { controller: "ColumnsCtrl" } } %th.left-actions + %a{ 'ng-click' => 'toggleShowAllVariants()', :style => 'color: red' } + Expand All %th.producer{ 'ng-show' => 'columns.producer.visible' } Producer %th.sku{ 'ng-show' => 'columns.sku.visible' } SKU %th.name{ 'ng-show' => 'columns.name.visible' } Name diff --git a/app/views/spree/admin/products/bulk_edit/_products_product.html.haml b/app/views/spree/admin/products/bulk_edit/_products_product.html.haml index 376e88071a..6ac25ae286 100644 --- a/app/views/spree/admin/products/bulk_edit/_products_product.html.haml +++ b/app/views/spree/admin/products/bulk_edit/_products_product.html.haml @@ -1,6 +1,6 @@ %tr.product{ :id => "p_{{product.id}}" } %td.left-actions - %a{ 'ofn-toggle-variants' => 'true', :class => "view-variants icon-chevron-right", 'ng-show' => 'hasVariants(product)' } + %a{ 'ofn-toggle-variants' => 'true', :class => "view-variants", 'ng-show' => 'hasVariants(product)' } %a{ :class => "add-variant icon-plus-sign", 'ng-click' => "addVariant(product)", 'ng-show' => "!hasVariants(product) && hasUnit(product)" } %td.producer{ 'ng-show' => 'columns.producer.visible' } %select.select2.fullwidth{ 'ng-model' => 'product.producer_id', :name => 'producer_id', 'ofn-track-product' => 'producer_id', 'ng-options' => 'producer.id as producer.name for producer in producers' } diff --git a/app/views/spree/admin/products/bulk_edit/_products_variant.html.haml b/app/views/spree/admin/products/bulk_edit/_products_variant.html.haml index cc85566577..fb68704b79 100644 --- a/app/views/spree/admin/products/bulk_edit/_products_variant.html.haml +++ b/app/views/spree/admin/products/bulk_edit/_products_variant.html.haml @@ -4,6 +4,7 @@ %a{ :class => "add-variant icon-plus-sign", 'ng-click' => "addVariant(product)", 'ng-show' => "$last" } %td{ 'ng-show' => 'columns.producer.visible' } %td{ 'ng-show' => 'columns.sku.visible' } + %input{ 'ng-model' => "variant.sku", :name => 'variant_sku', 'ofn-track-variant' => 'sku', :type => 'text' } %td{ 'ng-show' => 'columns.name.visible' } %input{ 'ng-model' => 'variant.display_name', :name => 'variant_display_name', 'ofn-track-variant' => 'display_name', :type => 'text', placeholder: "{{ product.name }}" } %td.unit_value{ 'ng-show' => 'columns.unit.visible' } @@ -24,6 +25,6 @@ %td.actions %a{ 'ng-click' => 'editWarn(product,variant)', :class => "edit-variant icon-edit no-text", 'ng-show' => "variantSaved(variant)" } %td.actions - %span.icon-warning-sign.with-tip{ 'ng-if' => 'variant.variant_overrides', title: "This variant has {{variant.variant_overrides.length}} override(s)" } + %span.icon-warning-sign{ 'ng-if' => 'variant.variant_overrides', 'ofn-with-tip' => "This variant has {{variant.variant_overrides.length}} override(s)" } %td.actions %a{ 'ng-click' => 'deleteVariant(product,variant)', "ng-class" => '{disabled: product.variants.length < 2}', :class => "delete-variant icon-trash no-text" } diff --git a/app/views/spree/admin/reports/_packing_description.html.haml b/app/views/spree/admin/reports/_packing_description.html.haml new file mode 100644 index 0000000000..3d7855cb89 --- /dev/null +++ b/app/views/spree/admin/reports/_packing_description.html.haml @@ -0,0 +1,4 @@ +%ul{style: "margin-left: 12pt"} + - report_types.each do |report_type| + %li + = link_to report_type[0], "#{packing_admin_reports_url}?report_type=#{report_type[1]}" diff --git a/app/views/spree/admin/reports/bulk_coop.html.haml b/app/views/spree/admin/reports/bulk_coop.html.haml index d80a449bb4..8d9a1234ce 100644 --- a/app/views/spree/admin/reports/bulk_coop.html.haml +++ b/app/views/spree/admin/reports/bulk_coop.html.haml @@ -1,4 +1,4 @@ -= form_for @search, :url => spree.bulk_coop_admin_reports_path do |f| += form_for @report.search, :url => spree.bulk_coop_admin_reports_path do |f| = render 'date_range_form', f: f .row @@ -20,7 +20,7 @@ %table#listing_orders.index %thead %tr{'data-hook' => "orders_header"} - - @header.each do |heading| + - @report.header.each do |heading| %th=heading %tbody - @table.each do |row| diff --git a/app/views/spree/admin/reports/order_cycle_management.html.haml b/app/views/spree/admin/reports/order_cycle_management.html.haml index 418cba8b75..5ab379a669 100644 --- a/app/views/spree/admin/reports/order_cycle_management.html.haml +++ b/app/views/spree/admin/reports/order_cycle_management.html.haml @@ -1,47 +1,46 @@ -= form_tag spree.order_cycle_management_admin_reports_url do |f| - %br - = label_tag nil, "Order Cycle: " - = select_tag(:order_cycle_id, - options_for_select(report_order_cycle_options(@order_cycles), params[:order_cycle_id]), - include_blank: true) - %br - %br - = label_tag nil, "Payment Methods (hold Ctrl to select multiple payment methods)" - %br += form_for @report.search, :url => spree.order_cycle_management_admin_reports_path do |f| + = render 'date_range_form', f: f - = select_tag(:payment_method_name, - options_for_select(report_payment_method_options(@orders), params[:payment_method_name]), - multiple: true, include_blank: true, size: 10) - %br - %br - = label_tag nil, "Shipping Method: " - = select_tag(:shipping_method_name, - options_for_select(report_shipping_method_options(@orders), params[:shipping_method_name]), - include_blank: true) - %br - %br - = label_tag nil, "Report Type: " - = select_tag(:report_type, options_for_select(@report_types, @report_type)) - %br - %br - = check_box_tag :csv - = label_tag :csv, "Download as csv" - %br - %br + .row + .alpha.two.columns= label_tag nil, "Hubs: " + .omega.fourteen.columns= f.collection_select(:distributor_id_in, @distributors, :id, :name, {}, {class: "select2 fullwidth", multiple: true}) + + .row + .alpha.two.columns= label_tag nil, "Order Cycles: " + .omega.fourteen.columns + = f.select(:order_cycle_id_in, report_order_cycle_options(@order_cycles), {selected: params[:q][:order_cycle_id_in]}, {class: "select2 fullwidth", multiple: true}) + + .row + .alpha.two.columns= label_tag nil, "Payment Methods: " + .omega.fourteen.columns= select_tag(:payment_method_in, options_for_select(report_payment_method_options(@report.orders), params[:payment_method_in]), {class: "select2 fullwidth", multiple: true}) + + .row + .alpha.two.columns= label_tag nil, "Shipping Methods: " + .omega.fourteen.columns= select_tag(:shipping_method_in, options_for_select(report_shipping_method_options(@report.orders), params[:shipping_method_in]), {class: "select2 fullwidth", multiple: true}) + + .row + .alpha.two.columns= label_tag nil, "Report Type: " + .omega.fourteen.columns= select_tag(:report_type, options_for_select(@report_types, @report_type)) + + .row + = check_box_tag :csv + = label_tag :csv, "Download as csv" + + .row = button t(:search) %br %br -%table#listing_order_payment_methods.index +%table#listing_ocm_orders.index %thead %tr{'data-hook' => "orders_header"} - @report.header.each do |heading| %th=heading %tbody - - @report.table.each do |row| + - @table.each do |row| %tr - row.each do |column| %td= column - - if @report.table.empty? + - if @table.empty? %tr %td{:colspan => "2"}= t(:none) diff --git a/app/views/spree/admin/reports/orders_and_fulfillment.html.haml b/app/views/spree/admin/reports/orders_and_fulfillment.html.haml index 081cb43384..2465fe0ff2 100644 --- a/app/views/spree/admin/reports/orders_and_fulfillment.html.haml +++ b/app/views/spree/admin/reports/orders_and_fulfillment.html.haml @@ -1,4 +1,4 @@ -= form_for @search, :url => spree.orders_and_fulfillment_admin_reports_path do |f| += form_for @report.search, :url => spree.orders_and_fulfillment_admin_reports_path do |f| = render 'date_range_form', f: f .row @@ -30,7 +30,7 @@ %table#listing_orders.index %thead %tr{'data-hook' => "orders_header"} - - @header.each do |heading| + - @report.header.each do |heading| %th=heading %tbody - @table.each do |row| diff --git a/app/views/spree/admin/reports/packing.html.haml b/app/views/spree/admin/reports/packing.html.haml new file mode 100644 index 0000000000..2aa8d8c8b9 --- /dev/null +++ b/app/views/spree/admin/reports/packing.html.haml @@ -0,0 +1,42 @@ += form_for @report.search, :url => spree.packing_admin_reports_path do |f| + = render 'date_range_form', f: f + + .row + .alpha.two.columns= label_tag nil, "Hubs: " + .omega.fourteen.columns= f.collection_select(:distributor_id_in, @distributors, :id, :name, {}, {class: "select2 fullwidth", multiple: true}) + + .row + .alpha.two.columns= label_tag nil, "Producers: " + .omega.fourteen.columns= select_tag(:supplier_id_in, options_from_collection_for_select(@suppliers, :id, :name, params[:supplier_id_in]), {class: "select2 fullwidth", multiple: true}) + + .row + .alpha.two.columns= label_tag nil, "Order Cycles: " + .omega.fourteen.columns + = f.select(:order_cycle_id_in, report_order_cycle_options(@order_cycles), {selected: params[:q][:order_cycle_id_in]}, {class: "select2 fullwidth", multiple: true}) + + .row + .alpha.two.columns= label_tag nil, "Report Type: " + .omega.fourteen.columns= select_tag(:report_type, options_for_select(@report_types, @report_type)) + + .row + = check_box_tag :csv + = label_tag :csv, "Download as csv" + + .row + = button t(:search) + +%br +%br +%table#listing_orders.index + %thead + %tr{'data-hook' => "orders_header"} + - @report.header.each do |heading| + %th=heading + %tbody + - @table.each do |row| + %tr + - row.each do |column| + %td= column + - if @table.empty? + %tr + %td{:colspan => "2"}= t(:none) diff --git a/app/views/spree/admin/reports/payments.html.haml b/app/views/spree/admin/reports/payments.html.haml index 1a92d507db..909846f47f 100644 --- a/app/views/spree/admin/reports/payments.html.haml +++ b/app/views/spree/admin/reports/payments.html.haml @@ -1,4 +1,4 @@ -= form_for @search, :url => spree.payments_admin_reports_path do |f| += form_for @report.search, :url => spree.payments_admin_reports_path do |f| = render 'date_range_form', f: f .row @@ -20,7 +20,7 @@ %table#listing_orders.index %thead %tr{'data-hook' => "orders_header"} - - @header.each do |heading| + - @report.header.each do |heading| %th=heading %tbody - @table.each do |row| diff --git a/app/views/spree/admin/reports/sales_tax.html.haml b/app/views/spree/admin/reports/sales_tax.html.haml index a7b3d9275d..b0a115a74b 100644 --- a/app/views/spree/admin/reports/sales_tax.html.haml +++ b/app/views/spree/admin/reports/sales_tax.html.haml @@ -1,4 +1,4 @@ -= form_for @search, :url => spree.sales_tax_admin_reports_path do |f| += form_for @report.search, :url => spree.sales_tax_admin_reports_path do |f| = render 'date_range_form', f: f .row @@ -29,4 +29,3 @@ - if @report.table.empty? %tr %td{:colspan => @report.header.count}= t(:none) - diff --git a/app/views/spree/admin/reports/xero_invoices.html.haml b/app/views/spree/admin/reports/xero_invoices.html.haml new file mode 100644 index 0000000000..eca51fed4f --- /dev/null +++ b/app/views/spree/admin/reports/xero_invoices.html.haml @@ -0,0 +1,47 @@ += form_for @report.search, url: spree.xero_invoices_admin_reports_path do |f| + = render 'date_range_form', f: f + + .row + .four.columns.alpha= label_tag :report_type, "Report Type: " + .four.columns.omega= select_tag :report_type, options_for_select(xero_report_types, params[:report_type]), {include_blank: false, class: "select2 fullwidth"} + .row + .four.columns.alpha= label_tag nil, "Hub: " + .four.columns.omega= f.collection_select(:distributor_id_eq, @distributors, :id, :name, {:include_blank => 'All'}, {:class => "select2 fullwidth"}) + .row + .four.columns.alpha= label_tag nil, "Order Cycle: " + .four.columns.omega= f.select(:order_cycle_id_eq, + options_for_select(report_order_cycle_options(@order_cycles), params[:q][:order_cycle_id_eq]), + {:include_blank => true}, {:class => "select2 fullwidth"}) + + .row + .four.columns.alpha= label_tag :initial_invoice_number, "Initial invoice number:" + .twelve.columns.omega= text_field_tag :initial_invoice_number, params[:initial_invoice_number] + .row + .four.columns.alpha= label_tag :invoice_date, "Invoice date:" + .twelve.columns.omega= text_field_tag :invoice_date, params[:invoice_date], class: 'datetimepicker' + .row + .four.columns.alpha= label_tag :due_date, "Due date:" + .twelve.columns.omega= text_field_tag :due_date, params[:due_date], class: 'datetimepicker' + .row + .four.columns.alpha= label_tag :account_code, "Account code:" + .twelve.columns.omega= text_field_tag :account_code, params[:account_code] + .row + .four.columns.alpha= label_tag :csv, "Download as CSV:" + .twelve.columns.omega= check_box_tag :csv + .row + .four.columns.alpha= button t(:search) + + +%table#listing_invoices.index + %thead + %tr + - @report.header.each do |header| + %th= header + %tbody + - @report.table.each do |row| + %tr + - row.each do |column| + %td= column + - if @report.table.empty? + %tr + %td{:colspan => "2"}= t(:none) diff --git a/app/views/spree/admin/tax_settings/edit.html.haml b/app/views/spree/admin/tax_settings/edit.html.haml new file mode 100644 index 0000000000..edd5a3b401 --- /dev/null +++ b/app/views/spree/admin/tax_settings/edit.html.haml @@ -0,0 +1,23 @@ += render :partial => 'spree/admin/shared/configuration_menu' + +- content_for :page_title do + = t(:tax_settings) + += form_tag admin_tax_settings_path, :method => :put do + + .field.align-center{ "data-hook" => "products_require_tax_category" } + = hidden_field_tag 'preferences[products_require_tax_category]', '0' + = check_box_tag 'preferences[products_require_tax_category]', '1', Spree::Config[:products_require_tax_category] + = label_tag nil, t(:products_require_tax_category) + + .field.align-center{"data-hook" => "shipment_vat"} + = hidden_field_tag 'preferences[shipment_inc_vat]', '0' + = check_box_tag 'preferences[shipment_inc_vat]', '1', Spree::Config[:shipment_inc_vat] + = label_tag nil, t(:shipment_inc_vat) + + .field.align-center{ "data-hook" => "shipping_tax_rate" } + = number_field_tag "preferences[shipping_tax_rate]", Spree::Config[:shipping_tax_rate].to_f, in: 0.0..1.0, step: 0.01 + = label_tag nil, t(:shipping_tax_rate) + + .form-buttons{"data-hook" => "buttons"} + = button t(:update), 'icon-refresh' diff --git a/app/views/spree/api/products/bulk_show.v1.rabl b/app/views/spree/api/products/bulk_show.v1.rabl index d2cd23bd03..583418c07b 100644 --- a/app/views/spree/api/products/bulk_show.v1.rabl +++ b/app/views/spree/api/products/bulk_show.v1.rabl @@ -1,13 +1,21 @@ object @product -attributes :id, :name, :variant_unit, :variant_unit_scale, :variant_unit_name, :on_demand + +# TODO: This is used by bulk product edit when a product is cloned. +# But the list of products is serialized by Api::Admin::ProductSerializer. +# This should probably be unified. + +attributes :id, :name, :sku, :variant_unit, :variant_unit_scale, :variant_unit_name, :on_demand, :inherits_properties +attributes :on_hand, :price, :available_on, :permalink_live, :tax_category_id # Infinity is not a valid JSON object, but Rails encodes it anyway node( :taxon_ids ) { |p| p.taxons.map{ |t| t.id }.join(",") } -node( :on_hand ) { |p| p.on_hand.nil? ? 0 : p.on_hand.to_f.finite? ? p.on_hand : "On demand" } +node( :on_hand ) { |p| p.on_hand.nil? ? 0 : p.on_hand.to_f.finite? ? p.on_hand : t(:on_demand) } node( :price ) { |p| p.price.nil? ? '0.0' : p.price } node( :available_on ) { |p| p.available_on.blank? ? "" : p.available_on.strftime("%F %T") } node( :permalink_live ) { |p| p.permalink } +node( :producer_id ) { |p| p.supplier_id } +node( :category_id ) { |p| p.primary_taxon_id } node( :supplier ) do |p| partial 'api/enterprises/bulk_show', :object => p.supplier end diff --git a/app/views/spree/api/variants/bulk_show.v1.rabl b/app/views/spree/api/variants/bulk_show.v1.rabl index 4a2a6bae9c..8044ded0a0 100644 --- a/app/views/spree/api/variants/bulk_show.v1.rabl +++ b/app/views/spree/api/variants/bulk_show.v1.rabl @@ -3,5 +3,5 @@ object @variant attributes :id, :options_text, :unit_value, :unit_description, :on_demand, :display_as, :display_name # Infinity is not a valid JSON object, but Rails encodes it anyway -node( :on_hand ) { |v| v.on_hand.nil? ? 0 : ( v.on_hand.to_f.finite? ? v.on_hand : "On demand" ) } -node( :price ) { |v| v.price.nil? ? 0.to_f : v.price } \ No newline at end of file +node( :on_hand ) { |v| v.on_hand.nil? ? 0 : ( v.on_hand.to_f.finite? ? v.on_hand : t(:on_demand) ) } +node( :price ) { |v| v.price.nil? ? 0.to_f : v.price } diff --git a/app/views/spree/checkout/payment/_gateway.html.haml b/app/views/spree/checkout/payment/_gateway.html.haml index 0b801cb420..33dcbda8fc 100644 --- a/app/views/spree/checkout/payment/_gateway.html.haml +++ b/app/views/spree/checkout/payment/_gateway.html.haml @@ -1,22 +1,23 @@ .row .small-6.columns %label - First Name + = t :first_name %input{type: :text, disabled: true, "ng-value" => "order.bill_address.firstname"} .small-6.columns %label - Last Name + = t :last_name %input{type: :text, disabled: true, "ng-value" => "order.bill_address.lastname"} .small-6.columns - = validated_input "Card Number", "secrets.card_number", required: true, maxlength: 19, autocomplete: "off" + = validated_input t(:card_number), "secrets.card_number", required: true, maxlength: 19, autocomplete: "off" .small-6.columns - = validated_input "Security Code", "secrets.card_verification_value", required: true + = validated_input t(:card_securitycode), "secrets.card_verification_value", required: true .row .small-12.columns - %label{for: "secrets.card_month"} Expiry Date + %label{for: "secrets.card_month"} + = t :card_expiry_date .row .small-6.columns diff --git a/app/views/spree/order_mailer/_order_summary.html.haml b/app/views/spree/order_mailer/_order_summary.html.haml index 86b1af1f3b..a5104c12f4 100644 --- a/app/views/spree/order_mailer/_order_summary.html.haml +++ b/app/views/spree/order_mailer/_order_summary.html.haml @@ -2,23 +2,19 @@ %thead %tr %th{:align => "left"} - %h4 Item + %h4 + = t :email_order_summary_item %th{:align => "right", :width => "25%"} - %h4 Qty + %h4 + = t :email_order_summary_quantity %th{:align => "right", :width => "25%"} - %h4 Price + %h4 + = t :email_order_summary_price %tbody - @order.line_items.each do |item| %tr %td - - if item.variant.product.name == item.variant.name_to_display - %strong= "#{raw(item.variant.product.name)}" - - else - %strong - %span= "#{raw(item.variant.product.name)}" - %span= "- " + "#{raw(item.variant.name_to_display)}" - - if item.variant.options_text - = "(" + "#{raw(item.variant.options_text)}" + ")" + = render 'spree/shared/line_item_name', line_item: item %br %small %em= raw(item.variant.product.supplier.name) @@ -29,7 +25,7 @@ %tfoot %tr %td{:align => "right", :colspan => "2"} - Subtotal: + = t :email_order_summary_subtotal %td{:align => "right"} = display_checkout_subtotal(@order) - checkout_adjustments_for(@order, exclude: [:line_item]).reject{ |a| a.amount == 0 }.reverse_each do |adjustment| @@ -40,7 +36,8 @@ = adjustment.display_amount %tr %td{:align => "right", :colspan => "2"} - %strong Total: + %strong + = t :email_order_summary_total %td{:align => "right"} %strong= @order.display_total diff --git a/app/views/spree/order_mailer/_payment.html.haml b/app/views/spree/order_mailer/_payment.html.haml index 41968ba1ad..98022ffb3d 100644 --- a/app/views/spree/order_mailer/_payment.html.haml +++ b/app/views/spree/order_mailer/_payment.html.haml @@ -2,12 +2,13 @@ %p.callout %span{:style => "float:right;"} - if @order.paid? - PAID + = t :email_payment_paid - else - NOT PAID - %strong Payment summary + = t :email_payment_not_paid + %strong + = t :email_payment_summary %h4 - Paying via: + = t :email_payment_method %strong= @order.payments.first.andand.payment_method.andand.name.andand.html_safe %p %em= @order.payments.first.andand.payment_method.andand.description.andand.html_safe diff --git a/app/views/spree/order_mailer/_shipping.html.haml b/app/views/spree/order_mailer/_shipping.html.haml index cb9cde72ca..4b73f9c970 100644 --- a/app/views/spree/order_mailer/_shipping.html.haml +++ b/app/views/spree/order_mailer/_shipping.html.haml @@ -5,19 +5,20 @@ - if @order.shipping_method.andand.name #{@order.shipping_method.name.html_safe} - else - Delivery details + = t :email_shipping_delivery_details - if @order.order_cycle.andand.pickup_time_for(@order.distributor) - %h4 - Delivery on: + %h4 + = t :email_shipping_delivery_time %strong #{@order.order_cycle.pickup_time_for(@order.distributor)} - if @order.shipping_method.andand.description - %p + %p %em #{@order.shipping_method.description.html_safe} %br   - + - if @order.ship_address - %h4 Delivery address: + %h4 + = t :email_shipping_delivery_address %p #{@order.ship_address.full_name} %br @@ -34,26 +35,21 @@ - if @order.shipping_method.andand.name #{@order.shipping_method.name.html_safe} - else - Collection details + = t :email_shipping_collection_details - if @order.order_cycle.andand.pickup_time_for(@order.distributor).present? - %h4 - Ready for collection: + %h4 + = t :email_shipping_collection_time %strong #{@order.order_cycle.pickup_time_for(@order.distributor)} - + - if @order.shipping_method.andand.description.present? %p %em #{@order.shipping_method.description.html_safe} %br   - - if @order.ship_address.full_address - %p - %strong Collecting from: - %br - #{@order.ship_address.full_address} - - if @order.order_cycle.andand.pickup_instructions_for(@order.distributor).present? %p - %strong Collection instructions: + %strong + = t :email_shipping_collection_time %br #{@order.order_cycle.pickup_instructions_for(@order.distributor)} diff --git a/app/views/spree/order_mailer/_signoff.html.haml b/app/views/spree/order_mailer/_signoff.html.haml new file mode 100644 index 0000000000..669cf4b9ad --- /dev/null +++ b/app/views/spree/order_mailer/_signoff.html.haml @@ -0,0 +1,15 @@ +%br +%p.callout + = t :email_confirm_customer_signoff + %br + #{@order.distributor.contact} + %br + %br + = @order.distributor.name + %br + = @order.distributor.phone || "" + %br + %a{:href => "mailto:#{@order.distributor.email}", :target => "_blank"} + = @order.distributor.email + %br + = @order.distributor.website || "" diff --git a/app/views/spree/order_mailer/_special_instructions.html.haml b/app/views/spree/order_mailer/_special_instructions.html.haml index 5145260411..2d2f947405 100644 --- a/app/views/spree/order_mailer/_special_instructions.html.haml +++ b/app/views/spree/order_mailer/_special_instructions.html.haml @@ -2,6 +2,7 @@ %br %p %small - %strong Your notes: + %strong + = t :email_special_instructions %br #{@order.special_instructions} diff --git a/app/views/spree/order_mailer/confirm_email_for_customer.html.haml b/app/views/spree/order_mailer/confirm_email_for_customer.html.haml index 0816bd432d..f91692fdb6 100644 --- a/app/views/spree/order_mailer/confirm_email_for_customer.html.haml +++ b/app/views/spree/order_mailer/confirm_email_for_customer.html.haml @@ -5,10 +5,9 @@ %tr %td %h3 - Hi #{@order.bill_address.firstname}, + = t :email_confirm_customer_greeting, name: @order.bill_address.firstname %h4 - Thanks for shopping at - %strong= "#{@order.distributor.name}!" + = t :email_confirm_customer_intro_html, distributor: @order.distributor.name %table.column{:align => "left"} %tr %td{:align => "right"} @@ -17,31 +16,12 @@ %p   %h4 - Order confirmation - %strong ##{@order.number} + = t :email_confirm_customer_number_html, number: @order.number %p - Here are your order details from - %strong= "#{@order.distributor.name}:" + = t :email_confirm_customer_details_html, distributor: @order.distributor.name = render 'order_summary' = render 'payment' = render 'shipping' = render 'special_instructions' - -%br -%p.callout - Kind regards, - %br - #{@order.distributor.contact} - %br - %br - = @order.distributor.name - %br - = @order.distributor.phone || "" - %br - %a{:href => "mailto:#{@order.distributor.email}", :target => "_blank"} - = @order.distributor.email - %br - = @order.distributor.website || "" - - += render 'signoff' diff --git a/app/views/spree/order_mailer/confirm_email_for_shop.html.haml b/app/views/spree/order_mailer/confirm_email_for_shop.html.haml index f62b3cdfb4..5799f1e5c2 100644 --- a/app/views/spree/order_mailer/confirm_email_for_shop.html.haml +++ b/app/views/spree/order_mailer/confirm_email_for_shop.html.haml @@ -5,10 +5,9 @@ %tr %td %h3 - Hi #{@order.distributor.contact}, + = t :email_confirm_shop_greeting, name: @order.distributor.contact %h4 - Well done! You have a new order for - %strong= "#{@order.distributor.name}!" + = t :email_confirm_shop_order_html, distributor: @order.distributor.name %table.column{:align => "left"} %tr %td{:align => "right"} @@ -17,11 +16,11 @@ %p   %h4 - Order confirmation - %strong ##{@order.number} -%p + = t :email_confirm_shop_number_html, number: @order.number +%h5 %strong= "#{@order.bill_address.firstname} #{@order.bill_address.lastname}" - completed the following order at your shopfront: + = " <#{@order.email}>" if @order.email + = @order.bill_address.phone if @order.bill_address.phone = render 'order_summary' = render 'payment' diff --git a/app/views/spree/order_mailer/invoice_email.html.haml b/app/views/spree/order_mailer/invoice_email.html.haml new file mode 100644 index 0000000000..e2a2aa8967 --- /dev/null +++ b/app/views/spree/order_mailer/invoice_email.html.haml @@ -0,0 +1,10 @@ +%table.social.white-bg{:width => "100%"} + %tr + %td + %h3 + Hi #{@order.bill_address.firstname}, + %h4 + Please find attached an invoice for your recent order from + %strong= "#{@order.distributor.name}" + += render 'signoff' diff --git a/app/views/spree/orders/_adjustments.html.haml b/app/views/spree/orders/_adjustments.html.haml index 12b16375ec..14fba90f72 100644 --- a/app/views/spree/orders/_adjustments.html.haml +++ b/app/views/spree/orders/_adjustments.html.haml @@ -1,6 +1,7 @@ %tr{"data-hook" => "cart_adjustments_headers"} %td.cart-adjustments{colspan: "5"} - %a{ href: "#" } Fees... + %a{ href: "#" } + = t :orders_fees - checkout_line_item_adjustments(@order).each do |adjustment| %tr.cart_adjustment diff --git a/app/views/spree/orders/_form.html.haml b/app/views/spree/orders/_form.html.haml index e71f92452f..68ba409aa0 100644 --- a/app/views/spree/orders/_form.html.haml +++ b/app/views/spree/orders/_form.html.haml @@ -30,7 +30,7 @@ %td %td#empty-cart.text-center %span#clear_cart_link{"data-hook" => ""} - = link_to "Empty cart", empty_cart_path, method: :put, :class => 'not-bold small' + = link_to t(:orders_form_empty_cart), empty_cart_path, method: :put, :class => 'not-bold small' -#= form_tag empty_cart_path, :method => :put do -#= submit_tag t(:empty_cart), :class => 'button alert expand small' @@ -39,20 +39,23 @@ -# = render "spree/orders/adjustments" %tr - %td.text-right{colspan:"3"} Produce subtotal + %td.text-right{colspan:"3"} + = t :orders_form_subtotal %td.text-right %span.order-total.item-total= display_checkout_subtotal(@order) %td %tr - %td.text-right{colspan:"3"} Admin & handling + %td.text-right{colspan:"3"} + = t :orders_form_admin %td.text-right %span.order-total.distribution-total= display_checkout_admin_and_handling_adjustments_total_for(@order) %td %tr %td.text-right{colspan:"3"} - %h5 Total + %h5 + = t :orders_form_total %td.text-right %h5.order-total.grand-total= @order.display_total %td diff --git a/app/views/spree/orders/_line_item.html.haml b/app/views/spree/orders/_line_item.html.haml index d687290561..6014b617a9 100644 --- a/app/views/spree/orders/_line_item.html.haml +++ b/app/views/spree/orders/_line_item.html.haml @@ -15,15 +15,7 @@ - else = link_to image_tag(variant.images.first.attachment.url(:mini)), variant.product - - if variant.product.name == variant.name_to_display - %h5 - %span= variant.product.name - %span.text-small.text-skinny= " (" + variant.options_text + ")" unless variant.options_text.empty? - - else - %h5 - %span= variant.product.name - %span= "- " + variant.name_to_display - %span.text-small.text-skinny= " (" + variant.options_text + ")" unless variant.options_text.empty? + = render 'spree/shared/line_item_name', line_item: line_item - if @order.insufficient_stock_lines.include? line_item %span.out-of-stock diff --git a/app/views/spree/orders/edit.html.haml b/app/views/spree/orders/edit.html.haml index 5f8186e568..2fa5c043db 100644 --- a/app/views/spree/orders/edit.html.haml +++ b/app/views/spree/orders/edit.html.haml @@ -1,19 +1,23 @@ -= inject_enterprises +- content_for(:title) do + = t :orders_edit_title + += inject_enterprises .darkswarm - content_for :order_cycle_form do - %closing Your shopping cart - %p - Order ready for + %closing + = t :orders_edit_headline + %p + = t :orders_edit_time %strong - if @order.order_cycle - = pickup_time @order.order_cycle + = pickup_time @order.order_cycle - else = @order.distributor.next_collection_at = render partial: "shopping_shared/details" - - %fieldset + + %fieldset.footer-pad - if @order.line_items.empty? %div.row{"data-hook" => "empty_cart"} %p= t(:your_cart_is_empty) @@ -31,14 +35,13 @@ .links{'data-hook' => "cart_buttons"} .row .columns.large-8{"data-hook" => ""} - + %a.button.large.secondary{href: main_app.shop_path} - %i.ofn-i_008-caret-left - Continue shopping + %i.ofn-i_008-caret-left + = t :orders_edit_continue .columns.large-4.text-right %a#checkout-link.button.large.primary{href: main_app.checkout_path} - Checkout + = t :orders_edit_checkout %i.ofn-i_007-caret-right = render partial: "shared/footer" - diff --git a/app/views/spree/orders/order_cycle_expired.html.haml b/app/views/spree/orders/order_cycle_expired.html.haml index c5726cf64e..66bb134b7a 100644 --- a/app/views/spree/orders/order_cycle_expired.html.haml +++ b/app/views/spree/orders/order_cycle_expired.html.haml @@ -1,17 +1,17 @@ -%h2 Orders have closed for this order cycle +%h2 + = t :orders_oc_expired_headline %p - = succeed "." do - Sorry, orders for this order cycle closed - = distance_of_time_in_words_to_now @order_cycle.orders_close_at - ago! Please contact your hub directly to see if they can accept late orders - - - if active_order_cycle_for_distributor? current_distributor - %strong= link_to 'or see the other order cycles available at this hub', spree.clear_orders_path + - if active_order_cycle_for_distributor? current_distributor + = t :orders_oc_expired_text_others_html, time: distance_of_time_in_words_to_now(@order_cycle.orders_close_at), link: link_to t(:orders_oc_expired_text_link), spree.clear_orders_path + - else + = t :orders_oc_expired_text, time: distance_of_time_in_words_to_now(@order_cycle.orders_close_at) %p - %strong Email: + %strong + = t :orders_oc_expired_email = current_distributor.email %br/ - %strong Phone: + %strong + = t :orders_oc_expired_phone = current_distributor.phone diff --git a/app/views/spree/orders/show.html.haml b/app/views/spree/orders/show.html.haml index 82120add82..438a611a68 100644 --- a/app/views/spree/orders/show.html.haml +++ b/app/views/spree/orders/show.html.haml @@ -1,21 +1,24 @@ +- content_for(:title) do + = t :orders_show_title + = inject_enterprises .darkswarm - content_for :order_cycle_form do %strong.avenir - Order ready on + = t :orders_show_time - if @order.order_cycle = @order.order_cycle.pickup_time_for(@order.distributor) - else = @order.distributor.next_collection_at - = render "shopping_shared/details" + = render "shopping_shared/details" if current_distributor.present? - %fieldset#order_summary{"data-hook" => ""} + %fieldset#order_summary.footer-pad{"data-hook" => ""} .row .columns.large-12.text-center %h2 - Order confirmation + = t :orders_show_number = " #" + @order.number #order{"data-hook" => ""} @@ -30,3 +33,6 @@ - unless params.has_key? :checkout_complete - if try_spree_current_user && respond_to?(:spree_account_path) = link_to t(:my_account), spree_account_path, :class => "button" + + += render partial: "shared/footer" diff --git a/app/views/spree/products/_add_to_cart_distributor_choice.html.haml b/app/views/spree/products/_add_to_cart_distributor_choice.html.haml index 79936442c7..6348760786 100644 --- a/app/views/spree/products/_add_to_cart_distributor_choice.html.haml +++ b/app/views/spree/products/_add_to_cart_distributor_choice.html.haml @@ -1,2 +1,3 @@ -.distributor Distributor for your order: +.distributor + = t :products_cart_distributor_choice = select_tag "distributor_id", options_from_collection_for_select([Enterprise.new]+distributor_collection, "id", "name", current_distributor.andand.id) diff --git a/app/views/spree/products/_add_to_cart_distributor_fixed.html.haml b/app/views/spree/products/_add_to_cart_distributor_fixed.html.haml index 7927f9fc3e..1c82199c89 100644 --- a/app/views/spree/products/_add_to_cart_distributor_fixed.html.haml +++ b/app/views/spree/products/_add_to_cart_distributor_fixed.html.haml @@ -1,6 +1,7 @@ = hidden_field_tag "distributor_id", distributor.id -- if changing_distributor - .distributor-fixed= "Your distributor for this order will be changed to #{distributor.name} if you add this product to your cart." -- else - .distributor-fixed= "Your distributor for this order is #{distributor.name}" +.distributor-fixed + - if changing_distributor + = t :products_cart_distributor_change, name: distributor.name + - else + = t :products_cart_distributor_is, name: distributor.name diff --git a/app/views/spree/products/_add_to_cart_distributor_unavailable.html.haml b/app/views/spree/products/_add_to_cart_distributor_unavailable.html.haml index fb84bd1e9f..283c628c72 100644 --- a/app/views/spree/products/_add_to_cart_distributor_unavailable.html.haml +++ b/app/views/spree/products/_add_to_cart_distributor_unavailable.html.haml @@ -1,4 +1,2 @@ .error-distributor - Please complete your order at - = link_to current_distributor.name, root_path - before shopping with another distributor. + = t :products_distributor_error, link: link_to(current_distributor.name, root_path) diff --git a/app/views/spree/products/_add_to_cart_order_cycle_choice.html.haml b/app/views/spree/products/_add_to_cart_order_cycle_choice.html.haml index 14c5ac5faa..18091d2dfe 100644 --- a/app/views/spree/products/_add_to_cart_order_cycle_choice.html.haml +++ b/app/views/spree/products/_add_to_cart_order_cycle_choice.html.haml @@ -1,2 +1,3 @@ -%div Order cycle for your order: -= select_tag "order_cycle_id", options_from_collection_for_select([OrderCycle.new(name: 'None')]+order_cycle_collection, "id", "name", current_order_cycle.andand.id) +%div + = t :products_oc += select_tag "order_cycle_id", options_from_collection_for_select([OrderCycle.new(name: t(:none))]+order_cycle_collection, "id", "name", current_order_cycle.andand.id) diff --git a/app/views/spree/products/_add_to_cart_order_cycle_fixed.html.haml b/app/views/spree/products/_add_to_cart_order_cycle_fixed.html.haml index 377b2c8e88..492ee0d156 100644 --- a/app/views/spree/products/_add_to_cart_order_cycle_fixed.html.haml +++ b/app/views/spree/products/_add_to_cart_order_cycle_fixed.html.haml @@ -1,6 +1,7 @@ = hidden_field_tag "order_cycle_id", order_cycle.id -- if changing_order_cycle - .order-cycle-fixed= "Your order cycle for this order will be changed to #{order_cycle.name} if you add this product to your cart." -- else - .order-cycle-fixed= "Your order cycle for this order is #{order_cycle.name}" +.order-cycle-fixed + - if changing_order_cycle + = t :products_oc_change, name: order_cycle.name + - else + = t :products_oc_is, name: order_cycle.name diff --git a/app/views/spree/products/_add_to_cart_order_cycle_unavailable.html.haml b/app/views/spree/products/_add_to_cart_order_cycle_unavailable.html.haml index d061f0595c..a123da18fe 100644 --- a/app/views/spree/products/_add_to_cart_order_cycle_unavailable.html.haml +++ b/app/views/spree/products/_add_to_cart_order_cycle_unavailable.html.haml @@ -1,4 +1,2 @@ .error-distributor - Please complete your order from - = link_to (current_order_cycle.andand.name || 'your current order cycle'), root_path - before shopping in a different order cycle. + = t :products_oc_error, link: = link_to((current_order_cycle.andand.name || t(:products_oc_current)), root_path) diff --git a/app/views/spree/products/_add_to_cart_quantity_fields.html.haml b/app/views/spree/products/_add_to_cart_quantity_fields.html.haml index 826464b162..7235690c52 100644 --- a/app/views/spree/products/_add_to_cart_quantity_fields.html.haml +++ b/app/views/spree/products/_add_to_cart_quantity_fields.html.haml @@ -1,7 +1,9 @@ %div(class = "columns alpha two") - %div Quantity + %div + = t :products_quantity = number_field_tag (product.has_variants? ? :quantity : "variants[#{product.master.id}]"), 1, :class => 'title', :in => 1..product.on_hand - if product.group_buy %div(class = "columns alpha three") - %div Max quantity + %div + = t :products_max_quantity = number_field_tag (product.has_variants? ? :max_quantity : "variant_attributes[#{product.master.id}][max_quantity]"), 1, :class => 'title max_quantity', :in => 1..product.on_hand diff --git a/app/views/spree/products/_distributor_details.html.haml b/app/views/spree/products/_distributor_details.html.haml index a8fe4af9a3..e91c0ea6e5 100644 --- a/app/views/spree/products/_distributor_details.html.haml +++ b/app/views/spree/products/_distributor_details.html.haml @@ -1,8 +1,9 @@ %fieldset#product-distributor-details.columns.five.omega - %legend Distributor + %legend + = t :products_distributor .distributor-details - order = current_order(false) - if order.andand.distributor.present? = render 'enterprises/distributor_details', :distributor => order.distributor - else - When you select a distributor for your order, their address and pickup times will be displayed here. + = t :products_distributor_info diff --git a/app/views/spree/shared/_line_item_name.html.haml b/app/views/spree/shared/_line_item_name.html.haml new file mode 100644 index 0000000000..12a32772a4 --- /dev/null +++ b/app/views/spree/shared/_line_item_name.html.haml @@ -0,0 +1,6 @@ +%h5.inline-header + = "#{raw(line_item.product.name)}" + - unless line_item.product.name.include? line_item.name_to_display + %span= "- #{raw(line_item.name_to_display)}" +- if line_item.options_text + = "(#{raw(line_item.options_text)})" diff --git a/app/views/spree/shared/_order_details.html.haml b/app/views/spree/shared/_order_details.html.haml index 4fe07f07aa..d0a7e99ddf 100644 --- a/app/views/spree/shared/_order_details.html.haml +++ b/app/views/spree/shared/_order_details.html.haml @@ -3,23 +3,23 @@ .order-summary.text-small .right - if order.paid? - PAID + = t :order_paid - else - NOT PAID + = t :order_not_paid %span - Total order + = t :order_total %strong = order.display_total.to_html .pad .text-big - Paying via: + = t :order_payment %strong= order.payments.first.andand.payment_method.andand.name.andand.html_safe %p.text-small.text-skinny.pre-line %em= order.payments.first.andand.payment_method.andand.description.andand.html_safe .order-summary.text-small %strong - Billing address + = t :order_billing_address .pad %p.text-small = order.bill_address.firstname + " " + order.bill_address.lastname @@ -35,13 +35,13 @@ %strong= order.shipping_method.name .pad .text-big - Delivery on + = t :order_delivery_time %strong #{order.order_cycle.pickup_time_for(order.distributor)} %p.text-small.text-skinny.pre-line %em= order.shipping_method.description.andand.html_safe || "" .order-summary.text-small %strong - Delivery address + = t :order_delivery_address .pad %p.text-small = order.ship_address.firstname + " " + order.ship_address.lastname @@ -52,7 +52,8 @@ - if order.special_instructions.present? %br %p.light.small - %strong Your notes: + %strong + = t :order_special_instructions %br = order.special_instructions - else @@ -61,29 +62,24 @@ %strong= order.shipping_method.name .pad .text-big - Ready for collection + = t :order_pickup_time %strong #{order.order_cycle.pickup_time_for(order.distributor)} %p.text-small.text-skinny.pre-line %em= order.shipping_method.description.andand.html_safe || "" - .order-summary.text-small - %strong - Collection Address - .pad - %p.text-small - = order.ship_address.full_address - if order.order_cycle.pickup_instructions_for(order.distributor).present? %br %p.text-small %strong - Collection Instructions + = t :order_pickup_instructions %br #{order.order_cycle.pickup_instructions_for(order.distributor)} - if order.special_instructions.present? %br %p.light.small - %strong Your notes: + %strong + = t :order_special_instructions %br = order.special_instructions @@ -114,15 +110,7 @@ = link_to image_tag(item.variant.images.first.attachment.url(:mini)), item.variant.product - - if item.variant.product.name == item.variant.name_to_display - %h5 - %span= item.variant.product.name - %span.text-small.text-skinny= "(" + variant_options(item.variant) + ")" unless item.variant.option_values.empty? - - else - %h5 - %span= item.variant.product.name - %span= "- " + item.variant.name_to_display - %span.text-small.text-skinny= "(" + variant_options(item.variant) + ")" unless item.variant.option_values.empty? + = render 'spree/shared/line_item_name', line_item: item %td.text-right.price{"data-hook" => "order_item_price"} %span= item.single_display_amount_with_adjustments.to_html @@ -135,7 +123,7 @@ %tr#subtotal-row.total %td.text-right{colspan: "3"} %strong - Produce + = t :order_produce %td.text-right.total %span= display_checkout_subtotal(order) @@ -152,7 +140,7 @@ %tr.total %td.text-right{colspan: "3"} %h5 - Total + = t :order_total_price %td.text-right.total %h5#order_total= order.display_total.to_html @@ -160,6 +148,6 @@ #tax{"data-hook" => "order_details_tax"} %tr#tax-row.total %td.text-right{colspan: "3"} - (includes tax) + = t :order_includes_tax %td.text-right.total %span= display_checkout_tax_total(order) diff --git a/app/views/spree/shared/_order_details_steps_data.html.erb b/app/views/spree/shared/_order_details_steps_data.html.erb index 68549ebbc6..eabe239c78 100644 --- a/app/views/spree/shared/_order_details_steps_data.html.erb +++ b/app/views/spree/shared/_order_details_steps_data.html.erb @@ -4,8 +4,8 @@
<%= "Customer Details" %> <%= link_to "(#{t(:edit)})", checkout_state_path(:address) unless @order.completed? %>
- Name: <%= order.bill_address.full_name %>
- Address: <%= order.bill_address.address1 + ", " + order.bill_address.city %> + <%= "#{t(:name)}: #{order.bill_address.full_name}" %>
+ <%= "#{t(:address)}: #{order.bill_address.address1}, #{order.bill_address.city}" %>

@@ -14,7 +14,7 @@
<%= t(:payment_information) %> <%= link_to "(#{t(:edit)})", checkout_state_path(:payment) unless @order.completed? %>
<% if payment.payment_method.name.include? "PayPal" %> -
Your payment via PayPal has been processed successfully.
+
<% t(:order_payment_paypal_successful) %>
<% elsif payment.payment_method.name.include? "EFT" %> <%= payment.payment_method.description.html_safe %> <% elsif order.credit_cards.empty? == false %> @@ -37,7 +37,7 @@
-
Hub Info
+
<% t(:order_hub_info) %>
<%= render 'enterprises/distributor_details', :distributor => order.distributor %>
diff --git a/app/views/spree/shared/_products_by_distribution.html.haml b/app/views/spree/shared/_products_by_distribution.html.haml index 2bc3088adb..39a50f80ac 100644 --- a/app/views/spree/shared/_products_by_distribution.html.haml +++ b/app/views/spree/shared/_products_by_distribution.html.haml @@ -6,10 +6,11 @@ - else #products-local %h2 - Products - = "in #{current_order_cycle.name}" if current_order_cycle - = "at #{current_distributor.name}" if current_distributor + = t :products + = t :products_in, oc: current_order_cycle.name if current_order_cycle + = t :products_at, distributor: current_distributor.name if current_distributor = render 'spree/shared/products', :products => @products_local, :taxon => @taxon #products-remote - %h2 Products found elsewhere + %h2 + = t :products_elsewhere = render 'spree/shared/products', :products => @products_remote, :taxon => @taxon diff --git a/app/views/spree/user_mailer/signup_confirmation.html.haml b/app/views/spree/user_mailer/signup_confirmation.html.haml index fb52f57119..e45b99a000 100644 --- a/app/views/spree/user_mailer/signup_confirmation.html.haml +++ b/app/views/spree/user_mailer/signup_confirmation.html.haml @@ -1,32 +1,27 @@ %h3 - Hello! + = t :email_signup_greeting %p.lead - = "Welcome to #{ Spree::Config[:site_name] }!" + = t :email_signup_welcome, sitename: Spree::Config[:site_name] / Heading Panel %p   %p.callout %strong - Your login + = t :email_signup_login %p - Your login email is + = t :email_signup_email %strong = @user.email %p - You can start shopping online now at - %a{:href => "#{ spree.root_url }", :target => "_blank"} - -# Remove http:// and trailing slashes from root url if they exist - = spree.root_url.sub(/http:\/\//,"").sub(/\/$/,"") + -# Remove http:// and trailing slashes from root url if they exist + = t :email_signup_shop_html, link: link_to(spree.root_url.sub(/http:\/\//,"").sub(/\/$/,""), spree.root_url, target: '_blank') %p   %hr/ %p   %p.lead - Thanks for joining the network. We look forward to introducing you to many fantastic farmers, wonderful food hubs and delicious food! + = t :email_signup_text %p - We welcome all your questions and feedback; you can use the - %em - Send Feedback - button on the site or email us at + = t :email_signup_help_html %a{:href => "mailto:hello@openfoodnetwork.org", :target => "_blank"} hello@openfoodnetwork.org diff --git a/config/application.rb b/config/application.rb index 2d9dcd4982..1fb6c88bbf 100644 --- a/config/application.rb +++ b/config/application.rb @@ -47,7 +47,10 @@ module Openfoodnetwork # -- all .rb files in that directory are automatically loaded. # Custom directories with classes and modules you want to be autoloadable. - config.autoload_paths += %W(#{config.root}/app/presenters) + config.autoload_paths += %W( + #{config.root}/app/presenters + #{config.root}/app/jobs + ) # Only load the plugins named here, in the order given (default is alphabetical). # :all can be used as a placeholder for all plugins not explicitly named. @@ -63,6 +66,7 @@ module Openfoodnetwork # The default locale is :en and all translations from config/locales/*.rb,yml are auto loaded. # config.i18n.load_path += Dir[Rails.root.join('my', 'locales', '*.{rb,yml}').to_s] config.i18n.default_locale = ENV["LOCALE"] + I18n.locale = config.i18n.locale = config.i18n.default_locale # Setting this to true causes a performance regression in Rails 3.2.17 # When we're on a version with the fix below, we can set it to true @@ -79,7 +83,7 @@ module Openfoodnetwork config.assets.enabled = true # Version of your assets, change this if you want to expire all your assets - config.assets.version = '1.0' + config.assets.version = '1.2' config.sass.load_paths += [ "#{Gem.loaded_specs['foundation-rails'].full_gem_path}/vendor/assets/stylesheets/foundation/components", @@ -92,11 +96,12 @@ module Openfoodnetwork config.assets.initialize_on_precompile = true config.assets.precompile += ['store/all.css', 'store/all.js', 'store/shop_front.js', 'iehack.js'] config.assets.precompile += ['admin/all.css', 'admin/restore_spree_from_cms.css', 'admin/*.js', 'admin/**/*.js'] - config.assets.precompile += ['darkswarm/all.css', 'darkswarm/all.js'] + config.assets.precompile += ['darkswarm/all.css', 'darkswarm/all_split2.css', 'darkswarm/all.js'] config.assets.precompile += ['mail/all.css'] config.assets.precompile += ['comfortable_mexican_sofa/*'] config.assets.precompile += ['search/all.css', 'search/*.js'] config.assets.precompile += ['shared/*'] + config.active_support.escape_html_entities_in_json = true end end diff --git a/config/application.yml.example b/config/application.yml.example index 45fface302..0b1871466b 100644 --- a/config/application.yml.example +++ b/config/application.yml.example @@ -13,3 +13,11 @@ LOCALE: en CHECKOUT_ZONE: Australia # Find currency codes at http://en.wikipedia.org/wiki/ISO_4217. CURRENCY: AUD + +# SingleSignOn login for Discourse +# +# DISCOURSE_SSO_SECRET should be a random string. It must be the same as provided to your Discourse instance. +#DISCOURSE_SSO_SECRET: "" +# +# DISCOURSE_URL must be the URL of your Discourse instance. +#DISCOURSE_URL: "https://noticeboard.openfoodnetwork.org.au" diff --git a/config/database.yml b/config/database.yml index d74ed6256a..7eef2396f9 100644 --- a/config/database.yml +++ b/config/database.yml @@ -10,7 +10,7 @@ development: test: adapter: postgresql encoding: unicode - database: open_food_network_test + database: open_food_network_test<%= ENV['TEST_ENV_NUMBER'] %> pool: 5 host: localhost username: ofn diff --git a/config/environments/development.rb b/config/environments/development.rb index efa229e33c..200484122a 100644 --- a/config/environments/development.rb +++ b/config/environments/development.rb @@ -31,7 +31,7 @@ Openfoodnetwork::Application.configure do # Show emails using Letter Opener config.action_mailer.delivery_method = :letter_opener - config.action_mailer.default_url_options = { host: "test.com" } + config.action_mailer.default_url_options = { host: "0.0.0.0:3000" } end diff --git a/config/environments/test.rb b/config/environments/test.rb index dd3413c8f0..228bd1736d 100644 --- a/config/environments/test.rb +++ b/config/environments/test.rb @@ -11,6 +11,10 @@ Openfoodnetwork::Application.configure do config.serve_static_assets = true config.static_cache_control = "public, max-age=3600" + # Separate cache stores when running in parallel + config.cache_store = :file_store, Rails.root.join("tmp", "cache", "paralleltests#{ENV['TEST_ENV_NUMBER']}") + + # Log error messages when you accidentally call methods on nil config.whiny_nils = true @@ -29,6 +33,9 @@ Openfoodnetwork::Application.configure do # ActionMailer::Base.deliveries array. config.action_mailer.delivery_method = :test + # Tests assume English text on the site. + config.i18n.default_locale = "en" + # Use SQL instead of Active Record's schema dumper when creating the test database. # This is necessary if your schema can't be completely dumped by the schema dumper, # like if you have constraints or database-specific column types @@ -37,6 +44,11 @@ Openfoodnetwork::Application.configure do # Print deprecation notices to the stderr config.active_support.deprecation = :stderr config.action_mailer.default_url_options = { :host => "test.host" } + + # To block requests before running the database cleaner + require 'open_food_network/rack_request_blocker' + # Make sure the middleware is inserted first in middleware chain + config.middleware.insert_before('ActionDispatch::Static', 'RackRequestBlocker') end # Allows us to use _url helpers in Rspec diff --git a/config/initializers/0_depricated_db2fog.rb b/config/initializers/0_depricated_db2fog.rb new file mode 100644 index 0000000000..f2a2e76bc9 --- /dev/null +++ b/config/initializers/0_depricated_db2fog.rb @@ -0,0 +1,15 @@ +# Depricated: this initializer contains an invalid bucket name. +# Users of DB2fog should be able to configure DB2fog without changing the code. +# +# Name your configuration file `db2fog.rb`. It will be ignored by git. +# And it will overwrite this depricated configuration. +# +# See: https://github.com/yob/db2fog +# +# TODO: Remove this file in a future release. +DB2Fog.config = { + :aws_access_key_id => Spree::Config[:s3_access_key], + :aws_secret_access_key => Spree::Config[:s3_secret], + :directory => "db-backup_#{Spree::Config[:s3_bucket]}", + :provider => 'AWS' +} diff --git a/config/initializers/content_config.rb b/config/initializers/content_config.rb new file mode 100644 index 0000000000..abbd2c70e9 --- /dev/null +++ b/config/initializers/content_config.rb @@ -0,0 +1 @@ +ContentConfig = ContentConfiguration.new diff --git a/config/initializers/db2fog.rb b/config/initializers/db2fog.rb deleted file mode 100644 index 4c02b5dbcc..0000000000 --- a/config/initializers/db2fog.rb +++ /dev/null @@ -1,6 +0,0 @@ -DB2Fog.config = { - :aws_access_key_id => Spree::Config[:s3_access_key], - :aws_secret_access_key => Spree::Config[:s3_secret], - :directory => "db-backup_#{Spree::Config[:s3_bucket]}", - :provider => 'AWS' -} diff --git a/config/initializers/delayed_job.rb b/config/initializers/delayed_job.rb index 8fc9aa8ec7..80dc11d3aa 100644 --- a/config/initializers/delayed_job.rb +++ b/config/initializers/delayed_job.rb @@ -2,6 +2,10 @@ Delayed::Worker.logger = Logger.new(Rails.root.join('log', 'delayed_job.log')) Delayed::Worker.destroy_failed_jobs = false Delayed::Worker.max_run_time = 15.minutes +# Uncomment the next line if you want jobs to be executed straight away. +# For example you want emails to be opened in your browser while testing. +#Delayed::Worker.delay_jobs = false + # Notify bugsnag when a job fails # Code adapted from http://trevorturk.com/2011/01/25/notify-hoptoad-if-theres-an-exception-in-delayedjob/ class Delayed::Worker diff --git a/config/initializers/session_store.rb b/config/initializers/session_store.rb index 721e495265..d301b88739 100644 --- a/config/initializers/session_store.rb +++ b/config/initializers/session_store.rb @@ -1,8 +1,10 @@ # Be sure to restart your server when you modify this file. -Openfoodnetwork::Application.config.session_store :cookie_store, key: '_openfoodnetwork_session' +# The cookie_store can be too small for very long URLs stored by Devise. +# The maximum size of cookies is 4096 bytes. +#Openfoodnetwork::Application.config.session_store :cookie_store, key: '_openfoodnetwork_session' # Use the database for sessions instead of the cookie-based default, # which shouldn't be used to store highly confidential information # (create the session table with "rails generate session_migration") -# Openfoodnetwork::Application.config.session_store :active_record_store +Openfoodnetwork::Application.config.session_store :active_record_store diff --git a/config/initializers/user_class_extensions.rb b/config/initializers/user_class_extensions.rb new file mode 100644 index 0000000000..cd9ff60a0c --- /dev/null +++ b/config/initializers/user_class_extensions.rb @@ -0,0 +1,11 @@ +Spree::Core::Engine.config.to_prepare do + if Spree.user_class + Spree.user_class.class_eval do + + # Override of spree method to ignore orders associated with account_invoices + def last_incomplete_spree_order + spree_orders.incomplete.where("id NOT IN (?)", account_invoices.map(&:order_id)).order('created_at DESC').first + end + end + end +end diff --git a/config/initializers/wicked_pdf.rb b/config/initializers/wicked_pdf.rb new file mode 100644 index 0000000000..d460773073 --- /dev/null +++ b/config/initializers/wicked_pdf.rb @@ -0,0 +1,5 @@ +WickedPdf.config = { + #:wkhtmltopdf => '/usr/local/bin/wkhtmltopdf', + #:layout => "pdf.html", + :exe_path => `bundle exec which wkhtmltopdf`.chomp +} diff --git a/config/locales/en-GB.yml b/config/locales/en-GB.yml index 50fbe8a6db..7301ef4f2c 100644 --- a/config/locales/en-GB.yml +++ b/config/locales/en-GB.yml @@ -16,9 +16,622 @@ en-GB: Invalid email or password. Were you a guest last time? Perhaps you need to create an account or reset your password. home: "OFN" - + title: Open Food Network welcome_to: 'Welcome to ' search_by_name: Search by name... producers: UK Producers producers_join: UK producers are now welcome to join Open Food Network UK. charges_sales_tax: Charges sales tax? + print: "Print" + + logo: "Logo (640x130)" + logo_mobile: "Mobile logo (75x26)" + logo_mobile_svg: "Mobile logo (SVG)" + home_hero: "Hero image" + home_show_stats: "Show statistics" + footer_logo: "Logo (220x76)" + footer_facebook_url: "Facebook URL" + footer_twitter_url: "Twitter URL" + footer_instagram_url: "Instagram URL" + footer_linkedin_url: "LinkedIn URL" + footer_googleplus_url: "Google Plus URL" + footer_pinterest_url: "Pinterest URL" + footer_email: "Email" + footer_links_md: "Links" + footer_about_url: "About URL" + footer_tos_url: "Terms of Service URL" + invoice: "Invoice" + + name: Name + first_name: First name + last_name: Last name + email: Email + phone: Phone + next: Next + address: Address + address2: Address (contd.) + city: City + state: County + postcode: Postcode + country: Country + unauthorized: Unauthorized + terms_of_service: "Terms of service" + on_demand: On demand + none: None + + alert_selling_on_ofn: "Interested in selling food on the Open Food Network?" + alert_start_here: "Start here" + label_shops: "Shops" + label_map: "Map" + label_producers: "Producers" + label_groups: "Groups" + label_about: "About" + label_shopping: "Shopping" + label_login: "Login" + label_logout: "Logout" + label_signup: "Sign up" + label_administration: "Administration" + label_admin: "Admin" + label_account: "Account" + label_more: "More" + label_less: "Show less" + + items: "items" + cart_headline: "Your shopping cart" + total: "Total" + checkout: "Checkout now" + cart_updating: "Updating cart..." + cart_empty: "Cart empty" + cart_edit: "Edit your cart" + + card_number: Card number + card_securitycode: "Security code" + card_expiry_date: Expiry date + + ofn_cart_headline: "Current cart for:" + ofn_cart_distributor: "Distributor:" + ofn_cart_oc: "Order cycle:" + ofn_cart_from: "From:" + ofn_cart_to: "To:" + ofn_cart_product: "Product:" + ofn_cart_quantitiy: "Quantity:" + ofn_cart_send: "Buy me" + + ie_warning_headline: "Your browser is out of date :-(" + ie_warning_text: "For the best Open Food Network experience, we strongly recommend upgrading your browser:" + ie_warning_chrome: Download Chrome + ie_warning_firefox: Download Firefox + ie_warning_ie: Upgrade Internet Explorer + ie_warning_other: "Can't upgrade your browser? Try Open Food Network on your smartphone :-)" + + footer_global_headline: "OFN Global" + footer_global_home: "Home" + footer_global_news: "News" + footer_global_about: "About" + footer_global_contact: "Contact" + + footer_sites_headline: "OFN Sites" + footer_sites_developer: "Developer" + footer_sites_community: "Community" + footer_sites_userguide: "User Guide" + + footer_secure: "Secure and trusted." + footer_secure_text: "Open Food Network uses SSL encryption (2048 bit RSA) everywhere to keep your shopping and payment information private. Our servers do not store your credit card details and payments are processed by PCI-compliant services." + + footer_contact_headline: "Keep in touch" + footer_contact_email: "Email us" + + footer_nav_headline: "Navigate" + footer_join_headline: "Join us" + footer_join_producers: "Producers sign-up" + footer_join_hubs: "Hubs sign-up" + footer_join_groups: "Groups sign-up" + footer_join_partners: "Food systems partners" + + footer_legal_call: "Read our" + footer_legal_tos: "Terms and conditions" + footer_legal_visit: "Find us on" + footer_legal_text_html: "Open Food Network is a free and open source software platform. Our content is licensed with %{content_license} and our code with %{code_license}." + + home_shop: Shop Now + + brandstory_headline: "Food, unincorporated." + brandstory_intro: "Sometimes the best way to fix the system is to start a new one…" + brandstory_part1: "We begin from the ground up. With farmers and growers ready to tell their stories proudly and truly. With distributors ready to connect people with products fairly and honestly. With buyers who believe that better weekly shopping decisions can seriously change the world." + brandstory_part2: "Then we need a way to make it real. A way to empower everyone who grows, sells and buys food. A way to tell all the stories, to handle all the logistics. A way to turn transaction into transformation every day." + brandstory_part3: "So we build an online marketplace that levels the playing field. It’s transparent, so it creates real relationships. It’s open source, so it’s owned by everyone. It scales to regions and nations, so people start versions across the world." + brandstory_part4: "It works everywhere. It changes everything." + brandstory_part5_strong: "We call it Open Food Network." + brandstory_part6: "We all love food. Now we can love our food system too." + + system_headline: "Here's how it works." + system_step1: "1. Search" + system_step1_text: "Search our diverse, independent shops for seasonal local food. Search by neighbourhood and food category, or whether you prefer delivery or pickup." + system_step2: "2. Shop" + system_step2_text: "Transform your transactions with affordable local food from diverse producers and hubs. Know the stories behind your food and the people who make it!" + system_step3: "3. Pick-up / Delivery" + system_step3_text: "Hang on for your delivery, or visit your producer or hub for a more personal connection with your food. Food shopping as diverse as nature intended it." + + cta_headline: "Shopping that makes the world a better place." + cta_label: "I'm Ready" + + stats_headline: "We're creating a new food system." + stats_producers: "food producers" + stats_shops: "food shops" + stats_shoppers: "food shoppers" + stats_orders: "food orders" + + checkout_title: Checkout + checkout_now: Checkout now + checkout_order_ready: Order ready for + checkout_hide: Hide + checkout_expand: Expand + checkout_headline: "Ok, ready to checkout?" + checkout_as_guest: "Checkout as guest" + checkout_details: "Your details" + checkout_billing: "Billing info" + checkout_shipping: Shipping info + checkout_method_free: Free + checkout_address_same: Shipping address same as billing address? + checkout_ready_for: "Ready for:" + checkout_instructions: "Any comments or special instructions?" + checkout_payment: Payment + checkout_send: Place order now + checkout_your_order: Your order + checkout_cart_total: Cart total + checkout_shipping_price: Shipping + checkout_total_price: Total + checkout_back_to_cart: "Back to cart" + + order_paid: PAID + order_not_paid: NOT PAID + order_total: Total order + order_payment: "Paying via:" + order_billing_address: Billing address + order_delivery_on: Delivery on + order_delivery_address: Delivery address + order_special_instructions: "Your notes:" + order_pickup_instructions: Collection instructions + order_produce: Produce + order_total_price: Total + order_includes_tax: (includes tax) + order_payment_paypal_successful: Your payment via PayPal has been processed successfully. + order_hub_info: Hub info + + products: "Products" + products_in: "in %{oc}" + products_at: "at %{distributor}" + products_elsewhere: "Products found elsewhere" + + email_welcome: "Welcome" + email_confirmed: "Thank you for confirming your email address." + email_registered: "is now part of" + email_userguide_html: "The User Guide with detailed support for setting up your Producer or Hub is here: +%{link}" + email_admin_html: "You can manage your account by logging into the %{link} or by clicking on the cog in the top right hand side of the homepage, and selecting Administration." + email_community_html: "We also have an online forum for community discussion related to OFN software and the unique challenges of running a food enterprise. You are encouraged to join in. We are constantly evolving and your input into this forum will shape what happens next. +%{link}" + email_help: "If you have any difficulties, check out our FAQs, browse the forum or post a 'Support' topic and someone will help you out!" + email_confirmation_greeting: "Hi, %{contact}!" + email_confirmation_profile_created: "A profile for %{name} has been successfully created! +To activate your Profile we need to confirm this email address." + email_confirmation_click_link: "Please click the link below to confirm your email and to continue setting up your profile." + email_confirmation_link_label: "Confirm this email address »" + email_confirmation_help_html: "After confirming your email you can access your administration account for this enterprise. +See the %{link} to find out more about %{sitename}'s features and to start using your profile or online store." + email_confirmation_userguide: "User Guide" + email_social: "Connect with Us:" + email_contact: "Email us:" + email_signoff: "Cheers," + email_signature: "%{sitename} Team" + + email_confirm_customer_greeting: "Hi %{name}," + email_confirm_customer_intro_html: "Thanks for shopping at %{distributor}!" + email_confirm_customer_number_html: "Order confirmation #%{number}" + email_confirm_customer_details_html: "Here are your order details from %{distributor}:" + email_confirm_customer_signoff: "Kind regards," + email_confirm_shop_greeting: "Hi %{name}," + email_confirm_shop_order_html: "Well done! You have a new order for %{distributor}!" + email_confirm_shop_number_html: "Order confirmation #%{number}" + email_order_summary_item: "Item" + email_order_summary_quantity: "Qty" + email_order_summary_price: "Price" + email_order_summary_subtotal: "Subtotal:" + email_order_summary_total: "Total:" + email_payment_paid: PAID + email_payment_not_paid: NOT PAID + email_payment_summary: Payment summary + email_payment_method: "Paying via:" + email_shipping_delivery_details: Delivery details + email_shipping_delivery_time: "Delivery on:" + email_shipping_delivery_address: "Delivery address:" + email_shipping_collection_details: Collection details + email_shipping_collection_time: "Ready for collection:" + email_shipping_collection_instructions: "Collection instructions:" + email_special_instructions: "Your notes:" + + email_signup_greeting: Hello! + email_signup_welcome: "Welcome to %{sitename}!" + email_signup_login: Your login + email_signup_email: Your login email is + email_signup_shop_html: "You can start shopping online now at %{link}." + email_signup_text: "Thanks for joining the network. + If you are a customer, we look forward to introducing you to many fantastic farmers, wonderful food hubs and delicious food! + If you are a producer or food enterprise, we are excited to have you as a part of the network." + email_signup_help_html: "We welcome all your questions and feedback; you can use the Send Feedback button on the site or email us at" + + shopping_oc_closed: Orders are closed + shopping_oc_closed_description: "Please wait until the next cycle opens (or contact us directly to see if we can accept any late orders)" + shopping_oc_last_closed: "The last cycle closed %{distance_of_time} ago" + shopping_oc_next_open: "The next cycle opens in %{distance_of_time}" + shopping_tabs_about: "About %{distributor}" + shopping_tabs_contact: "Contact" + shopping_contact_address: "Address" + shopping_contact_web: "Contact" + shopping_contact_social: "Follow" + shopping_groups_part_of: "is part of:" + shopping_producers_of_hub: "%{hub}'s producers:" + + enterprises_next_closing: "Next order closing" + enterprises_ready_for: "Ready for" + enterprises_choose: "Choose when you want your order:" + + hubs_buy: "Shop for:" + hubs_shopping_here: "Shopping here" + hubs_orders_closed: "Orders closed" + hubs_profile_only: "Profile only" + hubs_delivery_options: "Delivery options" + hubs_pickup: "Pickup" + hubs_delivery: "Delivery" + hubs_producers: "Our producers" + hubs_filter_by: "Filter by" + hubs_filter_type: "Type" + hubs_filter_delivery: "Delivery" + hubs_matches: "Did you mean?" + hubs_intro: Shop in your local area + hubs_distance: Closest to + hubs_distance_filter: "Show me shops near %{location}" + + products_clear_all: Clear all + products_showing: "Showing:" + products_with: with + products_search: "Search by product or producer" + products_loading: "Loading products..." + products_updating_cart: "Updating cart..." + products_cart_empty: "Cart empty" + products_edit_cart: "Edit your cart" + products_from: from + + search_no_results_html: "Sorry, no results found for %{query}. Try another search?" + + components_profiles_popover: "Profiles do not have a shopfront on the Open Food Network, but may have their own physical or online shop elsewhere" + components_profiles_show: "Show profiles" + components_filters_nofilters: "No filters" + components_filters_clearfilters: "Clear all filters" + + groups_title: Groups + groups_headline: Groups / regions + groups_search: "Search name or keyword" + groups_no_groups: "No groups found" + groups_about: "About Us" + groups_producers: "Our producers" + groups_hubs: "Our hubs" + groups_contact_web: Contact + groups_contact_social: Follow + groups_contact_address: Address + groups_contact_email: Email us + groups_contact_website: Visit our website + groups_contact_facebook: Follow us on Facebook + groups_signup_title: Sign up as a group + groups_signup_headline: Groups sign up + groups_signup_intro: "We're an amazing platform for collaborative marketing, the easiest way for your members and stakeholders to reach new markets. We're non-profit, affordable, and simple." + groups_signup_email: Email us + groups_signup_motivation1: We transform food systems fairly. + groups_signup_motivation2: It's why we get out of bed every day. We're a global non-profit, based on open source code. We play fair. You can always trust us. + groups_signup_motivation3: We know you have big ideas, and we want to help. We'll share our knowledge, networks and resources. We know that isolation doesn't create change, so we'll partner with you. + groups_signup_motivation4: We meet you where you are. + groups_signup_motivation5: You might be an alliance of food hubs, producers, or distributors, and an industry body, or a local government. + groups_signup_motivation6: Whatever your role in your local food movement, we're ready to help. However you come to wonder what Open Food Network would look like or is doing in your part of the world, let's start the conversation. + groups_signup_motivation7: We make food movements make more sense. + groups_signup_motivation8: You need to activate and enable your networks, we offer a platform for conversation and action. You need real engagement. We’ll help reach all the players, all the stakeholders, all the sectors. + groups_signup_motivation9: You need resourcing. We’ll bring all our experience to bear. You need cooperation. We’ll better connect you to a global network of peers. + groups_signup_pricing: Group Account + groups_signup_studies: Case Studies + groups_signup_contact: Ready to discuss? + groups_signup_contact_text: "Get in touch to discover what OFN can do for you:" + groups_signup_detail: "Here's the detail." + + login_invalid: "Invalid email or password" + + modal_hubs: "Food Hubs" + modal_hubs_abstract: Our food hubs are the point of contact between you and the people who make your food! + modal_hubs_content1: You can search for a convenient hub by location or name. Some hubs have multiple points where you can pick-up your purchases, and some will also provide delivery options. Each food hub is a sales point with independent business operations and logistics - so variations between hubs are to be expected. + modal_hubs_content2: You can only shop at one food hub at a time. + + modal_groups: "Groups / Regions" + modal_groups_content1: These are the organisations and relationships between hubs which make up the Open Food Network. + modal_groups_content2: Some groups are clustered by location or council, others by non-geographic similarities. + + modal_how: "How it works" + modal_how_shop: Shop the Open Food Network + modal_how_shop_explained: Search for a food hub near you to start shopping! You can expand each food hub to see what kinds of goodies are available, and click through to start shopping. (You can only shop one food hub at a time.) + modal_how_pickup: Pick-ups, delivery and shipping costs + modal_how_pickup_explained: Some food hubs deliver to your door, while others require you to pick-up your purchases. You can see which options are available on the homepage, and select which you'd like at the shopping and check-out pages. Delivery will cost more, and pricing differs from hub-to-hub. Each food hub is a sales point with independent business operations and logisitics - so variations between hubs are to be expected. + modal_how_more: Learn more + modal_how_more_explained: "If you want to learn more about the Open Food Network, how it works, and get involved, check out:" + + modal_producers: "Producers" + modal_producers_explained: "Our producers make all the delicious food you can shop for on the Open Food Network." + + ocs_choice_hub: "Hub:" + ocs_choice_oc: "Order Cycle:" + ocs_choice_text: "You have not yet picked where you will get your order from." + ocs_closed_headline: Orders are currently closed for this hub + ocs_closed_time: "The last cycle closed %{time} ago." + ocs_closed_contact: "Please contact your hub directly to see if they accept late orders, or wait until the next cycle opens." + ocs_closed_opens: "The next order cycle opens in %{time}" + ocs_closed_email: "Email: %{email}" + ocs_closed_phone: "Phone: %{phone}" + ocs_pickup_time: "Your order will be ready on %{pickup_time}" + ocs_change_date: "Change Collection Date" + ocs_change_date_notice: "(This will reset your cart)" + ocs_close_time: "ORDERS CLOSE" + ocs_when_headline: When do you want your order? + ocs_when_text: No products are displayed until you select a date. + ocs_when_closing: "Closing on" + ocs_when_choose: "Choose Order Cycle" + ocs_list: "List view" + + producers_about: About us + producers_buy: Shop for + producers_contact: Contact + producers_contact_phone: Call + producers_contact_social: Follow + producers_buy_at_html: "Shop for %{enterprise} products at:" + producers_filter: Filter by + producers_filter_type: Type + producers_title: Producers + producers_headline: Find local producers + producers_signup_title: Sign up as a producer + producers_signup_headline: Food producers, empowered. + producers_signup_motivation: Sell your food and tell your stories to diverse new markets. Save time and money on every overhead. We support innovation without the risk. We've levelled the playing field. + producers_signup_send: Join now + producers_signup_enterprise: Enterprise Accounts + producers_signup_studies: Stories from our producers. + producers_signup_cta_headline: Join now! + producers_signup_cta_action: Join now + producers_signup_detail: Here's the detail. + + products_item: Item + products_description: Description + products_variant: Variant + products_quantity: Quantity + products_availabel: Available? + products_price: Price + + register_title: Register + + shops_title: Shops + shops_headline: Shopping, transformed. + shops_text: Food grows in cycles, farmers harvest in cycles, and we order food in cycles. If you find an order cycle closed, check back soon. + shops_signup_title: Sign up as a hub + shops_signup_headline: Food hubs, unlimited. + shops_signup_motivation: Whatever your model, we support you. However you change, we're with you. We're non-profit, independent, and open-sourced. We're the software partners you've dreamed of. + shops_signup_action: Join now + shops_signup_pricing: Enterprise Accounts + shops_signup_stories: Stories from our hubs. + shops_signup_help: We're ready to help. + shops_signup_help_text: You need a better return. You need new buyers and logistics partners. You need your story told across wholesale, retail, and the kitchen table. + shops_signup_detail: Here's the detail. + + orders_fees: Fees... + orders_edit_title: Shopping cart + orders_edit_headline: Your shopping cart + orders_edit_time: Order ready for + orders_edit_continue: Continue shopping + orders_edit_checkout: Checkout + orders_form_empty_cart: "Empty cart" + orders_form_subtotal: Produce subtotal + orders_form_admin: Admin and handling + orders_form_total: Total + orders_oc_expired_headline: Orders have closed for this order cycle + orders_oc_expired_text: "Sorry, orders for this order cycle closed %{time} ago! Please contact your hub directly to see if they can accept late orders." + orders_oc_expired_text_others_html: "Sorry, orders for this order cycle closed %{time} ago! Please contact your hub directly to see if they can accept late orders %{link}." + orders_oc_expired_text_link: "or see the other order cycles available at this hub" + orders_oc_expired_email: "Email:" + orders_oc_expired_phone: "Phone:" + orders_show_title: Order Confirmation + orders_show_time: Order ready on + orders_show_number: Order confirmation + + products_cart_distributor_choice: "Distributor for your order:" + products_cart_distributor_change: "Your distributor for this order will be changed to %{name} if you add this product to your cart." + products_cart_distributor_is: "Your distributor for this order is %{name}." + products_distributor_error: "Please complete your order at %{link} before shopping with another distributor." + products_oc: "Order cycle for your order:" + products_oc_change: "Your order cycle for this order will be changed to %{name} if you add this product to your cart." + products_oc_is: "Your order cycle for this order is %{name}." + products_oc_error: "Please complete your order from %{link} before shopping in a different order cycle." + products_oc_current: "your current order cycle" + products_quantity: Quantity + products_max_quantity: Max quantity + products_distributor: Distributor + products_distributor_info: When you select a distributor for your order, their address and pickup times will be displayed here. + + # keys used in javascript + password: Password + remember_me: Remember Me + are_you_sure: "Are you sure?" + orders_open: Orders open + closing: "Closing " + going_back_to_home_page: "Taking you back to the home page" + creating: Creating + updating: Updating + failed_to_create_enterprise: "Failed to create your enterprise." + failed_to_create_enterprise_unknown: "Failed to create your enterprise.\nPlease ensure all fields are completely filled out." + failed_to_update_enterprise_unknown: "Failed to update your enterprise.\nPlease ensure all fields are completely filled out." + order_not_saved_yet: "Your order hasn't been saved yet. Give us a few seconds to finish!" + filter_by: "Filter by" + hide_filters: "Hide filters" + one_filter_applied: "1 filter applied" + x_filters_applied: " filters applied" + submitting_order: "Submitting your order: please wait" + confirm_hub_change: "Are you sure? This will change your selected hub and remove any items in your shopping cart." + confirm_oc_change: "Are you sure? This will change your selected order cycle and remove any items in your shopping cart." + location_placeholder: "Type in a location..." + error_required: "can't be blank" + error_number: "must be number" + error_email: "must be email address" + item_handling_fees: "Item Handling Fees (included in item totals)" + january: "January" + february: "February" + march: "March" + april: "April" + may: "May" + june: "June" + july: "July" + august: "August" + september: "September" + october: "October" + november: "November" + december: "December" + email_not_found: "Email address not found" + email_required: "You must provide an email address" + logging_in: "Hold on a moment, we're logging you in" + signup_email: "Your email" + choose_password: "Choose a password" + confirm_password: "Confirm password" + action_signup: "Sign up now" + welcome_to_ofn: "Welcome to the Open Food Network!" + signup_or_login: "Start By signing up (or logging in)" + have_an_account: "Already have an account?" + action_login: "Log in now." + forgot_password: "Forgot password?" + password_reset_sent: "An email with instructions on resetting your password has been sent!" + reset_password: "Reset password" + registration_greeting: "Greetings!" + who_is_managing_enterprise: "Who is responsible for managing %{enterprise}?" + enterprise_contact: "Primary contact" + enterprise_contact_required: "You need to enter a primary contact." + enterprise_email: "Email address" + enterprise_email_required: "You need to enter valid email address." + enterprise_phone: "Phone number" + back: "Back" + continue: "Continue" + limit_reached_headline: "Oh no!" + limit_reached_message: "You have reached the limit!" + limit_reached_text: "You have reached the limit for the number of enterprises you are allowed to own on the" + limit_reached_action: "Return to the homepage" + select_promo_image: "Step 3. Select promo image" + promo_image_tip: "Tip: Shown as a banner, preferred size is 1200×260px" + promo_image_label: "Choose a promo image" + action_or: "OR" + promo_image_drag: "Drag and drop your promo here" + review_promo_image: "Step 4. Review your promo banner" + review_promo_image_tip: "Tip: for best results, your promo image should fill the available space" + promo_image_placeholder: "Your logo will appear here for review once uploaded" + uploading: "Uploading..." + select_logo: "Step 1. Select logo image" + logo_tip: "Tip: Square images will work best, preferably at least 300×300px" + logo_label: "Choose a logo image" + logo_drag: "Drag and drop your logo here" + review_logo: "Step 2. Review your logo" + review_logo_tip: "Tip: for best results, your logo should fill the available space" + logo_placeholder: "Your logo will appear here for review once uploaded" + enterprise_about_headline: "Nice one!" + enterprise_about_message: "Now let's flesh out the details about" + enterprise_success: "Success! %{enterprise} added to the Open Food Network " + enterprise_registration_exit_message: "If you exit this wizard at any stage, you need to click the confirmation link in the email you have received. This will take you to your admin interface where you can continue setting up your profile." + enterprise_description: "Short Description" + enterprise_description_placeholder: "A short sentence describing your enterprise" + enterprise_long_desc: "Long Description" + enterprise_long_desc_placeholder: "This is your opportunity to tell the story of your enterprise - what makes you different and wonderful? We'd suggest keeping your description to under 600 characters or 150 words." + enterprise_long_desc_length: "%{num} characters / up to 600 recommended" + enterprise_abn: "ABN" + enterprise_abn_placeholder: "eg. 99 123 456 789" + enterprise_acn: "ACN" + enterprise_acn_placeholder: "eg. 123 456 789" + enterprise_tax_required: "You need to make a selection." + enterprise_final_step: "Final step!" + enterprise_social_text: "How can people find %{enterprise} online?" + website: "Website" + website_placeholder: "eg. openfoodnetwork.org.au" + facebook: "Facebook" + facebook_placeholder: "eg. www.facebook.com/PageNameHere" + linkedin: "LinkedIn" + linkedin_placeholder: "eg. www.linkedin.com/YourNameHere" + twitter: "Twitter" + twitter_placeholder: "eg. @twitter_handle" + instagram: "Instagram" + instagram_placeholder: "eg. @instagram_handle" + registration_greeting: "Hi there!" + registration_intro: "You can now create a profile for your Producer or Hub" + registration_action: "Let's get started!" + registration_checklist: "You'll need" + registration_time: "5-10 minutes" + registration_enterprise_address: "Enterprise address" + registration_contact_details: "Primary contact details" + registration_logo: "Your logo image" + registration_promo_image: "Landscape image for your profile" + registration_about_us: "'About Us' text" + registration_outcome_headline: "What do I get?" + registration_outcome1_html: "Your profile helps people find and contact you on the Open Food Network." + registration_outcome2: "Use this space to tell the story of your enterprise, to help drive connections to your social and online presence. " + registration_outcome3: "It's also the first step towards trading on the Open Food Network, or opening an online store." + registration_finished_headline: "Finished!" + registration_finished_thanks: "Thanks for filling out the details for %{enterprise}." + registration_finished_login: "You can change or update your enterprise at any stage by logging into Open Food Network and going to Admin." + registration_finished_activate: "Activate %{enterprise}." + registration_finished_activate_instruction_html: "We've sent a confirmation email to %{email} if it hasn't been activated before.
+Please follow the instructions there to make your enterprise visible on the Open Food Network." + registration_finished_action: "Open Food Network home" + registration_type_headline: "Last step to add %{enterprise}!" + registration_type_question: "Are you a producer?" + registration_type_producer: "Yes, I'm a producer" + registration_type_no_producer: "No, I'm not a producer" + registration_type_error: "Please choose one. Are you are producer?" + registration_type_producer_help: "Producers make yummy things to eat and/or drink. You're a producer if you grow it, raise it, brew it, bake it, ferment it, milk it or mould it." + registration_type_no_producer_help: "If you’re not a producer, you’re probably someone who sells and distributes food. You might be a hub, coop, buying group, retailer, wholesaler or other." + create_profile: "Create profile" + registration_images_headline: "Thanks!" + registration_images_description: "Let's upload some pretty pictures so your profile looks great! :)" + registration_detail_headline: "Let's get started" + registration_detail_enterprise: "Woot! First we need to know a little bit about your enterprise:" + registration_detail_producer: "Woot! First we need to know a little bit about your farm:" + registration_detail_name_enterprise: "Enterprise name:" + registration_detail_name_producer: "Farm name:" + registration_detail_name_placeholder: "e.g. Charlie's Awesome Farm" + registration_detail_name_error: "Please choose a unique name for your enterprise" + registration_detail_address1: "Address line 1:" + registration_detail_address1_placeholder: "e.g. 123 Cranberry Drive" + registration_detail_address1_error: "Please enter an address" + registration_detail_address2: "Address line 2:" + registration_detail_suburb: "Suburb:" + registration_detail_suburb_placeholder: "e.g. Northcote" + registration_detail_suburb_error: "Please enter a suburb" + registration_detail_postcode: "Postcode:" + registration_detail_postcode_placeholder: "e.g. 3070" + registration_detail_postcode_error: "Postcode required" + registration_detail_state: "State:" + registration_detail_state_error: "State required" + registration_detail_country: "Country:" + registration_detail_country_error: "Please select a country" + fees: "Fees" + item_cost: "Item cost" + bulk: "Bulk" + shop_variant_quantity_min: "min" + shop_variant_quantity_max: "max" + contact: "Contact" + follow: "Follow" + shop_for_products_html: "Shop for %{enterprise} products at:" + change_shop: "Change shop to:" + shop_at: "Shop now at:" + price_breakdown: "Full price breakdown" + admin_fee: "Admin fee" + sales_fee: "Sales fee" + packing_fee: "Packing fee" + transport_fee: "Transport fee" + fundraising_fee: "Fundraising fee" + price_graph: "Price graph" + included_tax: "Included tax" + remove_tax: "Remove tax" diff --git a/config/locales/en.yml b/config/locales/en.yml index fe7d3c53aa..a5c5ba7903 100644 --- a/config/locales/en.yml +++ b/config/locales/en.yml @@ -1,5 +1,18 @@ -# Sample localization file for English. Add more files in this directory for other locales. -# See https://github.com/svenfuchs/rails-i18n/tree/master/rails%2Flocale for starting points. +# English language file +# --------------------- +# +# This is the source language file maintained by the Australian OFN team. +# Visit Transifex to translate this file into other languages: +# +# https://www.transifex.com/open-food-foundation/open-food-network/ +# +# If you translate this file in a text editor, please share your results with us by +# +# - uploading the file to Transifex or +# - opening a pull request at GitHub. +# +# +# See http://community.openfoodnetwork.org/t/localisation-ofn-in-your-language/397 en: devise: @@ -14,8 +27,653 @@ en: confirmation_sent: "Confirmation email sent!" confirmation_not_sent: "Could not send a confirmation email." home: "OFN" + title: Open Food Network welcome_to: 'Welcome to ' + site_meta_description: "We begin from the ground up. With farmers and growers ready to tell their stories proudly and truly. With distributors ready to connect people with products fairly and honestly. With buyers who believe that better weekly shopping decisions can…" search_by_name: Search by name or suburb... producers: Aussie Producers producers_join: Australian producers are now welcome to join the Open Food Network. charges_sales_tax: Charges GST? + print_invoice: "Print Invoice" + send_invoice: "Send Invoice" + resend_confirmation: "Resend Confirmation" + view_order: "View Order" + edit_order: "Edit Order" + ship_order: "Ship Order" + cancel_order: "Cancel Order" + confirm_send_invoice: "An invoice for this order will be sent to the customer. Are you sure you want to continue?" + confirm_resend_order_confirmation: "Are you sure you want to resend the order confirmation email?" + must_have_valid_business_number: "%{enterprise_name} must have a valid ABN before invoices can be sent." + invoice: "Invoice" + percentage_of_sales: "%{percentage} of sales" + percentage_of_turnover: "Percentage of turnover" + monthly_cap_excl_tax: "monthly cap (excl. GST)" + capped_at_cap: "capped at %{cap}" + per_month: "per month" + free: "free" + plus_tax: "plus GST" + total_monthly_bill_incl_tax: "Total Monthly Bill (Incl. Tax)" + say_no: "No" + say_yes: "Yes" + + sort_order_cycles_on_shopfront_by: "Sort Order Cycles On Shopfront By" + + # Printable Invoice Columns + invoice_column_item: "Item" + invoice_column_qty: "Qty" + invoice_column_tax: "GST" + invoice_column_price: "Price" + + logo: "Logo (640x130)" + logo_mobile: "Mobile logo (75x26)" + logo_mobile_svg: "Mobile logo (SVG)" + home_hero: "Hero image" + home_show_stats: "Show statistics" + footer_logo: "Logo (220x76)" + footer_facebook_url: "Facebook URL" + footer_twitter_url: "Twitter URL" + footer_instagram_url: "Instagram URL" + footer_linkedin_url: "LinkedIn URL" + footer_googleplus_url: "Google Plus URL" + footer_pinterest_url: "Pinterest URL" + footer_email: "Email" + footer_links_md: "Links" + footer_about_url: "About URL" + footer_tos_url: "Terms of Service URL" + + name: Name + first_name: First Name + last_name: Last Name + email: Email + phone: Phone + next: Next + address: Address + address2: Address (contd.) + city: City + state: State + postcode: Postcode + country: Country + unauthorized: Unauthorized + terms_of_service: "Terms of service" + on_demand: On demand + none: None + + alert_selling_on_ofn: "Interested in selling food on the Open Food Network?" + alert_start_here: "Start here" + label_shops: "Shops" + label_map: "Map" + label_producers: "Producers" + label_groups: "Groups" + label_about: "About" + label_shopping: "Shopping" + label_login: "Login" + label_logout: "Logout" + label_signup: "Sign up" + label_administration: "Administration" + label_admin: "Admin" + label_account: "Account" + label_more: "More" + label_less: "Show less" + label_notices: "Notices" + + items: "items" + cart_headline: "Your shopping cart" + total: "Total" + checkout: "Checkout now" + cart_updating: "Updating cart..." + cart_empty: "Cart empty" + cart_edit: "Edit your cart" + + card_number: Card Number + card_securitycode: "Security Code" + card_expiry_date: Expiry Date + + ofn_cart_headline: "Current cart for:" + ofn_cart_distributor: "Distributor:" + ofn_cart_oc: "Order cycle:" + ofn_cart_from: "From:" + ofn_cart_to: "To:" + ofn_cart_product: "Product:" + ofn_cart_quantitiy: "Quantity:" + ofn_cart_send: "Buy me" + + ie_warning_headline: "Your browser is out of date :-(" + ie_warning_text: "For the best Open Food Network experience, we strongly recommend upgrading your browser:" + ie_warning_chrome: Download Chrome + ie_warning_firefox: Download Firefox + ie_warning_ie: Upgrade Internet Explorer + ie_warning_other: "Can't upgrade your browser? Try Open Food Network on your smartphone :-)" + + footer_global_headline: "OFN Global" + footer_global_home: "Home" + footer_global_news: "News" + footer_global_about: "About" + footer_global_contact: "Contact" + + footer_sites_headline: "OFN Sites" + footer_sites_developer: "Developer" + footer_sites_community: "Community" + footer_sites_userguide: "User Guide" + + footer_secure: "Secure and trusted." + footer_secure_text: "Open Food Network uses SSL encryption (2048 bit RSA) everywhere to keep your shopping and payment information private. Our servers do not store your credit card details and payments are processed by PCI-compliant services." + + footer_contact_headline: "Keep in touch" + footer_contact_email: "Email us" + + footer_nav_headline: "Navigate" + footer_join_headline: "Join us" + footer_join_producers: "Producers sign-up" + footer_join_hubs: "Hubs sign-up" + footer_join_groups: "Groups sign-up" + footer_join_partners: "Food systems partners" + + footer_legal_call: "Read our" + footer_legal_tos: "Terms and conditions" + footer_legal_visit: "Find us on" + footer_legal_text_html: "Open Food Network is a free and open source software platform. Our content is licensed with %{content_license} and our code with %{code_license}." + + home_shop: Shop Now + + brandstory_headline: "Food, unincorporated." + brandstory_intro: "Sometimes the best way to fix the system is to start a new one…" + brandstory_part1: "We begin from the ground up. With farmers and growers ready to tell their stories proudly and truly. With distributors ready to connect people with products fairly and honestly. With buyers who believe that better weekly shopping decisions can seriously change the world." + brandstory_part2: "Then we need a way to make it real. A way to empower everyone who grows, sells and buys food. A way to tell all the stories, to handle all the logistics. A way to turn transaction into transformation every day." + brandstory_part3: "So we build an online marketplace that levels the playing field. It’s transparent, so it creates real relationships. It’s open source, so it’s owned by everyone. It scales to regions and nations, so people start versions across the world." + brandstory_part4: "It works everywhere. It changes everything." + brandstory_part5_strong: "We call it Open Food Network." + brandstory_part6: "We all love food. Now we can love our food system too." + + system_headline: "Here's how it works." + system_step1: "1. Search" + system_step1_text: "Search our diverse, independent shops for seasonal local food. Search by neighbourhood and food category, or whether you prefer delivery or pickup." + system_step2: "2. Shop" + system_step2_text: "Transform your transactions with affordable local food from diverse producers and hubs. Know the stories behind your food and the people who make it!" + system_step3: "3. Pick-up / Delivery" + system_step3_text: "Hang on for your delivery, or visit your producer or hub for a more personal connection with your food. Food shopping as diverse as nature intended it." + + cta_headline: "Shopping that makes the world a better place." + cta_label: "I'm Ready" + + stats_headline: "We're creating a new food system." + stats_producers: "food producers" + stats_shops: "food shops" + stats_shoppers: "food shoppers" + stats_orders: "food orders" + + checkout_title: Checkout + checkout_now: Checkout now + checkout_order_ready: Order ready for + checkout_hide: Hide + checkout_expand: Expand + checkout_headline: "Ok, ready to checkout?" + checkout_as_guest: "Checkout as guest" + checkout_details: "Your details" + checkout_billing: "Billing info" + checkout_shipping: Shipping info + checkout_method_free: Free + checkout_address_same: Shipping address same as billing address? + checkout_ready_for: "Ready for:" + checkout_instructions: "Any comments or special instructions?" + checkout_payment: Payment + checkout_send: Place order now + checkout_your_order: Your order + checkout_cart_total: Cart total + checkout_shipping_price: Shipping + checkout_total_price: Total + checkout_back_to_cart: "Back to Cart" + + order_paid: PAID + order_not_paid: NOT PAID + order_total: Total order + order_payment: "Paying via:" + order_billing_address: Billing address + order_delivery_on: Delivery on + order_delivery_address: Delivery address + order_special_instructions: "Your notes:" + order_pickup_time: Ready for collection + order_pickup_instructions: Collection Instructions + order_produce: Produce + order_total_price: Total + order_includes_tax: (includes tax) + order_payment_paypal_successful: Your payment via PayPal has been processed successfully. + order_hub_info: Hub Info + + products: "Products" + products_in: "in %{oc}" + products_at: "at %{distributor}" + products_elsewhere: "Products found elsewhere" + + email_welcome: "Welcome" + email_confirmed: "Thank you for confirming your email address." + email_registered: "is now part of" + email_userguide_html: "The User Guide with detailed support for setting up your Producer or Hub is here: +%{link}" + email_admin_html: "You can manage your account by logging into the %{link} or by clicking on the cog in the top right hand side of the homepage, and selecting Administration." + email_community_html: "We also have an online forum for community discussion related to OFN software and the unique challenges of running a food enterprise. You are encouraged to join in. We are constantly evolving and your input into this forum will shape what happens next. +%{link}" + email_help: "If you have any difficulties, check out our FAQs, browse the forum or post a 'Support' topic and someone will help you out!" + email_confirmation_greeting: "Hi, %{contact}!" + email_confirmation_profile_created: "A profile for %{name} has been successfully created! +To activate your Profile we need to confirm this email address." + email_confirmation_click_link: "Please click the link below to confirm your email and to continue setting up your profile." + email_confirmation_link_label: "Confirm this email address »" + email_confirmation_help_html: "After confirming your email you can access your administration account for this enterprise. +See the %{link} to find out more about %{sitename}'s features and to start using your profile or online store." + email_confirmation_userguide: "User Guide" + email_social: "Connect with Us:" + email_contact: "Email us:" + email_signoff: "Cheers," + email_signature: "%{sitename} Team" + + email_confirm_customer_greeting: "Hi %{name}," + email_confirm_customer_intro_html: "Thanks for shopping at %{distributor}!" + email_confirm_customer_number_html: "Order confirmation #%{number}" + email_confirm_customer_details_html: "Here are your order details from %{distributor}:" + email_confirm_customer_signoff: "Kind regards," + email_confirm_shop_greeting: "Hi %{name}," + email_confirm_shop_order_html: "Well done! You have a new order for %{distributor}!" + email_confirm_shop_number_html: "Order confirmation #%{number}" + email_order_summary_item: "Item" + email_order_summary_quantity: "Qty" + email_order_summary_price: "Price" + email_order_summary_subtotal: "Subtotal:" + email_order_summary_total: "Total:" + email_payment_paid: PAID + email_payment_not_paid: NOT PAID + email_payment_summary: Payment summary + email_payment_method: "Paying via:" + email_shipping_delivery_details: Delivery details + email_shipping_delivery_time: "Delivery on:" + email_shipping_delivery_address: "Delivery address:" + email_shipping_collection_details: Collection details + email_shipping_collection_time: "Ready for collection:" + email_shipping_collection_instructions: "Collection instructions:" + email_special_instructions: "Your notes:" + + email_signup_greeting: Hello! + email_signup_welcome: "Welcome to %{sitename}!" + email_signup_login: Your login + email_signup_email: Your login email is + email_signup_shop_html: "You can start shopping online now at %{link}." + email_signup_text: "Thanks for joining the network. + If you are a customer, we look forward to introducing you to many fantastic farmers, wonderful food hubs and delicious food! + If you are a producer or food enterprise, we are excited to have you as a part of the network." + email_signup_help_html: "We welcome all your questions and feedback; you can use the Send Feedback button on the site or email us at" + + shopping_oc_closed: Orders are closed + shopping_oc_closed_description: "Please wait until the next cycle opens (or contact us directly to see if we can accept any late orders)" + shopping_oc_last_closed: "The last cycle closed %{distance_of_time} ago" + shopping_oc_next_open: "The next cycle opens in %{distance_of_time}" + shopping_tabs_about: "About %{distributor}" + shopping_tabs_contact: "Contact" + shopping_contact_address: "Address" + shopping_contact_web: "Contact" + shopping_contact_social: "Follow" + shopping_groups_part_of: "is part of:" + shopping_producers_of_hub: "%{hub}'s producers:" + + enterprises_next_closing: "Next order closing" + enterprises_ready_for: "Ready for" + enterprises_choose: "Choose when you want your order:" + + hubs_buy: "Shop for:" + hubs_shopping_here: "Shopping here" + hubs_orders_closed: "Orders closed" + hubs_profile_only: "Profile only" + hubs_delivery_options: "Delivery options" + hubs_pickup: "Pickup" + hubs_delivery: "Delivery" + hubs_producers: "Our producers" + hubs_filter_by: "Filter by" + hubs_filter_type: "Type" + hubs_filter_delivery: "Delivery" + hubs_matches: "Did you mean?" + hubs_intro: Shop in your local area + hubs_distance: Closest to + hubs_distance_filter: "Show me shops near %{location}" + + products_clear_all: Clear all + products_showing: "Showing:" + products_with: with + products_search: "Search by product or producer" + products_loading: "Loading products..." + products_updating_cart: "Updating cart..." + products_cart_empty: "Cart empty" + products_edit_cart: "Edit your cart" + products_from: from + + search_no_results_html: "Sorry, no results found for %{query}. Try another search?" + + components_profiles_popover: "Profiles do not have a shopfront on the Open Food Network, but may have their own physical or online shop elsewhere" + components_profiles_show: "Show profiles" + components_filters_nofilters: "No filters" + components_filters_clearfilters: "Clear all filters" + + groups_title: Groups + groups_headline: Groups / regions + groups_text: "Every producer is unique. Every business has something different to offer. Our groups are collectives of producers, hubs and distributors who share something in common like location, farmers market or philosophy. This makes your shopping experience easier. So explore our groups and have the curating done for you." + groups_search: "Search name or keyword" + groups_no_groups: "No groups found" + groups_about: "About Us" + groups_producers: "Our producers" + groups_hubs: "Our hubs" + groups_contact_web: Contact + groups_contact_social: Follow + groups_contact_address: Address + groups_contact_email: Email us + groups_contact_website: Visit our website + groups_contact_facebook: Follow us on Facebook + groups_signup_title: Sign up as a group + groups_signup_headline: Groups sign up + groups_signup_intro: "We're an amazing platform for collaborative marketing, the easiest way for your members and stakeholders to reach new markets. We're non-profit, affordable, and simple." + groups_signup_email: Email us + groups_signup_motivation1: We transform food systems fairly. + groups_signup_motivation2: It's why we get out of bed every day. We're a global non-profit, based on open source code. We play fair. You can always trust us. + groups_signup_motivation3: We know you have big ideas, and we want to help. We'll share our knowledge, networks and resources. We know that isolation doesn't create change, so we'll partner with you. + groups_signup_motivation4: We meet you where you are. + groups_signup_motivation5: You might be an alliance of food hubs, producers, or distributors, and an industry body, or a local government. + groups_signup_motivation6: Whatever your role in your local food movement, we're ready to help. However you come to wonder what Open Food Network would look like or is doing in your part of the world, let's start the conversation. + groups_signup_motivation7: We make food movements make more sense. + groups_signup_motivation8: You need to activate and enable your networks, we offer a platform for conversation and action. You need real engagement. We’ll help reach all the players, all the stakeholders, all the sectors. + groups_signup_motivation9: You need resourcing. We’ll bring all our experience to bear. You need cooperation. We’ll better connect you to a global network of peers. + groups_signup_pricing: Group Account + groups_signup_studies: Case Studies + groups_signup_contact: Ready to discuss? + groups_signup_contact_text: "Get in touch to discover what OFN can do for you:" + groups_signup_detail: "Here's the detail." + + login_invalid: "Invalid email or password" + + modal_hubs: "Food Hubs" + modal_hubs_abstract: Our food hubs are the point of contact between you and the people who make your food! + modal_hubs_content1: You can search for a convenient hub by location or name. Some hubs have multiple points where you can pick-up your purchases, and some will also provide delivery options. Each food hub is a sales point with independent business operations and logistics - so variations between hubs are to be expected. + modal_hubs_content2: You can only shop at one food hub at a time. + + modal_groups: "Groups / Regions" + modal_groups_content1: These are the organisations and relationships between hubs which make up the Open Food Network. + modal_groups_content2: Some groups are clustered by location or council, others by non-geographic similarities. + + modal_how: "How it works" + modal_how_shop: Shop the Open Food Network + modal_how_shop_explained: Search for a food hub near you to start shopping! You can expand each food hub to see what kinds of goodies are available, and click through to start shopping. (You can only shop one food hub at a time.) + modal_how_pickup: Pick-ups, delivery and shipping costs + modal_how_pickup_explained: Some food hubs deliver to your door, while others require you to pick-up your purchases. You can see which options are available on the homepage, and select which you'd like at the shopping and check-out pages. Delivery will cost more, and pricing differs from hub-to-hub. Each food hub is a sales point with independent business operations and logisitics - so variations between hubs are to be expected. + modal_how_more: Learn more + modal_how_more_explained: "If you want to learn more about the Open Food Network, how it works, and get involved, check out:" + + modal_producers: "Producers" + modal_producers_explained: "Our producers make all the delicious food you can shop for on the Open Food Network." + + ocs_choice_hub: "Hub:" + ocs_choice_oc: "Order Cycle:" + ocs_choice_text: "You have not yet picked where you will get your order from." + ocs_closed_headline: Orders are currently closed for this hub + ocs_closed_time: "The last cycle closed %{time} ago." + ocs_closed_contact: "Please contact your hub directly to see if they accept late orders, or wait until the next cycle opens." + ocs_closed_opens: "The next order cycle opens in %{time}" + ocs_closed_email: "Email: %{email}" + ocs_closed_phone: "Phone: %{phone}" + ocs_pickup_time: "Your order will be ready on %{pickup_time}" + ocs_change_date: "Change Collection Date" + ocs_change_date_notice: "(This will reset your cart)" + ocs_close_time: "ORDERS CLOSE" + ocs_when_headline: When do you want your order? + ocs_when_text: No products are displayed until you select a date. + ocs_when_closing: "Closing On" + ocs_when_choose: "Choose Order Cycle" + ocs_list: "List View" + + producers_about: About us + producers_buy: Shop for + producers_contact: Contact + producers_contact_phone: Call + producers_contact_social: Follow + producers_buy_at_html: "Shop for %{enterprise} products at:" + producers_filter: Filter by + producers_filter_type: Type + producers_title: Producers + producers_headline: Find local producers + producers_signup_title: Sign up as a producer + producers_signup_headline: Food producers, empowered. + producers_signup_motivation: Sell your food and tell your stories to diverse new markets. Save time and money on every overhead. We support innovation without the risk. We've levelled the playing field. + producers_signup_send: Join now + producers_signup_enterprise: Enterprise Accounts + producers_signup_studies: Stories from our producers. + producers_signup_cta_headline: Join now! + producers_signup_cta_action: Join now + producers_signup_detail: Here's the detail. + + products_item: Item + products_description: Description + products_variant: Variant + products_quantity: Quantity + products_availabel: Available? + products_price: Price + + register_title: Register + + shops_title: Shops + shops_headline: Shopping, transformed. + shops_text: Food grows in cycles, farmers harvest in cycles, and we order food in cycles. If you find an order cycle closed, check back soon. + shops_signup_title: Sign up as a hub + shops_signup_headline: Food hubs, unlimited. + shops_signup_motivation: Whatever your model, we support you. However you change, we're with you. We're non-profit, independent, and open-sourced. We're the software partners you've dreamed of. + shops_signup_action: Join now + shops_signup_pricing: Enterprise Accounts + shops_signup_stories: Stories from our hubs. + shops_signup_help: We're ready to help. + shops_signup_help_text: You need a better return. You need new buyers and logistics partners. You need your story told across wholesale, retail, and the kitchen table. + shops_signup_detail: Here's the detail. + + orders_fees: Fees... + orders_edit_title: Shopping Cart + orders_edit_headline: Your shopping cart + orders_edit_time: Order ready for + orders_edit_continue: Continue shopping + orders_edit_checkout: Checkout + orders_form_empty_cart: "Empty cart" + orders_form_subtotal: Produce subtotal + orders_form_admin: Admin and handling + orders_form_total: Total + orders_oc_expired_headline: Orders have closed for this order cycle + orders_oc_expired_text: "Sorry, orders for this order cycle closed %{time} ago! Please contact your hub directly to see if they can accept late orders." + orders_oc_expired_text_others_html: "Sorry, orders for this order cycle closed %{time} ago! Please contact your hub directly to see if they can accept late orders %{link}." + orders_oc_expired_text_link: "or see the other order cycles available at this hub" + orders_oc_expired_email: "Email:" + orders_oc_expired_phone: "Phone:" + orders_show_title: Order Confirmation + orders_show_time: Order ready on + orders_show_number: Order confirmation + + products_cart_distributor_choice: "Distributor for your order:" + products_cart_distributor_change: "Your distributor for this order will be changed to %{name} if you add this product to your cart." + products_cart_distributor_is: "Your distributor for this order is %{name}." + products_distributor_error: "Please complete your order at %{link} before shopping with another distributor." + products_oc: "Order cycle for your order:" + products_oc_change: "Your order cycle for this order will be changed to %{name} if you add this product to your cart." + products_oc_is: "Your order cycle for this order is %{name}." + products_oc_error: "Please complete your order from %{link} before shopping in a different order cycle." + products_oc_current: "your current order cycle" + products_quantity: Quantity + products_max_quantity: Max quantity + products_distributor: Distributor + products_distributor_info: When you select a distributor for your order, their address and pickup times will be displayed here. + + # keys used in javascript + password: Password + remember_me: Remember Me + are_you_sure: "Are you sure?" + orders_open: Orders open + closing: "Closing " + going_back_to_home_page: "Taking you back to the home page" + creating: Creating + updating: Updating + failed_to_create_enterprise: "Failed to create your enterprise." + failed_to_create_enterprise_unknown: "Failed to create your enterprise.\nPlease ensure all fields are completely filled out." + failed_to_update_enterprise_unknown: "Failed to update your enterprise.\nPlease ensure all fields are completely filled out." + order_not_saved_yet: "Your order hasn't been saved yet. Give us a few seconds to finish!" + filter_by: "Filter by" + hide_filters: "Hide filters" + one_filter_applied: "1 filter applied" + x_filters_applied: " filters applied" + submitting_order: "Submitting your order: please wait" + confirm_hub_change: "Are you sure? This will change your selected hub and remove any items in your shopping cart." + confirm_oc_change: "Are you sure? This will change your selected order cycle and remove any items in your shopping cart." + location_placeholder: "Type in a location..." + error_required: "can't be blank" + error_number: "must be number" + error_email: "must be email address" + item_handling_fees: "Item Handling Fees (included in item totals)" + january: "January" + february: "February" + march: "March" + april: "April" + may: "May" + june: "June" + july: "July" + august: "August" + september: "September" + october: "October" + november: "November" + december: "December" + email_not_found: "Email address not found" + email_required: "You must provide an email address" + logging_in: "Hold on a moment, we're logging you in" + signup_email: "Your email" + choose_password: "Choose a password" + confirm_password: "Confirm password" + action_signup: "Sign up now" + welcome_to_ofn: "Welcome to the Open Food Network!" + signup_or_login: "Start By Signing Up (or logging in)" + have_an_account: "Already have an account?" + action_login: "Log in now." + forgot_password: "Forgot Password?" + password_reset_sent: "An email with instructions on resetting your password has been sent!" + reset_password: "Reset password" + registration_greeting: "Greetings!" + who_is_managing_enterprise: "Who is responsible for managing %{enterprise}?" + enterprise_contact: "Primary Contact" + enterprise_contact_required: "You need to enter a primary contact." + enterprise_email: "Email address" + enterprise_email_required: "You need to enter valid email address." + enterprise_phone: "Phone number" + back: "Back" + continue: "Continue" + limit_reached_headline: "Oh no!" + limit_reached_message: "You have reached the limit!" + limit_reached_text: "You have reached the limit for the number of enterprises you are allowed to own on the" + limit_reached_action: "Return to the homepage" + select_promo_image: "Step 3. Select Promo Image" + promo_image_tip: "Tip: Shown as a banner, preferred size is 1200×260px" + promo_image_label: "Choose a promo image" + action_or: "OR" + promo_image_drag: "Drag and drop your promo here" + review_promo_image: "Step 4. Review Your Promo Banner" + review_promo_image_tip: "Tip: for best results, your promo image should fill the available space" + promo_image_placeholder: "Your logo will appear here for review once uploaded" + uploading: "Uploading..." + select_logo: "Step 1. Select Logo Image" + logo_tip: "Tip: Square images will work best, preferably at least 300×300px" + logo_label: "Choose a logo image" + logo_drag: "Drag and drop your logo here" + review_logo: "Step 2. Review Your Logo" + review_logo_tip: "Tip: for best results, your logo should fill the available space" + logo_placeholder: "Your logo will appear here for review once uploaded" + enterprise_about_headline: "Nice one!" + enterprise_about_message: "Now let's flesh out the details about" + enterprise_success: "Success! %{enterprise} added to the Open Food Network " + enterprise_registration_exit_message: "If you exit this wizard at any stage, you need to click the confirmation link in the email you have received. This will take you to your admin interface where you can continue setting up your profile." + enterprise_description: "Short Description" + enterprise_description_placeholder: "A short sentence describing your enterprise" + enterprise_long_desc: "Long Description" + enterprise_long_desc_placeholder: "This is your opportunity to tell the story of your enterprise - what makes you different and wonderful? We'd suggest keeping your description to under 600 characters or 150 words." + enterprise_long_desc_length: "%{num} characters / up to 600 recommended" + enterprise_abn: "ABN" + enterprise_abn_placeholder: "eg. 99 123 456 789" + enterprise_acn: "ACN" + enterprise_acn_placeholder: "eg. 123 456 789" + enterprise_tax_required: "You need to make a selection." + enterprise_final_step: "Final step!" + enterprise_social_text: "How can people find %{enterprise} online?" + website: "Website" + website_placeholder: "eg. openfoodnetwork.org.au" + facebook: "Facebook" + facebook_placeholder: "eg. www.facebook.com/PageNameHere" + linkedin: "LinkedIn" + linkedin_placeholder: "eg. www.linkedin.com/YourNameHere" + twitter: "Twitter" + twitter_placeholder: "eg. @twitter_handle" + instagram: "Instagram" + instagram_placeholder: "eg. @instagram_handle" + registration_greeting: "Hi there!" + registration_intro: "You can now create a profile for your Producer or Hub" + registration_action: "Let's get started!" + registration_checklist: "You'll need" + registration_time: "5-10 minutes" + registration_enterprise_address: "Enterprise address" + registration_contact_details: "Primary contact details" + registration_logo: "Your logo image" + registration_promo_image: "Landscape image for your profile" + registration_about_us: "'About Us' text" + registration_outcome_headline: "What do I get?" + registration_outcome1_html: "Your profile helps people find and contact you on the Open Food Network." + registration_outcome2: "Use this space to tell the story of your enterprise, to help drive connections to your social and online presence. " + registration_outcome3: "It's also the first step towards trading on the Open Food Network, or opening an online store." + registration_finished_headline: "Finished!" + registration_finished_thanks: "Thanks for filling out the details for %{enterprise}." + registration_finished_login: "You can change or update your enterprise at any stage by logging into Open Food Network and going to Admin." + registration_finished_activate: "Activate %{enterprise}." + registration_finished_activate_instruction_html: "We've sent a confirmation email to %{email} if it hasn't been activated before.
+Please follow the instructions there to make your enterprise visible on the Open Food Network." + registration_finished_action: "Open Food Network home" + registration_type_headline: "Last step to add %{enterprise}!" + registration_type_question: "Are you a producer?" + registration_type_producer: "Yes, I'm a producer" + registration_type_no_producer: "No, I'm not a producer" + registration_type_error: "Please choose one. Are you are producer?" + registration_type_producer_help: "Producers make yummy things to eat and/or drink. You're a producer if you grow it, raise it, brew it, bake it, ferment it, milk it or mould it." + registration_type_no_producer_help: "If you’re not a producer, you’re probably someone who sells and distributes food. You might be a hub, coop, buying group, retailer, wholesaler or other." + create_profile: "Create Profile" + registration_images_headline: "Thanks!" + registration_images_description: "Let's upload some pretty pictures so your profile looks great! :)" + registration_detail_headline: "Let's Get Started" + registration_detail_enterprise: "Woot! First we need to know a little bit about your enterprise:" + registration_detail_producer: "Woot! First we need to know a little bit about your farm:" + registration_detail_name_enterprise: "Enterprise Name:" + registration_detail_name_producer: "Farm Name:" + registration_detail_name_placeholder: "e.g. Charlie's Awesome Farm" + registration_detail_name_error: "Please choose a unique name for your enterprise" + registration_detail_address1: "Address line 1:" + registration_detail_address1_placeholder: "e.g. 123 Cranberry Drive" + registration_detail_address1_error: "Please enter an address" + registration_detail_address2: "Address line 2:" + registration_detail_suburb: "Suburb:" + registration_detail_suburb_placeholder: "e.g. Northcote" + registration_detail_suburb_error: "Please enter a suburb" + registration_detail_postcode: "Postcode:" + registration_detail_postcode_placeholder: "e.g. 3070" + registration_detail_postcode_error: "Postcode required" + registration_detail_state: "State:" + registration_detail_state_error: "State required" + registration_detail_country: "Country:" + registration_detail_country_error: "Please select a country" + fees: "Fees" + item_cost: "Item cost" + bulk: "Bulk" + shop_variant_quantity_min: "min" + shop_variant_quantity_max: "max" + contact: "Contact" + follow: "Follow" + shop_for_products_html: "Shop for %{enterprise} products at:" + change_shop: "Change shop to:" + shop_at: "Shop now at:" + price_breakdown: "Full price breakdown" + admin_fee: "Admin fee" + sales_fee: "Sales fee" + packing_fee: "Packing fee" + transport_fee: "Transport fee" + fundraising_fee: "Fundraising fee" + price_graph: "Price graph" + included_tax: "Included tax" + remove_tax: "Remove tax" diff --git a/config/locales/es.yml b/config/locales/es.yml new file mode 100644 index 0000000000..061638b06e --- /dev/null +++ b/config/locales/es.yml @@ -0,0 +1,580 @@ +es: + devise: + failure: + invalid: | + Correo o contraseña inválidos. + ¿Fue un invitado la vez pasada? Tal vez necesite crear una cuenta o recuperar su contraseña. + enterprise_confirmations: + enterprise: + confirmed: Gracias, la dirección de correo fue confirmada. + not_confirmed: La dirección de correo no pudo ser confirmada. ¿Tal vez ya completó este paso? + confirmation_sent: "¡Correo de confirmación enviado!" + confirmation_not_sent: "No se pudo enviar un correo de confirmación." + home: "OFN" + title: Open Food Network + welcome_to: 'Bienvenido a ' + search_by_name: Buscar por nombre o barrio... + producers: Productores australianos + producers_join: Los productores australianos ahora son bienvenidos a unirse a Open Food Network. + charges_sales_tax: ¿Cargos de GST? + print: "Imprimir" + confirm_send_invoice: "Una factura para esta orde se envió al cliente. ¿Está seguro que quiere continuar?" + must_have_valid_business_number: "%{enterprise_name} debe tener un ABN válido antes de que las facturas se puedan enviar." + logo: "Logo (640x130)" + logo_mobile: "Logo para móvil (75x26)" + logo_mobile_svg: "Logo para móvil (SVG)" + home_hero: "Hero image" + home_show_stats: "Mostrar estadísticas" + footer_logo: "Logo (220x76)" + footer_facebook_url: "URL de Facebook" + footer_twitter_url: "URL de Twitter" + footer_instagram_url: "URL de Instagram" + footer_linkedin_url: "URL de LinkedIn" + footer_googleplus_url: "URL de Google Plus" + footer_pinterest_url: "URL de Pinterest" + footer_email: "Correo electrónico" + footer_links_md: "Enlaces" + footer_about_url: "URL acerca de" + footer_tos_url: "URL de términos y servicios" + invoice: "Factura" + name: Nombre + first_name: Nombre + last_name: Apellido + email: Correo electrónico + phone: Teléfono + next: Siguiente + address: Dirección + address2: Dirección (cont.) + city: Ciudad + state: Estado + postcode: Código postal + country: País + unauthorized: No autorizado + terms_of_service: "Términos de servicio" + on_demand: Bajo demanda + none: Ninguno + alert_selling_on_ofn: "¿Interesado en vender comida en Open Food Network?" + alert_start_here: "Empiece aquí" + label_shops: "Tiendas" + label_map: "Mapa" + label_producers: "Productores" + label_groups: "Grupos" + label_about: "Acerca de" + label_shopping: "Tienda" + label_login: "Iniciar de sesión" + label_logout: "Cerrar sesión" + label_signup: "Registrarse" + label_administration: "Administración" + label_admin: "Admin" + label_account: "Cuenta" + label_more: "Más" + label_less: "Mostrar menos" + items: "artículos" + cart_headline: "Su carrito de compras" + total: "Total" + checkout: "Revisar el carrito de compras" + cart_updating: "Actualizando el carrito..." + cart_empty: "Carrito vacío" + cart_edit: "Editar carrito" + card_number: Número de tarjeta + card_securitycode: "Código de seguridad" + card_expiry_date: Fecha de expiración + ofn_cart_headline: "Tarjeta actual para:" + ofn_cart_distributor: "Distribuidor:" + ofn_cart_oc: "Ciclo de orden:" + ofn_cart_from: "De:" + ofn_cart_to: "Para:" + ofn_cart_product: "Producto:" + ofn_cart_quantitiy: "Cantidad:" + ofn_cart_send: "Comprar me" + ie_warning_headline: "Su navegador está desactualizado :-(" + ie_warning_text: "Para la mejor esperiencia de Open Food Network, recomendamos actualizar su navegador:" + ie_warning_chrome: Descargar Chrome + ie_warning_firefox: Descargar Firefox + ie_warning_ie: Actualizar Internet Explorer + ie_warning_other: "¿No puede actualizar su navegador? Pruebe Open Food Network en su teléfono :-)" + footer_global_headline: "OFN Global" + footer_global_home: "Inicio" + footer_global_news: "Noticas" + footer_global_about: "Acerca de" + footer_global_contact: "Contacto" + footer_sites_headline: "Sitios OFN" + footer_sites_developer: "Desarrollador" + footer_sites_community: "Comunidad" + footer_sites_userguide: "Guía de usuario" + footer_secure: "Seguro y de confianza." + footer_secure_text: "Open Food Network usa cifrado SSL (RSA de 2048 bit) en todo lado para mantener privada la información de compras y pagos. Nuestros servidores no almacenan los detalles de tarjetas de créditos y los pagos son procesados por servicios que cumplen con PCI." + footer_contact_headline: "Mantenerse en contacto" + footer_contact_email: "Envíenos un correo" + footer_nav_headline: "Navegar" + footer_join_headline: "Unirse" + footer_join_producers: "Registro de productores" + footer_join_hubs: "Registro de centros de acopio" + footer_join_groups: "Registro de grupos" + footer_join_partners: "Socios de sistemas de comida" + footer_legal_call: "Leer nuestros" + footer_legal_tos: "Términos y condiciones" + footer_legal_visit: "Encuéntrenos en" + footer_legal_text_html: "Open Food Network es una plataforma libre y de código abierto. Nuestro contenido tiene una licencia %{content_license} y nuestro código %{code_license}." + home_shop: Comprar ahora + brandstory_headline: "Comida, no incorporada." + brandstory_intro: "A veces la mejor forma de arreglar el sistema es empezar uno nuevo…" + brandstory_part1: "Nosotros empezamos desde abajo. Con granjeros y productores listos para contar sus historias con orgullo y autenticidad. Con distribuidores listos para conectar gente con productos de forma justa y honesta. Con compradores que creen que mejores decisiones de compras semanales pueden seriamente cambiar el mundo." + brandstory_part2: "Luego necesitamos una forma de hacerlo real. Una forma de empoderar a todos los que producen, venden y compran comida. Una forma de contar todas las historias, de manejar todas las logísticas. Una forma de convertir transacción en transformación todos los días." + brandstory_part3: "Entonces contruímos un mercado que nivela el campo de juego. Es transparente, de forma que crea relaciones reales. Es de código abierto, de forma que todos son los dueños." + brandstory_part4: "Funciona en todo lado. Cambia todo." + brandstory_part5_strong: "Le llamamos Open Food Network." + brandstory_part6: "Todos amamos la comida. Ahora podemos amar nuestro sistema de comida también." + system_headline: "Así es como funciona." + system_step1: "1. Buscar" + system_step1_text: "Busque comida local y de temporada en nuestras tiendas diversas e independientes. Busque por barrio o categoría de comida, o si prefiere servicio de entrega o recogerla." + system_step2: "2. Comprar" + system_step2_text: "Transforme sus transacciones con comida local y asequible de diversos productores y centros de acopio. ¡Conozca las historias detrásde su comida y la gente que la hace!" + system_step3: "3. Recoger / Servicio de entrega" + system_step3_text: "Espere su entrega, o visite a su productor o centro de acopio para una conexión más personal con su comida. Compra de comida tan diversa como la naturaleza lo previó." + cta_headline: "Compras que hacen el mundo un mejor lugar." + cta_label: "Estoy listo" + stats_headline: "Estamos creando un nuevo sistema de comida." + stats_producers: "productores de comida" + stats_shops: "tiendas de comida" + stats_shoppers: "compradores de comida" + stats_orders: "ordenes de comida" + checkout_title: Revisar el carrito + checkout_now: Revisare el carrito ahora + checkout_order_ready: Orden lista para + checkout_hide: Esconder + checkout_expand: Expandir + checkout_headline: "Esta bien, ¿listo para pagar?" + checkout_as_guest: "Pagar como invitado" + checkout_details: "Sus detalles" + checkout_billing: "Información de cobro" + checkout_shipping: Información de entrega + checkout_method_free: Gratis + checkout_address_same: ¿Dirección de entrega igual a la dirección de cobro? + checkout_ready_for: "Listo para:" + checkout_instructions: "¿Algún comentario o intrucciones especiales?" + checkout_payment: Pago + checkout_send: Realizar la orden ya + checkout_your_order: Su orden + checkout_cart_total: Total del carrito + checkout_shipping_price: Entrega + checkout_total_price: Total + checkout_back_to_cart: "De vuelta al carrito" + order_paid: PAGADO + order_not_paid: NO PAGADO + order_total: Orden total + order_payment: "Pagando con:" + order_billing_address: Dirección de cobro + order_delivery_on: Entregar en + order_delivery_address: Dirección de entrega + order_special_instructions: "Sus notas:" + order_pickup_instructions: Instrucciones de recolección + order_produce: Productos + order_total_price: Total + order_includes_tax: (incluye impuesto) + order_payment_paypal_successful: Su pago a través de PayPal ha sido procesado con éxito. + order_hub_info: Información del centro de acopio + products: "Productos" + products_in: "en %{oc}" + products_at: "en %{distributor}" + products_elsewhere: "Productos encontrados en otros lugares" + email_welcome: "Bienvenido" + email_confirmed: "Gracias por confirmar la dirección de correo electrónico." + email_registered: "ahora es parte de" + email_userguide_html: "La Guía de Usuario con soporte detallado para configura su Procutor o Centro de acopio está aquí: %{link}" + email_admin_html: "Puede administrar su cuenta iniciando sesión en %{link} o haciendo clic en el engrane arriba a la derecha de la página de inicio, y seleccionando Administración." + email_community_html: "También tenemos un foro en líea para la discusión comunal relacionada con el programa OFN y los retos únicos del funcionamiento de una empresa de alimentación. Lo invitamos a unirse. Estamos evolucionando de forma constante y su aporte en este formo le dará forma a lo que pase luego. %{link}" + email_help: "Si tiene dificultades, revise nuestras preguntas frecuentes, navegue el foro o haga una entrada de con tema de 'Soporte' y ¡alguien le ayudará!" + email_confirmation_greeting: "Hola, %{contact}!" + email_confirmation_profile_created: "¡Se creó un un perfil para %{name} con éxito! Para activar su Perfil necesitamos que confirme esta dirección de correos." + email_confirmation_click_link: "Por favor haga clic en el enlace de abajo para confirmar el correo electrónico y continuar configurando su perfil." + email_confirmation_link_label: "Confirmar este correo electrónico »" + email_confirmation_help_html: "Después de confirmar el correo electrónico puede accesar su cuenta de administración para esta empresa. Vea el %{link} para encontrar más acerca de las características de %{sitename} e iniciar a usar su perfil o tienda en línea." + email_confirmation_userguide: "Guía de Usuario" + email_social: "Conecte con nosotros:" + email_contact: "Envíenos un correo electrónico:" + email_signoff: "Saludos," + email_signature: "El equipo de %{sitename}" + email_confirm_customer_greeting: "Hola %{name}," + email_confirm_customer_intro_html: "¡Gracias por comprar en %{distributor}!" + email_confirm_customer_number_html: "Confirmación de orden #%{number}" + email_confirm_customer_details_html: "Aquí están los detalles de su orden de %{distributor}:" + email_confirm_customer_signoff: "Saludos cordiales," + email_confirm_shop_greeting: "Hola %{name}," + email_confirm_shop_order_html: "¡Bien hecho! ¡Usted tiene una nueva orden en %{distributor}!" + email_confirm_shop_number_html: "Confirmación de orden #%{number}" + email_order_summary_item: "Elemento" + email_order_summary_quantity: "Cantidad" + email_order_summary_price: "Precio" + email_order_summary_subtotal: "Subtotal:" + email_order_summary_total: "Total:" + email_payment_paid: PAGADO + email_payment_not_paid: NO PAGADO + email_payment_summary: Resumen de pago + email_order_summary_method: "Pagar con:" + email_shipping_delivery_details: Detalles de entrega + email_shipping_delivery_time: "Entregar en:" + email_shipping_delivery_address: "Dirección de entrega:" + email_shipping_collection_details: Detalles de recolección + email_shipping_collection_time: "Listo para la recolección:" + email_shipping_collection_instructions: "Instrucciones de recolección:" + email_special_instructions: "Sus notas:" + email_signup_greeting: ¡Hola! + email_signup_welcome: "Bienvenido a %{sitename}!" + email_signup_login: Su nombre de usuario + email_signup_email: Su correo electrónico para el inicio de sesión es + email_signup_shop_html: "Puede empezar a comprar en línea ahora en %{link}." + email_signup_text: "Gracias por unirse a la red. Si usted es un cliente, ¡esperamos presentarle muchos fantásticos agricultores, maravillosos sentros de acopio de comida y deliciosa comida! Si usted es un productor o forma parte de una empresa de alimentos, estamos emocionados de que forme parte de la red." + email_signup_help_html: "Damos la bienvenida a todas sus preguntas y retroalimentación; usted puede usar el botón de Enviar Retroalimentación en el sitio o enviarnos un correo electrónico a" + shopping_oc_closed: Las ordenes están cerradas + shopping_oc_closed_description: "Por favor espere hasta que el próximo ciclo abra (o contactenos de forma directa para ver si podemos aceptar algunas ordenes tardías)" + shopping_oc_last_closed: "El último cilco cerró hace %{distance_of_time}" + shopping_oc_next_open: "El próximo ciclo abrirá en %{distance_of_time}" + shopping_tabs_about: "Acerca de %{distributor}" + shopping_tabs_contact: "Contacto" + shopping_contact_address: "Dirección" + shopping_contact_web: "Contacto" + shopping_contact_social: "Seguir" + shopping_groups_part_of: "es parte de:" + shopping_producers_of_hub: "productores de %{hub}:" + enterprises_next_closing: "Próxima orden cerrando" + enterprises_ready_for: "Listo para" + enterprises_choose: "Escoger cuando quiere la orden:" + hubs_buy: "Comprar:" + hubs_shopping_here: "Comprando aquí" + hubs_orders_closed: "Ordenes cerradas" + hubs_profile_only: "Solo perfil" + hubs_delivery_options: "Opciones de entrega" + hubs_pickup: "Recoger" + hubs_delivery: "Entrega" + hubs_producers: "Nuestros productores" + hubs_filter_by: "Filtrar por" + hubs_filter_type: "Tipo" + hubs_filter_delivery: "Entrega" + hubs_matches: "¿Quizo decir?" + hubs_intro: Comprar en su área local + hubs_distance: Más cercano a + hubs_distance_filter: "Mustreme tiendas cerca de %{location}" + products_clear_all: Limpiar todo + products_showing: "Mostrando:" + products_with: con + products_search: "Buscar por producto o productor" + products_loading: "Cargando productos..." + products_updating_cart: "Actualizando su carrito..." + products_cart_empty: "Carrito vacío" + products_edit_cart: "Editar su carrito" + products_from: desde + search_no_results_html: "Lo sentimos, no hay resultados para %{query}. ¿Intentar otra búsqueda?" + components_profiles_popover: "Los perfiles no tienen escaparate en Open Food Network, pero pueden tener su propia tienda física o en línea en otro lugar" + components_profiles_show: "Mostrar perfiles" + components_filters_nofilters: "Sin filtros" + components_filters_clearfilters: "Limpiar todos los filtros" + groups_title: Groupos + groups_headline: Groupos / regiones + groups_search: "Buscar nombre o palabra clave" + groups_no_groups: "No se encontraron grupos" + groups_about: "Acerca de nosotros" + groups_producers: "Nuestros productores" + groups_hubs: "Nuestro centros de acopio" + groups_contact_web: Contacto + groups_contact_social: Seguir + groups_contact_address: Dirección + groups_contact_email: Envíenos un correo electrónico + groups_contact_website: Visite nuestro sitio web + groups_contact_facebook: Síganos en Facebook + groups_signup_title: Registrarse como un grupo + groups_signup_headline: Registro de grupos + groups_signup_intro: "Somos una asombrosa plataforma de mercadeo colaborativo, la forma más sencilla para que sus miembros e interesados encuentren nuevos mercados. No tenemos fines de lucro, somos asequibles y simples." + groups_signup_email: Envíenos un correo electrónico + groups_signup_motivation1: Nosotros transformamos sistemas de comida de forma justa. + groups_signup_motivation2: Es por lo que salimos de la cama cada día. Somos una organización sin fines de lucro global, basada en código de fuente abierta. Jugamos de forma justa. Siempre puede confiar en nosotros. + groups_signup_motivation3: Sabemos que tiene grandes ideas, y queremos ayudar. Compartiremos nuestro conocimiento, redes y recursos. Sabemos que el aislamiento no crea cambio, entonces nos asociaremos con usted. + groups_signup_motivation4: Nos reunimos con usted en donde esté. + groups_signup_motivation5: Usted puede ser una alianza de centros de acopio de comida, productores o distribuidores, y un organismo de la industria o un gobierno local. + groups_signup_motivation6: Cualquiera que sea su rol en el movimiento de comida local, estamos listos para ayudar. De cualquier forma en que se pregunte cómo se vería Open Food Network o qué está haciendo en su parte del mundo, empecemos la conversación. + groups_signup_motivation7: Hacemos que los movimientos alimenticios tengan más sentido. + groups_signup_motivation8: Necesita activar y habilitar sus redes. Le ofrecemos una plataforma para conversación y acción. Necesita involucramiento real. Le ayudaremos a alcanzar todos los actores, todos los interesados, todos los sectores. + groups_signup_motivation9: Necesita recursos. Le brindaremos todas nuestras experiencias para portar. Necesita cooperación. Lo conectaremos mejor a una red global de pares. + groups_signup_pricing: Cuenta de grupo + groups_signup_studies: Casos de estudio + groups_signup_contact: ¿Listo para discutir? + groups_signup_contact_text: "Póngase en conta para descubrir qué puede hacer OFN por usted:" + groups_signup_detail: "Aquí está el detalle." + login_invalid: "Correo electrónico o contraseña inválidos" + modal_hubs: "Centros de acopio de comida" + modal_hubs_abstract: ¡Nuestros centros de acopio de comida son el punto de contacto entre usted y la gente que hace su comida! + modal_hubs_content1: Puede buscar un centro de acopio conveniente por ubicación o nombre. Algunos centros de acopio tienen múltiples puntos en loa que puede recoger las compras, y algunos también brindan opciones de entrega a domicilio. Cada centro de acopio de comida es un punto de venta con operaciones de negocio y logística independientes, entonces puede esperar diferencias entre centros de acopio. + modal_hubs_content2: Sólo puede comprar en un centro de acopio a la vez. + modal_groups: "Grupos / Regiones" + modal_groups_content1: Estas son las organizaciones y relaciones entre centros de acopio que conforman el Open Food Network. + modal_groups_content2: Algunos grupos están organizados por ubicación o junta, otros por similaridades no geográficas. + modal_how: "Cómo funciona" + modal_how_shop: Comprar en Open Food Network + modal_how_shop_explained: ¡Buscar un centro de acopio cerca suyo para empezar a hacer compras! Puede expandir cada centro de acopio de alimentos para ver qué tipos de bienes están disponibles, y hacer clic para empezar a comprar. (Sólo puede comprar en un centro de acopio a la vez.) + modal_how_pickup: Retiro, entrega y costos de envío + modal_how_pickup_explained: Algunos centros de acopio hacen entregas hasta su puerta, mientras otros requieren que recoja las compras. Puede ver cuáles opciones están disponibles en su página de inicio, y seleccionar cuál le gustaría en las páginas de compras y revisión. Las entregas costarán más, y el precio cambia entre centros de acopio. Cada centro de acopio de alimentos es un punto de venta con operaciones de negocio y logísitcas independientes, entonces es de esperar que hayan diferencias. + modal_how_more: Aprender más + modal_how_more_explained: "Si quiere aprender más acerca del Open Food Network, cómo trabaja y colaborar, revise:" + modal_producers: "Productores" + modal_producers_explained: "Nuestros productores hacen todos los deliciosos alimentos que pueden comprar en la Open Food Network." + ocs_choice_hub: "Centro de acopio:" + ocs_choice_oc: "Ciclo de orden:" + ocs_choice_text: "No ha seleccionado aún el lugar de dónde va a recibir su orden." + ocs_closed_headline: Las ordenes están cerradas para este centro de acopio + ocs_closed_time: "El último ciclo cerró hace %{time}." + ocs_closed_contact: "Contacte su centro de acopio directamente para ver si aceptan ordenes tardías, o espere hasta que el próximo ciclo abra." + ocs_closed_opens: "El próximo ciclo de ordenes abre en %{time}" + ocs_closed_email: "Correo electrónico: %{email}" + ocs_closed_phone: "Teléfono: %{phone}" + ocs_pickup_time: "Su orden estará lista en %{pickup_time}" + ocs_change_date: "Cambia su fecha de recolección" + ocs_change_date_notice: "(Esto reiniciará su carrito)" + ocs_close_time: "ORDENES CERRADAS" + ocs_when_headline: ¿Cuándo quiere su orden? + ocs_when_text: Ningún producto se mostrará hasta que seleccione una fecha. + ocs_when_closing: "Cerrando en" + ocs_when_choose: "Seleccione el ciclo de orden" + ocs_list: "Vista de lista" + producers_about: Acerca de nosotros + producers_buy: Comprar + producers_contact: Contacto + producers_contact_phone: Llamar + producers_contact_social: Seguir + producers_buy_at_html: "Comprar productos de %{enterprise} en:" + producers_filter: Filtrar por + producers_filter_type: Tipo + producers_title: Productores + producers_headline: Encuentrar productores locales + producers_signup_title: Registrarse como productor + producers_signup_headline: Productores de alimentos, empoderados. + producers_signup_motivation: Venda sus alimentos y cuente sus historias en distintos nuevos mercados. Ahorre tiempo y dinero en costos administrativos. Apoyamos la innovación sin el riesgo. Hemos nivelado el campo de juego. + producers_signup_send: Únase ahora + producers_signup_enterprise: Cuentas de empresa + producers_signup_studies: Historias de nuestros productores. + producers_signup_cta_headline: ¡Únase ahora! + producers_signup_cta_action: Únase ahora + producers_signup_detail: Aquí esta el detalle. + products_item: Artículo + products_description: Descripción + products_variant: Variante + products_availabel: ¿Disponible? + products_price: Precio + register_title: Registro + shops_title: Tiendas + shops_headline: Compras, transformadas. + shops_text: Los alimentos crecen en ciclos, los agricultores cosechan en ciclos, y nosotros ordenamos comida en ciclos. Si encuentra una orden con el ciclo cerrado, vuelva a revisar pronto. + shops_signup_title: Registrarse como un centro de acopio + shops_signup_headline: Centros de acopio, sin límites. + shops_signup_motivation: Cualquiera que sea su modelo, lo apoyamos. De cualquier forma que cambie, estamos con usted. Somos una organización sin findes de lucro, independiente, y de código abierto. Somos los socios de software con los que ha soñado. + shops_signup_action: Únase ahora + shops_signup_pricing: Cuentas de empresa + shops_signup_stories: Historias de nuestros centros de acopio. + shops_signup_help: Estamos listos para ayudar. + shops_signup_help_text: Usted necesita un mejor retorno. Usted necesita nuevos compradores y socios de logística. Usted necesita que su historia sea contada a través de ventas al por mayor, al detalle y en la mesa de la cocina. + shops_signup_detail: Aquí está el detalle. + orders_fees: Tarifas... + orders_edit_title: Carrito de compras + orders_edit_headline: Su carrito de compras + orders_edit_time: Orden lista para + orders_edit_continue: Continuar comprando + orders_edit_checkout: Revisar + orders_form_empty_cart: "Vaciar carrito" + orders_form_subtotal: Subtotal de productos + orders_form_admin: Administración y manejo + orders_form_total: Total + orders_oc_expired_headline: Las ordenes están cerradas para este ciclo + orders_oc_expired_text: "Lo sentimos, ¡las ordenes para este ciclo cerraron hace %{time}! Contacte a su centro de acopio directamente para ver si pueden aceptar ordenes tardías." + orders_oc_expired_text_others_html: "Lo sentimos, ¡las ordenes para este ciclo cerraron hace %{time}! Contacte su centro de acopio directamente para ver si pueden aceptar ordenes tardías %{link}." + orders_oc_expired_text_link: "o vea los otros ciclos de ordenes disponibles en este centro de acopio" + orders_oc_expired_email: "Correo electrónico:" + orders_oc_expired_phone: "Teléfono:" + orders_show_title: Confirmación de orden + orders_show_time: Orden lista en + orders_show_number: Confirmación de orden + products_cart_distributor_choice: "Distribuidor para su orden:" + products_cart_distributor_change: "Su distribuidor para esta orden se cambiará a %{name} si agrega este producto al carrito." + products_cart_distributor_is: "Su distribuidor para esta orden es %{name}." + products_distributor_error: "Complete su orden en %{link} antes de comprar con otro distribuidor." + products_oc: "Ciclo de orden para su orden:" + products_oc_change: "Su ciclo de orden para esta orden será cambiado a %{name} si agrega este producto al carrito." + products_oc_is: "Su ciclo de orden para esta orden es %{name}." + products_oc_error: "Complete esta orden de %{link} antes de comprar en un ciclo de orden diferente." + products_oc_current: "su ciclo de orden actual" + products_quantity: Cantidad + products_max_quantity: Cantidad máxima + products_distributor: Distribuidor + products_distributor_info: Cuando seleccione un distribuidor para su orden, su dirección y tiempo de recolección se mostrarán aquí. + password: Contraseña + remember_me: Recordarme + are_you_sure: "¿Está seguro?" + orders_open: Ordenes abiertas + closing: "Cerrando " + going_back_to_home_page: "Le estamos llevando de vuelta a la página de inicio" + creating: Creando + updating: Actualizando + failed_to_create_enterprise: "Error al crear su empresa." + failed_to_create_enterprise_unknown: "Error al crear su empresa.\nAsegúrese que haya llenado todos los campos de forma completa." + failed_to_update_enterprise_unknown: "Error al actualizar su empresa.\nAsegurese que haya llenado todos los campos de forma completa." + order_not_saved_yet: "Su orden aun no ha sido guardada. ¡Denos unos cuantos segundos para terminar!" + filter_by: "Fitrar por" + hide_filters: "Esconder filtros" + one_filter_applied: "1 filtro aplicado" + x_filters_applied: "filtros aplicados" + submitting_order: "Enviando su orden: espere" + confirm_hub_change: "¿Está seguro? Esto cambiará su centro de acopio seleccionado y eliminará cualquier artículo en su carrito de compras." + confirm_oc_change: "¿Está seguro? Esto cambiará su ciclo de orden seleccionado y eliminará cualquier artículo en su carrito de compras." + location_placeholder: "Escriba una ubicación..." + error_required: "no puede estar vacío" + error_number: "debe ser un número" + error_email: "debe ser una dirección de correo electrónico" + item_handling_fees: "Tarifa de manejo de artículo (incluída en el total de artículos)" + january: "Enero" + february: "Febrero" + march: "Marzo" + april: "Abril" + may: "Mayo" + june: "Junio" + july: "Julio" + august: "Agosto" + september: "Setiembre" + october: "Octubre" + november: "Noviembre" + december: "Diciembre" + email_not_found: "Dirección de correo electrónico no encontrada" + email_required: "Debe brindar una dirección de correo electrónico" + logging_in: "Espere un momento, le vamos a iniciar una sesión" + signup_email: "Su correo electrónico" + choose_password: "Escoja una contraseña" + confirm_password: "Confirmar contraseña" + action_signup: "Registrarse ahora" + welcome_to_ofn: "¡Bienvenido a Open Food Network!" + signup_or_login: "Empiece registrándose (o iniciando sesión)" + have_an_account: "¿Ya tiene una cuenta?" + action_login: "Inicie sesión ahora." + forgot_password: "¿Olvidó la contraseña?" + password_reset_sent: "¡Le enviamos un correo electrónico con instrucciones para restaurar la contraseña!" + reset_password: "Restaurar contraseña" + who_is_managing_enterprise: "¿Quién es responsable de administrar %{enterprise}?" + enterprise_contact: "Contacto principal" + enterprise_contact_required: "Debe ingresar un contacto principal." + enterprise_email: "Dirección de correo electrónico" + enterprise_email_required: "Necesita ingresar una dirección de correo electrónico." + enterprise_phone: "Número de teléfono" + back: "Atrás" + continue: "Continuar" + limit_reached_headline: "¡Ay no!" + limit_reached_message: "¡Ha alcanzado el límite!" + limit_reached_text: "Ha alcanzado el límite del número de empresas de las que puede ser dueño en el" + limit_reached_action: "Regresar a la página de inicio" + select_promo_image: "Paso 3. Seleccione una imagen promocional" + promo_image_tip: "Consejo: Se muestra como una pancarta, el tamaño preferido es 1200×260px" + promo_image_label: "Escoja una imagen promocional" + action_or: "Ó" + promo_image_drag: "Arrastre y suelte su imagen promocional aquí" + review_promo_image: "Paso 4. Revise su pancarta promocional" + review_promo_image_tip: "Consejo: para mejores resultados, su imagen promocional debería llenar el espacio disponible" + promo_image_placeholder: "El logo aparecerá aquí para ser revisado cuando se haya subido" + uploading: "Subiendo..." + select_logo: "Paso 1. Seleccione una imagen de logotipo" + logo_tip: "Consejo: Imágenes cuadradas funcionan mejor, de preferencia por lo menos de 300×300px" + logo_label: "Escoja una imagen de logo" + logo_drag: "Arrastre y suelte su logo aquí" + review_logo: "Paso 2. Revise su logo" + review_logo_tip: "Consejo: para mejores resultados, su logo debería llenar el espacio disponible" + logo_placeholder: "El logo aparecerá aquí para ser revisado cuando se haya subido" + enterprise_about_headline: "¡Buena esa!" + enterprise_about_message: "Ahora vamos a profundizar en los detalles acerca de" + enterprise_success: "¡Éxito! %{enterprise} se agregó a la Open Food Network " + enterprise_registration_exit_message: "Si sale de este asistente en cualquier etapa, debe hacer clic en el enlace de confirmación el el correo electrónico que recibió. Esto lo llevará a la interfaz de administración en la que puede continuar configurando su perfil." + enterprise_description: "Descripción corta" + enterprise_description_placeholder: "Una oración corta que describa su empresa" + enterprise_long_desc: "Descripción larga" + enterprise_long_desc_placeholder: "Esta es su oportunidad de contar la historia de su empresa - ¿qué la hace diferente y asombrosa? Le sugerimos mantener la descripción en menos de 600 caracteres o 150 palabras." + enterprise_long_desc_length: "%{num} caracteres / recomentdamos hasta 600" + enterprise_abn: "ABN" + enterprise_abn_placeholder: "eg. 99 123 456 789" + enterprise_acn: "ACN" + enterprise_acn_placeholder: "eg. 123 456 789" + enterprise_tax_required: "Necesita seleccionar algo." + enterprise_final_step: "¡Paso final!" + enterprise_social_text: "¿Cómo puede la gente encontrar a %{enterprise} en línea?" + website: "Sitio web" + website_placeholder: "eg. openfoodnetwork.org.au" + facebook: "Facebook" + facebook_placeholder: "eg. www.facebook.com/NombreDePáginaAquí" + linkedin: "LinkedIn" + linkedin_placeholder: "eg. www.linkedin.com/SuNombreAquí" + twitter: "Twitter" + twitter_placeholder: "eg. @usuario_de_twitter" + instagram: "Instagram" + instagram_placeholder: "eg. @usuario_de_instagram" + registration_greeting: "¡Hola!" + registration_intro: "Ahora puede crear un perfil para su Productor o Centro de acopio" + registration_action: "¡Empecemos!" + registration_checklist: "Necesitará" + registration_time: "5-10 minutos" + registration_enterprise_address: "Dirección de la empresa" + registration_contact_details: "Detalles de contacto primario" + registration_logo: "Su imagen de logo" + registration_promo_image: "Imagen en formato apaisado para su perfil" + registration_about_us: "Texto 'Acerca de nosotros'" + registration_outcome_headline: "¿Qué recibo?" + registration_outcome1_html: "Su perfil le ayuda a las personas a encontrarle y contactarle en la Open Food Network." + registration_outcome2: "Use este espacio para contar la historia de su empresa, para ayudar a dirigir conexiones a su presencia social y en línea." + registration_outcome3: "También es un primer paso para negociar en la Open Food Network, o abrir una tienda en línea." + registration_finished_headline: "¡Terminado!" + registration_finished_thanks: "Gracias por llenar los detalles de %{enterprise}." + registration_finished_login: "Puede cambiar o actualizar su negocio en cualquier etapa iniciando sesión en Open Food Network y yendo a Admin." + registration_finished_activate: "Activar %{enterprise}." + registration_finished_activate_instruction_html: "Hemos enviado un correo de confirmación a %{email} si no ha sido activado antes.
Siga las instrucciones ahí para hacer su empresa visible en Open Food Network." + registration_finished_action: "Página de inicio de Open Food Network" + registration_type_headline: "¡Último paso para agregar %{enterprise}!" + registration_type_question: "¿Es un productor?" + registration_type_producer: "Sí, soy un productor" + registration_type_no_producer: "No, no soy un produtor" + registration_type_error: "Escoja una. ¿Es un productor?" + registration_type_producer_help: "Los productores pueden crear cosas deliciosas para comer o beber. Usted es un productor si cultiva, mejora, fermenta, ordeña o moldea." + registration_type_no_producer_help: "Si usted no es un productor, probablemente conozca a alguien que venda y distribuya comida. También podría convertirse en un centro de acopio, un grupo de compra, un revendedor, mayorista u otro." + create_profile: "Crear Perfil" + registration_images_headline: "¡Gracias!" + registration_images_description: "¡Suba algunas buenas fotografías así el perfil se verá muy bien! :)" + registration_detail_headline: "Empecemos..." + registration_detail_enterprise: "¡Ups! Primer necesitamos saber un poco más acerca de su empresa:" + registration_detail_producer: "¡Ups! Primero necesitamos saber un poco más acerca de su granja:" + registration_detail_name_enterprise: "Nombre de la empresa:" + registration_detail_name_producer: "Nombre de la granja:" + registration_detail_name_placeholder: "e.g. La asombrosa granja de Carlos" + registration_detail_name_error: "Por favor elija un nombre único para su empresa" + registration_detail_address1: "Línea de dirección 1:" + registration_detail_address1_placeholder: "e.g. 123 Cranberry Drive" + registration_detail_address1_error: "Ingrese una dirección" + registration_detail_address2: "Línea de dirección 2:" + registration_detail_suburb: "Barrio:" + registration_detail_suburb_placeholder: "e.g. Northcote" + registration_detail_suburb_error: "Ingrese el barrio" + registration_detail_postcode: "Código postal:" + registration_detail_postcode_placeholder: "e.g. 3070" + registration_detail_postcode_error: "Código postal requerido" + registration_detail_state: "Estado:" + registration_detail_state_error: "Estado requerido" + registration_detail_country: "País:" + registration_detail_country_error: "Seleccione un país" + fees: "Tarifas" + item_cost: "Costo del artículo" + bulk: "Agrupar" + shop_variant_quantity_min: "mínimo" + shop_variant_quantity_max: "máximo" + contact: "Contacto" + follow: "Seguir" + shop_for_products_html: "Comprar productos de %{enterprise} en:" + change_shop: "Cambiar de tienda:" + shop_at: "Comprar en:" + price_breakdown: "Desglose de precios completo" + admin_fee: "Tarifa de administración" + sales_fee: "Tarifa de ventas" + packing_fee: "Tarifa de empaquetado" + transport_fee: "Tarifa de transporte" + fundraising_fee: "Tarifa para recaudación de fondos" + price_graph: "Gráfico de precios" + included_tax: "Impuesto incluido" + remove_tax: "Eliminar impuesto" diff --git a/config/locales/fr.yml b/config/locales/fr.yml new file mode 100644 index 0000000000..c4d0939e18 --- /dev/null +++ b/config/locales/fr.yml @@ -0,0 +1,601 @@ +fr: + devise: + failure: + invalid: | + Email / mot de passe incorrect. + Créez votre compte ou réinitialisez votre mot de passe. + enterprise_confirmations: + enterprise: + confirmed: Merci, votre adresse email est confirmée. + not_confirmed: Votre adresse email n'a pas pu être confirmée. Peut-être l'aviez-vous déjà confirmée? + confirmation_sent: "Un mail de confirmation a été envoyé!" + confirmation_not_sent: "Impossible d'envoyer le mail de confirmation." + home: "OFN" + title: Open Food France + welcome_to: 'Bienvenue sur ' + search_by_name: Recherche pas nom ou région... + producers: Producteurs et hubs + producers_join: Les producteurs et autres hubs basés en France sont invités à rejoindre Open Food France. + charges_sales_tax: Soumis à la TVA? + print_invoice: "Imprimer la facture" + send_invoice: "Envoyer la facture" + resend_confirmation: "Renvoyer la confirmation" + view_order: "Voir la commande" + edit_order: "Editer la commande" + ship_order: "Envoyer la commande" + cancel_order: "Annuler la commande" + confirm_send_invoice: "La facture de cette commande va être transmise au client. Etes-vous sûr de vouloir continuer ?" + confirm_resend_order_confirmation: "Etes-vous sûr de vouloir renvoyer le mail de confirmation de commande ?" + must_have_valid_business_number: "%{enterprise_name} doit avoir un SIRET valide avant que les factures puissent être envoyées." + invoice: "Facture" + percentage_of_sales: "%{percentage} des ventes" + percentage_of_turnover: "Pourcentage du chiffre d'affaire" + monthly_cap_excl_tax: "Cumul mensuel (sans TVA)" + capped_at_cap: "plafonné à %{cap}" + per_month: "par mois" + free: "gratuit" + plus_tax: "plus TVA" + total_monthly_bill_incl_tax: "Facture mensuelle totale (taxes incluses)" + sort_order_cycles_on_shopfront_by: "Trier les cycles de vente par" + invoice_column_item: "Produit" + invoice_column_qty: "Qté" + invoice_column_tax: "TVA" + invoice_column_price: "Prix" + logo: "Logo (640x130)" + logo_mobile: "Logo smartphone (75x26)" + logo_mobile_svg: "Logo smartphone (SVG)" + home_hero: "Bannière" + home_show_stats: "Afficher statistiques" + footer_logo: "Logo (220x76)" + footer_facebook_url: "Facebook URL" + footer_twitter_url: "Twitter URL" + footer_instagram_url: "Instagram URL" + footer_linkedin_url: "LinkedIn URL" + footer_googleplus_url: "Google Plus URL" + footer_pinterest_url: "Pinterest URL" + footer_email: "Email" + footer_links_md: "Liens" + footer_about_url: "A propos URL" + footer_tos_url: "Conditions d'utilisation URL" + name: Nom + first_name: Prénom + last_name: Nom de famille + email: Email + phone: Téléphone + next: Suivant + address: Adresse + address2: Adresse (suite) + city: Ville + state: Région + postcode: Code postal + country: Pays + unauthorized: Non authorisé + terms_of_service: "Conditions d'utilisation" + on_demand: A volonté + none: Aucun + alert_selling_on_ofn: "Vous souhaitez proposer vos produits sur Open Food France?" + alert_start_here: "Démarrez ici" + label_shops: "Boutiques" + label_map: "Carte" + label_producers: "Producteurs" + label_groups: "Groupes" + label_about: "A propos" + label_shopping: "Achats" + label_login: "Login" + label_logout: "Logout" + label_signup: "Inscription" + label_administration: "Administration" + label_admin: "Admin" + label_account: "Compte" + label_more: "Afficher" + label_less: "Masquer" + items: "produits" + cart_headline: "Votre panier" + total: "Total" + checkout: "Régler la commande" + cart_updating: "Mettre à jour le panier" + cart_empty: "Panier vide" + cart_edit: "Modifier votre panier" + card_number: Numéro de carte + card_securitycode: "Cryptogramme visuel" + card_expiry_date: Date d'expiration + ofn_cart_headline: "Panier actuel pour:" + ofn_cart_distributor: "Distributeur:" + ofn_cart_oc: "Cycle de vente:" + ofn_cart_from: "De:" + ofn_cart_to: "Pour:" + ofn_cart_product: "Produit:" + ofn_cart_quantitiy: "Quantité:" + ofn_cart_send: "Acheter" + ie_warning_headline: "Votre navigateur n'est pas à jour :-(" + ie_warning_text: "Pour une expérience optimale sur Open Food France, nous vous recommandons fortement de mettre à jour votre navigateur:" + ie_warning_chrome: Télécharger Chrome + ie_warning_firefox: Télécharger Firefox + ie_warning_ie: Mettre à jour Internet Explorer + ie_warning_other: "Impossible de mettre à jour votre navigateur? Essayez Open Food France sur votre smartphone :-)" + footer_global_headline: "OFN Global" + footer_global_home: "Accueil" + footer_global_news: "News" + footer_global_about: "A propos" + footer_global_contact: "Contact" + footer_sites_headline: "Sites OFN" + footer_sites_developer: "Developpeur" + footer_sites_community: "Communauté" + footer_sites_userguide: "Guide utilisateur" + footer_secure: "Fiable et sécurisé." + footer_secure_text: "Open Food France utilise un certificat type SSL (2048 bit RSA) pour garantir la confidentialité de votre commandes et données bancaires. Nos serveurs ne conservent pas vos données bancaires et les paiements sont effectués conformément aux normes de sécurité PCI." + footer_contact_headline: "Restez en contact" + footer_contact_email: "Nous écrire" + footer_nav_headline: "Naviguer" + footer_join_headline: "Nous rejoindre" + footer_join_producers: "Inscription producteurs" + footer_join_hubs: "Inscription hubs" + footer_join_groups: "Inscription groupes" + footer_join_partners: "Partenaires" + footer_legal_call: "Lire nos" + footer_legal_tos: "Termes et conditions" + footer_legal_visit: "Nous trouver sur" + footer_legal_text_html: "Open Food Network est une plateforme logicielle open source, libre et gratuite. Nos données sont protégées sous licence %{content_license} et notre code sous %{code_license}." + home_shop: Faire mes courses + brandstory_headline: "Des aliments porteurs de sens." + brandstory_intro: "Parfois, le meilleur moyen de réparer le système, c'est d'en inventer un autre..." + brandstory_part1: "Tout commence dans le sol. Avec ces paysans, agriculteurs, producteurs, engagés pour une agriculture durable et régénératrice, et désireux de partager leur histoire et leur passion avec fierté. Avec ces distributeurs souhaitant reconnecter les individus à leurs aliments et aux gens qui les produisent, soutenir les prises de conscience, dans une démarche de transparence, d'honnêteté, en assurant une juste rémunération des producteurs. Avec ces acheteurs qui croient que de meilleures décisions d'achats peuvent véritablement changer le monde." + brandstory_part2: "Nous avons besoin d'un outil pour rendre tout ça réel. Un moyen de redonner le pouvoir à ceux qui cultivent, vendent et achètent la nourriture. Un moyen de raconter les histoires, de gérer la logistique. Un moyen de transformer chaque jour les transactions en actions porteuses de changement." + brandstory_part3: "C'est pour cela que nous construisons cette plateforme, ce \"marché en ligne\", afin de rééquilibrer les échanges et redistribuer le pouvoir. Elle est transparente, pour assurer des relations équitables et favoriser les prises de conscience. Elle est open source, donc possédée par tout le monde. Elle se déploie aux échelles régionales et nationales, et des gens lancent de multiples versions à travers le monde." + brandstory_part4: "Elle fonctionne partout. Elle change tout." + brandstory_part5_strong: "Cette plateforme s'appelle Open Food Network." + brandstory_part6: "Nous aimons notre nourriture. Maintenant nous pouvons aussi aimer notre système alimentaire." + system_headline: "Comment ça marche?" + system_step1: "1. Recherche" + system_step1_text: "Recherchez des produits locaux, de saison, parmi nos multiples boutiques indépendantes. Filtrez par localisation ou catégorie de produits, livraison en point retrait ou à domicile." + system_step2: "2. Achat" + system_step2_text: "Transformez vos achats en choisissant des produits locaux et abordables, proposés par les divers producteurs et hubs. Découvrez les histoires et les personnes qui se cachent derrière les produits!" + system_step3: "3. Retrait / Livraison" + system_step3_text: "Réceptionnez vos produits à domicile, ou rendez vous chez votre producteur ou hub pour rencontrer les gens qui se cachent derrière les produits. Au delà de la bio-diversité, nous cultivons l'éco-diversité: vivez des expériences d'achat de nourriture uniques et humaines." + cta_headline: "Des achats qui rendent le monde un peu meilleur." + cta_label: "Je vote avec mes achats" + stats_headline: "Nous créons un nouveau système alimentaire." + stats_producers: "agriculteurs et producteurs" + stats_shops: "boutiques" + stats_shoppers: "acheteurs" + stats_orders: "commandes" + checkout_title: Paiement + checkout_now: Régler la commande + checkout_order_ready: Commande prête pour + checkout_hide: Masquer + checkout_expand: Afficher + checkout_headline: "Ok, prêt pour le paiement?" + checkout_as_guest: "Régler en mode invité" + checkout_details: "Vos informations" + checkout_billing: "Informations de facturation" + checkout_shipping: Informations de livraison + checkout_method_free: Gratuit + checkout_address_same: Adresse de livraison identique à l'adresse de facturation? + checkout_ready_for: "Prêt pour:" + checkout_instructions: "Commentaires ou demandes spécifiques?" + checkout_payment: Paiement + checkout_send: Passer la commande + checkout_your_order: Votre commande + checkout_cart_total: Panier total + checkout_shipping_price: Livraison + checkout_total_price: Total + checkout_back_to_cart: "Retour au Panier" + order_paid: RÉGLÉ + order_not_paid: NON RÉGLÉ + order_total: Total commande + order_payment: "Payer via:" + order_billing_address: Adresse de facturation + order_delivery_on: Livraison prévue + order_delivery_address: Adresse de livraison + order_special_instructions: "Vos commentaires:" + order_pickup_time: Disponible pour retrait + order_pickup_instructions: Instructions de retrait + order_produce: Produit + order_total_price: Total + order_includes_tax: (TVA inclue) + order_payment_paypal_successful: Votre paiement via PayPal a été réalisé avec succès. + order_hub_info: Hub Info + products: "Produits" + products_in: "dans %{oc}" + products_at: "à %{distributor}" + products_elsewhere: "Produits trouvés ailleurs" + email_welcome: "Bienvenue" + email_confirmed: "Veuillez confirmer votre adresse email." + email_registered: "fait maintenant partie de" + email_userguide_html: "Le Guide Utilisateur expliquant comment mettre en place son profil producteur ou son hub est accessible ici: %{link}" + email_admin_html: "Vous pouvez gérer votre compte en vous connectant ici %{link} ou en cliquant sur la roue en haut à droite de la page d'accueil et en sélectionnant Administration." + email_community_html: "Nous avons aussi un forum de discussion en ligne (en anglais) pour échanger avec la communauté sur des questions liées au logiciel OFN et aux défis de la gestion d'un food hub. Nous vous invitons à y participer. Nous sommes en constante évolution et vos contributions à ce forum vont façonner les prochaines étapes. %{link}" + email_help: "En cas de difficulté, consultez notre FAQ, parcourez le forum (en anglais) ou postez un message dans la section 'Support' et quelqu'un viendra vous aider!" + email_confirmation_greeting: "Bonjour %{contact}!" + email_confirmation_profile_created: "Le profil pour %{name} a été créé avec succès! Pour activer votre Profil nous devons vérifier cette adresse email." + email_confirmation_click_link: "Veuillez cliquer sur le lien ci-dessous pour confirmer votre email et continuer la configuration de votre compte." + email_confirmation_link_label: "Confirmer cette adresse email »" + email_confirmation_help_html: "Après confirmation de votre email, vous pourrez accéder au compte d'administration de cette entreprise. Voir %{link} pour en savoir plus à propos de %{sitename} et commencer à utiliser votre profil et/ou boutique en ligne." + email_confirmation_userguide: "Guide Utilisateur" + email_social: "Nous suivre:" + email_contact: "Nous écrire:" + email_signoff: "Cordialement," + email_signature: "L'équipe %{sitename}" + email_confirm_customer_greeting: "Bonjour %{name}," + email_confirm_customer_intro_html: "Merci d'avoir passé commande chez %{distributor}!" + email_confirm_customer_number_html: "Confirmation de commande #%{number}" + email_confirm_customer_details_html: "Détails de votre commande chez %{distributor}:" + email_confirm_customer_signoff: "Cordialement," + email_confirm_shop_greeting: "Bonjour %{name}," + email_confirm_shop_order_html: "Bravo! Vous avez reçu une nouvelle commande pour %{distributor}!" + email_confirm_shop_number_html: "Confirmation de commande #%{number}" + email_order_summary_item: "Produit" + email_order_summary_quantity: "Qté" + email_order_summary_price: "Prix" + email_order_summary_subtotal: "Sous-total:" + email_order_summary_total: "Total:" + email_payment_paid: RÉGLÉ + email_payment_not_paid: NON RÉGLÉ + email_payment_summary: Résumé du paiement + email_payment_method: "Payer via :" + email_shipping_delivery_details: Détails de livraison + email_shipping_delivery_time: "Livré le:" + email_shipping_delivery_address: "Adresse de livraison:" + email_shipping_collection_details: Détails de retrait + email_shipping_collection_time: "Prêt pour retrait:" + email_shipping_collection_instructions: "Instructions de retrait:" + email_special_instructions: "Vos commentaires:" + email_signup_greeting: Bonjour! + email_signup_welcome: "Bienvenue sur %{sitename}!" + email_signup_login: Votre login + email_signup_email: Votre email de connexion est + email_signup_shop_html: "Vous pouvez maintenant commencer vos achats sur %{link}." + email_signup_text: "Merci d'avoir rejoint le réseau. Si vous êtes un client, nous sommes impatients de vous faire découvrir de nombreux agriculteurs fantastiques, de merveilleux hubs de distribution et des plats délicieux! Si vous êtes un producteur ou autre entreprise alimentaire, nous sommes ravis de vous compter parmi les membres du réseau." + email_signup_help_html: "Vos questions et suggestions sont les bienvenues; vous pouvez utiliser le bouton Envoyer un commentaire sur le site ou envoyez-nous un email à" + shopping_oc_closed: La boutique est actuellement fermée + shopping_oc_closed_description: "Veuillez attendre l'ouverture du prochain cycle de vente (ou contactez-nous directement pour voir si nous pouvez accépter une commande tardive)" + shopping_oc_last_closed: "Le dernier cycle de vente s'est terminé il y a %{distance_of_time}" + shopping_oc_next_open: "Le prochain cycle de vente ouvrira dans %{distance_of_time}" + shopping_tabs_about: "A propos de %{distributor}" + shopping_tabs_contact: "Contact" + shopping_contact_address: "Adresse" + shopping_contact_web: "Contact" + shopping_contact_social: "Suivre" + shopping_groups_part_of: "fait partie de:" + shopping_producers_of_hub: "Les producteurs de %{hub}:" + enterprises_next_closing: "Prochaine commande à passer avant" + enterprises_ready_for: "Prêt pour" + enterprises_choose: "Choisissez votre date de commande:" + hubs_buy: "Acheter:" + hubs_shopping_here: "Achats en cours" + hubs_orders_closed: "Boutique fermée" + hubs_profile_only: "Fiche profil" + hubs_delivery_options: "Options de livraison" + hubs_pickup: "Retrait" + hubs_delivery: "Livraison" + hubs_producers: "Nos producteurs" + hubs_filter_by: "Filtrer par" + hubs_filter_type: "Catégorie" + hubs_filter_delivery: "Livraison" + hubs_matches: "Vous voulez dire?" + hubs_intro: Passez commande près de chez vous + hubs_distance: Le plus près de + hubs_distance_filter: "Afficher les boutiques près de %{location}" + products_clear_all: Vider + products_showing: "Afficher:" + products_with: avec + products_search: "Recherche par produit ou producteur" + products_loading: "Produits en cours de chargement..." + products_updating_cart: "Actualisation du panier..." + products_cart_empty: "Panier vide" + products_edit_cart: "Modifier votre panier" + products_from: de + search_no_results_html: "Désolé, aucun résultat pour %{query}. Autre recherche?" + components_profiles_popover: "Certaines entreprises ont juste créé leur profil sur Open Food France mais ne vendent pas via la plateforme. Elles ont peut-être une boutique physique, ou une boutique en ligne sur une autre plateforme." + components_profiles_show: "Afficher aussi les profils" + components_filters_nofilters: "Pas de filtre" + components_filters_clearfilters: "Vider les filtres" + groups_title: Groupes + groups_headline: Groupes / réseaux territoriaux + groups_search: "Recherche par nom ou mot-clé" + groups_no_groups: "Aucun groupe trouvé" + groups_about: "A propos" + groups_producers: "Nos producteurs" + groups_hubs: "Nos hubs" + groups_contact_web: Contact + groups_contact_social: Suivre + groups_contact_address: Adresse + groups_contact_email: Nous écrire + groups_contact_website: Visiter notre site web + groups_contact_facebook: Nous suivre sur Facebook + groups_signup_title: S'inscrire en tant que groupe + groups_signup_headline: Inscription groupe + groups_signup_intro: "Nous sommes une plate-forme très efficace pour le marketing collaboratif, une excellente manière pour vos membres et parties prenantes d'atteindre de nouveaux marchés. Nous sommes à but non lucratif, abordable et simple." + groups_signup_email: Nous écrire + groups_signup_motivation1: Nous transformons les systèmes alimentaires pour remettre de l'équité dans les échanges. + groups_signup_motivation2: C'est pourquoi nous sortons du lit chaque matin. Nous sommes une organisation à but non lucratif, basée sur un code source ouvert. Nous opérons en toute transparence. + groups_signup_motivation3: Vous avez de belles idées, et nous voulons vous aider. Nous partageons nos connaissances, réseaux et ressources. Nous savons que l'isolement ne crée pas le changement, alors coopérons. + groups_signup_motivation4: Nous venons à votre rencontrer. + groups_signup_motivation5: Vous êtes un réseau de circuits de distribution alternatifs, de producteurs, de distributeurs, une administration liée à l'industrie alimentaire ou une autorité locale? + groups_signup_motivation6: Quel que soit votre rôle dans la relocalisation des systèmes alimentaires, nous sommes prêts à vous soutenir. Si vous vous demandez à quoi Open Food Network ressemble / pourrait ressembler dans votre coin du monde, contactez-nous. + groups_signup_motivation7: Nous contribuons à remettre du sens dans les systèmes alimentaires. + groups_signup_motivation8: Vous avez besoin de connecter et d'outiller vos réseaux, nous offrons une plate-forme pour la coopération et l'action. Vous souhaitez de l'engagement. Nous vous aidons à atteindre les acteurs, les parties-prenantes, les secteurs. + groups_signup_motivation9: Vous avez besoin de ressources. Nous mettons à votre service notre expérience. Vous avez besoin de coopération. Nous vous connectons à un large réseau d'acteurs et d'organisations soeurs partout dans le monde. + groups_signup_pricing: Compte groupe + groups_signup_studies: Etudes de cas + groups_signup_contact: Vous voulez discuter? + groups_signup_contact_text: "Prenez contact et découvrez ce qu'Open Food France peut faire pour vous:" + groups_signup_detail: "Plus de précisions." + login_invalid: "Email ou mot de passe erroné" + modal_hubs: "Food Hubs" + modal_hubs_abstract: Nos food hubs sont les points de contact entre vous et les personnes qui produisent votre nourriture! + modal_hubs_content1: Vous pouvez chercher le hub qui vous convient par localisation ou par nom. Certains hubs ont de multiples points de retrait de vos achats, et certains proposent également la livraison à domicile. Chaque food hub est un point de vente et gère de façon indépendante ses opérations et sa logistique - attendez-vous donc à des disparités de fonctionnement entre les hubs. + modal_hubs_content2: Vous pouvez uniquement faire vos courses dans un hub à la fois. + modal_groups: "Groupes / réseaux territoriaux" + modal_groups_content1: Voilà les organisations et les relations inter-hubs qui constituent l'Open Food Network. + modal_groups_content2: Certains groupes sont regroupés pas localisation ou région, d'autres sur des smilitudes non géographiques. + modal_how: "Comment ça marche" + modal_how_shop: Faire vos courses sur Open Food France + modal_how_shop_explained: Recherchez un food hub près de chez vous et commencez vos achats! Vous pouvez afficher plus d'infos sur chaque food hub pour voir le type de produits qu'il propose, et cliquer sur le hub pour commencer vos achats. (Vous ne pouvez faire vos courses que dans un food hub à la fois.) + modal_how_pickup: Frais de retraits, livraison, transport + modal_how_pickup_explained: Certains food hubs livrent à domicile, d'autres vous demandent de venir récupérer vos achats dans un point de retrait. Vous pouvez voir quelle options sont proposées sur la page d'accueil du hub, et sélectionner votre choix au moment de la validation de la commande. La livraison à domicile coûtera souvent plus cher, et les prix diffèrent selon le hub. Chaque food hub est un point de vente et gère de façon indépendante ses opérations et sa logistique - attendez-vous donc à des disparités de fonctionnement entre les hubs. + modal_how_more: En savoir plus + modal_how_more_explained: "Pour en savoir plus sur Open Food France, comment ça marche, et contribuer, allez voir:" + modal_producers: "Producteurs" + modal_producers_explained: "Nos producteurs font pousser et fabriquent tous les délicieux produits que vous pouvez acheter sur Open Food France." + ocs_choice_hub: "Hub:" + ocs_choice_oc: "Cycle de vente:" + ocs_choice_text: "Vous n'avez pas encore sélectionné votre point de retrait." + ocs_closed_headline: La boutique est actuellement fermée + ocs_closed_time: "Le dernier cycle de vente s'est terminé il y a %{time}." + ocs_closed_contact: "Veuillez contacter le hub en direct pour voir s'il accepte des commandes tardives, ou patientez jusqu'à l'ouverture du prochain cycle de vente." + ocs_closed_opens: "Le prochain cycle de vente commence dans %{time}" + ocs_closed_email: "Email: %{email}" + ocs_closed_phone: "Téléphone: %{phone}" + ocs_pickup_time: "Votre commande sera prête pour %{pickup_time}" + ocs_change_date: "Changer la date de retrait" + ocs_change_date_notice: "(Votre panier sera vidé)" + ocs_close_time: "BOUTIQUE FERMÉE" + ocs_when_headline: Quand voulez-vous commander? + ocs_when_text: Les produits s'afficheront quand vous aurez sélectionné une date. + ocs_when_closing: "Fermera" + ocs_when_choose: "Choisir le cycle de vente" + ocs_list: "Afficher la liste" + producers_about: A propos + producers_buy: Acheter + producers_contact: Contact + producers_contact_phone: Appeler + producers_contact_social: Suivre + producers_buy_at_html: "Acheter les produits de %{enterprise} dans les boutiques suivantes:" + producers_filter: Filtrer par + producers_filter_type: Catégorie + producers_title: Producteurs + producers_headline: Trouvez un producteur local + producers_signup_title: S'inscrire en tant que producteur + producers_signup_headline: Des producteurs, indépendants + producers_signup_motivation: Vendez vos produits et racontez vos histoires pour toucher de nouveaux marchés. Gagnez du temps et de l'argent sur la gestion des opérations courantes. Vous pouvez innover sans prendre de risque. Nous nivellons le terrain de jeu pour des échanges plus équitables. + producers_signup_send: Rejoindre le réseau + producers_signup_enterprise: Comptes entreprises + producers_signup_studies: Les histoires de nos producteurs. + producers_signup_cta_headline: Rejoindre le réseau! + producers_signup_cta_action: Rejoindre le réseau + producers_signup_detail: Comment ça marche. + products_item: Produit + products_description: Description + products_variant: Variante + products_availabel: En stock? + products_price: Prix + register_title: S'inscrire + shops_title: Boutiques + shops_headline: Des achats qui transforment. + shops_text: Les aliments poussent selon des cycles naturels, les fermiers récoltent en cycles. Alors ici, nous achetons aussi en cycles. Si un cycle de vente est terminé, attendez le suivant ou demandez des infos au hub ! + shops_signup_title: S'inscrire en tant que hub + shops_signup_headline: Des hubs divers et variés + shops_signup_motivation: Quel que soit votre modèle, vous pouvez vous appuyer sur Open Food France. Si vous voulez le faire évoluer, nous sommes là pour vous aider. Nous agissons selon des principes de non-lucrativité, d'indépendance, et de transparence. Et nous faisons tout notre possible pour répondre à vos besoins et vous accompagner en toute circonstance. + shops_signup_action: Rejoindre le réseau + shops_signup_pricing: Comptes entreprises + shops_signup_stories: Histoires de hubs. + shops_signup_help: Nous sommes là pour vous aider. + shops_signup_help_text: Vous avez besoin de pouvoir travailler de manière efficace. Vous avez besoin de nouveaux acheteurs et de partenaires logistiques. Vous souhaitez que votre histoire soit racontée tout au long du circuit, que l'acheteur final sache qui se trouve derrière les produits. + shops_signup_detail: Comment ça marche. + orders_fees: Frais... + orders_edit_title: Panier + orders_edit_headline: Votre panier + orders_edit_time: Commande prête pour + orders_edit_continue: Poursuivre mes achats + orders_edit_checkout: Régler ma commande + orders_form_empty_cart: "Vider le panier" + orders_form_subtotal: Sous-total + orders_form_admin: Admin et opérations + orders_form_total: Total + orders_oc_expired_headline: Les commandes ne sont plus possibles pour ce cycle de vente. + orders_oc_expired_text: "Désolé, les commandes pour ce cycle de vente ont été clôturées il y a %{time}! Veuillez contacter directement le hub pour voir s'il accepte les commandes tardives." + orders_oc_expired_text_others_html: "Désolé, les commandes pour ce cycle de vente ont été clôturées il y a %{time}! Veuillez contacter directement le hub pour voir s'il accepte les commandes tardives %{link}." + orders_oc_expired_text_link: "ou voir si d'autres cycles de vente sont ouverts pour ce hub" + orders_oc_expired_email: "Email:" + orders_oc_expired_phone: "Téléphone:" + orders_show_title: Confirmation de commande + orders_show_time: Commande prête pour + orders_show_number: Confirmation de commande + products_cart_distributor_choice: "Distributeur pour votre commande:" + products_cart_distributor_change: "Vore distributeur pour cette commande sera dorénavant %{name} si vous ajoutez ce produit à votre panier." + products_cart_distributor_is: "Votre distributeur pour cette commande est %{name}." + products_distributor_error: "Terminez votre commande chez %{link} avant de faire vos courses chez un autre distributeur." + products_oc: "Cycle de vente pour votre commande:" + products_oc_change: "Votre cycle de vente pour cette commande sera dorénavant %{name} si vous ajoutez ce produit à votre panier." + products_oc_is: "Votre cycle de vente pour cette commande est %{name}." + products_oc_error: "Veuillez terminer votre commande pour %{link} avant de faire vos courses pour un autre cycle de vente." + products_oc_current: "votre cycle de vente actuel" + products_quantity: Quantité + products_max_quantity: Quantité max + products_distributor: Distributeur + products_distributor_info: Quand vous choisissez un distributeur pour votre commande, les adresse et date de retrait seront affichées ici. + password: Mot de passe + remember_me: Se souvenir de moi + are_you_sure: "Confirmer?" + orders_open: Boutique ouverte + closing: "Fermeture" + going_back_to_home_page: "Retour à la page d'accueil" + creating: Création + updating: Mettre à jour + failed_to_create_enterprise: "Impossible de créer votre entreprise" + failed_to_create_enterprise_unknown: "Impossible de créer votre entreprise.\nVérifiez que tous les champs sont remplis." + failed_to_update_enterprise_unknown: "Impossible de mettre à jour votre entreprise.\nVérifiez que tous les champs sont remplis." + order_not_saved_yet: "Votre commande n'a pas encore été enregistrée. Attendez quelques secondes!" + filter_by: "Filtrer par" + hide_filters: "Masquer les filtres" + one_filter_applied: "1 filtre appliqué" + x_filters_applied: "filtres appliqués" + submitting_order: "Votre commande est en cours d'envoi: veuillez patienter" + confirm_hub_change: "Confirmer? Cette action modifiera la boutique sélectionnée et tous les articles de votre panier seront effacés." + confirm_oc_change: "Confirmer? Cette action modifiera le cycle de vente sélectionné et tous les articles de votre panier seront effacés." + location_placeholder: "Saisissez une localisation..." + error_required: "Champ obligatoire" + error_number: "saisir un nombre" + error_email: "saisir une adresse email" + item_handling_fees: "Frais logistiques (inclus dans le prix affiché)" + january: "Janvier" + february: "Février" + march: "Mars" + april: "Avril" + may: "Mai" + june: "Juin" + july: "Juillet" + august: "Août" + september: "Septembre" + october: "Octobre" + november: "Novembre" + december: "Décembre" + email_not_found: "Adresse email non trouvée" + email_required: "Vous devez saisir une adresse email" + logging_in: "Veuillez patienter, connexion en cours" + signup_email: "Votre email" + choose_password: "Choisissez un mot de passe" + confirm_password: "Confirmez votre mot de passe" + action_signup: "S'inscrire" + welcome_to_ofn: "Bienvenue sur Open Food France" + signup_or_login: "Commencez par vous inscrire (ou connexion)" + have_an_account: "Déjà inscrit?" + action_login: "Se connecter." + forgot_password: "Mot de passe oublié?" + password_reset_sent: "Un email contenant les instructions pour changer votre mot de passe a été envoyé!" + reset_password: "Changer de mot de passe" + who_is_managing_enterprise: "Qui gère %{enterprise}?" + enterprise_contact: "Personne référente" + enterprise_contact_required: "Vous devez saisir une personne référente" + enterprise_email: "Adresse email" + enterprise_email_required: "Veuillez saisir une adresse email valide." + enterprise_phone: "Numéro de téléphone" + back: "Retour" + continue: "Suivant" + limit_reached_headline: "Oh non!" + limit_reached_message: "Vous avez atteint la limite!" + limit_reached_text: "Vous avez atteint la limite du nombre d'entreprises que vous êtes autorisés à gérer sur" + limit_reached_action: "Retour sur la page d'accueil" + select_promo_image: "Etape 3. Sélectionnez une image promotionnelle" + promo_image_tip: "Conseil: affichée en format bannière, taille optimale 1200×260px" + promo_image_label: "Choisissez une image promotionnelle" + action_or: "OU" + promo_image_drag: "Glissez déplacez votre image promotionnelle ici" + review_promo_image: "Etape 4. Validez votre bannière promotionnelle" + review_promo_image_tip: "Conseil: pour un résultat optimal, votre image promotionnelle doit être adaptée à l'espace disponible" + promo_image_placeholder: "Votre logo apparaîtra ici pour validation une fois uploadé" + uploading: "Upload en cours..." + select_logo: "Etape 1. Insérez votre logo" + logo_tip: "Conseil: utilisez un format d'image carré de préférence, min 300×300px" + logo_label: "Insérez votre logo" + logo_drag: "Glissez déplacez votre logo ici" + review_logo: "Etape 2: Validez votre logo" + review_logo_tip: "Conseil: pour un résultat optimal, votre logo doit être adapté à l'espace disponible" + logo_placeholder: "Votre logo apparaîtra ici pour validation une fois uploadé" + enterprise_about_headline: "Bien joué!" + enterprise_about_message: "A présent, allons un peu plus dans les détails concernant" + enterprise_success: "Opération réussie! %{enterprise} a été ajoutée à Open Food France" + enterprise_registration_exit_message: "Si vous quittez cet assistant en cours de saisie, vous devrez cliquer sur le lien de confirmation figurant dans l'email que vous avez reçu. Ce lien vous renverra vers votre interface administrateur où vous pourrez poursuivre la configuration de votre profil." + enterprise_description: "Description courte" + enterprise_description_placeholder: "Une phrase pour décrire votre organisation" + enterprise_long_desc: "Description longue" + enterprise_long_desc_placeholder: "Vous pouvez ici raconter l'histoire de votre organisation - votre projet, les valeurs que vous défendez. Nous vous conseillons de ne pas dépasser 600 caractères ou 150 mots." + enterprise_long_desc_length: "%{num} caractères / inférieur à 600 recommandé" + enterprise_abn: "SIRET" + enterprise_abn_placeholder: "ex: 404 833 048 00022" + enterprise_acn: "n° TVA intracommunautaire" + enterprise_acn_placeholder: "ex: 404 833 048" + enterprise_tax_required: "Merci de choisir." + enterprise_final_step: "Dernière étape!" + enterprise_social_text: "Comment trouver la boutique en ligne %{enterprise}" + website: "Site internet" + website_placeholder: "ex: openfoodfrance.fr" + facebook: "Facebook" + facebook_placeholder: "ex: www.facebook.com/NomDeLaPage" + linkedin: "LinkedIn" + linkedin_placeholder: "ex: www.linkedin.com/VotreNom" + twitter: "Twitter" + twitter_placeholder: "ex: @twitter_pseudo" + instagram: "Instagram" + instagram_placeholder: "ex: @instagram_pseudo" + registration_greeting: "Bonjour!" + registration_intro: "Vous pouvez maintenant créer votre profil \"Producteur\" ou \"Hub\"" + registration_action: "Démarrons!" + registration_checklist: "Vous aurez besoin de" + registration_time: "5-10 minutes" + registration_enterprise_address: "L'adresse de l'entreprise" + registration_contact_details: "Les détails du contact référent" + registration_logo: "Votre logo" + registration_promo_image: "Une image bannière pour votre profil" + registration_about_us: "Un texte \"A propos\"" + registration_outcome_headline: "Qu'est-ce que ça m'apporte?" + registration_outcome1_html: "Votre profil permet aux gens de vous trouver et de vous contacter via Open Food France." + registration_outcome2: "Utilisez cet espace pour raconter l'histoire de votre entreprise, et stimuler les visites vers vos points de présence en ligne." + registration_outcome3: "C'est aussi le premier pas vers la vente via Open Food France, ou l'ouverture de votre boutique en ligne." + registration_finished_headline: "C'est terminé!" + registration_finished_thanks: "Merci d'avoir complété le profil de %{enterprise}" + registration_finished_login: "Vous pouvez modifier ou mettre à jour les détails de votre entreprise à tout moment en vous connectant sur Open Food France, rubrique Admin." + registration_finished_activate: "Activez %{enterprise}." + registration_finished_activate_instruction_html: "Nous avons envoyé un email de confirmation à %{email} s'il n'a pas été activé auparavant.
Veuillez suivre les instructions dans l'email pour rendre votre organisation visible sur Open Food France." + registration_finished_action: "Accueil Open Food France" + registration_type_headline: "Dernière étape pour ajouter %{enterprise}!" + registration_type_question: "Etes-vous un producteur?" + registration_type_producer: "Oui, je suis un producteur" + registration_type_no_producer: "Non, je ne suis pas un producteur" + registration_type_error: "Veuillez faire un choix. Etes vous un producteur?" + registration_type_producer_help: "Un \"producteur\" produit de toutes bonnes choses à manger ou à boire. Vous êtes un producteur si vous les faites pousser, si vous les élevez, si vous les pétrissez, transformez, fermentez, si vous les réduisez en grains, etc." + registration_type_no_producer_help: "Si vous n'êtes pas un producteur, vous êtes probablement un revendeur ou distributeur alimentaire: un \"hub\", une coopérative, un groupement d'achat, un revendeur, un grossiste, ou autre." + create_profile: "Créer votre profil" + registration_images_headline: "Merci!" + registration_images_description: "Ajoutez maintenant de jolies photos pour que votre profil soit attractif! :)" + registration_detail_headline: "Commençons" + registration_detail_enterprise: "Woohoo! Dites-nous déjà quelques mots à propos de votre entreprise:" + registration_detail_producer: "Woohoo! Dites-nous déjà quelques mots à propos de votre ferme:" + registration_detail_name_enterprise: "Nom de l'entreprise:" + registration_detail_name_producer: "Nom de la ferme:" + registration_detail_name_placeholder: "ex: La super ferme de Charlie" + registration_detail_name_error: "Veuillez choisir le nom de votre entreprise" + registration_detail_address1: "Adresse ligne 1" + registration_detail_address1_placeholder: "ex: 123 rue des étangs" + registration_detail_address1_error: "Veuillez saisir une adresse" + registration_detail_address2: "Adresse ligne 2" + registration_detail_suburb: "Département:" + registration_detail_suburb_placeholder: "ex: Vendée" + registration_detail_suburb_error: "Veuillez saisir un Département" + registration_detail_postcode: "Code postal:" + registration_detail_postcode_placeholder: "ex: 44000" + registration_detail_postcode_error: "Veuillez saisir le code postal" + registration_detail_state: "Région:" + registration_detail_state_error: "Veuillez saisir une Région" + registration_detail_country: "Pays:" + registration_detail_country_error: "Veuillez saisir une Pays" + fees: "Frais" + item_cost: "Coût du produit" + bulk: "Vrac" + shop_variant_quantity_min: "min" + shop_variant_quantity_max: "max" + contact: "Contact" + follow: "Suivre" + shop_for_products_html: "Acheter les produits de %{enterprise} dans les boutiques suivantes:" + change_shop: "Changer de boutique pour:" + shop_at: "Acheter maintenant :" + price_breakdown: "Détail du prix:" + admin_fee: "Frais de gestion admin" + sales_fee: "Frais de ventes/marketing" + packing_fee: "Frais de packaging" + transport_fee: "Frais logistiques" + fundraising_fee: "Frais recherche de financement" + price_graph: "Légende détail du prix" + included_tax: "Inclut TVA" + remove_tax: "Retirer TVA" diff --git a/config/locales/nb.yml b/config/locales/nb.yml new file mode 100644 index 0000000000..59fa27ffa8 --- /dev/null +++ b/config/locales/nb.yml @@ -0,0 +1,600 @@ +nb: + devise: + failure: + invalid: | + Ugyldig epost eller passord. + Var du gjest forrige gang? Kanskje du må opprette en konto eller nullstille passordet. + enterprise_confirmations: + enterprise: + confirmed: Takk, din epostadresse er bekreftet. + not_confirmed: Din epostadresse kan ikke bekreftes. Kanskje du allerede har fullført dette steget? + confirmation_sent: "Bekreftelse på epost er sendt!" + confirmation_not_sent: "Kunne ikke sende bekreftelse på epost." + home: "OFN" + title: Open Food Network + welcome_to: 'Velkommen til ' + search_by_name: Søk på navn eller sted... + producers: Norske Produsenter + producers_join: Norske produsenter er nå velkommen til å bli med i Open Food Network. + charges_sales_tax: MVA-pliktig? + print_invoice: "Skriv ut Faktura" + send_invoice: "Send Faktura" + resend_confirmation: "Send Bekreftelse på nytt" + view_order: "Se Bestilling" + edit_order: "Rediger Bestilling" + ship_order: "Send Bestilling" + cancel_order: "Avbryt Bestilling" + confirm_send_invoice: "En faktura for denne bestillingen vil bli sendt til kunden. Er du sikker på at du vil fortsette?" + confirm_resend_order_confirmation: "Er du sikker på at du vil sende ordrebekreftelse via epost på nytt?" + must_have_valid_business_number: "%{enterprise_name} må ha et gyldig ORG nr. før fakturaer kan sendes." + invoice: "Faktura" + percentage_of_sales: "%{percentage} av handel" + percentage_of_turnover: "Prosent av omsetning" + monthly_cap_excl_tax: "månedlig tak (eks. MVA)" + capped_at_cap: "tak på %{cap}" + per_month: "pr. måned" + free: "gratis" + plus_tax: "pluss MVA" + total_monthly_bill_incl_tax: "Total månedlig regning (Inkl. Avgift)" + sort_order_cycles_on_shopfront_by: "Sorter Bestillingsrunder i Nettbutikk etter" + invoice_column_item: "Vare" + invoice_column_qty: "Mengde" + invoice_column_tax: "MVA" + invoice_column_price: "Pris" + logo: "Logo (640x130)" + logo_mobile: "Mobil logo (75x26)" + logo_mobile_svg: "Mobil logo (SVG)" + home_hero: "Heltebilde" + home_show_stats: "Vis statistikk" + footer_logo: "Logo (220x76)" + footer_facebook_url: "Facebook URL" + footer_twitter_url: "Twitter URL" + footer_instagram_url: "Instagram URL" + footer_linkedin_url: "LinkedIn URL" + footer_googleplus_url: "Google Plus URL" + footer_pinterest_url: "Pinterest URL" + footer_email: "Epost" + footer_links_md: "Linker" + footer_about_url: "Om URL" + footer_tos_url: "Vilkår URL" + name: Navn + first_name: Fornavn + last_name: Etternavn + email: Epost + phone: Telefon + next: Neste + address: Adresse + address2: Adresse (forts.) + city: Kommune + state: Fylke + postcode: Postnummer + country: Land + unauthorized: Uautorisert + terms_of_service: "Vilkår" + on_demand: Ved forespørsel + none: Ingen + alert_selling_on_ofn: "Interessert i å selge mat gjennom Open Food Network?" + alert_start_here: "Start her" + label_shops: "Butikker" + label_map: "Kart" + label_producers: "Produsenter" + label_groups: "Grupper" + label_about: "Om" + label_shopping: "Handle" + label_login: "Logg inn" + label_logout: "Logg ut" + label_signup: "Bli medlem" + label_administration: "Administrasjon" + label_admin: "Admin" + label_account: "Konto" + label_more: "Mer" + label_less: "Vis mindre" + items: "varer" + cart_headline: "Din handlekurv" + total: "Sum" + checkout: "Gå til kassen" + cart_updating: "Oppdaterer handlekurv..." + cart_empty: "Handlekurven er tom" + cart_edit: "Rediger handlekurv" + card_number: Kortnummer + card_securitycode: "Sikkerhetskode" + card_expiry_date: Utløpsdato + ofn_cart_headline: "Gjeldende handlekurv for:" + ofn_cart_distributor: "Distributør:" + ofn_cart_oc: "Bestillingsrunde:" + ofn_cart_from: "Fra:" + ofn_cart_to: "Til:" + ofn_cart_product: "Produkt:" + ofn_cart_quantitiy: "Antall:" + ofn_cart_send: "Kjøp" + ie_warning_headline: "Din nettleser er for gammel :-(" + ie_warning_text: "For den beste opplevelsen med Open Food Network anbefaler vi på det sterkeste å oppgradere nettleseren din:" + ie_warning_chrome: Last ned Chrome + ie_warning_firefox: Last ned Firefox + ie_warning_ie: Oppgrader Internet Explorer + ie_warning_other: "Kan ikke oppgradere nettleseren din? Prøv Open Food Network på smart-telefonen din :-)" + footer_global_headline: "OFN Globalt" + footer_global_home: "Hjem" + footer_global_news: "Nyheter" + footer_global_about: "Om" + footer_global_contact: "Kontakt" + footer_sites_headline: "OFN nettsteder" + footer_sites_developer: "Utvikler" + footer_sites_community: "Forum" + footer_sites_userguide: "Brukerhåndbok" + footer_secure: "Sikker og klarert." + footer_secure_text: "Open Food Network bruker SSL-kryptering (2048 bit RSA) overalt for å holde handlingen og betalingen din privat. Våre servere lagrer ikke kortopplysninger og betalinger behandles av PCI-kompatible tjenester." + footer_contact_headline: "Hold kontakten" + footer_contact_email: "Send oss en epost" + footer_nav_headline: "Naviger" + footer_join_headline: "Bli med" + footer_join_producers: "Bli med som Produsent" + footer_join_hubs: "Bli med som Hub" + footer_join_groups: "Bli med som Gruppe" + footer_join_partners: "Samarbeidspartnere" + footer_legal_call: "Les våre" + footer_legal_tos: "Vilkår og betingelser" + footer_legal_visit: "Finn oss på" + footer_legal_text_html: "Open Food Network er en plattform med fri og åpen kildekode. Vårt innhold er lisensiert med %{content_license} og vår kode med %{code_license}." + home_shop: Handle nå + brandstory_headline: "Food, unincorporated." + brandstory_intro: "Noen ganger er det best å fikse systemet ved å starte et nytt..." + brandstory_part1: "Vi begynner fra grunnen. Med bønder og dyrkere klare til å fortelle sine historier, stolt og virkelig. Med distributører klare til å koble mennesker med produkter på en rettferdig og ærlig måte. Med kjøpere som tror på at ukentlige innkjøpsrutiner kan bidra til å forandre verden." + brandstory_part2: "Da trenger vi en ordentlig måte å gjøre det på. En måte som styrker alle som dyrker, selger og kjøper mat. En måte å fortelle alle historiene på, håndtere all logistikk på. En måte å forvandle transaksjon til transformasjon hver dag." + brandstory_part3: "Derfor bygger vi et online marked som endrer spillereglene. Det er transparent, slik at det skaper ekte relasjoner. Det er open-source, slik at det er eid av alle. Metoden er skalérbar for regioner og land, så folk kan starte versjoner over hele verden." + brandstory_part4: "Det fungerer overalt. Det forandrer alt." + brandstory_part5_strong: "Vi kaller det Open Food Network." + brandstory_part6: "Alle er vi glad i mat. Nå kan vi elske vårt matsystem også." + system_headline: "Slik fungerer det." + system_step1: "1. Søk" + system_step1_text: "Søk blant våre mangfoldige, uavhengige butikker for lokal mat i sesong. Søk i nabolag og matkategori, eller om du foretrekker levering eller å hente selv." + system_step2: "2. Handle" + system_step2_text: "Endre dine kjøpevaner med rimelig lokal mat fra mangfoldige produsenter og hubs. Oppdag historiene bak maten din og de som lager den!" + system_step3: "3. Hent / Få det levert" + system_step3_text: "Vent på din levering, eller besøk produsenten eller hub'en for en mer personlig kobling til maten din. Mathandling så mangfoldig slik det var ment fra naturens side." + cta_headline: "Handling som gjør verden til et bedre sted." + cta_label: "Jeg er klar" + stats_headline: "Vi skaper et nytt matsystem." + stats_producers: "matprodusenter" + stats_shops: "matbutikker" + stats_shoppers: "matkunder" + stats_orders: "matbestillinger" + checkout_title: Kasse + checkout_now: Gå til kassen + checkout_order_ready: Bestilling klar for + checkout_hide: Skjul + checkout_expand: Utvid + checkout_headline: "Ok, gå til kassen?" + checkout_as_guest: "Betal som gjest" + checkout_details: "Dine detaljer" + checkout_billing: "Betalingsinformasjon" + checkout_shipping: Leveringsinformasjon + checkout_method_free: Gratis + checkout_address_same: Leveringsadresse samme som fakturaadresse? + checkout_ready_for: "Klar for:" + checkout_instructions: "Kommentarer eller spesielle instruksjoner?" + checkout_payment: Betaling + checkout_send: Send bestilling + checkout_your_order: Din bestilling + checkout_cart_total: Sum handlekurv + checkout_shipping_price: Levering + checkout_total_price: Sum + checkout_back_to_cart: "Tilbake til Handlekurv" + order_paid: BETALT + order_not_paid: IKKE BETALT + order_total: Sum bestilling + order_payment: "Betaler via:" + order_billing_address: Fakturaadresse + order_delivery_on: Levering på + order_delivery_address: Leveringsadresse + order_special_instructions: "Dine kommentarer:" + order_pickup_instructions: Henteinstruksjoner + order_produce: Varer + order_total_price: Sum + order_includes_tax: (inkludert MVA) + order_payment_paypal_successful: Din betaling via PayPal har blitt godkjent. + order_hub_info: Hub info + products: "Produkter" + products_in: "i %{oc}" + products_at: "hos %{distributor}" + products_elsewhere: "Produkter funnet andre steder" + email_welcome: "Velkommen" + email_confirmed: "Takk for at du bekrefter din epostadresse" + email_registered: "er nå en del av" + email_userguide_html: "Brukerhåndboken med detaljert støtte om hvordan man kommer i gang som Produsent eller Hub finnes her: %{link}" + email_admin_html: "Du kan administrere din konto ved å logge inn på %{link} eller ved klikke på tannhjulet øverst til høyre på hjemmesiden og velge Administrasjon." + email_community_html: "Vi har også et online forum for diskusjon relatert til OFN programvaren og de forskjellige utfordringene med å drive et matfirma. Vi oppfordrer deg til å bli med. Vi utvikler oss hele tiden og dine innspill til dette forumet vil forme det som skjer videre. %{link}" + email_help: "Hvis du har problemer, sjekk vår FAQ, utforsk forumet eller skriv et 'Support'-emne og noen vil hjelpe deg!" + email_confirmation_greeting: "Hei, %{contact}!" + email_confirmation_profile_created: "En profil for %{name} har blitt opprettet! For å aktivere din profil må du bekrefte denne epostadressen." + email_confirmation_click_link: "Trykk på linken under for å bekrefte din epost og for å fortsette oppsettet av din profil." + email_confirmation_link_label: "Bekreft denne epostadressen »" + email_confirmation_help_html: "Etter du har bekreftet epostadressen din får du tilgang til din administrasjonskonto for denne bedriften. Se linken %{link} for å finne ut mer om %{sitename}s funksjoner og for å begynne å bruke din profil eller nettbutikk." + email_confirmation_userguide: "Brukerhåndbok" + email_social: "Her finner du oss:" + email_contact: "Send oss en epost:" + email_signoff: "Mvh," + email_signature: "%{sitename} Team" + email_confirm_customer_greeting: "Hei %{name}," + email_confirm_customer_intro_html: "Takk for at du handler hos %{distributor}!" + email_confirm_customer_number_html: "Ordrebekreftelse #%{number}" + email_confirm_customer_details_html: "Her er dine ordredetaljer fra %{distributor}:" + email_confirm_customer_signoff: "Vennlig hilsen," + email_confirm_shop_greeting: "Hei %{name}," + email_confirm_shop_order_html: "Bra jobbet! Du har en ny ordre fra %{distributor}!" + email_confirm_shop_number_html: "Ordrebekreftelse #%{number}" + email_order_summary_item: "Vare" + email_order_summary_quantity: "Stk" + email_order_summary_price: "Pris" + email_order_summary_subtotal: "Delsum:" + email_order_summary_total: "Sum:" + email_payment_paid: BETALT + email_payment_not_paid: IKKE BETALT + email_payment_summary: Betalingssammendrag + email_payment_method: "Betaler via:" + email_shipping_delivery_details: Leveringsdetaljer + email_shipping_delivery_time: "Levering på:" + email_shipping_delivery_address: "Leveringsadresse:" + email_shipping_collection_details: Hentedetaljer + email_shipping_collection_time: "Klar for henting:" + email_shipping_collection_instructions: "Henteinstruksjoner:" + email_special_instructions: "Dine kommentarer:" + email_signup_greeting: Hei! + email_signup_welcome: "Velkommen til %{sitename}!" + email_signup_login: Din innlogging + email_signup_email: Din innloggingsepost er + email_signup_shop_html: "Du kan begynne å handle på nett nå på %{link}." + email_signup_text: "Takk for at du ble med i nettverket. Hvis du er kunde ser vi frem til å vise deg mange fantastiske bønder, flotte mathubs og deilig mat! Hvis du er produsent eller selskap er vi glade for å ha deg som en del av nettverket." + email_signup_help_html: "Vi tar i mot alle dine spørsmål og tilbakemeldinger; du kan bruke Send tilbakemelding-knappen på nettsiden eller sende oss en epost på" + shopping_oc_closed: Stengt for bestilling + shopping_oc_closed_description: "Vent til neste runde åpner (eller kontakt oss direkte for å se om vi tar i mot sene bestillinger)" + shopping_oc_last_closed: "Den siste runden stengte for %{distance_of_time} siden" + shopping_oc_next_open: "Neste runde åpner om %{distance_of_time}" + shopping_tabs_about: "Om %{distributor}" + shopping_tabs_contact: "Kontakt" + shopping_contact_address: "Adresse" + shopping_contact_web: "Kontakt" + shopping_contact_social: "Følg" + shopping_groups_part_of: "er en del av:" + shopping_producers_of_hub: "%{hub}s produsenter:" + enterprises_next_closing: "Neste runde stenger" + enterprises_ready_for: "Klar til" + enterprises_choose: "Velg når du ønsker din bestilling:" + hubs_buy: "Handle:" + hubs_shopping_here: "Handler her" + hubs_orders_closed: "Stengt for bestilling" + hubs_profile_only: "Kun profil" + hubs_delivery_options: "Leveringsvalg" + hubs_pickup: "Henting" + hubs_delivery: "Levering" + hubs_producers: "Våre produsenter" + hubs_filter_by: "Filtrer" + hubs_filter_type: "Type" + hubs_filter_delivery: "Levering" + hubs_matches: "Mente du?" + hubs_intro: Handle lokalt + hubs_distance: Nærmest + hubs_distance_filter: "Vis meg butikker nær %{location}" + products_clear_all: Fjern alt + products_showing: "Viser:" + products_with: med + products_search: "Søk på produkt eller produsent" + products_loading: "Laster produkter..." + products_updating_cart: "Oppdaterer handlekurv..." + products_cart_empty: "Handlekurv tom" + products_edit_cart: "Rediger handlekurv" + products_from: fra + search_no_results_html: "Beklager, ingen treff på %{query}. Prøv på nytt?" + components_profiles_popover: "Profiler har ikke butikkvindu på Open Food Network men kan ha sin egen fysiske butikk eller nettbutikk et annet sted" + components_profiles_show: "Vis profiler" + components_filters_nofilters: "Ingen filter" + components_filters_clearfilters: "Fjern alle filtre" + groups_title: Grupper + groups_headline: Grupper / regioner + groups_search: "Søk på navn eller nøkkelord" + groups_no_groups: "Fant ingen grupper" + groups_about: "Om oss" + groups_producers: "Våre produsenter" + groups_hubs: "Våre hubs" + groups_contact_web: Kontakt + groups_contact_social: Følg + groups_contact_address: Adresse + groups_contact_email: Send oss epost + groups_contact_website: Besøk oss på nett + groups_contact_facebook: Følg oss på Facebook + groups_signup_title: Bli med som gruppe + groups_signup_headline: Bli med som gruppe + groups_signup_intro: "Vi er en fantastisk plattform for samarbeidende markedsføring, den enkleste måten for medlemmer og interessenter å nå nye markeder. Vi er non-profit, rimelig og enkel." + groups_signup_email: Send oss epost + groups_signup_motivation1: Vi forvandler matsystemer rettferdig. + groups_signup_motivation2: Det er grunnen til at vi står opp om morgenen. Vi er en global non-profit, basert på åpen kildekode. We opptrer rettferdig. Du kan alltid stole på oss. + groups_signup_motivation3: Vi vet du har gode ideer og vi ønsker å hjelpe. Vi deler vår kunnskap, våre nettverk og ressurser. Vi vet at isolasjon ikke skaper endring så vi vil samarbeide med deg. + groups_signup_motivation4: Vi møter deg der du er. + groups_signup_motivation5: Du kan være en allianse av mathubs, produsenter eller distributører, i industri eller lokale myndigheter. + groups_signup_motivation6: Uansett din rolle i den lokale matkjeden, vi ønsker å hjelpe. Hvis du lurer på hvordan Open Food Network vil se ut eller hva de gjør i din del av verden, la oss snakke sammen. + groups_signup_motivation7: Vi gir matkjeden mer mening. + groups_signup_motivation8: Du trenger å aktivere og tilrettelegge for dine nettverk, vi tilbyr en plattform for samtale og handling. Du trenger ekte engasjement. Vi hjelper til med å nå alle aktører, alle interessenter, alle sektorer. + groups_signup_motivation9: Du trenger flere ressurser. We kommer med all erfaring vi kan bære. Du trenger samarbeid. Vi kobler deg med et globalt nettverk med likesinnede. + groups_signup_pricing: Gruppekonto + groups_signup_studies: Brukerundersøkelser + groups_signup_contact: Klar for å snakke sammen? + groups_signup_contact_text: "Ta kontakt for å oppdage hva OFN kan gjøre for deg:" + groups_signup_detail: "Her er detaljene." + login_invalid: "Ugyldig epost eller passord" + modal_hubs: "Mathubs" + modal_hubs_abstract: Våre hubs er kontaktpunkt mellom deg og menneskene som lager maten din! + modal_hubs_content1: Du kan søke etter en passende hub på lokasjon eller navn. Noen hubs har flere hentepunkt hvor du kan plukke opp det du har kjøpt, og noen tilbyr også levering. Hver mathub er en butikk med uavhengig drift og logistikk - så det vil være forskjeller mellom huber. + modal_hubs_content2: Du kan kun handle hos en hub om gangen. + modal_groups: "Grupper / Regioner" + modal_groups_content1: Dette er organisasjonene og hub-koblingene som utgjør Open Food Network. + modal_groups_content2: Noen grupper er klynger basert på lokalnivå eller regionnivå, andre har ingen geografiske likheter. + modal_how: "Slik fungerer det" + modal_how_shop: Handle på Open Food Network + modal_how_shop_explained: Søk etter en mathub nær deg for å begynne å handle! Du kan se detaljer for hver mathub for å se hvilke godbiter som finnes, og klikk deg videre for å handle. (Du kan kun handle hos en mathub om gangen). + modal_how_pickup: Henting, levering og transportkostnader + modal_how_pickup_explained: Noen mathubs leverer på døren, mens andre krever at du henter varene du har kjøpt. Du kan se hvilke alternativ som er tilgjengelige på hjemmesiden, og velge hvilket du ønsker på handle- og betalingssidene. Levering koster mer, og prisene varierer fra hub til hub. Hver mathub er en forretning med uavhengig drift og logistikk - så variasjoner mellom hubs er naturlig. + modal_how_more: Finn ut mer + modal_how_more_explained: "Hvis du ønsker å lære mer om Open Food Network, hvordan det fungerer og ta del, sjekk ut:" + modal_producers: "Produsenter" + modal_producers_explained: "Våre produsenter lager all den herlige maten du kan handle på Open Food Network." + ocs_choice_hub: "Hub:" + ocs_choice_oc: "Bestillingsrunde:" + ocs_choice_text: "Du har ennå ikke valgt hvor du vil handle fra." + ocs_closed_headline: Bestillinger er for tiden stengt for denne huben + ocs_closed_time: "Den siste runden stengte for %{time} siden." + ocs_closed_contact: "Vennligst kontakt din hub direkte for å se om de godtar sene bestillinger, eller vent til neste runde åpner." + ocs_closed_opens: "Den neste bestillingsrunden åpner om %{time}" + ocs_closed_email: "Epost: %{email}" + ocs_closed_phone: "Telefon: %{phone}" + ocs_pickup_time: "Din bestilling vil være klar %{pickup_time}" + ocs_change_date: "Endre hentedato" + ocs_change_date_notice: "(Dette nullstiller kurven din)" + ocs_close_time: "BESTILLINGER STENGER" + ocs_when_headline: Når ønsker du bestillingen din? + ocs_when_text: Ingen varer vises før du velger en dato. + ocs_when_closing: "Stenger" + ocs_when_choose: "Velg Bestillingsrunde" + ocs_list: "Listevisning" + producers_about: Om oss + producers_buy: Handle + producers_contact: Kontakt + producers_contact_phone: Ring + producers_contact_social: Følg + producers_buy_at_html: "Handle produkter fra %{enterprise} hos:" + producers_filter: Filtrer på + producers_filter_type: Type + producers_title: Produsenter + producers_headline: Finn lokale produsenter + producers_signup_title: Bli med som produsent + producers_signup_headline: Matprodusenter, styrket. + producers_signup_motivation: Selg dine produkter og fortell dine historier til mangfoldige nye markeder. Spar tid og penger på alt du ikke ønsker å gjøre selv. Vi støtter nyskaping uten risiko. Vi jevner ut spillet. + producers_signup_send: Bli med nå + producers_signup_enterprise: Bedriftskonto + producers_signup_studies: Historier fra våre produsenter. + producers_signup_cta_headline: Bli med nå! + producers_signup_cta_action: Bli med nå + producers_signup_detail: Detaljene. + products_item: Vare + products_description: Beskrivelse + products_variant: Variant + products_availabel: Tilgjengelig? + products_price: Pris + register_title: Registrer + shops_title: Butikker + shops_headline: Handling på en ny måte. + shops_text: Mat gror i syklus, bønder høster i syklus, og vi bestiller mat i syklus. Hvis du møter en stengt bestillingsrunde, sjekk igjen snart. + shops_signup_title: Bli med som hub + shops_signup_headline: Mathubs, ubegrenset. + shops_signup_motivation: Uansett modell støtter vi deg. Uansett hvordan du forandrer deg er vi med deg. Vi er non-profit, uavhengig, og transparent. Vi er partneren du har drømt om. + shops_signup_action: Bli med nå + shops_signup_pricing: Bedriftskonto + shops_signup_stories: Historier fra våre hubs. + shops_signup_help: Vi er klar til å hjelpe. + shops_signup_help_text: Du trenger bedre resultater. Du trenger nye kunder og logistikkpartnere. Du trenger å få din historie fortalt hos grossister, i dagligvaren og rundt kjøkkenbordet. + shops_signup_detail: Detaljene. + orders_fees: Gebyrer... + orders_edit_title: Handlekurv + orders_edit_headline: Din handlekurv + orders_edit_time: Bestilling klar for + orders_edit_continue: Fortsett å handle + orders_edit_checkout: Kassen + orders_form_empty_cart: "Tøm handlekurv" + orders_form_subtotal: Delsum varer + orders_form_admin: Admin og håndtering + orders_form_total: Total + orders_oc_expired_headline: Bestillinger stengt for denne runden + orders_oc_expired_text: "Beklager, bestillinger for denne runden stengte for %{time} siden! Kontakt din hub direkte for å høre om de tar i mot sene bestillinger." + orders_oc_expired_text_others_html: "Beklager, bestillinger for denne runden stengte for %{time} siden! Kontakt din hub direkte for å høre om de tar i mot sene bestillinger %{link}." + orders_oc_expired_text_link: "eller se på de andre bestillinsrundene tilgjengelig fra denne huben" + orders_oc_expired_email: "Epost:" + orders_oc_expired_phone: "Telefon:" + orders_show_title: Ordrebekreftelse + orders_show_time: Bestilling klar for + orders_show_number: Ordrebekreftelse + products_cart_distributor_choice: "Distributør for bestillingen:" + products_cart_distributor_change: "Din distributør for denne ordren vil bli endret til %{name} hvis du legger til dette produktet i handlekurven din." + products_cart_distributor_is: "Din distributør for denne ordren er %{name}." + products_distributor_error: "Vennligst fullfør din bestilling hos %{link} før du handler hos en annen distributør." + products_oc: "Bestillingsrunde for din bestilling:" + products_oc_change: "Din bestillingsrunde for denne bestillingen vil bli endret til %{name} hvis du legger til dette produktet i den handlekurv." + products_oc_is: "Din bestillingsrunde for denne bestillingen er %{name}." + products_oc_error: "Vennligst fullfør din bestilling hos %{link} før du handler i en annen bestillingsrunde." + products_oc_current: "din nåværende bestillingsrunde" + products_quantity: Mengde + products_max_quantity: Max mengde + products_distributor: Distributør + products_distributor_info: Når du velger en distributør for din bestilling, vil deres adresse og hentetider vises her. + password: Passord + remember_me: Husk meg + are_you_sure: "er du sikker?" + orders_open: Åpen for bestilling + closing: "stenger" + going_back_to_home_page: "Tar deg tilbake til hjemmesiden" + creating: Oppretter + updating: oppdatering + failed_to_create_enterprise: "Klarte ikke å opprette virksomheten." + failed_to_create_enterprise_unknown: "Klarte ikke å opprette virksomheten.\nKontroller at alle feltene er fylt ut." + failed_to_update_enterprise_unknown: "Klarte ikke å oppdatere virksomheten.\nKontroller at alle feltene er fylt ut." + order_not_saved_yet: "Bestillingen er ikke lagret ennå. Gi oss noen få sekunder for å fullføre!" + filter_by: "Filtrer på" + hide_filters: "Skjul filtre" + one_filter_applied: "1 filter påført" + x_filters_applied: "filter påført" + submitting_order: "Bestilling sendes: vennligst vent" + confirm_hub_change: "Er du sikker? Dette vil endre din valgte hub og fjerne eventuelle varer i handlekurven." + confirm_oc_change: "Er du sikker? Dette vil endre din valgte bestilingsrunde og fjerne eventuelle varer i handlekurven." + location_placeholder: "Fyll inn sted..." + error_required: "kan ikke være tomt" + error_number: "må være tall" + error_email: "må være epostadresse" + item_handling_fees: "Håndteringsavgifter for varen (inkludert i varens totaler)" + january: "januar" + february: "februar" + march: "mars" + april: "april" + may: "mai" + june: "juni" + july: "juli" + august: "august" + september: "september" + october: "oktober" + november: "november" + december: "desember" + email_not_found: "epostadresse ikke funnet" + email_required: "Du må oppgi en epostadresse" + logging_in: "Et øyeblikk, vi logger deg inn" + signup_email: "Din epost" + choose_password: "Velg et passord" + confirm_password: "Bekreft passord" + action_signup: "Registrer deg nå" + welcome_to_ofn: "Velkommen til Open Food Network!" + signup_or_login: "Kom i gang ved å registrere deg (eller logge inn)" + have_an_account: "Har du allerede en konto?" + action_login: "Logg inn nå." + forgot_password: "Glemt passord?" + password_reset_sent: "En epost med instruksjoner om å nullstille passordet har blitt sendt!" + reset_password: "Tilbakestill passord" + who_is_managing_enterprise: "Hvem er ansvarlig for å administrere %{enterprise}?" + enterprise_contact: "Primærkontakt" + enterprise_contact_required: "Du må oppgi en primærkontakt." + enterprise_email: "Epostadresse" + enterprise_email_required: "Du må oppgi en gyldig epostadresse" + enterprise_phone: "Telefonnummer" + back: "Tilbake" + continue: "Fortsett" + limit_reached_headline: "Å nei!" + limit_reached_message: "Du har nådd grensen!" + limit_reached_text: "Du har nådd grensen for antall foretak du har lov til å eie på" + limit_reached_action: "Gå tilbake til hjemmesiden" + select_promo_image: "Steg 3. Velg Promo-bilde" + promo_image_tip: "Tips: Vises som en banner, foretrukket størrelse er 1200 × 260px" + promo_image_label: "Velg et promo-bilde" + action_or: "ELLER" + promo_image_drag: "Dra og slipp promo her" + review_promo_image: "Steg 4. Sjekk din Promo-banner" + review_promo_image_tip: "Tips: For best resultat bør promo-bildet fylle det tilgjengelige området" + promo_image_placeholder: "Logoen din vil vises her til vurdering når den er lastet opp" + uploading: "Laster opp..." + select_logo: "Steg 1. Velg Logo-bilde" + logo_tip: "Tips: Firkantede bilder vil fungere best, helst minst 300 × 300px" + logo_label: "Velg et logo-bilde" + logo_drag: "Dra og slipp logoen her" + review_logo: "Steg 2. Sjekk din Logo" + review_logo_tip: "Tips: For best resultat bør logoen fylle det tilgjengelige området" + logo_placeholder: "Logoen din vil vises her til vurdering når den er lastet opp" + enterprise_about_headline: "Bra!" + enterprise_about_message: "Nå la oss finne ut detaljene om" + enterprise_success: "Suksess! %{enterprise} lagt til Open Food Network" + enterprise_registration_exit_message: "Hvis du avslutter denne veiviseren uansett steg må du klikke på bekreftelseslinken i eposten du har mottatt. Dette vil ta deg til din admin-side der du kan fortsette å sette opp din profil." + enterprise_description: "Kort beskrivelse" + enterprise_description_placeholder: "En kort setning som beskriver virksomheten din" + enterprise_long_desc: "Lang beskrivelse" + enterprise_long_desc_placeholder: "Dette er muligheten din til å fortelle historien om din virksomhet - hva gjør deg annerledes og flott? Vi vil foreslå å holde din beskrivelse til under 600 tegn eller 150 ord." + enterprise_long_desc_length: "%{num} tegn / opptil 600 anbefales" + enterprise_abn: "ORG#" + enterprise_abn_placeholder: "f.eks. 999 000 123" + enterprise_acn: "MVA#" + enterprise_acn_placeholder: "f.eks. 999 000 123" + enterprise_tax_required: "Du må gjøre et valg." + enterprise_final_step: "Siste steg!" + enterprise_social_text: "Hvordan kan folk finne %{enterprise} på nettet?" + website: "Hjemmeside" + website_placeholder: "f.eks. openfoodnetwork.no" + facebook: "Facebook" + facebook_placeholder: "f.eks. www.facebook.com/FirmaNavnHer" + linkedin: "LinkedIn" + linkedin_placeholder: "f.eks. www.linkedin.com/DittNavnHer" + twitter: "Twitter" + twitter_placeholder: "f.eks. @twitter_handle" + instagram: "Instagram" + instagram_placeholder: "f.eks. @instagram_handle" + registration_greeting: "Hei der!" + registration_intro: "Du kan nå opprette en profil for din Produsent eller Hub" + registration_action: "La oss komme i gang!" + registration_checklist: "Du trenger" + registration_time: "5-10 minutter" + registration_enterprise_address: "Bedriftsadresse" + registration_contact_details: "Kontaktdetaljer" + registration_logo: "Ditt logo-bilde" + registration_promo_image: "Profilbilde i landskapsformat" + registration_about_us: "'Om oss' tekst" + registration_outcome_headline: "Hva får jeg?" + registration_outcome1_html: "Profilen din hjelper folk å finne og kontakte deg på Open Food Network" + registration_outcome2: "Bruk denne plassen til å fortelle historien om din bedrift for å øke antall koblinger til dine sosiale og online media." + registration_outcome3: "Det er også det første skrittet mot handel på Open Food Network eller å åpne en nettbutikk." + registration_finished_headline: "Ferdig!" + registration_finished_thanks: "Takk for at du fyller ut detaljene for %{enterprise}." + registration_finished_login: "Du kan endre eller oppdatere din virksomhet når som helst ved å logge inn på Open Food Network og gå til Admin." + registration_finished_activate: "Aktivere %{enterprise}." + registration_finished_activate_instruction_html: "Vi har sendt en epostbekreftelse til %{email} hvis den ikke har blitt aktivert før.
Følg instruksjonene der for å gjøre bedriften synlig på Open Food Network." + registration_finished_action: "Open Food Network hjem" + registration_type_headline: "Siste stegfor å legge til %{enterprise}!" + registration_type_question: "Er du en produsent?" + registration_type_producer: "Ja, jeg er en produsent" + registration_type_no_producer: "Nei, jeg er ikke en produsent" + registration_type_error: "Vennligst velg en. Er du produsent?" + registration_type_producer_help: "Produsenter lager masse god mat og drikke. Du er en produsent hvis du dyrker, driver med hysdyrhold, brygger, baker, fermenterer, melker eller former." + registration_type_no_producer_help: "Hvis du ikke er en produsent, er du sannsynligvis noen som selger og distribuerer mat. Du kan være en hub, samvirke, kjøpegruppe, forhandler, grossist eller annet." + create_profile: "Opprett profil" + registration_images_headline: "Takk!" + registration_images_description: "La oss laste opp noen fine bilder så profilen din ser flott ut! :)" + registration_detail_headline: "La oss komme i gang" + registration_detail_enterprise: "Kult! Først må vi vite litt om bedriften:" + registration_detail_producer: "Kult! Først må vi vite litt om gården din:" + registration_detail_name_enterprise: "Bedriftsnavn:" + registration_detail_name_producer: "Gårdsnavn:" + registration_detail_name_placeholder: "f.eks. Hønemor Gård" + registration_detail_name_error: "Vennligst velg et unikt navn for din bedrift" + registration_detail_address1: "Adresselinje 1:" + registration_detail_address1_placeholder: "f.eks. Kongleveien 2" + registration_detail_address1_error: "Vennligst oppgi en adresse" + registration_detail_address2: "Adresselinje 2:" + registration_detail_suburb: "Område:" + registration_detail_suburb_placeholder: "f.eks. Nesodden" + registration_detail_suburb_error: "Vennligst fyll inn område" + registration_detail_postcode: "Postnummer:" + registration_detail_postcode_placeholder: "f.eks. 1450" + registration_detail_postcode_error: "Postnummer kreves" + registration_detail_state: "Fylke:" + registration_detail_state_error: "Fylke kreves" + registration_detail_country: "Land:" + registration_detail_country_error: "Vennligst velg et land" + fees: "Gebyrer" + item_cost: "Varepris" + bulk: "Bulk" + shop_variant_quantity_min: "min" + shop_variant_quantity_max: "max" + contact: "Kontakt" + follow: "Følg" + shop_for_products_html: "Handle produkter fra %{enterprise} på:" + change_shop: "Endre butikk til:" + shop_at: "Handle nå på:" + price_breakdown: "Prisfordeling" + admin_fee: "Administrasjonsgebyr" + sales_fee: "Salgsgebyr" + packing_fee: "Pakkegebyr" + transport_fee: "Transportgebyr" + fundraising_fee: "Pengeinnsamlingsgebyr" + price_graph: "Prisgraf" + included_tax: "inkludert avgift" + remove_tax: "Fjern avgift" diff --git a/config/ng-test.conf.js b/config/ng-test.conf.js index 0456d5eb88..0cc4fc385e 100644 --- a/config/ng-test.conf.js +++ b/config/ng-test.conf.js @@ -11,6 +11,7 @@ module.exports = function(config) { 'app/assets/javascripts/shared/angular-local-storage.js', 'app/assets/javascripts/shared/bindonce.min.js', 'app/assets/javascripts/shared/ng-infinite-scroll.min.js', + 'app/assets/javascripts/shared/angular-slideables.js', 'app/assets/javascripts/admin/*.js*', 'app/assets/javascripts/admin/*/*.js*', // Pull in top level files in each folder first (often these are module declarations) @@ -24,6 +25,7 @@ module.exports = function(config) { '**/.#*', 'app/assets/javascripts/darkswarm/all.js.coffee', 'app/assets/javascripts/darkswarm/overrides.js.coffee', + 'app/assets/javascripts/darkswarm/i18n.js.erb', 'app/assets/javascripts/admin/util.js.erb' ], diff --git a/config/routes.rb b/config/routes.rb index f8b70ba4d6..62c4153d14 100644 --- a/config/routes.rb +++ b/config/routes.rb @@ -1,10 +1,19 @@ Openfoodnetwork::Application.routes.draw do root :to => 'home#index' + # Redirects from old URLs avoid server errors and helps search engines + get "/enterprises", to: redirect("/") + get "/products", to: redirect("/") + get "/products/:id", to: redirect("/") + get "/t/products/:id", to: redirect("/") + get "/about_us", to: redirect(ContentConfig.footer_about_url) get "/#/login", to: "home#index", as: :spree_login get "/login", to: redirect("/#/login") + get "/discourse/login", to: "discourse_sso#login" + get "/discourse/sso", to: "discourse_sso#sso" + get "/map", to: "map#index", as: :map get "/register", to: "registration#index", as: :registration @@ -16,8 +25,23 @@ Openfoodnetwork::Application.routes.draw do get :order_cycle end - resources :groups - resources :producers + resources :producers, only: [:index] do + collection do + get :signup + end + end + + resources :shops, only: [:index] do + collection do + get :signup + end + end + + resources :groups, only: [:index, :show] do + collection do + get :signup + end + end get '/checkout', :to => 'checkout#edit' , :as => :checkout put '/checkout', :to => 'checkout#update' , :as => :update_checkout @@ -25,35 +49,39 @@ Openfoodnetwork::Application.routes.draw do resources :enterprises do collection do - get :suppliers - get :distributors post :search get :check_permalink end member do - get :shop_front # new world - get :shop # old world + get :shop end end get '/:id/shop', to: 'enterprises#shop', as: 'enterprise_shop' + get "/enterprises/:permalink", to: redirect("/") # Legacy enterprise URL devise_for :enterprise, controllers: { confirmations: 'enterprise_confirmations' } namespace :admin do resources :order_cycles do post :bulk_update, on: :collection, as: :bulk_update - get :clone, on: :member + + member do + get :clone + post :notify_producers + end end resources :enterprises do collection do get :for_order_cycle + get :for_line_items post :bulk_update, as: :bulk_update end member do - put :set_sells + get :welcome + put :register end resources :producer_properties do @@ -78,7 +106,23 @@ Openfoodnetwork::Application.routes.draw do resources :variant_overrides do post :bulk_update, on: :collection + post :bulk_reset, on: :collection end + + resources :customers, only: [:index, :update] + + resource :content + + resource :accounts_and_billing_settings, only: [:edit, :update] do + collection do + get :show_methods + get :start_job + end + end + + resource :business_model_configuration, only: [:edit, :update], controller: 'business_model_configuration' + + resource :account, only: [:show], controller: 'account' end namespace :api do @@ -93,8 +137,6 @@ Openfoodnetwork::Application.routes.draw do end end - get "about_us", :controller => 'home', :action => "about_us" - namespace :open_food_network do resources :cart do post :add_variant @@ -124,6 +166,7 @@ end Spree::Core::Engine.routes.prepend do match '/admin/reports/orders_and_distributors' => 'admin/reports#orders_and_distributors', :as => "orders_and_distributors_admin_reports", :via => [:get, :post] match '/admin/reports/order_cycle_management' => 'admin/reports#order_cycle_management', :as => "order_cycle_management_admin_reports", :via => [:get, :post] + match '/admin/reports/packing' => 'admin/reports#packing', :as => "packing_admin_reports", :via => [:get, :post] match '/admin/reports/group_buys' => 'admin/reports#group_buys', :as => "group_buys_admin_reports", :via => [:get, :post] match '/admin/reports/bulk_coop' => 'admin/reports#bulk_coop', :as => "bulk_coop_admin_reports", :via => [:get, :post] match '/admin/reports/payments' => 'admin/reports#payments', :as => "payments_admin_reports", :via => [:get, :post] @@ -134,6 +177,7 @@ Spree::Core::Engine.routes.prepend do match '/admin/orders/bulk_management' => 'admin/orders#bulk_management', :as => "admin_bulk_order_management" match '/admin/reports/products_and_inventory' => 'admin/reports#products_and_inventory', :as => "products_and_inventory_admin_reports", :via => [:get, :post] match '/admin/reports/customers' => 'admin/reports#customers', :as => "customers_admin_reports", :via => [:get, :post] + match '/admin/reports/xero_invoices' => 'admin/reports#xero_invoices', :as => "xero_invoices_admin_reports", :via => [:get, :post] match '/admin', :to => 'admin/overview#index', :as => :admin match '/admin/payment_methods/show_provider_preferences' => 'admin/payment_methods#show_provider_preferences', :via => :get @@ -171,8 +215,12 @@ Spree::Core::Engine.routes.prepend do end resources :orders do + get :invoice, on: :member + get :print, on: :member get :managed, on: :collection end + + resources :line_items, only: [:index], format: :json end resources :orders do diff --git a/config/schedule.rb b/config/schedule.rb index 0c98f72dc6..a09ca55dce 100644 --- a/config/schedule.rb +++ b/config/schedule.rb @@ -18,3 +18,12 @@ end every 4.hours do rake 'db2fog:backup' end + +every 1.day, at: '1:00am' do + rake 'openfoodnetwork:billing:update_account_invoices' +end + +# On the 2nd of every month at 1:30am +every '30 1 2 * *' do + rake 'openfoodnetwork:billing:finalize_account_invoices' +end diff --git a/config/unicorn.rb b/config/unicorn.rb index e5d7317940..22e954b4d0 100644 --- a/config/unicorn.rb +++ b/config/unicorn.rb @@ -1,27 +1,2 @@ -preload_app true # https://newrelic.com/docs/ruby/no-data-with-unicorn -worker_processes 4 # amount of unicorn workers to spin up -timeout 60 # restarts workers that hang for 30 seconds - - -# https://devcenter.heroku.com/articles/forked-pg-connections -before_fork do |server, worker| - - Signal.trap 'TERM' do - puts 'Unicorn master intercepting TERM and sending myself QUIT instead' - Process.kill 'QUIT', Process.pid - end - - defined?(ActiveRecord::Base) and - ActiveRecord::Base.connection.disconnect! -end - -after_fork do |server, worker| - - Signal.trap 'TERM' do - puts 'Unicorn worker intercepting TERM and doing nothing. Wait for master to sent QUIT' - end - - defined?(ActiveRecord::Base) and - ActiveRecord::Base.establish_connection - -end +worker_processes 2 # amount of unicorn workers to spin up +timeout 120 # restarts workers that hang for 30 seconds diff --git a/db/migrate/20120327000593_add_addresses_checkouts_indexes.rb b/db/migrate/20120327000593_add_addresses_checkouts_indexes.rb old mode 100755 new mode 100644 diff --git a/db/migrate/20140702053145_add_fields_to_distributors_shipping_methods.rb b/db/migrate/20140702053145_add_fields_to_distributors_shipping_methods.rb index 0e16a13a13..75fa60e7b7 100644 --- a/db/migrate/20140702053145_add_fields_to_distributors_shipping_methods.rb +++ b/db/migrate/20140702053145_add_fields_to_distributors_shipping_methods.rb @@ -9,7 +9,7 @@ class AddFieldsToDistributorsShippingMethods < ActiveRecord::Migration add_column :distributors_shipping_methods, :updated_at, :datetime DistributorShippingMethod.reset_column_information - DistributorShippingMethod.update_all created_at: Time.now, updated_at: Time.now + DistributorShippingMethod.update_all created_at: Time.zone.now, updated_at: Time.zone.now change_column :distributors_shipping_methods, :created_at, :datetime, null: false change_column :distributors_shipping_methods, :updated_at, :datetime, null: false diff --git a/db/migrate/20141010043405_add_confirmable_to_enterprise.rb b/db/migrate/20141010043405_add_confirmable_to_enterprise.rb index 22203fe419..9c29bd5f2c 100644 --- a/db/migrate/20141010043405_add_confirmable_to_enterprise.rb +++ b/db/migrate/20141010043405_add_confirmable_to_enterprise.rb @@ -7,7 +7,7 @@ class AddConfirmableToEnterprise < ActiveRecord::Migration add_index :enterprises, :confirmation_token, :unique => true # Existing enterprises are assumed to be confirmed - Enterprise.update_all(:confirmed_at => Time.now) + Enterprise.update_all(:confirmed_at => Time.zone.now) end def down diff --git a/db/migrate/20141229094516_add_receival_time_to_exchange.rb b/db/migrate/20141229094516_add_receival_time_to_exchange.rb new file mode 100644 index 0000000000..06933ed051 --- /dev/null +++ b/db/migrate/20141229094516_add_receival_time_to_exchange.rb @@ -0,0 +1,6 @@ +class AddReceivalTimeToExchange < ActiveRecord::Migration + def change + add_column :exchanges, :receival_time, :string + add_column :exchanges, :receival_instructions, :string + end +end diff --git a/db/migrate/20150508030520_acts_as_taggable_on_migration.acts_as_taggable_on_engine.rb b/db/migrate/20150508030520_acts_as_taggable_on_migration.acts_as_taggable_on_engine.rb new file mode 100644 index 0000000000..6bbd5594ea --- /dev/null +++ b/db/migrate/20150508030520_acts_as_taggable_on_migration.acts_as_taggable_on_engine.rb @@ -0,0 +1,31 @@ +# This migration comes from acts_as_taggable_on_engine (originally 1) +class ActsAsTaggableOnMigration < ActiveRecord::Migration + def self.up + create_table :tags do |t| + t.string :name + end + + create_table :taggings do |t| + t.references :tag + + # You should make sure that the column created is + # long enough to store the required class names. + t.references :taggable, polymorphic: true + t.references :tagger, polymorphic: true + + # Limit is created to prevent MySQL error on index + # length for MyISAM table type: http://bit.ly/vgW2Ql + t.string :context, limit: 128 + + t.datetime :created_at + end + + add_index :taggings, :tag_id + add_index :taggings, [:taggable_id, :taggable_type, :context] + end + + def self.down + drop_table :taggings + drop_table :tags + end +end diff --git a/db/migrate/20150508030521_add_missing_unique_indices.acts_as_taggable_on_engine.rb b/db/migrate/20150508030521_add_missing_unique_indices.acts_as_taggable_on_engine.rb new file mode 100644 index 0000000000..4ca676f6c7 --- /dev/null +++ b/db/migrate/20150508030521_add_missing_unique_indices.acts_as_taggable_on_engine.rb @@ -0,0 +1,20 @@ +# This migration comes from acts_as_taggable_on_engine (originally 2) +class AddMissingUniqueIndices < ActiveRecord::Migration + def self.up + add_index :tags, :name, unique: true + + remove_index :taggings, :tag_id + remove_index :taggings, [:taggable_id, :taggable_type, :context] + add_index :taggings, + [:tag_id, :taggable_id, :taggable_type, :context, :tagger_id, :tagger_type], + unique: true, name: 'taggings_idx' + end + + def self.down + remove_index :tags, :name + + remove_index :taggings, name: 'taggings_idx' + add_index :taggings, :tag_id + add_index :taggings, [:taggable_id, :taggable_type, :context] + end +end diff --git a/db/migrate/20150508030522_add_taggings_counter_cache_to_tags.acts_as_taggable_on_engine.rb b/db/migrate/20150508030522_add_taggings_counter_cache_to_tags.acts_as_taggable_on_engine.rb new file mode 100644 index 0000000000..8edb508078 --- /dev/null +++ b/db/migrate/20150508030522_add_taggings_counter_cache_to_tags.acts_as_taggable_on_engine.rb @@ -0,0 +1,15 @@ +# This migration comes from acts_as_taggable_on_engine (originally 3) +class AddTaggingsCounterCacheToTags < ActiveRecord::Migration + def self.up + add_column :tags, :taggings_count, :integer, default: 0 + + ActsAsTaggableOn::Tag.reset_column_information + ActsAsTaggableOn::Tag.find_each do |tag| + ActsAsTaggableOn::Tag.reset_counters(tag.id, :taggings) + end + end + + def self.down + remove_column :tags, :taggings_count + end +end diff --git a/db/migrate/20150508030523_add_missing_taggable_index.acts_as_taggable_on_engine.rb b/db/migrate/20150508030523_add_missing_taggable_index.acts_as_taggable_on_engine.rb new file mode 100644 index 0000000000..71f2d7f433 --- /dev/null +++ b/db/migrate/20150508030523_add_missing_taggable_index.acts_as_taggable_on_engine.rb @@ -0,0 +1,10 @@ +# This migration comes from acts_as_taggable_on_engine (originally 4) +class AddMissingTaggableIndex < ActiveRecord::Migration + def self.up + add_index :taggings, [:taggable_id, :taggable_type, :context] + end + + def self.down + remove_index :taggings, [:taggable_id, :taggable_type, :context] + end +end diff --git a/db/migrate/20150508030524_change_collation_for_tag_names.acts_as_taggable_on_engine.rb b/db/migrate/20150508030524_change_collation_for_tag_names.acts_as_taggable_on_engine.rb new file mode 100644 index 0000000000..bfb06bc7cd --- /dev/null +++ b/db/migrate/20150508030524_change_collation_for_tag_names.acts_as_taggable_on_engine.rb @@ -0,0 +1,10 @@ +# This migration comes from acts_as_taggable_on_engine (originally 5) +# This migration is added to circumvent issue #623 and have special characters +# work properly +class ChangeCollationForTagNames < ActiveRecord::Migration + def up + if ActsAsTaggableOn::Utils.using_mysql? + execute("ALTER TABLE tags MODIFY name varchar(255) CHARACTER SET utf8 COLLATE utf8_bin;") + end + end +end diff --git a/db/migrate/20150508072454_remove_customer_code_not_null_constraint.rb b/db/migrate/20150508072454_remove_customer_code_not_null_constraint.rb new file mode 100644 index 0000000000..deeacbc608 --- /dev/null +++ b/db/migrate/20150508072454_remove_customer_code_not_null_constraint.rb @@ -0,0 +1,9 @@ +class RemoveCustomerCodeNotNullConstraint < ActiveRecord::Migration + def up + change_column :customers, :code, :string, null: true + end + + def down + change_column :customers, :code, :string, null: false + end +end diff --git a/db/migrate/20150508072938_add_customer_to_orders.rb b/db/migrate/20150508072938_add_customer_to_orders.rb new file mode 100644 index 0000000000..79d69baf49 --- /dev/null +++ b/db/migrate/20150508072938_add_customer_to_orders.rb @@ -0,0 +1,16 @@ +class AddCustomerToOrders < ActiveRecord::Migration + def change + add_column :spree_orders, :customer_id, :integer + add_index :spree_orders, :customer_id + add_foreign_key :spree_orders, :customers, column: :customer_id + + Spree::Order.where("spree_orders.email IS NOT NULL AND distributor_id IS NOT NULL AND customer_id IS NULL").each do |order| + customer = Customer.find_by_email_and_enterprise_id(order.email, order.distributor_id) + unless customer.present? + user = Spree::User.find_by_email(order.email) + customer = Customer.create!(email: order.email, enterprise_id: order.distributor_id, user_id: user.andand.id ) + end + order.update_attribute(:customer, customer) + end + end +end diff --git a/db/migrate/20150603001843_add_unique_index_to_enterprise_permalink.rb b/db/migrate/20150603001843_add_unique_index_to_enterprise_permalink.rb new file mode 100644 index 0000000000..e8841b2c5f --- /dev/null +++ b/db/migrate/20150603001843_add_unique_index_to_enterprise_permalink.rb @@ -0,0 +1,16 @@ +class AddUniqueIndexToEnterprisePermalink < ActiveRecord::Migration + def change + duplicates = Enterprise.group(:permalink).having('count(*) > 1').pluck(:permalink) + duplicates.each { |p| resolve_permalink(p) }; + add_index :enterprises, :permalink, :unique => true + end + + def resolve_permalink(permalink) + conflicting = Enterprise.where(permalink: permalink) + while conflicting.size > 1 do + enterprise = conflicting.pop + enterprise.permalink = nil + enterprise.save + end + end +end diff --git a/db/migrate/20150604045725_add_sessions_table.rb b/db/migrate/20150604045725_add_sessions_table.rb new file mode 100644 index 0000000000..4c879564a5 --- /dev/null +++ b/db/migrate/20150604045725_add_sessions_table.rb @@ -0,0 +1,12 @@ +class AddSessionsTable < ActiveRecord::Migration + def change + create_table :sessions do |t| + t.string :session_id, :null => false + t.text :data + t.timestamps + end + + add_index :sessions, :session_id + add_index :sessions, :updated_at + end +end diff --git a/db/migrate/20150605052516_dependent_delete_adjustment_metadata.rb b/db/migrate/20150605052516_dependent_delete_adjustment_metadata.rb new file mode 100644 index 0000000000..ee9d8d4af8 --- /dev/null +++ b/db/migrate/20150605052516_dependent_delete_adjustment_metadata.rb @@ -0,0 +1,11 @@ +class DependentDeleteAdjustmentMetadata < ActiveRecord::Migration + def up + remove_foreign_key "adjustment_metadata", name: "adjustment_metadata_adjustment_id_fk" + add_foreign_key "adjustment_metadata", "spree_adjustments", name: "adjustment_metadata_adjustment_id_fk", column: "adjustment_id", dependent: :delete + end + + def down + remove_foreign_key "adjustment_metadata", name: "adjustment_metadata_adjustment_id_fk" + add_foreign_key "adjustment_metadata", "spree_adjustments", name: "adjustment_metadata_adjustment_id_fk", column: "adjustment_id" + end +end diff --git a/db/migrate/20150612045544_make_enterprises_name_unique.rb b/db/migrate/20150612045544_make_enterprises_name_unique.rb new file mode 100644 index 0000000000..5722284d94 --- /dev/null +++ b/db/migrate/20150612045544_make_enterprises_name_unique.rb @@ -0,0 +1,20 @@ +class MakeEnterprisesNameUnique < ActiveRecord::Migration + def up + dup_names = Enterprise.group('name').select('name, COUNT(*) AS num_enterprises') + + dup_names.each do |data| + (data.num_enterprises.to_i - 1).times do |i| + e = Enterprise.find_by_name data.name + new_name = "#{data.name}-#{i+1}" + e.update_column :name, new_name + say "Renamed enterprise #{data.name} to #{new_name}" + end + end + + add_index :enterprises, :name, unique: true + end + + def down + remove_index :enterprises, :name + end +end diff --git a/db/migrate/20150619020711_create_versions.rb b/db/migrate/20150619020711_create_versions.rb new file mode 100644 index 0000000000..23be970c66 --- /dev/null +++ b/db/migrate/20150619020711_create_versions.rb @@ -0,0 +1,13 @@ +class CreateVersions < ActiveRecord::Migration + def change + create_table :versions do |t| + t.string :item_type, :null => false + t.integer :item_id, :null => false + t.string :event, :null => false + t.string :whodunnit + t.text :object + t.datetime :created_at + end + add_index :versions, [:item_type, :item_id] + end +end diff --git a/db/migrate/20150619100137_create_bill_items.rb b/db/migrate/20150619100137_create_bill_items.rb new file mode 100644 index 0000000000..fec80692b1 --- /dev/null +++ b/db/migrate/20150619100137_create_bill_items.rb @@ -0,0 +1,15 @@ +class CreateBillItems < ActiveRecord::Migration + def change + create_table :bill_items do |t| + t.references :enterprise, nil: false + t.references :owner, nil: false + t.datetime :begins_at, default: nil + t.datetime :ends_at, default: nil + t.string :sells, default: nil + t.boolean :trial, default: false + t.decimal :turnover, default: 0.0 + t.foreign_key :enterprises + t.foreign_key :spree_users, column: :owner_id + end + end +end diff --git a/db/migrate/20150626090338_rename_bill_items_to_billable_periods.rb b/db/migrate/20150626090338_rename_bill_items_to_billable_periods.rb new file mode 100644 index 0000000000..caa2fb9a4d --- /dev/null +++ b/db/migrate/20150626090338_rename_bill_items_to_billable_periods.rb @@ -0,0 +1,9 @@ +class RenameBillItemsToBillablePeriods < ActiveRecord::Migration + def up + rename_table :bill_items, :billable_periods + end + + def down + rename_table :billable_periods, :bill_items + end +end diff --git a/db/migrate/20150701034055_add_timestamps_to_billable_periods.rb b/db/migrate/20150701034055_add_timestamps_to_billable_periods.rb new file mode 100644 index 0000000000..6c3ea52356 --- /dev/null +++ b/db/migrate/20150701034055_add_timestamps_to_billable_periods.rb @@ -0,0 +1,8 @@ +class AddTimestampsToBillablePeriods < ActiveRecord::Migration + def change + change_table(:billable_periods) do |t| + t.datetime :deleted_at, default: nil + t.timestamps + end + end +end diff --git a/db/migrate/20150719111807_add_default_stock_to_variant_overrides.rb b/db/migrate/20150719111807_add_default_stock_to_variant_overrides.rb new file mode 100644 index 0000000000..880a1b8349 --- /dev/null +++ b/db/migrate/20150719111807_add_default_stock_to_variant_overrides.rb @@ -0,0 +1,5 @@ +class AddDefaultStockToVariantOverrides < ActiveRecord::Migration + def change + add_column :variant_overrides, :default_stock, :integer + end +end diff --git a/db/migrate/20150719153136_rename_line_item_unit_value.rb b/db/migrate/20150719153136_rename_line_item_unit_value.rb new file mode 100644 index 0000000000..9dbdce75c5 --- /dev/null +++ b/db/migrate/20150719153136_rename_line_item_unit_value.rb @@ -0,0 +1,5 @@ +class RenameLineItemUnitValue < ActiveRecord::Migration + def change + rename_column :spree_line_items, :unit_value, :final_weight_volume + end +end diff --git a/db/migrate/20150719153732_update_precision_on_line_item_final_weight_volume.rb b/db/migrate/20150719153732_update_precision_on_line_item_final_weight_volume.rb new file mode 100644 index 0000000000..c1b3482c0f --- /dev/null +++ b/db/migrate/20150719153732_update_precision_on_line_item_final_weight_volume.rb @@ -0,0 +1,11 @@ +class UpdatePrecisionOnLineItemFinalWeightVolume < ActiveRecord::Migration + def up + change_column :spree_line_items, :final_weight_volume, :decimal, :precision => 10, :scale => 2 + end + + def down + raise ActiveRecord::IrreversibleMigration + end +end + + diff --git a/db/migrate/20150730160010_update_spree_line_item_final_weight_volume.rb b/db/migrate/20150730160010_update_spree_line_item_final_weight_volume.rb new file mode 100644 index 0000000000..4d51a7160b --- /dev/null +++ b/db/migrate/20150730160010_update_spree_line_item_final_weight_volume.rb @@ -0,0 +1,9 @@ +class UpdateSpreeLineItemFinalWeightVolume < ActiveRecord::Migration + def up + execute "UPDATE spree_line_items SET final_weight_volume = final_weight_volume * quantity" + end + + def down + raise ActiveRecord::IrreversibleMigration + end +end diff --git a/db/migrate/20150827194622_add_enable_reset_to_variant_overrides.rb b/db/migrate/20150827194622_add_enable_reset_to_variant_overrides.rb new file mode 100644 index 0000000000..172cce6588 --- /dev/null +++ b/db/migrate/20150827194622_add_enable_reset_to_variant_overrides.rb @@ -0,0 +1,5 @@ +class AddEnableResetToVariantOverrides < ActiveRecord::Migration + def change + add_column :variant_overrides, :enable_reset, :boolean + end +end diff --git a/db/migrate/20150916012814_create_account_invoices.rb b/db/migrate/20150916012814_create_account_invoices.rb new file mode 100644 index 0000000000..7e92a53593 --- /dev/null +++ b/db/migrate/20150916012814_create_account_invoices.rb @@ -0,0 +1,18 @@ +class CreateAccountInvoices < ActiveRecord::Migration + def change + create_table :account_invoices do |t| + t.references :user, null: false + t.references :order + t.integer :year, null: false + t.integer :month, null: false + t.datetime :issued_at + + t.timestamps + end + add_index :account_invoices, :user_id + add_index :account_invoices, :order_id + + add_foreign_key :account_invoices, :spree_orders, column: :order_id + add_foreign_key :account_invoices, :spree_users, column: :user_id + end +end diff --git a/db/migrate/20150916061809_add_account_invoice_to_billable_periods.rb b/db/migrate/20150916061809_add_account_invoice_to_billable_periods.rb new file mode 100644 index 0000000000..ee1d9d5d93 --- /dev/null +++ b/db/migrate/20150916061809_add_account_invoice_to_billable_periods.rb @@ -0,0 +1,7 @@ +class AddAccountInvoiceToBillablePeriods < ActiveRecord::Migration + def change + add_column :billable_periods, :account_invoice_id, :integer, null: false + add_index :billable_periods, :account_invoice_id + add_foreign_key :billable_periods, :account_invoices, column: :account_invoice_id + end +end diff --git a/db/migrate/20150924054538_add_option_values_line_items_join_table.rb b/db/migrate/20150924054538_add_option_values_line_items_join_table.rb new file mode 100644 index 0000000000..7a51c851ec --- /dev/null +++ b/db/migrate/20150924054538_add_option_values_line_items_join_table.rb @@ -0,0 +1,14 @@ +class AddOptionValuesLineItemsJoinTable < ActiveRecord::Migration + def change + create_table :spree_option_values_line_items, :id => false, :force => true do |t| + t.integer :line_item_id + t.integer :option_value_id + end + + Spree::LineItem.all.each do |line_item| + line_item.update_units + end + + add_index :spree_option_values_line_items, :line_item_id, :name => 'index_option_values_line_items_on_line_item_id' + end +end diff --git a/db/migrate/20151002020537_ensure_address_for_account_invoice_orders.rb b/db/migrate/20151002020537_ensure_address_for_account_invoice_orders.rb new file mode 100644 index 0000000000..6d2c7d45c5 --- /dev/null +++ b/db/migrate/20151002020537_ensure_address_for_account_invoice_orders.rb @@ -0,0 +1,15 @@ +class EnsureAddressForAccountInvoiceOrders < ActiveRecord::Migration + def up + AccountInvoice.where('order_id IS NOT NULL').each do |account_invoice| + billable_periods = account_invoice.billable_periods.order(:enterprise_id).reject{ |bp| bp.turnover == 0 } + + if billable_periods.any? + address = billable_periods.first.enterprise.address + account_invoice.order.update_attributes(bill_address: address, ship_address: address) + end + end + end + + def down + end +end diff --git a/db/migrate/20151125051510_combine_exchange_receival_time_receival_instructions.rb b/db/migrate/20151125051510_combine_exchange_receival_time_receival_instructions.rb new file mode 100644 index 0000000000..434a2053ab --- /dev/null +++ b/db/migrate/20151125051510_combine_exchange_receival_time_receival_instructions.rb @@ -0,0 +1,9 @@ +class CombineExchangeReceivalTimeReceivalInstructions < ActiveRecord::Migration + def up + remove_column :exchanges, :receival_time + end + + def down + add_column :exchanges, :receival_time, :string + end +end diff --git a/db/migrate/20151126235409_add_on_demand_and_sku_to_variant_overrides.rb b/db/migrate/20151126235409_add_on_demand_and_sku_to_variant_overrides.rb new file mode 100644 index 0000000000..9c47bfbc27 --- /dev/null +++ b/db/migrate/20151126235409_add_on_demand_and_sku_to_variant_overrides.rb @@ -0,0 +1,6 @@ +class AddOnDemandAndSkuToVariantOverrides < ActiveRecord::Migration + def change + add_column :variant_overrides, :sku, :string, :default => nil, :after => :hub_id + add_column :variant_overrides, :on_demand, :boolean, :default => nil, :after => :count_on_hand + end +end diff --git a/db/migrate/20151128185900_rename_enable_reset_to_resettable.rb b/db/migrate/20151128185900_rename_enable_reset_to_resettable.rb new file mode 100644 index 0000000000..461e2efe25 --- /dev/null +++ b/db/migrate/20151128185900_rename_enable_reset_to_resettable.rb @@ -0,0 +1,3 @@ +class RenameEnableResetToResettable < ActiveRecord::Migration + rename_column :variant_overrides, :enable_reset, :resettable +end diff --git a/db/migrate/20160205044930_add_email_address_to_enterprises.rb b/db/migrate/20160205044930_add_email_address_to_enterprises.rb new file mode 100644 index 0000000000..b1047fe4b8 --- /dev/null +++ b/db/migrate/20160205044930_add_email_address_to_enterprises.rb @@ -0,0 +1,5 @@ +class AddEmailAddressToEnterprises < ActiveRecord::Migration + def change + add_column :enterprises, :email_address, :string + end +end diff --git a/db/migrate/20160212092908_set_enterprise_email_address.rb b/db/migrate/20160212092908_set_enterprise_email_address.rb new file mode 100644 index 0000000000..a43591624a --- /dev/null +++ b/db/migrate/20160212092908_set_enterprise_email_address.rb @@ -0,0 +1,8 @@ +class SetEnterpriseEmailAddress < ActiveRecord::Migration + def up + Enterprise.all.each do |enterprise| + enterprise.email_address = enterprise.email + enterprise.save + end + end +end diff --git a/db/schema.rb b/db/schema.rb index 6d9de020f1..57fbf4f52d 100644 --- a/db/schema.rb +++ b/db/schema.rb @@ -11,7 +11,20 @@ # # It's strongly recommended to check this file into your version control system. -ActiveRecord::Schema.define(:version => 20150527004427) do +ActiveRecord::Schema.define(:version => 20160212092908) do + + create_table "account_invoices", :force => true do |t| + t.integer "user_id", :null => false + t.integer "order_id" + t.integer "year", :null => false + t.integer "month", :null => false + t.datetime "issued_at" + t.datetime "created_at", :null => false + t.datetime "updated_at", :null => false + end + + add_index "account_invoices", ["order_id"], :name => "index_account_invoices_on_order_id" + add_index "account_invoices", ["user_id"], :name => "index_account_invoices_on_user_id" create_table "adjustment_metadata", :force => true do |t| t.integer "adjustment_id" @@ -24,6 +37,22 @@ ActiveRecord::Schema.define(:version => 20150527004427) do add_index "adjustment_metadata", ["adjustment_id"], :name => "index_adjustment_metadata_on_adjustment_id" add_index "adjustment_metadata", ["enterprise_id"], :name => "index_adjustment_metadata_on_enterprise_id" + create_table "billable_periods", :force => true do |t| + t.integer "enterprise_id" + t.integer "owner_id" + t.datetime "begins_at" + t.datetime "ends_at" + t.string "sells" + t.boolean "trial", :default => false + t.decimal "turnover", :default => 0.0 + t.datetime "deleted_at" + t.datetime "created_at" + t.datetime "updated_at" + t.integer "account_invoice_id", :null => false + end + + add_index "billable_periods", ["account_invoice_id"], :name => "index_billable_periods_on_account_invoice_id" + create_table "carts", :force => true do |t| t.integer "user_id" end @@ -158,7 +187,7 @@ ActiveRecord::Schema.define(:version => 20150527004427) do create_table "customers", :force => true do |t| t.string "email", :null => false t.integer "enterprise_id", :null => false - t.string "code", :null => false + t.string "code" t.integer "user_id" t.datetime "created_at", :null => false t.datetime "updated_at", :null => false @@ -317,12 +346,15 @@ ActiveRecord::Schema.define(:version => 20150527004427) do t.boolean "producer_profile_only", :default => false t.string "permalink", :null => false t.boolean "charges_sales_tax", :default => false, :null => false + t.string "email_address" end add_index "enterprises", ["address_id"], :name => "index_enterprises_on_address_id" add_index "enterprises", ["confirmation_token"], :name => "index_enterprises_on_confirmation_token", :unique => true add_index "enterprises", ["is_primary_producer", "sells"], :name => "index_enterprises_on_is_primary_producer_and_sells" + add_index "enterprises", ["name"], :name => "index_enterprises_on_name", :unique => true add_index "enterprises", ["owner_id"], :name => "index_enterprises_on_owner_id" + add_index "enterprises", ["permalink"], :name => "index_enterprises_on_permalink", :unique => true add_index "enterprises", ["sells"], :name => "index_enterprises_on_sells" create_table "exchange_fees", :force => true do |t| @@ -355,6 +387,7 @@ ActiveRecord::Schema.define(:version => 20150527004427) do t.datetime "created_at", :null => false t.datetime "updated_at", :null => false t.boolean "incoming", :default => false, :null => false + t.string "receival_instructions" end add_index "exchanges", ["order_cycle_id"], :name => "index_exchanges_on_order_cycle_id" @@ -396,6 +429,16 @@ ActiveRecord::Schema.define(:version => 20150527004427) do add_index "product_distributions", ["enterprise_fee_id"], :name => "index_product_distributions_on_enterprise_fee_id" add_index "product_distributions", ["product_id"], :name => "index_product_distributions_on_product_id" + create_table "sessions", :force => true do |t| + t.string "session_id", :null => false + t.text "data" + t.datetime "created_at", :null => false + t.datetime "updated_at", :null => false + end + + add_index "sessions", ["session_id"], :name => "index_sessions_on_session_id" + add_index "sessions", ["updated_at"], :name => "index_sessions_on_updated_at" + create_table "spree_activators", :force => true do |t| t.string "description" t.datetime "expires_at" @@ -551,7 +594,7 @@ ActiveRecord::Schema.define(:version => 20150527004427) do t.string "currency" t.decimal "distribution_fee", :precision => 10, :scale => 2 t.string "shipping_method_name" - t.decimal "unit_value", :precision => 8, :scale => 2 + t.decimal "final_weight_volume", :precision => 10, :scale => 2 end add_index "spree_line_items", ["order_id"], :name => "index_line_items_on_order_id" @@ -594,6 +637,13 @@ ActiveRecord::Schema.define(:version => 20150527004427) do t.datetime "updated_at", :null => false end + create_table "spree_option_values_line_items", :id => false, :force => true do |t| + t.integer "line_item_id" + t.integer "option_value_id" + end + + add_index "spree_option_values_line_items", ["line_item_id"], :name => "index_option_values_line_items_on_line_item_id" + create_table "spree_option_values_variants", :id => false, :force => true do |t| t.integer "variant_id" t.integer "option_value_id" @@ -625,8 +675,10 @@ ActiveRecord::Schema.define(:version => 20150527004427) do t.string "last_ip_address" t.integer "order_cycle_id" t.integer "cart_id" + t.integer "customer_id" end + add_index "spree_orders", ["customer_id"], :name => "index_spree_orders_on_customer_id" add_index "spree_orders", ["number"], :name => "index_orders_on_number" create_table "spree_payment_methods", :force => true do |t| @@ -1083,17 +1135,59 @@ ActiveRecord::Schema.define(:version => 20150527004427) do t.integer "state_id" end + create_table "taggings", :force => true do |t| + t.integer "tag_id" + t.integer "taggable_id" + t.string "taggable_type" + t.integer "tagger_id" + t.string "tagger_type" + t.string "context", :limit => 128 + t.datetime "created_at" + end + + add_index "taggings", ["tag_id", "taggable_id", "taggable_type", "context", "tagger_id", "tagger_type"], :name => "taggings_idx", :unique => true + add_index "taggings", ["taggable_id", "taggable_type", "context"], :name => "index_taggings_on_taggable_id_and_taggable_type_and_context" + + create_table "tags", :force => true do |t| + t.string "name" + t.integer "taggings_count", :default => 0 + end + + add_index "tags", ["name"], :name => "index_tags_on_name", :unique => true + create_table "variant_overrides", :force => true do |t| t.integer "variant_id", :null => false t.integer "hub_id", :null => false t.decimal "price", :precision => 8, :scale => 2 t.integer "count_on_hand" + t.integer "default_stock" + t.boolean "resettable" + t.string "sku" + t.boolean "on_demand" end add_index "variant_overrides", ["variant_id", "hub_id"], :name => "index_variant_overrides_on_variant_id_and_hub_id" + create_table "versions", :force => true do |t| + t.string "item_type", :null => false + t.integer "item_id", :null => false + t.string "event", :null => false + t.string "whodunnit" + t.text "object" + t.datetime "created_at" + end + + add_index "versions", ["item_type", "item_id"], :name => "index_versions_on_item_type_and_item_id" + + add_foreign_key "account_invoices", "spree_orders", name: "account_invoices_order_id_fk", column: "order_id" + add_foreign_key "account_invoices", "spree_users", name: "account_invoices_user_id_fk", column: "user_id" + add_foreign_key "adjustment_metadata", "enterprises", name: "adjustment_metadata_enterprise_id_fk" - add_foreign_key "adjustment_metadata", "spree_adjustments", name: "adjustment_metadata_adjustment_id_fk", column: "adjustment_id" + add_foreign_key "adjustment_metadata", "spree_adjustments", name: "adjustment_metadata_adjustment_id_fk", column: "adjustment_id", dependent: :delete + + add_foreign_key "billable_periods", "account_invoices", name: "billable_periods_account_invoice_id_fk" + add_foreign_key "billable_periods", "enterprises", name: "bill_items_enterprise_id_fk" + add_foreign_key "billable_periods", "spree_users", name: "bill_items_owner_id_fk", column: "owner_id" add_foreign_key "carts", "spree_users", name: "carts_user_id_fk", column: "user_id" @@ -1188,6 +1282,7 @@ ActiveRecord::Schema.define(:version => 20150527004427) do add_foreign_key "spree_option_values_variants", "spree_variants", name: "spree_option_values_variants_variant_id_fk", column: "variant_id" add_foreign_key "spree_orders", "carts", name: "spree_orders_cart_id_fk" + add_foreign_key "spree_orders", "customers", name: "spree_orders_customer_id_fk" add_foreign_key "spree_orders", "enterprises", name: "spree_orders_distributor_id_fk", column: "distributor_id" add_foreign_key "spree_orders", "order_cycles", name: "spree_orders_order_cycle_id_fk" add_foreign_key "spree_orders", "spree_addresses", name: "spree_orders_bill_address_id_fk", column: "bill_address_id" diff --git a/db/seeds.rb b/db/seeds.rb index 9e199be968..82f95651bd 100644 --- a/db/seeds.rb +++ b/db/seeds.rb @@ -26,7 +26,3 @@ unless Spree::State.find_by_name 'Victoria' Spree::State.create!({"name"=>state[0], "abbr"=>state[1], :country=>country}, :without_protection => true) end end - -# -- Suburbs -require_relative './suburb_seeds' -SuburbSeeder.seed_suburbs unless Suburb.find_by_name("Dayton") diff --git a/db/suburb_seeds.rb b/db/suburb_seeds.rb deleted file mode 100644 index cd07f46ed3..0000000000 --- a/db/suburb_seeds.rb +++ /dev/null @@ -1,16897 +0,0 @@ -module SuburbSeeder - def self.seed_suburbs - state_id_act = Spree::State.where(abbr: "ACT").first.id - state_id_nsw = Spree::State.where(abbr: "NSW").first.id - state_id_nt = Spree::State.where(abbr: "NT").first.id - state_id_qld = Spree::State.where(abbr: "QLD").first.id - state_id_sa = Spree::State.where(abbr: "SA").first.id - state_id_tas = Spree::State.where(abbr: "Tas").first.id - state_id_vic = Spree::State.where(abbr: "Vic").first.id - state_id_wa = Spree::State.where(abbr: "WA").first.id - - connection = ActiveRecord::Base.connection() - - puts "-- Seeding Australian suburbs" - connection.execute(" - INSERT INTO suburbs (postcode,name,state_id,latitude,longitude) VALUES - ($$200$$,$$AUSTRALIAN NATIONAL UNIVERSITY$$,#{state_id_act},-35.277272,149.117136), - ($$221$$,$$BARTON$$,#{state_id_act},-35.201372,149.095065), - ($$800$$,$$DARWIN$$,#{state_id_nt},-12.801028,130.955789), - ($$801$$,$$DARWIN$$,#{state_id_nt},-12.801028,130.955789), - ($$804$$,$$PARAP$$,#{state_id_nt},-12.432181,130.84331), - ($$810$$,$$ALAWA$$,#{state_id_nt},-12.378451,130.877014), - ($$810$$,$$BRINKIN$$,#{state_id_nt},-12.378451,130.877014), - ($$810$$,$$CASUARINA$$,#{state_id_nt},-12.378451,130.877014), - ($$810$$,$$COCONUT GROVE$$,#{state_id_nt},-12.378451,130.877014), - ($$810$$,$$JINGILI$$,#{state_id_nt},-12.378451,130.877014), - ($$810$$,$$LEE POINT$$,#{state_id_nt},-12.378451,130.877014), - ($$810$$,$$LYONS$$,#{state_id_nt},-12.378451,130.877014), - ($$810$$,$$MILLNER$$,#{state_id_nt},-12.378451,130.877014), - ($$810$$,$$MOIL$$,#{state_id_nt},-12.378451,130.877014), - ($$810$$,$$MUIRHEAD$$,#{state_id_nt},-12.378451,130.877014), - ($$810$$,$$NAKARA$$,#{state_id_nt},-12.378451,130.877014), - ($$810$$,$$NIGHTCLIFF$$,#{state_id_nt},-12.378451,130.877014), - ($$810$$,$$RAPID CREEK$$,#{state_id_nt},-12.378451,130.877014), - ($$810$$,$$TIWI$$,#{state_id_nt},-12.378451,130.877014), - ($$810$$,$$WAGAMAN$$,#{state_id_nt},-12.378451,130.877014), - ($$810$$,$$WANGURI$$,#{state_id_nt},-12.378451,130.877014), - ($$811$$,$$CASUARINA$$,#{state_id_nt},-12.376597,130.850489), - ($$812$$,$$ANULA$$,#{state_id_nt},-12.400091,130.913672), - ($$812$$,$$BUFFALO CREEK$$,#{state_id_nt},-12.400091,130.913672), - ($$812$$,$$HOLMES$$,#{state_id_nt},-12.400091,130.913672), - ($$812$$,$$KARAMA$$,#{state_id_nt},-12.400091,130.913672), - ($$812$$,$$LEANYER$$,#{state_id_nt},-12.400091,130.913672), - ($$812$$,$$MALAK$$,#{state_id_nt},-12.400091,130.913672), - ($$812$$,$$MARRARA$$,#{state_id_nt},-12.400091,130.913672), - ($$812$$,$$NORTHLAKES$$,#{state_id_nt},-12.400091,130.913672), - ($$812$$,$$SANDERSON$$,#{state_id_nt},-12.400091,130.913672), - ($$812$$,$$WOODLEIGH GARDENS$$,#{state_id_nt},-12.400091,130.913672), - ($$812$$,$$WULAGI$$,#{state_id_nt},-12.400091,130.913672), - ($$813$$,$$SANDERSON$$,#{state_id_nt},0.0,0.0), - ($$814$$,$$NIGHTCLIFF$$,#{state_id_nt},-12.382572,130.853877), - ($$815$$,$$CHARLES DARWIN UNIVERSITY$$,#{state_id_nt},0.0,0.0), - ($$820$$,$$BAGOT$$,#{state_id_nt},-12.410444,130.856124), - ($$820$$,$$BAYVIEW$$,#{state_id_nt},-12.410444,130.856124), - ($$820$$,$$CHARLES DARWIN$$,#{state_id_nt},-12.410444,130.856124), - ($$820$$,$$COONAWARRA$$,#{state_id_nt},-12.410444,130.856124), - ($$820$$,$$CULLEN BAY$$,#{state_id_nt},-12.410444,130.856124), - ($$820$$,$$DARWIN DC$$,#{state_id_nt},-12.410444,130.856124), - ($$820$$,$$DARWIN INTERNATIONAL AIRPORT$$,#{state_id_nt},-12.410444,130.856124), - ($$820$$,$$DARWIN MC$$,#{state_id_nt},-12.410444,130.856124), - ($$820$$,$$EAST POINT$$,#{state_id_nt},-12.410444,130.856124), - ($$820$$,$$EATON$$,#{state_id_nt},-12.410444,130.856124), - ($$820$$,$$FANNIE BAY$$,#{state_id_nt},-12.410444,130.856124), - ($$820$$,$$LARRAKEYAH$$,#{state_id_nt},-12.410444,130.856124), - ($$820$$,$$LUDMILLA$$,#{state_id_nt},-12.410444,130.856124), - ($$820$$,$$PARAP$$,#{state_id_nt},-12.410444,130.856124), - ($$820$$,$$RAAF BASE DARWIN$$,#{state_id_nt},-12.410444,130.856124), - ($$820$$,$$STUART PARK$$,#{state_id_nt},-12.410444,130.856124), - ($$820$$,$$THE GARDENS$$,#{state_id_nt},-12.410444,130.856124), - ($$820$$,$$THE NARROWS$$,#{state_id_nt},-12.410444,130.856124), - ($$820$$,$$WINNELLIE$$,#{state_id_nt},-12.410444,130.856124), - ($$820$$,$$WOOLNER$$,#{state_id_nt},-12.410444,130.856124), - ($$821$$,$$WINNELLIE$$,#{state_id_nt},-12.426641,130.882367), - ($$822$$,$$ACACIA HILLS$$,#{state_id_nt},-12.799278,131.131697), - ($$822$$,$$ANGURUGU$$,#{state_id_nt},-12.799278,131.131697), - ($$822$$,$$ANINDILYAKWA$$,#{state_id_nt},-12.799278,131.131697), - ($$822$$,$$ANNIE RIVER$$,#{state_id_nt},-12.799278,131.131697), - ($$822$$,$$BATHURST ISLAND$$,#{state_id_nt},-12.799278,131.131697), - ($$822$$,$$BEES CREEK$$,#{state_id_nt},-12.799278,131.131697), - ($$822$$,$$BELYUEN$$,#{state_id_nt},-12.799278,131.131697), - ($$822$$,$$BLACK JUNGLE$$,#{state_id_nt},-12.799278,131.131697), - ($$822$$,$$BLACKMORE$$,#{state_id_nt},-12.799278,131.131697), - ($$822$$,$$BORDER STORE$$,#{state_id_nt},-12.799278,131.131697), - ($$822$$,$$BURRUNDIE$$,#{state_id_nt},-12.799278,131.131697), - ($$822$$,$$BYNOE$$,#{state_id_nt},-12.799278,131.131697), - ($$822$$,$$BYNOE HARBOUR$$,#{state_id_nt},-12.799278,131.131697), - ($$822$$,$$CAMP CREEK$$,#{state_id_nt},-12.799278,131.131697), - ($$822$$,$$CHANNEL ISLAND$$,#{state_id_nt},-12.799278,131.131697), - ($$822$$,$$CHARLES DARWIN$$,#{state_id_nt},-12.799278,131.131697), - ($$822$$,$$CHARLOTTE$$,#{state_id_nt},-12.799278,131.131697), - ($$822$$,$$CLARAVALE$$,#{state_id_nt},-12.799278,131.131697), - ($$822$$,$$COBOURG$$,#{state_id_nt},-12.799278,131.131697), - ($$822$$,$$COLLETT CREEK$$,#{state_id_nt},-12.799278,131.131697), - ($$822$$,$$COOMALIE CREEK$$,#{state_id_nt},-12.799278,131.131697), - ($$822$$,$$COX PENINSULA$$,#{state_id_nt},-12.799278,131.131697), - ($$822$$,$$CROKER ISLAND$$,#{state_id_nt},-12.799278,131.131697), - ($$822$$,$$DALY$$,#{state_id_nt},-12.799278,131.131697), - ($$822$$,$$DALY RIVER$$,#{state_id_nt},-12.799278,131.131697), - ($$822$$,$$DARWIN MC$$,#{state_id_nt},-12.799278,131.131697), - ($$822$$,$$DARWIN RIVER DAM$$,#{state_id_nt},-12.799278,131.131697), - ($$822$$,$$DELISSAVILLE$$,#{state_id_nt},-12.799278,131.131697), - ($$822$$,$$DOUGLAS-DALY$$,#{state_id_nt},-12.799278,131.131697), - ($$822$$,$$EAST ARM$$,#{state_id_nt},-12.799278,131.131697), - ($$822$$,$$EAST ARNHEM$$,#{state_id_nt},-12.799278,131.131697), - ($$822$$,$$ELRUNDIE$$,#{state_id_nt},-12.799278,131.131697), - ($$822$$,$$EVA VALLEY$$,#{state_id_nt},-12.799278,131.131697), - ($$822$$,$$FINNISS VALLEY$$,#{state_id_nt},-12.799278,131.131697), - ($$822$$,$$FLEMING$$,#{state_id_nt},-12.799278,131.131697), - ($$822$$,$$FLY CREEK$$,#{state_id_nt},-12.799278,131.131697), - ($$822$$,$$FREDS PASS$$,#{state_id_nt},-12.799278,131.131697), - ($$822$$,$$GALIWINKU$$,#{state_id_nt},-12.799278,131.131697), - ($$822$$,$$GLYDE POINT$$,#{state_id_nt},-12.799278,131.131697), - ($$822$$,$$GOULBURN ISLAND$$,#{state_id_nt},-12.799278,131.131697), - ($$822$$,$$GUNBALANYA$$,#{state_id_nt},-12.799278,131.131697), - ($$822$$,$$GUNN POINT$$,#{state_id_nt},-12.799278,131.131697), - ($$822$$,$$HAYES CREEK$$,#{state_id_nt},-12.799278,131.131697), - ($$822$$,$$HIDDEN VALLEY$$,#{state_id_nt},-12.799278,131.131697), - ($$822$$,$$HOTHAM$$,#{state_id_nt},-12.799278,131.131697), - ($$822$$,$$HUGHES$$,#{state_id_nt},-12.799278,131.131697), - ($$822$$,$$KAKADU$$,#{state_id_nt},-12.799278,131.131697), - ($$822$$,$$KOOLPINYAH$$,#{state_id_nt},-12.799278,131.131697), - ($$822$$,$$LAKE BENNETT$$,#{state_id_nt},-12.799278,131.131697), - ($$822$$,$$LAMBELLS LAGOON$$,#{state_id_nt},-12.799278,131.131697), - ($$822$$,$$LITCHFIELD PARK$$,#{state_id_nt},-12.799278,131.131697), - ($$822$$,$$LIVINGSTONE$$,#{state_id_nt},-12.799278,131.131697), - ($$822$$,$$LLOYD CREEK$$,#{state_id_nt},-12.799278,131.131697), - ($$822$$,$$MANDORAH$$,#{state_id_nt},-12.799278,131.131697), - ($$822$$,$$MANINGRIDA$$,#{state_id_nt},-12.799278,131.131697), - ($$822$$,$$MARANUNGA$$,#{state_id_nt},-12.799278,131.131697), - ($$822$$,$$MARGARET RIVER$$,#{state_id_nt},-12.799278,131.131697), - ($$822$$,$$MARRAKAI$$,#{state_id_nt},-12.799278,131.131697), - ($$822$$,$$MCMINNS LAGOON$$,#{state_id_nt},-12.799278,131.131697), - ($$822$$,$$MICKETT CREEK$$,#{state_id_nt},-12.799278,131.131697), - ($$822$$,$$MIDDLE POINT$$,#{state_id_nt},-12.799278,131.131697), - ($$822$$,$$MILIKAPITI$$,#{state_id_nt},-12.799278,131.131697), - ($$822$$,$$MILINGIMBI$$,#{state_id_nt},-12.799278,131.131697), - ($$822$$,$$MILLWOOD$$,#{state_id_nt},-12.799278,131.131697), - ($$822$$,$$MILYAKBURRA$$,#{state_id_nt},-12.799278,131.131697), - ($$822$$,$$MINJILANG$$,#{state_id_nt},-12.799278,131.131697), - ($$822$$,$$MOUNT BUNDEY$$,#{state_id_nt},-12.799278,131.131697), - ($$822$$,$$MURRUMUJUK$$,#{state_id_nt},-12.799278,131.131697), - ($$822$$,$$NAUIYU$$,#{state_id_nt},-12.799278,131.131697), - ($$822$$,$$NEMARLUK$$,#{state_id_nt},-12.799278,131.131697), - ($$822$$,$$NGANMARRIYANGA$$,#{state_id_nt},-12.799278,131.131697), - ($$822$$,$$NGUIU$$,#{state_id_nt},-12.799278,131.131697), - ($$822$$,$$NUMBULWAR$$,#{state_id_nt},-12.799278,131.131697), - ($$822$$,$$NUMBURINDI$$,#{state_id_nt},-12.799278,131.131697), - ($$822$$,$$OENPELLI$$,#{state_id_nt},-12.799278,131.131697), - ($$822$$,$$PALUMPA$$,#{state_id_nt},-12.799278,131.131697), - ($$822$$,$$PEPPIMENARTI$$,#{state_id_nt},-12.799278,131.131697), - ($$822$$,$$PIRLANGIMPI$$,#{state_id_nt},-12.799278,131.131697), - ($$822$$,$$POINT STEPHENS$$,#{state_id_nt},-12.799278,131.131697), - ($$822$$,$$POINT STUART$$,#{state_id_nt},-12.799278,131.131697), - ($$822$$,$$PULARUMPI$$,#{state_id_nt},-12.799278,131.131697), - ($$822$$,$$RAKULA$$,#{state_id_nt},-12.799278,131.131697), - ($$822$$,$$RAMINGINING$$,#{state_id_nt},-12.799278,131.131697), - ($$822$$,$$ROBIN FALLS$$,#{state_id_nt},-12.799278,131.131697), - ($$822$$,$$RUM JUNGLE$$,#{state_id_nt},-12.799278,131.131697), - ($$822$$,$$SANDPALMS ROADHOUSE$$,#{state_id_nt},-12.799278,131.131697), - ($$822$$,$$SOUTHPORT$$,#{state_id_nt},-12.799278,131.131697), - ($$822$$,$$STAPLETON$$,#{state_id_nt},-12.799278,131.131697), - ($$822$$,$$THAMARRURR$$,#{state_id_nt},-12.799278,131.131697), - ($$822$$,$$TIPPERARY$$,#{state_id_nt},-12.799278,131.131697), - ($$822$$,$$TIVENDALE$$,#{state_id_nt},-12.799278,131.131697), - ($$822$$,$$TIWI ISLANDS$$,#{state_id_nt},-12.799278,131.131697), - ($$822$$,$$TORTILLA FLATS$$,#{state_id_nt},-12.799278,131.131697), - ($$822$$,$$TUMBLING WATERS$$,#{state_id_nt},-12.799278,131.131697), - ($$822$$,$$UMBAKUMBA$$,#{state_id_nt},-12.799278,131.131697), - ($$822$$,$$VERNON ISLANDS$$,#{state_id_nt},-12.799278,131.131697), - ($$822$$,$$WADEYE$$,#{state_id_nt},-12.799278,131.131697), - ($$822$$,$$WAGAIT BEACH$$,#{state_id_nt},-12.799278,131.131697), - ($$822$$,$$WAK WAK$$,#{state_id_nt},-12.799278,131.131697), - ($$822$$,$$WARRUWI$$,#{state_id_nt},-12.799278,131.131697), - ($$822$$,$$WEDDELL$$,#{state_id_nt},-12.799278,131.131697), - ($$822$$,$$WEST ARNHEM$$,#{state_id_nt},-12.799278,131.131697), - ($$822$$,$$WICKHAM$$,#{state_id_nt},-12.799278,131.131697), - ($$822$$,$$WINNELLIE$$,#{state_id_nt},-12.799278,131.131697), - ($$822$$,$$WISHART$$,#{state_id_nt},-12.799278,131.131697), - ($$822$$,$$WOOLANING$$,#{state_id_nt},-12.799278,131.131697), - ($$828$$,$$BERRIMAH$$,#{state_id_nt},-12.474896,130.907378), - ($$828$$,$$KNUCKEY LAGOON$$,#{state_id_nt},-12.474896,130.907378), - ($$829$$,$$HOLTZE$$,#{state_id_nt},-14.460879,132.280002), - ($$829$$,$$PINELANDS$$,#{state_id_nt},-14.460879,132.280002), - ($$830$$,$$ARCHER$$,#{state_id_nt},-12.487233,130.972637), - ($$830$$,$$DRIVER$$,#{state_id_nt},-12.487233,130.972637), - ($$830$$,$$DURACK$$,#{state_id_nt},-12.487233,130.972637), - ($$830$$,$$FARRAR$$,#{state_id_nt},-12.487233,130.972637), - ($$830$$,$$GRAY$$,#{state_id_nt},-12.487233,130.972637), - ($$830$$,$$MARLOW LAGOON$$,#{state_id_nt},-12.487233,130.972637), - ($$830$$,$$MOULDEN$$,#{state_id_nt},-12.487233,130.972637), - ($$830$$,$$PALMERSTON$$,#{state_id_nt},-12.487233,130.972637), - ($$830$$,$$SHOAL BAY$$,#{state_id_nt},-12.487233,130.972637), - ($$830$$,$$WOODROFFE$$,#{state_id_nt},-12.487233,130.972637), - ($$830$$,$$YARRAWONGA$$,#{state_id_nt},-12.487233,130.972637), - ($$831$$,$$PALMERSTON$$,#{state_id_nt},-12.480066,130.984006), - ($$832$$,$$BAKEWELL$$,#{state_id_nt},-12.492269,130.990891), - ($$832$$,$$BELLAMACK$$,#{state_id_nt},-12.492269,130.990891), - ($$832$$,$$GUNN$$,#{state_id_nt},-12.492269,130.990891), - ($$832$$,$$JOHNSTON$$,#{state_id_nt},-12.492269,130.990891), - ($$832$$,$$MITCHELL$$,#{state_id_nt},-12.492269,130.990891), - ($$832$$,$$ROSEBERY$$,#{state_id_nt},-12.492269,130.990891), - ($$832$$,$$ROSEBERY HEIGHTS$$,#{state_id_nt},-12.492269,130.990891), - ($$832$$,$$ZUCCOLI$$,#{state_id_nt},-12.492269,130.990891), - ($$835$$,$$COOLALINGA$$,#{state_id_nt},-12.48138,131.029173), - ($$835$$,$$HOWARD SPRINGS$$,#{state_id_nt},-12.48138,131.029173), - ($$835$$,$$VIRGINIA$$,#{state_id_nt},-12.48138,131.029173), - ($$836$$,$$GIRRAWEEN$$,#{state_id_nt},-12.525546,131.103025), - ($$836$$,$$HERBERT$$,#{state_id_nt},-12.525546,131.103025), - ($$836$$,$$HUMPTY DOO$$,#{state_id_nt},-12.525546,131.103025), - ($$837$$,$$MANTON$$,#{state_id_nt},-12.460094,130.842663), - ($$837$$,$$NOONAMAH$$,#{state_id_nt},-12.460094,130.842663), - ($$838$$,$$BERRY SPRINGS$$,#{state_id_nt},-12.709507,130.995407), - ($$840$$,$$DUNDEE BEACH$$,#{state_id_nt},-12.717562,130.351316), - ($$840$$,$$DUNDEE DOWNS$$,#{state_id_nt},-12.717562,130.351316), - ($$840$$,$$DUNDEE FOREST$$,#{state_id_nt},-12.717562,130.351316), - ($$841$$,$$DARWIN RIVER$$,#{state_id_nt},-12.801028,130.955789), - ($$845$$,$$BATCHELOR$$,#{state_id_nt},-13.038663,131.072091), - ($$846$$,$$ADELAIDE RIVER$$,#{state_id_nt},-13.226806,131.098416), - ($$847$$,$$PINE CREEK$$,#{state_id_nt},-13.824123,131.835799), - ($$850$$,$$COSSACK$$,#{state_id_nt},-14.464497,132.262021), - ($$850$$,$$EMUNGALAN$$,#{state_id_nt},-14.464497,132.262021), - ($$850$$,$$KATHERINE$$,#{state_id_nt},-14.464497,132.262021), - ($$850$$,$$KATHERINE EAST$$,#{state_id_nt},-14.464497,132.262021), - ($$850$$,$$KATHERINE SOUTH$$,#{state_id_nt},-14.464497,132.262021), - ($$850$$,$$LANSDOWNE$$,#{state_id_nt},-14.464497,132.262021), - ($$851$$,$$KATHERINE$$,#{state_id_nt},-14.464497,132.262021), - ($$852$$,$$ARNOLD$$,#{state_id_nt},-14.92267,133.064654), - ($$852$$,$$BAINES$$,#{state_id_nt},-14.92267,133.064654), - ($$852$$,$$BARUNGA$$,#{state_id_nt},-14.92267,133.064654), - ($$852$$,$$BESWICK$$,#{state_id_nt},-14.92267,133.064654), - ($$852$$,$$BESWICK CREEK$$,#{state_id_nt},-14.92267,133.064654), - ($$852$$,$$BINJARI$$,#{state_id_nt},-14.92267,133.064654), - ($$852$$,$$BIRDUM$$,#{state_id_nt},-14.92267,133.064654), - ($$852$$,$$BRADSHAW$$,#{state_id_nt},-14.92267,133.064654), - ($$852$$,$$BUCHANAN$$,#{state_id_nt},-14.92267,133.064654), - ($$852$$,$$BULMAN WEEMOL$$,#{state_id_nt},-14.92267,133.064654), - ($$852$$,$$CRESWELL$$,#{state_id_nt},-14.92267,133.064654), - ($$852$$,$$DAGURAGU$$,#{state_id_nt},-14.92267,133.064654), - ($$852$$,$$DALY WATERS$$,#{state_id_nt},-14.92267,133.064654), - ($$852$$,$$DELAMERE$$,#{state_id_nt},-14.92267,133.064654), - ($$852$$,$$DUNMARRA$$,#{state_id_nt},-14.92267,133.064654), - ($$852$$,$$EDITH$$,#{state_id_nt},-14.92267,133.064654), - ($$852$$,$$ELSEY$$,#{state_id_nt},-14.92267,133.064654), - ($$852$$,$$ELSEY STATION$$,#{state_id_nt},-14.92267,133.064654), - ($$852$$,$$FLORINA$$,#{state_id_nt},-14.92267,133.064654), - ($$852$$,$$FLYING FOX$$,#{state_id_nt},-14.92267,133.064654), - ($$852$$,$$GREGORY$$,#{state_id_nt},-14.92267,133.064654), - ($$852$$,$$GULUNG MARDRULK$$,#{state_id_nt},-14.92267,133.064654), - ($$852$$,$$GURINDJI$$,#{state_id_nt},-14.92267,133.064654), - ($$852$$,$$JILKMINGGAN$$,#{state_id_nt},-14.92267,133.064654), - ($$852$$,$$KALKARINDJI$$,#{state_id_nt},-14.92267,133.064654), - ($$852$$,$$KATHERINE$$,#{state_id_nt},-14.92267,133.064654), - ($$852$$,$$LAJAMANU$$,#{state_id_nt},-14.92267,133.064654), - ($$852$$,$$LARRIMAH$$,#{state_id_nt},-14.92267,133.064654), - ($$852$$,$$LIMMEN$$,#{state_id_nt},-14.92267,133.064654), - ($$852$$,$$MANBULLOO$$,#{state_id_nt},-14.92267,133.064654), - ($$852$$,$$MARANBOY$$,#{state_id_nt},-14.92267,133.064654), - ($$852$$,$$MATARANKA$$,#{state_id_nt},-14.92267,133.064654), - ($$852$$,$$MCARTHUR$$,#{state_id_nt},-14.92267,133.064654), - ($$852$$,$$MINIYERI$$,#{state_id_nt},-14.92267,133.064654), - ($$852$$,$$NGUKURR$$,#{state_id_nt},-14.92267,133.064654), - ($$852$$,$$NITMILUK$$,#{state_id_nt},-14.92267,133.064654), - ($$852$$,$$PELLEW ISLANDS$$,#{state_id_nt},-14.92267,133.064654), - ($$852$$,$$PIGEON HOLE$$,#{state_id_nt},-14.92267,133.064654), - ($$852$$,$$ROBINSON RIVER$$,#{state_id_nt},-14.92267,133.064654), - ($$852$$,$$STURT PLATEAU$$,#{state_id_nt},-14.92267,133.064654), - ($$852$$,$$TANAMI EAST$$,#{state_id_nt},-14.92267,133.064654), - ($$852$$,$$TIMBER CREEK$$,#{state_id_nt},-14.92267,133.064654), - ($$852$$,$$TOP SPRINGS$$,#{state_id_nt},-14.92267,133.064654), - ($$852$$,$$URALLA$$,#{state_id_nt},-14.92267,133.064654), - ($$852$$,$$VENN$$,#{state_id_nt},-14.92267,133.064654), - ($$852$$,$$VICTORIA RIVER$$,#{state_id_nt},-14.92267,133.064654), - ($$852$$,$$VICTORIA RIVER DOWNS$$,#{state_id_nt},-14.92267,133.064654), - ($$852$$,$$WARUMUNGU$$,#{state_id_nt},-14.92267,133.064654), - ($$852$$,$$WAVE HILL$$,#{state_id_nt},-14.92267,133.064654), - ($$852$$,$$WILTON$$,#{state_id_nt},-14.92267,133.064654), - ($$852$$,$$YARRALIN$$,#{state_id_nt},-14.92267,133.064654), - ($$853$$,$$TINDAL$$,#{state_id_nt},0.0,0.0), - ($$854$$,$$BORROLOOLA$$,#{state_id_nt},-16.81839,137.14707), - ($$854$$,$$KING ASH BAY$$,#{state_id_nt},-16.81839,137.14707), - ($$860$$,$$TENNANT CREEK$$,#{state_id_nt},-19.648306,134.186642), - ($$861$$,$$BRUNCHILLY$$,#{state_id_nt},-18.94406,134.318373), - ($$861$$,$$TENNANT CREEK$$,#{state_id_nt},-18.94406,134.318373), - ($$862$$,$$AVON DOWNS$$,#{state_id_nt},-20.231104,137.762232), - ($$862$$,$$CALVERT$$,#{state_id_nt},-20.231104,137.762232), - ($$862$$,$$CRESSWELL DOWNS$$,#{state_id_nt},-20.231104,137.762232), - ($$862$$,$$ELLIOTT$$,#{state_id_nt},-20.231104,137.762232), - ($$862$$,$$HELEN SPRINGS$$,#{state_id_nt},-20.231104,137.762232), - ($$862$$,$$MUCKATY STATION$$,#{state_id_nt},-20.231104,137.762232), - ($$862$$,$$NEWCASTLE WATERS$$,#{state_id_nt},-20.231104,137.762232), - ($$862$$,$$NICHOLSON$$,#{state_id_nt},-20.231104,137.762232), - ($$862$$,$$PAMAYU$$,#{state_id_nt},-20.231104,137.762232), - ($$862$$,$$PHILLIP CREEK STATION$$,#{state_id_nt},-20.231104,137.762232), - ($$862$$,$$RENNER SPRINGS$$,#{state_id_nt},-20.231104,137.762232), - ($$862$$,$$TABLELANDS$$,#{state_id_nt},-20.231104,137.762232), - ($$862$$,$$TENNANT CREEK$$,#{state_id_nt},-20.231104,137.762232), - ($$862$$,$$THREE WAYS$$,#{state_id_nt},-20.231104,137.762232), - ($$862$$,$$WARREGO$$,#{state_id_nt},-20.231104,137.762232), - ($$862$$,$$WOLLOGORANG STATION$$,#{state_id_nt},-20.231104,137.762232), - ($$862$$,$$WYCLIFFE WELL$$,#{state_id_nt},-20.231104,137.762232), - ($$870$$,$$ALICE SPRINGS$$,#{state_id_nt},-12.436101,130.84059), - ($$870$$,$$ARALUEN$$,#{state_id_nt},-12.436101,130.84059), - ($$870$$,$$ARUMBERA$$,#{state_id_nt},-12.436101,130.84059), - ($$870$$,$$BRAITLING$$,#{state_id_nt},-12.436101,130.84059), - ($$870$$,$$CICCONE$$,#{state_id_nt},-12.436101,130.84059), - ($$870$$,$$CONNELLAN$$,#{state_id_nt},-12.436101,130.84059), - ($$870$$,$$DESERT SPRINGS$$,#{state_id_nt},-12.436101,130.84059), - ($$870$$,$$EAST SIDE$$,#{state_id_nt},-12.436101,130.84059), - ($$870$$,$$FLYNN$$,#{state_id_nt},-12.436101,130.84059), - ($$870$$,$$GILLEN$$,#{state_id_nt},-12.436101,130.84059), - ($$870$$,$$ILPARPA$$,#{state_id_nt},-12.436101,130.84059), - ($$870$$,$$IRLPME$$,#{state_id_nt},-12.436101,130.84059), - ($$870$$,$$LARAPINTA$$,#{state_id_nt},-12.436101,130.84059), - ($$870$$,$$MOUNT JOHNS$$,#{state_id_nt},-12.436101,130.84059), - ($$870$$,$$ROSS$$,#{state_id_nt},-12.436101,130.84059), - ($$870$$,$$SADADEEN$$,#{state_id_nt},-12.436101,130.84059), - ($$870$$,$$STUART$$,#{state_id_nt},-12.436101,130.84059), - ($$870$$,$$THE GAP$$,#{state_id_nt},-12.436101,130.84059), - ($$870$$,$$UNDOOLYA$$,#{state_id_nt},-12.436101,130.84059), - ($$870$$,$$WHITE GUMS$$,#{state_id_nt},-12.436101,130.84059), - ($$871$$,$$ALICE SPRINGS$$,#{state_id_nt},-12.436101,130.84059), - ($$872$$,$$AHERRENGE$$,#{state_id_nt},-20.998545,134.3822), - ($$872$$,$$ALI CURUNG$$,#{state_id_nt},-20.998545,134.3822), - ($$872$$,$$ALICE SPRINGS$$,#{state_id_nt},-20.998545,134.3822), - ($$872$$,$$AMATA$$,#{state_id_nt},-20.998545,134.3822), - ($$872$$,$$AMOONGUNA$$,#{state_id_nt},-20.998545,134.3822), - ($$872$$,$$AMPILATWATJA$$,#{state_id_nt},-20.998545,134.3822), - ($$872$$,$$ANATYE$$,#{state_id_nt},-20.998545,134.3822), - ($$872$$,$$ANMATJERE$$,#{state_id_nt},-20.998545,134.3822), - ($$872$$,$$ANTEWENEGERRDE$$,#{state_id_nt},-20.998545,134.3822), - ($$872$$,$$AREYONGA$$,#{state_id_nt},-20.998545,134.3822), - ($$872$$,$$ATITJERE$$,#{state_id_nt},-20.998545,134.3822), - ($$872$$,$$AYERS ROCK$$,#{state_id_nt},-20.998545,134.3822), - ($$872$$,$$BARROW CREEK$$,#{state_id_nt},-20.998545,134.3822), - ($$872$$,$$BURT PLAIN$$,#{state_id_nt},-20.998545,134.3822), - ($$872$$,$$CANTEEN CREEK$$,#{state_id_nt},-20.998545,134.3822), - ($$872$$,$$CHILLA WELL$$,#{state_id_nt},-20.998545,134.3822), - ($$872$$,$$COSTELLO$$,#{state_id_nt},-20.998545,134.3822), - ($$872$$,$$DAVENPORT$$,#{state_id_nt},-20.998545,134.3822), - ($$872$$,$$DOCKER RIVER$$,#{state_id_nt},-20.998545,134.3822), - ($$872$$,$$ENGAWALA$$,#{state_id_nt},-20.998545,134.3822), - ($$872$$,$$ERLDUNDA$$,#{state_id_nt},-20.998545,134.3822), - ($$872$$,$$ERNABELLA$$,#{state_id_sa},-20.998545,134.3822), - ($$872$$,$$FINKE$$,#{state_id_nt},-20.998545,134.3822), - ($$872$$,$$FREGON$$,#{state_id_sa},-20.998545,134.3822), - ($$872$$,$$GHAN$$,#{state_id_nt},-20.998545,134.3822), - ($$872$$,$$GIBSON DESERT NORTH$$,#{state_id_wa},-20.998545,134.3822), - ($$872$$,$$GIBSON DESERT SOUTH$$,#{state_id_wa},-20.998545,134.3822), - ($$872$$,$$HAASTS BLUFF$$,#{state_id_nt},-20.998545,134.3822), - ($$872$$,$$HALE$$,#{state_id_nt},-20.998545,134.3822), - ($$872$$,$$HART$$,#{state_id_nt},-20.998545,134.3822), - ($$872$$,$$HART RANGE$$,#{state_id_nt},-20.998545,134.3822), - ($$872$$,$$HERMANNSBURG$$,#{state_id_nt},-20.998545,134.3822), - ($$872$$,$$HUGH$$,#{state_id_nt},-20.998545,134.3822), - ($$872$$,$$IMANPA$$,#{state_id_nt},-20.998545,134.3822), - ($$872$$,$$INDULKANA$$,#{state_id_sa},-20.998545,134.3822), - ($$872$$,$$ININTI STORE$$,#{state_id_nt},-20.998545,134.3822), - ($$872$$,$$JAY CREEK$$,#{state_id_nt},-20.998545,134.3822), - ($$872$$,$$KALTUKATJARA$$,#{state_id_nt},-20.998545,134.3822), - ($$872$$,$$KINTORE$$,#{state_id_nt},-20.998545,134.3822), - ($$872$$,$$KIWIRRKURRA$$,#{state_id_nt},-20.998545,134.3822), - ($$872$$,$$KULGERA$$,#{state_id_nt},-20.998545,134.3822), - ($$872$$,$$KUNPARRKA$$,#{state_id_nt},-20.998545,134.3822), - ($$872$$,$$LAKE MACKAY$$,#{state_id_nt},-20.998545,134.3822), - ($$872$$,$$LARAMBA$$,#{state_id_nt},-20.998545,134.3822), - ($$872$$,$$MEREENIE$$,#{state_id_nt},-20.998545,134.3822), - ($$872$$,$$MIMILI$$,#{state_id_sa},-20.998545,134.3822), - ($$872$$,$$MOUNT LIEBIG$$,#{state_id_nt},-20.998545,134.3822), - ($$872$$,$$MOUNT ZEIL$$,#{state_id_nt},-20.998545,134.3822), - ($$872$$,$$MULGA BORE$$,#{state_id_nt},-20.998545,134.3822), - ($$872$$,$$MURPUTJA HOMELANDS$$,#{state_id_nt},-20.998545,134.3822), - ($$872$$,$$MUTITJULU$$,#{state_id_nt},-20.998545,134.3822), - ($$872$$,$$NAMATJIRA$$,#{state_id_nt},-20.998545,134.3822), - ($$872$$,$$NGAANYATJARRA-GILES$$,#{state_id_wa},-20.998545,134.3822), - ($$872$$,$$NYAPARI$$,#{state_id_nt},-20.998545,134.3822), - ($$872$$,$$NYIRRIPI$$,#{state_id_nt},-20.998545,134.3822), - ($$872$$,$$PAPUNYA$$,#{state_id_nt},-20.998545,134.3822), - ($$872$$,$$PATJARR$$,#{state_id_nt},-20.998545,134.3822), - ($$872$$,$$PETERMANN$$,#{state_id_nt},-20.998545,134.3822), - ($$872$$,$$PITJANTJATJARA HOMELANDS$$,#{state_id_nt},-20.998545,134.3822), - ($$872$$,$$SANDOVER$$,#{state_id_nt},-20.998545,134.3822), - ($$872$$,$$SANTA TERESA$$,#{state_id_nt},-20.998545,134.3822), - ($$872$$,$$SIMPSON$$,#{state_id_nt},-20.998545,134.3822), - ($$872$$,$$TANAMI$$,#{state_id_nt},-20.998545,134.3822), - ($$872$$,$$TARA$$,#{state_id_nt},-20.998545,134.3822), - ($$872$$,$$THANGKENHARENGE$$,#{state_id_nt},-20.998545,134.3822), - ($$872$$,$$TI TREE$$,#{state_id_nt},-20.998545,134.3822), - ($$872$$,$$TITJIKALA$$,#{state_id_nt},-20.998545,134.3822), - ($$872$$,$$TJIRRKARLI$$,#{state_id_nt},-20.998545,134.3822), - ($$872$$,$$TJUKURLA$$,#{state_id_nt},-20.998545,134.3822), - ($$872$$,$$ULURU$$,#{state_id_nt},-20.998545,134.3822), - ($$872$$,$$UMPANGARA$$,#{state_id_nt},-20.998545,134.3822), - ($$872$$,$$URAPUNTJA$$,#{state_id_nt},-20.998545,134.3822), - ($$872$$,$$WALLACE ROCKHOLE$$,#{state_id_nt},-20.998545,134.3822), - ($$872$$,$$WANARN$$,#{state_id_nt},-20.998545,134.3822), - ($$872$$,$$WILLOWRA$$,#{state_id_nt},-20.998545,134.3822), - ($$872$$,$$WILORA$$,#{state_id_nt},-20.998545,134.3822), - ($$872$$,$$WINGELLINA$$,#{state_id_nt},-20.998545,134.3822), - ($$872$$,$$WUTUNUGURRA$$,#{state_id_nt},-20.998545,134.3822), - ($$872$$,$$YUELAMU$$,#{state_id_nt},-20.998545,134.3822), - ($$872$$,$$YUENDUMU$$,#{state_id_nt},-20.998545,134.3822), - ($$872$$,$$YULARA$$,#{state_id_nt},-20.998545,134.3822), - ($$880$$,$$GAPUWIYAK$$,#{state_id_nt},-12.378064,130.871791), - ($$880$$,$$GOVE$$,#{state_id_nt},-12.378064,130.871791), - ($$880$$,$$GUNYANGARA$$,#{state_id_nt},-12.378064,130.871791), - ($$880$$,$$NHULUNBUY$$,#{state_id_nt},-12.378064,130.871791), - ($$880$$,$$YIRRKALA$$,#{state_id_nt},-12.378064,130.871791), - ($$881$$,$$NHULUNBUY$$,#{state_id_nt},-12.18421,136.783889), - ($$885$$,$$ALYANGULA$$,#{state_id_nt},0.0,0.0), - ($$886$$,$$JABIRU$$,#{state_id_nt},-12.381028,130.893097), - ($$906$$,$$WINNELLIE$$,#{state_id_nt},-12.426641,130.882367), - ($$907$$,$$WINNELLIE$$,#{state_id_nt},-12.426641,130.882367), - ($$909$$,$$CHARLES DARWIN UNIVERSITY$$,#{state_id_nt},0.0,0.0), - ($$1001$$,$$SYDNEY$$,#{state_id_nsw},-33.794883,151.268071), - ($$1002$$,$$SYDNEY$$,#{state_id_nsw},-33.794883,151.268071), - ($$1003$$,$$SYDNEY$$,#{state_id_nsw},-33.794883,151.268071), - ($$1004$$,$$SYDNEY$$,#{state_id_nsw},-33.794883,151.268071), - ($$1005$$,$$SYDNEY$$,#{state_id_nsw},-33.794883,151.268071), - ($$1006$$,$$SYDNEY$$,#{state_id_nsw},-33.794883,151.268071), - ($$1007$$,$$SYDNEY$$,#{state_id_nsw},-33.794883,151.268071), - ($$1008$$,$$SYDNEY$$,#{state_id_nsw},-33.794883,151.268071), - ($$1009$$,$$SYDNEY$$,#{state_id_nsw},-33.794883,151.268071), - ($$1010$$,$$SYDNEY$$,#{state_id_nsw},-33.794883,151.268071), - ($$1011$$,$$SYDNEY$$,#{state_id_nsw},-33.794883,151.268071), - ($$1020$$,$$SYDNEY$$,#{state_id_nsw},-33.794883,151.268071), - ($$1021$$,$$SYDNEY$$,#{state_id_nsw},-33.794883,151.268071), - ($$1022$$,$$SYDNEY$$,#{state_id_nsw},-33.794883,151.268071), - ($$1023$$,$$SYDNEY$$,#{state_id_nsw},-33.794883,151.268071), - ($$1025$$,$$SYDNEY$$,#{state_id_nsw},-33.794883,151.268071), - ($$1026$$,$$SYDNEY$$,#{state_id_nsw},-33.794883,151.268071), - ($$1027$$,$$SYDNEY$$,#{state_id_nsw},-33.794883,151.268071), - ($$1028$$,$$SYDNEY$$,#{state_id_nsw},-33.794883,151.268071), - ($$1029$$,$$SYDNEY$$,#{state_id_nsw},-33.794883,151.268071), - ($$1030$$,$$SYDNEY$$,#{state_id_nsw},-33.794883,151.268071), - ($$1031$$,$$SYDNEY$$,#{state_id_nsw},-33.794883,151.268071), - ($$1032$$,$$SYDNEY$$,#{state_id_nsw},-33.662834,150.874182), - ($$1033$$,$$SYDNEY$$,#{state_id_nsw},-33.794883,151.268071), - ($$1034$$,$$SYDNEY$$,#{state_id_nsw},-33.794883,151.268071), - ($$1035$$,$$SYDNEY$$,#{state_id_nsw},-33.794883,151.268071), - ($$1036$$,$$SYDNEY$$,#{state_id_nsw},-33.794883,151.268071), - ($$1037$$,$$SYDNEY$$,#{state_id_nsw},-33.794883,151.268071), - ($$1038$$,$$SYDNEY$$,#{state_id_nsw},-33.794883,151.268071), - ($$1039$$,$$SYDNEY$$,#{state_id_nsw},-33.794883,151.268071), - ($$1040$$,$$SYDNEY$$,#{state_id_nsw},-33.794883,151.268071), - ($$1041$$,$$SYDNEY$$,#{state_id_nsw},-33.794883,151.268071), - ($$1042$$,$$SYDNEY$$,#{state_id_nsw},-33.794883,151.268071), - ($$1043$$,$$SYDNEY$$,#{state_id_nsw},-33.794883,151.268071), - ($$1044$$,$$SYDNEY$$,#{state_id_nsw},-33.794883,151.268071), - ($$1045$$,$$SYDNEY$$,#{state_id_nsw},-33.794883,151.268071), - ($$1046$$,$$SYDNEY$$,#{state_id_nsw},-33.794883,151.268071), - ($$1100$$,$$SYDNEY$$,#{state_id_nsw},-33.794883,151.268071), - ($$1101$$,$$SYDNEY$$,#{state_id_nsw},-33.794883,151.268071), - ($$1105$$,$$SYDNEY$$,#{state_id_nsw},-33.794883,151.268071), - ($$1106$$,$$SYDNEY$$,#{state_id_nsw},-33.794883,151.268071), - ($$1107$$,$$SYDNEY$$,#{state_id_nsw},-33.794883,151.268071), - ($$1108$$,$$SYDNEY$$,#{state_id_nsw},-33.794883,151.268071), - ($$1109$$,$$SYDNEY$$,#{state_id_nsw},-33.794883,151.268071), - ($$1110$$,$$SYDNEY$$,#{state_id_nsw},-33.794883,151.268071), - ($$1112$$,$$SYDNEY$$,#{state_id_nsw},-33.794883,151.268071), - ($$1113$$,$$SYDNEY$$,#{state_id_nsw},-33.794883,151.268071), - ($$1114$$,$$SYDNEY$$,#{state_id_nsw},-33.794883,151.268071), - ($$1115$$,$$SYDNEY$$,#{state_id_nsw},-33.794883,151.268071), - ($$1116$$,$$SYDNEY$$,#{state_id_nsw},-33.666729,150.866145), - ($$1117$$,$$SYDNEY$$,#{state_id_nsw},-33.664575,150.87022), - ($$1118$$,$$SYDNEY$$,#{state_id_nsw},-33.794883,151.268071), - ($$1119$$,$$SYDNEY$$,#{state_id_nsw},-33.794883,151.268071), - ($$1120$$,$$SYDNEY$$,#{state_id_nsw},-33.794883,151.268071), - ($$1121$$,$$SYDNEY$$,#{state_id_nsw},-33.794883,151.268071), - ($$1122$$,$$SYDNEY$$,#{state_id_nsw},-33.794883,151.268071), - ($$1123$$,$$SYDNEY$$,#{state_id_nsw},-33.794883,151.268071), - ($$1124$$,$$SYDNEY$$,#{state_id_nsw},-33.794883,151.268071), - ($$1125$$,$$SYDNEY$$,#{state_id_nsw},-33.794883,151.268071), - ($$1126$$,$$SYDNEY$$,#{state_id_nsw},-33.794883,151.268071), - ($$1127$$,$$SYDNEY$$,#{state_id_nsw},-33.794883,151.268071), - ($$1128$$,$$SYDNEY$$,#{state_id_nsw},-33.794883,151.268071), - ($$1129$$,$$SYDNEY$$,#{state_id_nsw},-33.794883,151.268071), - ($$1130$$,$$SYDNEY$$,#{state_id_nsw},-33.794883,151.268071), - ($$1131$$,$$SYDNEY$$,#{state_id_nsw},-33.794883,151.268071), - ($$1132$$,$$SYDNEY$$,#{state_id_nsw},-33.66279,150.874265), - ($$1133$$,$$SYDNEY$$,#{state_id_nsw},-33.794883,151.268071), - ($$1134$$,$$SYDNEY$$,#{state_id_nsw},-33.794883,151.268071), - ($$1135$$,$$SYDNEY$$,#{state_id_nsw},-33.794883,151.268071), - ($$1136$$,$$SYDNEY$$,#{state_id_nsw},-33.794883,151.268071), - ($$1137$$,$$SYDNEY$$,#{state_id_nsw},-33.794883,151.268071), - ($$1138$$,$$SYDNEY$$,#{state_id_nsw},-33.794883,151.268071), - ($$1139$$,$$SYDNEY$$,#{state_id_nsw},-33.794883,151.268071), - ($$1140$$,$$SYDNEY$$,#{state_id_nsw},-33.794883,151.268071), - ($$1141$$,$$SYDNEY$$,#{state_id_nsw},-33.794883,151.268071), - ($$1142$$,$$SYDNEY$$,#{state_id_nsw},-33.794883,151.268071), - ($$1143$$,$$SYDNEY$$,#{state_id_nsw},-33.794883,151.268071), - ($$1144$$,$$SYDNEY$$,#{state_id_nsw},-33.794883,151.268071), - ($$1145$$,$$SYDNEY$$,#{state_id_nsw},-33.794883,151.268071), - ($$1146$$,$$SYDNEY$$,#{state_id_nsw},-33.794883,151.268071), - ($$1147$$,$$SYDNEY$$,#{state_id_nsw},-33.794883,151.268071), - ($$1148$$,$$SYDNEY$$,#{state_id_nsw},-33.794883,151.268071), - ($$1149$$,$$SYDNEY$$,#{state_id_nsw},-33.794883,151.268071), - ($$1150$$,$$SYDNEY$$,#{state_id_nsw},-33.794883,151.268071), - ($$1151$$,$$SYDNEY$$,#{state_id_nsw},-33.794883,151.268071), - ($$1152$$,$$SYDNEY$$,#{state_id_nsw},-33.794883,151.268071), - ($$1153$$,$$SYDNEY$$,#{state_id_nsw},-33.794883,151.268071), - ($$1154$$,$$SYDNEY$$,#{state_id_nsw},-33.794883,151.268071), - ($$1155$$,$$SYDNEY$$,#{state_id_nsw},-33.794883,151.268071), - ($$1156$$,$$SYDNEY$$,#{state_id_nsw},-33.794883,151.268071), - ($$1157$$,$$SYDNEY$$,#{state_id_nsw},-33.794883,151.268071), - ($$1158$$,$$SYDNEY$$,#{state_id_nsw},-33.794883,151.268071), - ($$1159$$,$$SYDNEY$$,#{state_id_nsw},-33.794883,151.268071), - ($$1160$$,$$SYDNEY$$,#{state_id_nsw},-33.794883,151.268071), - ($$1161$$,$$SYDNEY$$,#{state_id_nsw},-33.794883,151.268071), - ($$1162$$,$$SYDNEY$$,#{state_id_nsw},-33.794883,151.268071), - ($$1163$$,$$SYDNEY$$,#{state_id_nsw},-33.794883,151.268071), - ($$1164$$,$$SYDNEY$$,#{state_id_nsw},-33.794883,151.268071), - ($$1165$$,$$SYDNEY$$,#{state_id_nsw},-33.794883,151.268071), - ($$1166$$,$$SYDNEY$$,#{state_id_nsw},-33.794883,151.268071), - ($$1167$$,$$SYDNEY$$,#{state_id_nsw},-33.794883,151.268071), - ($$1168$$,$$SYDNEY$$,#{state_id_nsw},-33.794883,151.268071), - ($$1169$$,$$SYDNEY$$,#{state_id_nsw},-33.794883,151.268071), - ($$1170$$,$$SYDNEY$$,#{state_id_nsw},-33.794883,151.268071), - ($$1171$$,$$SYDNEY$$,#{state_id_nsw},-33.794883,151.268071), - ($$1172$$,$$SYDNEY$$,#{state_id_nsw},-33.794883,151.268071), - ($$1173$$,$$SYDNEY$$,#{state_id_nsw},-33.794883,151.268071), - ($$1174$$,$$SYDNEY$$,#{state_id_nsw},-33.794883,151.268071), - ($$1175$$,$$SYDNEY$$,#{state_id_nsw},-33.794883,151.268071), - ($$1176$$,$$SYDNEY$$,#{state_id_nsw},-33.794883,151.268071), - ($$1177$$,$$SYDNEY$$,#{state_id_nsw},-33.794883,151.268071), - ($$1178$$,$$SYDNEY$$,#{state_id_nsw},-33.794883,151.268071), - ($$1179$$,$$SYDNEY$$,#{state_id_nsw},-33.794883,151.268071), - ($$1180$$,$$SYDNEY$$,#{state_id_nsw},-33.794883,151.268071), - ($$1181$$,$$SYDNEY$$,#{state_id_nsw},-33.794883,151.268071), - ($$1182$$,$$SYDNEY$$,#{state_id_nsw},-33.794883,151.268071), - ($$1183$$,$$SYDNEY$$,#{state_id_nsw},-33.794883,151.268071), - ($$1184$$,$$SYDNEY$$,#{state_id_nsw},-33.794883,151.268071), - ($$1185$$,$$SYDNEY$$,#{state_id_nsw},-33.794883,151.268071), - ($$1186$$,$$SYDNEY$$,#{state_id_nsw},-33.794883,151.268071), - ($$1187$$,$$SYDNEY$$,#{state_id_nsw},-33.794883,151.268071), - ($$1188$$,$$SYDNEY$$,#{state_id_nsw},-33.794883,151.268071), - ($$1189$$,$$SYDNEY$$,#{state_id_nsw},-33.794883,151.268071), - ($$1190$$,$$SYDNEY$$,#{state_id_nsw},-33.794883,151.268071), - ($$1191$$,$$SYDNEY$$,#{state_id_nsw},-33.794883,151.268071), - ($$1192$$,$$SYDNEY$$,#{state_id_nsw},-34.790684,147.685283), - ($$1193$$,$$SYDNEY$$,#{state_id_nsw},-33.794883,151.268071), - ($$1194$$,$$SYDNEY$$,#{state_id_nsw},-33.794883,151.268071), - ($$1195$$,$$SYDNEY$$,#{state_id_nsw},-33.794883,151.268071), - ($$1196$$,$$SYDNEY$$,#{state_id_nsw},-33.794883,151.268071), - ($$1197$$,$$SYDNEY$$,#{state_id_nsw},-33.794883,151.268071), - ($$1198$$,$$SYDNEY$$,#{state_id_nsw},-33.794883,151.268071), - ($$1199$$,$$SYDNEY$$,#{state_id_nsw},-33.794883,151.268071), - ($$1200$$,$$SYDNEY$$,#{state_id_nsw},-33.794883,151.268071), - ($$1201$$,$$SYDNEY$$,#{state_id_nsw},-33.794883,151.268071), - ($$1202$$,$$SYDNEY$$,#{state_id_nsw},-33.794883,151.268071), - ($$1203$$,$$SYDNEY$$,#{state_id_nsw},-33.794883,151.268071), - ($$1204$$,$$SYDNEY$$,#{state_id_nsw},-33.794883,151.268071), - ($$1205$$,$$SYDNEY$$,#{state_id_nsw},-33.794883,151.268071), - ($$1206$$,$$SYDNEY$$,#{state_id_nsw},-33.794883,151.268071), - ($$1207$$,$$SYDNEY$$,#{state_id_nsw},-33.794883,151.268071), - ($$1208$$,$$HAYMARKET$$,#{state_id_nsw},-29.816475,151.659454), - ($$1209$$,$$AUSTRALIA SQUARE$$,#{state_id_nsw},-33.891788,151.176251), - ($$1210$$,$$AUSTRALIA SQUARE$$,#{state_id_nsw},-33.891788,151.176251), - ($$1211$$,$$AUSTRALIA SQUARE$$,#{state_id_nsw},-33.891788,151.176251), - ($$1212$$,$$AUSTRALIA SQUARE$$,#{state_id_nsw},-33.891788,151.176251), - ($$1213$$,$$AUSTRALIA SQUARE$$,#{state_id_nsw},-33.891788,151.176251), - ($$1214$$,$$AUSTRALIA SQUARE$$,#{state_id_nsw},-33.891788,151.176251), - ($$1215$$,$$AUSTRALIA SQUARE$$,#{state_id_nsw},-33.891788,151.176251), - ($$1216$$,$$GROSVENOR PLACE$$,#{state_id_nsw},-33.741311,151.034025), - ($$1217$$,$$GROSVENOR PLACE$$,#{state_id_nsw},-33.741311,151.034025), - ($$1218$$,$$GROSVENOR PLACE$$,#{state_id_nsw},-33.741311,151.034025), - ($$1219$$,$$GROSVENOR PLACE$$,#{state_id_nsw},-33.741311,151.034025), - ($$1220$$,$$GROSVENOR PLACE$$,#{state_id_nsw},-33.741311,151.034025), - ($$1221$$,$$ROYAL EXCHANGE$$,#{state_id_nsw},-33.86533,151.207905), - ($$1222$$,$$ROYAL EXCHANGE$$,#{state_id_nsw},-33.86533,151.207905), - ($$1223$$,$$ROYAL EXCHANGE$$,#{state_id_nsw},-33.86533,151.207905), - ($$1224$$,$$ROYAL EXCHANGE$$,#{state_id_nsw},-33.86533,151.207905), - ($$1225$$,$$ROYAL EXCHANGE$$,#{state_id_nsw},-33.86533,151.207905), - ($$1226$$,$$QUEEN VICTORIA BUILDING$$,#{state_id_nsw},-33.871749,151.206708), - ($$1227$$,$$QUEEN VICTORIA BUILDING$$,#{state_id_nsw},-33.871749,151.206708), - ($$1228$$,$$QUEEN VICTORIA BUILDING$$,#{state_id_nsw},-33.871749,151.206708), - ($$1229$$,$$QUEEN VICTORIA BUILDING$$,#{state_id_nsw},-33.871749,151.206708), - ($$1230$$,$$QUEEN VICTORIA BUILDING$$,#{state_id_nsw},-33.871749,151.206708), - ($$1231$$,$$SYDNEY SOUTH$$,#{state_id_nsw},-33.815551,151.042528), - ($$1232$$,$$SYDNEY SOUTH$$,#{state_id_nsw},-33.815551,151.042528), - ($$1233$$,$$SYDNEY SOUTH$$,#{state_id_nsw},-33.815551,151.042528), - ($$1234$$,$$SYDNEY SOUTH$$,#{state_id_nsw},-33.815551,151.042528), - ($$1235$$,$$SYDNEY SOUTH$$,#{state_id_nsw},-33.815551,151.042528), - ($$1236$$,$$HAYMARKET$$,#{state_id_nsw},-29.816475,151.659454), - ($$1237$$,$$HAYMARKET$$,#{state_id_nsw},-29.816475,151.659454), - ($$1238$$,$$HAYMARKET$$,#{state_id_nsw},-29.816475,151.659454), - ($$1239$$,$$HAYMARKET$$,#{state_id_nsw},-29.816475,151.659454), - ($$1240$$,$$HAYMARKET$$,#{state_id_nsw},-29.816475,151.659454), - ($$1291$$,$$SYDNEY$$,#{state_id_nsw},-33.794883,151.268071), - ($$1292$$,$$SYDNEY$$,#{state_id_nsw},-33.794883,151.268071), - ($$1293$$,$$SYDNEY$$,#{state_id_nsw},-33.794883,151.268071), - ($$1294$$,$$SYDNEY$$,#{state_id_nsw},-33.794883,151.268071), - ($$1295$$,$$SYDNEY$$,#{state_id_nsw},-33.794883,151.268071), - ($$1296$$,$$SYDNEY$$,#{state_id_nsw},-33.794883,151.268071), - ($$1297$$,$$SYDNEY$$,#{state_id_nsw},-33.794883,151.268071), - ($$1298$$,$$SYDNEY$$,#{state_id_nsw},-33.794883,151.268071), - ($$1299$$,$$SYDNEY$$,#{state_id_nsw},-33.794883,151.268071), - ($$1300$$,$$DARLINGHURST$$,#{state_id_nsw},-33.877331,151.220876), - ($$1311$$,$$EASTERN SUBURBS MC$$,#{state_id_nsw},0.0,0.0), - ($$1312$$,$$EASTERN SUBURBS MC$$,#{state_id_nsw},0.0,0.0), - ($$1313$$,$$EASTERN SUBURBS MC$$,#{state_id_nsw},0.0,0.0), - ($$1314$$,$$EASTERN SUBURBS MC$$,#{state_id_nsw},0.0,0.0), - ($$1315$$,$$EASTERN SUBURBS MC$$,#{state_id_nsw},0.0,0.0), - ($$1316$$,$$EASTERN SUBURBS MC$$,#{state_id_nsw},0.0,0.0), - ($$1317$$,$$EASTERN SUBURBS MC$$,#{state_id_nsw},0.0,0.0), - ($$1318$$,$$EASTERN SUBURBS MC$$,#{state_id_nsw},0.0,0.0), - ($$1319$$,$$EASTERN SUBURBS MC$$,#{state_id_nsw},0.0,0.0), - ($$1320$$,$$EASTERN SUBURBS MC$$,#{state_id_nsw},0.0,0.0), - ($$1321$$,$$EASTERN SUBURBS MC$$,#{state_id_nsw},0.0,0.0), - ($$1322$$,$$EASTERN SUBURBS MC$$,#{state_id_nsw},0.0,0.0), - ($$1323$$,$$EASTERN SUBURBS MC$$,#{state_id_nsw},0.0,0.0), - ($$1324$$,$$EASTERN SUBURBS MC$$,#{state_id_nsw},0.0,0.0), - ($$1325$$,$$EASTERN SUBURBS MC$$,#{state_id_nsw},0.0,0.0), - ($$1326$$,$$EASTERN SUBURBS MC$$,#{state_id_nsw},0.0,0.0), - ($$1327$$,$$EASTERN SUBURBS MC$$,#{state_id_nsw},0.0,0.0), - ($$1328$$,$$EASTERN SUBURBS MC$$,#{state_id_nsw},0.0,0.0), - ($$1329$$,$$EASTERN SUBURBS MC$$,#{state_id_nsw},0.0,0.0), - ($$1330$$,$$EASTERN SUBURBS MC$$,#{state_id_nsw},0.0,0.0), - ($$1331$$,$$EASTERN SUBURBS MC$$,#{state_id_nsw},0.0,0.0), - ($$1332$$,$$EASTERN SUBURBS MC$$,#{state_id_nsw},0.0,0.0), - ($$1333$$,$$EASTERN SUBURBS MC$$,#{state_id_nsw},0.0,0.0), - ($$1334$$,$$EASTERN SUBURBS MC$$,#{state_id_nsw},0.0,0.0), - ($$1335$$,$$POTTS POINT$$,#{state_id_nsw},-33.82269,151.117575), - ($$1340$$,$$KINGS CROSS$$,#{state_id_nsw},-35.537721,148.021014), - ($$1350$$,$$WOOLLAHRA$$,#{state_id_nsw},0.0,0.0), - ($$1355$$,$$BONDI JUNCTION$$,#{state_id_nsw},-33.893739,151.262502), - ($$1360$$,$$DOUBLE BAY$$,#{state_id_nsw},-33.87584,151.241938), - ($$1391$$,$$ATO ACTIVITY STATEMENTS$$,#{state_id_nsw},0.0,0.0), - ($$1401$$,$$BROADWAY$$,#{state_id_nsw},-33.884217,151.199825), - ($$1416$$,$$SOUTHERN SUBURBS MC$$,#{state_id_nsw},0.0,0.0), - ($$1419$$,$$SOUTHERN SUBURBS MC$$,#{state_id_nsw},0.0,0.0), - ($$1420$$,$$STRAWBERRY HILLS$$,#{state_id_nsw},-33.726098,150.931838), - ($$1422$$,$$STRAWBERRY HILLS$$,#{state_id_nsw},-33.726098,150.931838), - ($$1423$$,$$STRAWBERRY HILLS$$,#{state_id_nsw},-33.726098,150.931838), - ($$1424$$,$$STRAWBERRY HILLS$$,#{state_id_nsw},-33.726098,150.931838), - ($$1425$$,$$STRAWBERRY HILLS$$,#{state_id_nsw},-33.726098,150.931838), - ($$1426$$,$$STRAWBERRY HILLS$$,#{state_id_nsw},-33.726098,150.931838), - ($$1427$$,$$STRAWBERRY HILLS$$,#{state_id_nsw},-33.726098,150.931838), - ($$1428$$,$$STRAWBERRY HILLS$$,#{state_id_nsw},-33.726098,150.931838), - ($$1429$$,$$STRAWBERRY HILLS$$,#{state_id_nsw},-33.726098,150.931838), - ($$1430$$,$$EVELEIGH$$,#{state_id_nsw},-33.890232,151.199489), - ($$1435$$,$$ALEXANDRIA$$,#{state_id_nsw},-33.711785,151.108248), - ($$1440$$,$$WATERLOO$$,#{state_id_nsw},-33.902836,151.057914), - ($$1441$$,$$WATERLOO$$,#{state_id_nsw},-33.902836,151.057914), - ($$1445$$,$$ROSEBERY$$,#{state_id_nsw},-34.083089,151.007691), - ($$1450$$,$$CAMPERDOWN$$,#{state_id_nsw},-30.305815,153.13679), - ($$1455$$,$$BOTANY$$,#{state_id_nsw},-33.947087,151.197644), - ($$1460$$,$$MASCOT$$,#{state_id_nsw},-33.926669,151.210791), - ($$1465$$,$$KENSINGTON$$,#{state_id_nsw},-33.888549,151.140735), - ($$1466$$,$$UNSW SYDNEY$$,#{state_id_nsw},-33.906561,151.234417), - ($$1470$$,$$DRUMMOYNE$$,#{state_id_nsw},-33.842999,151.151958), - ($$1475$$,$$MARRICKVILLE$$,#{state_id_nsw},-33.90911,151.15334), - ($$1476$$,$$MARRICKVILLE$$,#{state_id_nsw},-33.90911,151.15334), - ($$1480$$,$$KINGSGROVE$$,#{state_id_nsw},-33.935923,151.100027), - ($$1481$$,$$HURSTVILLE BC$$,#{state_id_nsw},0.0,0.0), - ($$1484$$,$$KINGSGROVE DC$$,#{state_id_nsw},0.0,0.0), - ($$1485$$,$$KOGARAH$$,#{state_id_nsw},0.0,0.0), - ($$1487$$,$$KOGARAH$$,#{state_id_nsw},0.0,0.0), - ($$1490$$,$$MIRANDA$$,#{state_id_nsw},-34.035878,151.107201), - ($$1493$$,$$HURSTVILLE$$,#{state_id_nsw},-33.975869,151.088939), - ($$1495$$,$$CARINGBAH$$,#{state_id_nsw},-34.046927,151.123943), - ($$1499$$,$$SUTHERLAND$$,#{state_id_nsw},-34.015705,151.0622), - ($$1515$$,$$WEST CHATSWOOD$$,#{state_id_nsw},-33.824607,151.207261), - ($$1560$$,$$NORTHBRIDGE$$,#{state_id_nsw},0.0,0.0), - ($$1565$$,$$MILSONS POINT$$,#{state_id_nsw},-33.865367,151.193071), - ($$1570$$,$$ARTARMON$$,#{state_id_nsw},-33.808087,151.192733), - ($$1582$$,$$CROWS NEST$$,#{state_id_nsw},-33.83459,151.20085), - ($$1585$$,$$CROWS NEST$$,#{state_id_nsw},-33.83459,151.20085), - ($$1590$$,$$ST LEONARDS$$,#{state_id_nsw},-33.292001,151.468652), - ($$1595$$,$$LANE COVE$$,#{state_id_nsw},-33.791875,151.187955), - ($$1597$$,$$LANE COVE$$,#{state_id_nsw},-33.791875,151.187955), - ($$1602$$,$$LANE COVE DC$$,#{state_id_nsw},0.0,0.0), - ($$1608$$,$$LANE COVE DC$$,#{state_id_nsw},0.0,0.0), - ($$1610$$,$$LANE COVE DC$$,#{state_id_nsw},0.0,0.0), - ($$1611$$,$$LANE COVE DC$$,#{state_id_nsw},0.0,0.0), - ($$1630$$,$$HORNSBY$$,#{state_id_nsw},-33.707684,151.099812), - ($$1635$$,$$HORNSBY WESTFIELD$$,#{state_id_nsw},0.0,0.0), - ($$1639$$,$$FRENCHS FOREST$$,#{state_id_nsw},-33.793137,151.246751), - ($$1640$$,$$FRENCHS FOREST$$,#{state_id_nsw},-33.793137,151.246751), - ($$1655$$,$$MANLY$$,#{state_id_nsw},-33.329799,151.505125), - ($$1658$$,$$MONA VALE$$,#{state_id_nsw},-33.698773,151.216799), - ($$1660$$,$$MONA VALE$$,#{state_id_nsw},-33.698773,151.216799), - ($$1670$$,$$NORTH RYDE BC$$,#{state_id_nsw},0.0,0.0), - ($$1675$$,$$GLADESVILLE$$,#{state_id_nsw},-33.833033,151.139681), - ($$1680$$,$$RYDE$$,#{state_id_nsw},-33.761498,151.137807), - ($$1685$$,$$WEST RYDE$$,#{state_id_nsw},-33.80406,151.09064), - ($$1690$$,$$NORTHERN SUBURBS MC$$,#{state_id_nsw},0.0,0.0), - ($$1691$$,$$NORTHERN SUBURBS MC$$,#{state_id_nsw},0.0,0.0), - ($$1692$$,$$NORTHERN SUBURBS MC$$,#{state_id_nsw},0.0,0.0), - ($$1693$$,$$NORTHERN SUBURBS MC$$,#{state_id_nsw},0.0,0.0), - ($$1694$$,$$NORTHERN SUBURBS MC$$,#{state_id_nsw},0.0,0.0), - ($$1695$$,$$NORTHERN SUBURBS MC$$,#{state_id_nsw},0.0,0.0), - ($$1696$$,$$NORTHERN SUBURBS MC$$,#{state_id_nsw},0.0,0.0), - ($$1697$$,$$NORTHERN SUBURBS MC$$,#{state_id_nsw},0.0,0.0), - ($$1698$$,$$NORTHERN SUBURBS MC$$,#{state_id_nsw},0.0,0.0), - ($$1699$$,$$NORTHERN SUBURBS MC$$,#{state_id_nsw},0.0,0.0), - ($$1700$$,$$ERMINGTON$$,#{state_id_nsw},-33.950299,151.206982), - ($$1701$$,$$RYDALMERE BC$$,#{state_id_nsw},0.0,0.0), - ($$1710$$,$$EPPING$$,#{state_id_nsw},-33.78417,151.116696), - ($$1712$$,$$EPPING$$,#{state_id_nsw},-33.78417,151.116696), - ($$1715$$,$$PENNANT HILLS$$,#{state_id_nsw},-33.758433,151.049106), - ($$1730$$,$$SEVEN HILLS$$,#{state_id_nsw},-33.760263,150.966912), - ($$1740$$,$$PARRAMATTA$$,#{state_id_nsw},-33.886166,151.139472), - ($$1741$$,$$PARRAMATTA$$,#{state_id_nsw},-33.886166,151.139472), - ($$1750$$,$$NORTH PARRAMATTA$$,#{state_id_nsw},-33.857053,151.023102), - ($$1755$$,$$BAULKHAM HILLS$$,#{state_id_nsw},-33.767239,150.968177), - ($$1765$$,$$CASTLE HILL$$,#{state_id_nsw},-33.735906,151.030535), - ($$1771$$,$$PENNANT HILLS$$,#{state_id_nsw},-33.758433,151.049106), - ($$1781$$,$$SEVEN HILLS MC$$,#{state_id_nsw},0.0,0.0), - ($$1790$$,$$ST MARYS$$,#{state_id_nsw},-33.859047,151.19554), - ($$1797$$,$$PENRITH SOUTH DC$$,#{state_id_nsw},0.0,0.0), - ($$1800$$,$$ASHFIELD$$,#{state_id_nsw},-34.096505,150.778939), - ($$1805$$,$$BURWOOD$$,#{state_id_nsw},-33.891556,151.10082), - ($$1811$$,$$SILVERWATER$$,#{state_id_nsw},-33.82328,151.051375), - ($$1816$$,$$STRATHFIELD$$,#{state_id_nsw},-33.877139,151.093326), - ($$1819$$,$$STRATHFIELD$$,#{state_id_nsw},-33.877139,151.093326), - ($$1825$$,$$LIDCOMBE$$,#{state_id_nsw},0.0,0.0), - ($$1826$$,$$LIDCOMBE$$,#{state_id_nsw},0.0,0.0), - ($$1830$$,$$GRANVILLE$$,#{state_id_nsw},-33.859289,150.948582), - ($$1831$$,$$GRANVILLE$$,#{state_id_nsw},-33.859289,150.948582), - ($$1835$$,$$AUBURN$$,#{state_id_nsw},-33.883928,151.023796), - ($$1848$$,$$GUILDFORD$$,#{state_id_nsw},-33.850193,150.966745), - ($$1851$$,$$WETHERILL PARK DC$$,#{state_id_nsw},0.0,0.0), - ($$1860$$,$$FAIRFIELD$$,#{state_id_nsw},-33.850457,150.961124), - ($$1871$$,$$LIVERPOOL$$,#{state_id_nsw},-33.888327,151.103632), - ($$1875$$,$$MOOREBANK$$,#{state_id_nsw},-33.954639,150.92236), - ($$1885$$,$$BANKSTOWN$$,#{state_id_nsw},-33.907417,151.024581), - ($$1888$$,$$BANKSTOWN$$,#{state_id_nsw},-33.907417,151.024581), - ($$1890$$,$$INGLEBURN$$,#{state_id_nsw},-33.960035,150.802088), - ($$1891$$,$$MILPERRA$$,#{state_id_nsw},-33.932221,151.000183), - ($$1900$$,$$LEIGHTONFIELD MC$$,#{state_id_nsw},0.0,0.0), - ($$1902$$,$$LEIGHTONFIELD MC$$,#{state_id_nsw},0.0,0.0), - ($$2000$$,$$BARANGAROO$$,#{state_id_nsw},-33.855601,151.20822), - ($$2000$$,$$DAWES POINT$$,#{state_id_nsw},-33.855601,151.20822), - ($$2000$$,$$HAYMARKET$$,#{state_id_nsw},-33.855601,151.20822), - ($$2000$$,$$MILLERS POINT$$,#{state_id_nsw},-33.855601,151.20822), - ($$2000$$,$$PARLIAMENT HOUSE$$,#{state_id_nsw},-33.855601,151.20822), - ($$2000$$,$$SYDNEY$$,#{state_id_nsw},-33.855601,151.20822), - ($$2000$$,$$SYDNEY SOUTH$$,#{state_id_nsw},-33.855601,151.20822), - ($$2000$$,$$THE ROCKS$$,#{state_id_nsw},-33.855601,151.20822), - ($$2001$$,$$SYDNEY$$,#{state_id_nsw},-33.794883,151.268071), - ($$2002$$,$$WORLD SQUARE$$,#{state_id_nsw},-35.974434,146.40506), - ($$2004$$,$$ALEXANDRIA MC$$,#{state_id_nsw},0.0,0.0), - ($$2004$$,$$EASTERN SUBURBS MC$$,#{state_id_nsw},0.0,0.0), - ($$2006$$,$$THE UNIVERSITY OF SYDNEY$$,#{state_id_nsw},-33.887926,151.186923), - ($$2007$$,$$BROADWAY$$,#{state_id_nsw},-33.884366,151.196502), - ($$2007$$,$$ULTIMO$$,#{state_id_nsw},-33.884366,151.196502), - ($$2008$$,$$CHIPPENDALE$$,#{state_id_nsw},-33.886844,151.201715), - ($$2008$$,$$DARLINGTON$$,#{state_id_nsw},-33.886844,151.201715), - ($$2009$$,$$PYRMONT$$,#{state_id_nsw},-33.869709,151.19393), - ($$2010$$,$$DARLINGHURST$$,#{state_id_nsw},-33.879825,151.21956), - ($$2010$$,$$SURRY HILLS$$,#{state_id_nsw},-33.879825,151.21956), - ($$2011$$,$$ELIZABETH BAY$$,#{state_id_nsw},-33.872829,151.226593), - ($$2011$$,$$HMAS KUTTABUL$$,#{state_id_nsw},-33.872829,151.226593), - ($$2011$$,$$POTTS POINT$$,#{state_id_nsw},-33.872829,151.226593), - ($$2011$$,$$RUSHCUTTERS BAY$$,#{state_id_nsw},-33.872829,151.226593), - ($$2011$$,$$WOOLLOOMOOLOO$$,#{state_id_nsw},-33.872829,151.226593), - ($$2012$$,$$STRAWBERRY HILLS$$,#{state_id_nsw},-33.726098,150.931838), - ($$2013$$,$$STRAWBERRY HILLS$$,#{state_id_nsw},-33.726098,150.931838), - ($$2015$$,$$ALEXANDRIA$$,#{state_id_nsw},-33.897571,151.195567), - ($$2015$$,$$BEACONSFIELD$$,#{state_id_nsw},-33.897571,151.195567), - ($$2015$$,$$EVELEIGH$$,#{state_id_nsw},-33.897571,151.195567), - ($$2016$$,$$REDFERN$$,#{state_id_nsw},-33.892778,151.203901), - ($$2017$$,$$WATERLOO$$,#{state_id_nsw},-33.9004,151.206144), - ($$2017$$,$$ZETLAND$$,#{state_id_nsw},-33.9004,151.206144), - ($$2018$$,$$EASTLAKES$$,#{state_id_nsw},-33.925133,151.213199), - ($$2018$$,$$ROSEBERY$$,#{state_id_nsw},-33.925133,151.213199), - ($$2019$$,$$BANKSMEADOW$$,#{state_id_nsw},-33.95742,151.206715), - ($$2019$$,$$BOTANY$$,#{state_id_nsw},-33.95742,151.206715), - ($$2020$$,$$MASCOT$$,#{state_id_nsw},-33.931189,151.19431), - ($$2020$$,$$SYDNEY DOMESTIC AIRPORT$$,#{state_id_nsw},-33.931189,151.19431), - ($$2020$$,$$SYDNEY INTERNATIONAL AIRPORT$$,#{state_id_nsw},-33.931189,151.19431), - ($$2021$$,$$CENTENNIAL PARK$$,#{state_id_nsw},-33.893632,151.219357), - ($$2021$$,$$MOORE PARK$$,#{state_id_nsw},-33.893632,151.219357), - ($$2021$$,$$PADDINGTON$$,#{state_id_nsw},-33.893632,151.219357), - ($$2022$$,$$BONDI JUNCTION$$,#{state_id_nsw},-33.892324,151.24733), - ($$2022$$,$$BONDI JUNCTION PLAZA$$,#{state_id_nsw},-33.892324,151.24733), - ($$2022$$,$$QUEENS PARK$$,#{state_id_nsw},-33.892324,151.24733), - ($$2023$$,$$BELLEVUE HILL$$,#{state_id_nsw},-33.887189,151.258935), - ($$2024$$,$$BRONTE$$,#{state_id_nsw},-33.902328,151.263838), - ($$2024$$,$$WAVERLEY$$,#{state_id_nsw},-33.902328,151.263838), - ($$2025$$,$$WOOLLAHRA$$,#{state_id_nsw},-33.885795,151.24413), - ($$2026$$,$$BONDI$$,#{state_id_nsw},-33.893739,151.262502), - ($$2026$$,$$BONDI BEACH$$,#{state_id_nsw},-33.893739,151.262502), - ($$2026$$,$$NORTH BONDI$$,#{state_id_nsw},-33.893739,151.262502), - ($$2026$$,$$TAMARAMA$$,#{state_id_nsw},-33.893739,151.262502), - ($$2027$$,$$DARLING POINT$$,#{state_id_nsw},-33.873808,151.236683), - ($$2027$$,$$EDGECLIFF$$,#{state_id_nsw},-33.873808,151.236683), - ($$2027$$,$$HMAS RUSHCUTTERS$$,#{state_id_nsw},-33.873808,151.236683), - ($$2027$$,$$POINT PIPER$$,#{state_id_nsw},-33.873808,151.236683), - ($$2028$$,$$DOUBLE BAY$$,#{state_id_nsw},-33.87906,151.243095), - ($$2029$$,$$ROSE BAY$$,#{state_id_nsw},-33.866555,151.280456), - ($$2030$$,$$DOVER HEIGHTS$$,#{state_id_nsw},-33.874405,151.280416), - ($$2030$$,$$HMAS WATSON$$,#{state_id_nsw},-33.874405,151.280416), - ($$2030$$,$$ROSE BAY NORTH$$,#{state_id_nsw},-33.874405,151.280416), - ($$2030$$,$$VAUCLUSE$$,#{state_id_nsw},-33.874405,151.280416), - ($$2030$$,$$WATSONS BAY$$,#{state_id_nsw},-33.874405,151.280416), - ($$2031$$,$$CLOVELLY$$,#{state_id_nsw},-33.912639,151.262021), - ($$2031$$,$$CLOVELLY WEST$$,#{state_id_nsw},-33.912639,151.262021), - ($$2031$$,$$RANDWICK$$,#{state_id_nsw},-33.912639,151.262021), - ($$2031$$,$$ST PAULS$$,#{state_id_nsw},-33.912639,151.262021), - ($$2032$$,$$DACEYVILLE$$,#{state_id_nsw},-33.928043,151.22513), - ($$2032$$,$$KINGSFORD$$,#{state_id_nsw},-33.928043,151.22513), - ($$2033$$,$$KENSINGTON$$,#{state_id_nsw},-33.912997,151.219017), - ($$2034$$,$$COOGEE$$,#{state_id_nsw},-33.920491,151.254401), - ($$2034$$,$$SOUTH COOGEE$$,#{state_id_nsw},-33.920491,151.254401), - ($$2035$$,$$MAROUBRA$$,#{state_id_nsw},-33.946123,151.242818), - ($$2035$$,$$MAROUBRA SOUTH$$,#{state_id_nsw},-33.946123,151.242818), - ($$2035$$,$$PAGEWOOD$$,#{state_id_nsw},-33.946123,151.242818), - ($$2036$$,$$CHIFLEY$$,#{state_id_nsw},-33.976546,151.240248), - ($$2036$$,$$EASTGARDENS$$,#{state_id_nsw},-33.976546,151.240248), - ($$2036$$,$$HILLSDALE$$,#{state_id_nsw},-33.976546,151.240248), - ($$2036$$,$$LA PEROUSE$$,#{state_id_nsw},-33.976546,151.240248), - ($$2036$$,$$LITTLE BAY$$,#{state_id_nsw},-33.976546,151.240248), - ($$2036$$,$$MALABAR$$,#{state_id_nsw},-33.976546,151.240248), - ($$2036$$,$$MATRAVILLE$$,#{state_id_nsw},-33.976546,151.240248), - ($$2036$$,$$PHILLIP BAY$$,#{state_id_nsw},-33.976546,151.240248), - ($$2036$$,$$PORT BOTANY$$,#{state_id_nsw},-33.976546,151.240248), - ($$2037$$,$$FOREST LODGE$$,#{state_id_nsw},-33.881215,151.181127), - ($$2037$$,$$GLEBE$$,#{state_id_nsw},-33.881215,151.181127), - ($$2038$$,$$ANNANDALE$$,#{state_id_nsw},-33.881435,151.170681), - ($$2039$$,$$ROZELLE$$,#{state_id_nsw},-33.863063,151.170573), - ($$2040$$,$$LEICHHARDT$$,#{state_id_nsw},-33.883793,151.157057), - ($$2040$$,$$LILYFIELD$$,#{state_id_nsw},-33.883793,151.157057), - ($$2041$$,$$BALMAIN$$,#{state_id_nsw},-33.856498,151.178009), - ($$2041$$,$$BALMAIN EAST$$,#{state_id_nsw},-33.856498,151.178009), - ($$2041$$,$$BIRCHGROVE$$,#{state_id_nsw},-33.856498,151.178009), - ($$2042$$,$$ENMORE$$,#{state_id_nsw},-33.899362,151.171098), - ($$2042$$,$$NEWTOWN$$,#{state_id_nsw},-33.899362,151.171098), - ($$2043$$,$$ERSKINEVILLE$$,#{state_id_nsw},-33.902234,151.186192), - ($$2044$$,$$ST PETERS$$,#{state_id_nsw},-33.911062,151.180126), - ($$2044$$,$$SYDENHAM$$,#{state_id_nsw},-33.911062,151.180126), - ($$2044$$,$$TEMPE$$,#{state_id_nsw},-33.911062,151.180126), - ($$2045$$,$$HABERFIELD$$,#{state_id_nsw},-33.880496,151.138839), - ($$2046$$,$$ABBOTSFORD$$,#{state_id_nsw},-33.852469,151.129453), - ($$2046$$,$$CANADA BAY$$,#{state_id_nsw},-33.852469,151.129453), - ($$2046$$,$$CHISWICK$$,#{state_id_nsw},-33.852469,151.129453), - ($$2046$$,$$FIVE DOCK$$,#{state_id_nsw},-33.852469,151.129453), - ($$2046$$,$$RODD POINT$$,#{state_id_nsw},-33.852469,151.129453), - ($$2046$$,$$RUSSELL LEA$$,#{state_id_nsw},-33.852469,151.129453), - ($$2046$$,$$WAREEMBA$$,#{state_id_nsw},-33.852469,151.129453), - ($$2047$$,$$DRUMMOYNE$$,#{state_id_nsw},-33.851056,151.154542), - ($$2048$$,$$STANMORE$$,#{state_id_nsw},-33.897351,151.16535), - ($$2048$$,$$WESTGATE$$,#{state_id_nsw},-33.897351,151.16535), - ($$2049$$,$$LEWISHAM$$,#{state_id_nsw},-33.894902,151.144413), - ($$2049$$,$$PETERSHAM$$,#{state_id_nsw},-33.894902,151.144413), - ($$2049$$,$$PETERSHAM NORTH$$,#{state_id_nsw},-33.894902,151.144413), - ($$2050$$,$$CAMPERDOWN$$,#{state_id_nsw},-33.88866,151.177188), - ($$2050$$,$$MISSENDEN ROAD$$,#{state_id_nsw},-33.88866,151.177188), - ($$2052$$,$$UNSW SYDNEY$$,#{state_id_nsw},-33.906561,151.234417), - ($$2055$$,$$NORTH SYDNEY$$,#{state_id_nsw},-33.802837,151.104935), - ($$2057$$,$$CHATSWOOD$$,#{state_id_nsw},-33.791988,151.1899), - ($$2058$$,$$NORTHERN SUBURBS MC$$,#{state_id_nsw},0.0,0.0), - ($$2059$$,$$NORTH SYDNEY$$,#{state_id_nsw},-33.802837,151.104935), - ($$2060$$,$$HMAS PLATYPUS$$,#{state_id_nsw},-33.840633,151.19497), - ($$2060$$,$$HMAS WATERHEN$$,#{state_id_nsw},-33.840633,151.19497), - ($$2060$$,$$LAVENDER BAY$$,#{state_id_nsw},-33.840633,151.19497), - ($$2060$$,$$MCMAHONS POINT$$,#{state_id_nsw},-33.840633,151.19497), - ($$2060$$,$$NORTH SYDNEY$$,#{state_id_nsw},-33.840633,151.19497), - ($$2060$$,$$NORTH SYDNEY SHOPPINGWORLD$$,#{state_id_nsw},-33.840633,151.19497), - ($$2060$$,$$WAVERTON$$,#{state_id_nsw},-33.840633,151.19497), - ($$2061$$,$$KIRRIBILLI$$,#{state_id_nsw},-33.846275,151.212705), - ($$2061$$,$$MILSONS POINT$$,#{state_id_nsw},-33.846275,151.212705), - ($$2062$$,$$CAMMERAY$$,#{state_id_nsw},-33.821953,151.21043), - ($$2063$$,$$NORTHBRIDGE$$,#{state_id_nsw},-33.815028,151.222266), - ($$2064$$,$$ARTARMON$$,#{state_id_nsw},-33.807664,151.189662), - ($$2065$$,$$CROWS NEST$$,#{state_id_nsw},-33.82609,151.199192), - ($$2065$$,$$GREENWICH$$,#{state_id_nsw},-33.82609,151.199192), - ($$2065$$,$$NAREMBURN$$,#{state_id_nsw},-33.82609,151.199192), - ($$2065$$,$$ROYAL NORTH SHORE HOSPITAL$$,#{state_id_nsw},-33.82609,151.199192), - ($$2065$$,$$ST LEONARDS$$,#{state_id_nsw},-33.82609,151.199192), - ($$2065$$,$$WOLLSTONECRAFT$$,#{state_id_nsw},-33.82609,151.199192), - ($$2066$$,$$LANE COVE$$,#{state_id_nsw},-33.814599,151.168722), - ($$2066$$,$$LANE COVE NORTH$$,#{state_id_nsw},-33.814599,151.168722), - ($$2066$$,$$LANE COVE WEST$$,#{state_id_nsw},-33.814599,151.168722), - ($$2066$$,$$LINLEY POINT$$,#{state_id_nsw},-33.814599,151.168722), - ($$2066$$,$$LONGUEVILLE$$,#{state_id_nsw},-33.814599,151.168722), - ($$2066$$,$$NORTHWOOD$$,#{state_id_nsw},-33.814599,151.168722), - ($$2066$$,$$RIVERVIEW$$,#{state_id_nsw},-33.814599,151.168722), - ($$2067$$,$$CHATSWOOD$$,#{state_id_nsw},-33.795617,151.185329), - ($$2067$$,$$CHATSWOOD WEST$$,#{state_id_nsw},-33.795617,151.185329), - ($$2068$$,$$CASTLECRAG$$,#{state_id_nsw},-33.802403,151.212643), - ($$2068$$,$$MIDDLE COVE$$,#{state_id_nsw},-33.802403,151.212643), - ($$2068$$,$$NORTH WILLOUGHBY$$,#{state_id_nsw},-33.802403,151.212643), - ($$2068$$,$$WILLOUGHBY$$,#{state_id_nsw},-33.802403,151.212643), - ($$2068$$,$$WILLOUGHBY EAST$$,#{state_id_nsw},-33.802403,151.212643), - ($$2068$$,$$WILLOUGHBY NORTH$$,#{state_id_nsw},-33.802403,151.212643), - ($$2069$$,$$CASTLE COVE$$,#{state_id_nsw},-33.784165,151.199948), - ($$2069$$,$$ROSEVILLE$$,#{state_id_nsw},-33.784165,151.199948), - ($$2069$$,$$ROSEVILLE CHASE$$,#{state_id_nsw},-33.784165,151.199948), - ($$2070$$,$$EAST LINDFIELD$$,#{state_id_nsw},-33.766415,151.186095), - ($$2070$$,$$LINDFIELD$$,#{state_id_nsw},-33.766415,151.186095), - ($$2070$$,$$LINDFIELD WEST$$,#{state_id_nsw},-33.766415,151.186095), - ($$2071$$,$$EAST KILLARA$$,#{state_id_nsw},-33.753498,151.170003), - ($$2071$$,$$KILLARA$$,#{state_id_nsw},-33.753498,151.170003), - ($$2072$$,$$GORDON$$,#{state_id_nsw},-33.757349,151.155678), - ($$2073$$,$$PYMBLE$$,#{state_id_nsw},-33.74414,151.141103), - ($$2073$$,$$WEST PYMBLE$$,#{state_id_nsw},-33.74414,151.141103), - ($$2074$$,$$NORTH TURRAMURRA$$,#{state_id_nsw},-33.713419,151.147146), - ($$2074$$,$$SOUTH TURRAMURRA$$,#{state_id_nsw},-33.713419,151.147146), - ($$2074$$,$$TURRAMURRA$$,#{state_id_nsw},-33.713419,151.147146), - ($$2074$$,$$WARRAWEE$$,#{state_id_nsw},-33.713419,151.147146), - ($$2075$$,$$ST IVES$$,#{state_id_nsw},-33.730601,151.158551), - ($$2075$$,$$ST IVES CHASE$$,#{state_id_nsw},-33.730601,151.158551), - ($$2076$$,$$NORMANHURST$$,#{state_id_nsw},-33.720999,151.097331), - ($$2076$$,$$NORTH WAHROONGA$$,#{state_id_nsw},-33.720999,151.097331), - ($$2076$$,$$WAHROONGA$$,#{state_id_nsw},-33.720999,151.097331), - ($$2077$$,$$ASQUITH$$,#{state_id_nsw},-33.687484,151.108685), - ($$2077$$,$$HORNSBY$$,#{state_id_nsw},-33.687484,151.108685), - ($$2077$$,$$HORNSBY HEIGHTS$$,#{state_id_nsw},-33.687484,151.108685), - ($$2077$$,$$WAITARA$$,#{state_id_nsw},-33.687484,151.108685), - ($$2079$$,$$MOUNT COLAH$$,#{state_id_nsw},-33.664817,151.117161), - ($$2080$$,$$MOUNT KURING-GAI$$,#{state_id_nsw},-33.628729,151.226792), - ($$2081$$,$$BEROWRA$$,#{state_id_nsw},-33.623581,151.150117), - ($$2081$$,$$COWAN$$,#{state_id_nsw},-33.623581,151.150117), - ($$2082$$,$$BEROWRA CREEK$$,#{state_id_nsw},-33.610968,151.136829), - ($$2082$$,$$BEROWRA HEIGHTS$$,#{state_id_nsw},-33.610968,151.136829), - ($$2082$$,$$BEROWRA WATERS$$,#{state_id_nsw},-33.610968,151.136829), - ($$2083$$,$$BAR POINT$$,#{state_id_nsw},-33.507463,151.163329), - ($$2083$$,$$BROOKLYN$$,#{state_id_nsw},-33.507463,151.163329), - ($$2083$$,$$CHEERO POINT$$,#{state_id_nsw},-33.507463,151.163329), - ($$2083$$,$$COGRA BAY$$,#{state_id_nsw},-33.507463,151.163329), - ($$2083$$,$$DANGAR ISLAND$$,#{state_id_nsw},-33.507463,151.163329), - ($$2083$$,$$MILSONS PASSAGE$$,#{state_id_nsw},-33.507463,151.163329), - ($$2083$$,$$MOONEY MOONEY$$,#{state_id_nsw},-33.507463,151.163329), - ($$2084$$,$$COTTAGE POINT$$,#{state_id_nsw},-33.619578,151.203831), - ($$2084$$,$$DUFFYS FOREST$$,#{state_id_nsw},-33.619578,151.203831), - ($$2084$$,$$TERREY HILLS$$,#{state_id_nsw},-33.619578,151.203831), - ($$2085$$,$$BELROSE$$,#{state_id_nsw},-33.739288,151.211439), - ($$2085$$,$$BELROSE WEST$$,#{state_id_nsw},-33.739288,151.211439), - ($$2085$$,$$DAVIDSON$$,#{state_id_nsw},-33.739288,151.211439), - ($$2086$$,$$FRENCHS FOREST$$,#{state_id_nsw},-33.750964,151.226036), - ($$2086$$,$$FRENCHS FOREST EAST$$,#{state_id_nsw},-33.750964,151.226036), - ($$2087$$,$$FORESTVILLE$$,#{state_id_nsw},-33.762011,151.21406), - ($$2087$$,$$KILLARNEY HEIGHTS$$,#{state_id_nsw},-33.762011,151.21406), - ($$2088$$,$$MOSMAN$$,#{state_id_nsw},-33.829077,151.24409), - ($$2088$$,$$SPIT JUNCTION$$,#{state_id_nsw},-33.829077,151.24409), - ($$2089$$,$$NEUTRAL BAY$$,#{state_id_nsw},-33.83112,151.221232), - ($$2089$$,$$NEUTRAL BAY JUNCTION$$,#{state_id_nsw},-33.83112,151.221232), - ($$2090$$,$$CREMORNE$$,#{state_id_nsw},-33.828131,151.230233), - ($$2090$$,$$CREMORNE JUNCTION$$,#{state_id_nsw},-33.828131,151.230233), - ($$2090$$,$$CREMORNE POINT$$,#{state_id_nsw},-33.828131,151.230233), - ($$2091$$,$$HMAS PENGUIN$$,#{state_id_nsw},0.0,0.0), - ($$2092$$,$$SEAFORTH$$,#{state_id_nsw},-33.797106,151.251146), - ($$2093$$,$$BALGOWLAH$$,#{state_id_nsw},-33.794121,151.26268), - ($$2093$$,$$BALGOWLAH HEIGHTS$$,#{state_id_nsw},-33.794121,151.26268), - ($$2093$$,$$CLONTARF$$,#{state_id_nsw},-33.794121,151.26268), - ($$2093$$,$$MANLY VALE$$,#{state_id_nsw},-33.794121,151.26268), - ($$2093$$,$$NORTH BALGOWLAH$$,#{state_id_nsw},-33.794121,151.26268), - ($$2094$$,$$FAIRLIGHT$$,#{state_id_nsw},-33.794163,151.273978), - ($$2095$$,$$MANLY$$,#{state_id_nsw},-33.797144,151.28804), - ($$2095$$,$$MANLY EAST$$,#{state_id_nsw},-33.797144,151.28804), - ($$2096$$,$$CURL CURL$$,#{state_id_nsw},-33.768937,151.294035), - ($$2096$$,$$FRESHWATER$$,#{state_id_nsw},-33.768937,151.294035), - ($$2096$$,$$QUEENSCLIFF$$,#{state_id_nsw},-33.768937,151.294035), - ($$2097$$,$$COLLAROY$$,#{state_id_nsw},-33.740969,151.303133), - ($$2097$$,$$COLLAROY BEACH$$,#{state_id_nsw},-33.740969,151.303133), - ($$2097$$,$$COLLAROY PLATEAU WEST$$,#{state_id_nsw},-33.740969,151.303133), - ($$2097$$,$$WHEELER HEIGHTS$$,#{state_id_nsw},-33.740969,151.303133), - ($$2099$$,$$CROMER$$,#{state_id_nsw},-33.740353,151.278523), - ($$2099$$,$$DEE WHY$$,#{state_id_nsw},-33.740353,151.278523), - ($$2099$$,$$NARRAWEENA$$,#{state_id_nsw},-33.740353,151.278523), - ($$2099$$,$$NORTH CURL CURL$$,#{state_id_nsw},-33.740353,151.278523), - ($$2100$$,$$ALLAMBIE HEIGHTS$$,#{state_id_nsw},-33.765076,151.248864), - ($$2100$$,$$BEACON HILL$$,#{state_id_nsw},-33.765076,151.248864), - ($$2100$$,$$BROOKVALE$$,#{state_id_nsw},-33.765076,151.248864), - ($$2100$$,$$NORTH MANLY$$,#{state_id_nsw},-33.765076,151.248864), - ($$2100$$,$$OXFORD FALLS$$,#{state_id_nsw},-33.765076,151.248864), - ($$2100$$,$$WARRINGAH MALL$$,#{state_id_nsw},-33.765076,151.248864), - ($$2101$$,$$ELANORA HEIGHTS$$,#{state_id_nsw},-33.695015,151.280156), - ($$2101$$,$$INGLESIDE$$,#{state_id_nsw},-33.695015,151.280156), - ($$2101$$,$$NARRABEEN$$,#{state_id_nsw},-33.695015,151.280156), - ($$2101$$,$$NORTH NARRABEEN$$,#{state_id_nsw},-33.695015,151.280156), - ($$2102$$,$$WARRIEWOOD$$,#{state_id_nsw},-33.686265,151.29908), - ($$2102$$,$$WARRIEWOOD SHOPPING SQUARE$$,#{state_id_nsw},-33.686265,151.29908), - ($$2103$$,$$MONA VALE$$,#{state_id_nsw},-33.67707,151.300316), - ($$2104$$,$$BAYVIEW$$,#{state_id_nsw},-33.66445,151.298945), - ($$2105$$,$$CHURCH POINT$$,#{state_id_nsw},-33.644873,151.284352), - ($$2105$$,$$ELVINA BAY$$,#{state_id_nsw},-33.644873,151.284352), - ($$2105$$,$$LOVETT BAY$$,#{state_id_nsw},-33.644873,151.284352), - ($$2105$$,$$MORNING BAY$$,#{state_id_nsw},-33.644873,151.284352), - ($$2105$$,$$SCOTLAND ISLAND$$,#{state_id_nsw},-33.644873,151.284352), - ($$2106$$,$$NEWPORT$$,#{state_id_nsw},-33.659896,151.309312), - ($$2106$$,$$NEWPORT BEACH$$,#{state_id_nsw},-33.659896,151.309312), - ($$2107$$,$$AVALON$$,#{state_id_nsw},-33.636325,151.330596), - ($$2107$$,$$AVALON BEACH$$,#{state_id_nsw},-33.636325,151.330596), - ($$2107$$,$$BILGOLA$$,#{state_id_nsw},-33.636325,151.330596), - ($$2107$$,$$CLAREVILLE$$,#{state_id_nsw},-33.636325,151.330596), - ($$2107$$,$$WHALE BEACH$$,#{state_id_nsw},-33.636325,151.330596), - ($$2108$$,$$COASTERS RETREAT$$,#{state_id_nsw},-33.6048,151.29883), - ($$2108$$,$$GREAT MACKEREL BEACH$$,#{state_id_nsw},-33.6048,151.29883), - ($$2108$$,$$PALM BEACH$$,#{state_id_nsw},-33.6048,151.29883), - ($$2109$$,$$MACQUARIE UNIVERSITY$$,#{state_id_nsw},-33.774321,151.111988), - ($$2110$$,$$HUNTERS HILL$$,#{state_id_nsw},-33.83484,151.154196), - ($$2110$$,$$WOOLWICH$$,#{state_id_nsw},-33.83484,151.154196), - ($$2111$$,$$BORONIA PARK$$,#{state_id_nsw},-33.820941,151.140067), - ($$2111$$,$$GLADESVILLE$$,#{state_id_nsw},-33.820941,151.140067), - ($$2111$$,$$HENLEY$$,#{state_id_nsw},-33.820941,151.140067), - ($$2111$$,$$HUNTLEYS COVE$$,#{state_id_nsw},-33.820941,151.140067), - ($$2111$$,$$HUNTLEYS POINT$$,#{state_id_nsw},-33.820941,151.140067), - ($$2111$$,$$MONASH PARK$$,#{state_id_nsw},-33.820941,151.140067), - ($$2111$$,$$TENNYSON POINT$$,#{state_id_nsw},-33.820941,151.140067), - ($$2112$$,$$DENISTONE EAST$$,#{state_id_nsw},-33.797177,151.097546), - ($$2112$$,$$PUTNEY$$,#{state_id_nsw},-33.797177,151.097546), - ($$2112$$,$$RYDE$$,#{state_id_nsw},-33.797177,151.097546), - ($$2113$$,$$BLENHEIM ROAD$$,#{state_id_nsw},-33.798595,151.133903), - ($$2113$$,$$EAST RYDE$$,#{state_id_nsw},-33.798595,151.133903), - ($$2113$$,$$MACQUARIE CENTRE$$,#{state_id_nsw},-33.798595,151.133903), - ($$2113$$,$$MACQUARIE PARK$$,#{state_id_nsw},-33.798595,151.133903), - ($$2113$$,$$NORTH RYDE$$,#{state_id_nsw},-33.798595,151.133903), - ($$2114$$,$$DENISTONE$$,#{state_id_nsw},-33.799441,151.07959), - ($$2114$$,$$DENISTONE WEST$$,#{state_id_nsw},-33.799441,151.07959), - ($$2114$$,$$MEADOWBANK$$,#{state_id_nsw},-33.799441,151.07959), - ($$2114$$,$$MELROSE PARK$$,#{state_id_nsw},-33.799441,151.07959), - ($$2114$$,$$WEST RYDE$$,#{state_id_nsw},-33.799441,151.07959), - ($$2115$$,$$ERMINGTON$$,#{state_id_nsw},-33.814144,151.054495), - ($$2116$$,$$RYDALMERE$$,#{state_id_nsw},-33.811244,151.034464), - ($$2117$$,$$DUNDAS$$,#{state_id_nsw},-33.799405,151.044189), - ($$2117$$,$$DUNDAS VALLEY$$,#{state_id_nsw},-33.799405,151.044189), - ($$2117$$,$$OATLANDS$$,#{state_id_nsw},-33.799405,151.044189), - ($$2117$$,$$TELOPEA$$,#{state_id_nsw},-33.799405,151.044189), - ($$2118$$,$$CARLINGFORD$$,#{state_id_nsw},-33.782959,151.047707), - ($$2118$$,$$CARLINGFORD COURT$$,#{state_id_nsw},-33.782959,151.047707), - ($$2118$$,$$CARLINGFORD NORTH$$,#{state_id_nsw},-33.782959,151.047707), - ($$2118$$,$$KINGSDENE$$,#{state_id_nsw},-33.782959,151.047707), - ($$2119$$,$$BEECROFT$$,#{state_id_nsw},-33.749498,151.064533), - ($$2119$$,$$CHELTENHAM$$,#{state_id_nsw},-33.749498,151.064533), - ($$2120$$,$$PENNANT HILLS$$,#{state_id_nsw},-33.738681,151.071433), - ($$2120$$,$$THORNLEIGH$$,#{state_id_nsw},-33.738681,151.071433), - ($$2120$$,$$WESTLEIGH$$,#{state_id_nsw},-33.738681,151.071433), - ($$2121$$,$$EPPING$$,#{state_id_nsw},-33.772549,151.082365), - ($$2121$$,$$NORTH EPPING$$,#{state_id_nsw},-33.772549,151.082365), - ($$2122$$,$$EASTWOOD$$,#{state_id_nsw},-33.789986,151.080914), - ($$2122$$,$$MARSFIELD$$,#{state_id_nsw},-33.789986,151.080914), - ($$2123$$,$$PARRAMATTA$$,#{state_id_nsw},-33.886166,151.139472), - ($$2124$$,$$PARRAMATTA$$,#{state_id_nsw},-33.886166,151.139472), - ($$2125$$,$$WEST PENNANT HILLS$$,#{state_id_nsw},-33.753676,151.039113), - ($$2126$$,$$CHERRYBROOK$$,#{state_id_nsw},-33.722019,151.041806), - ($$2127$$,$$NEWINGTON$$,#{state_id_nsw},-33.85283,151.076186), - ($$2127$$,$$SYDNEY OLYMPIC PARK$$,#{state_id_nsw},-33.85283,151.076186), - ($$2127$$,$$WENTWORTH POINT$$,#{state_id_nsw},-33.85283,151.076186), - ($$2128$$,$$SILVERWATER$$,#{state_id_nsw},-33.835928,151.047591), - ($$2129$$,$$SYDNEY MARKETS$$,#{state_id_nsw},-33.871209,151.191884), - ($$2130$$,$$SUMMER HILL$$,#{state_id_nsw},-33.891712,151.137258), - ($$2131$$,$$ASHFIELD$$,#{state_id_nsw},-33.889498,151.127444), - ($$2132$$,$$CROYDON$$,#{state_id_nsw},-33.883163,151.114771), - ($$2133$$,$$CROYDON PARK$$,#{state_id_nsw},-33.895299,151.108581), - ($$2133$$,$$ENFIELD SOUTH$$,#{state_id_nsw},-33.895299,151.108581), - ($$2134$$,$$BURWOOD$$,#{state_id_nsw},-33.877423,151.103682), - ($$2134$$,$$BURWOOD NORTH$$,#{state_id_nsw},-33.877423,151.103682), - ($$2135$$,$$STRATHFIELD$$,#{state_id_nsw},-33.873913,151.093993), - ($$2136$$,$$BURWOOD HEIGHTS$$,#{state_id_nsw},-33.888328,151.103412), - ($$2136$$,$$ENFIELD$$,#{state_id_nsw},-33.888328,151.103412), - ($$2136$$,$$STRATHFIELD SOUTH$$,#{state_id_nsw},-33.888328,151.103412), - ($$2137$$,$$BREAKFAST POINT$$,#{state_id_nsw},-33.841583,151.107502), - ($$2137$$,$$CABARITA$$,#{state_id_nsw},-33.841583,151.107502), - ($$2137$$,$$CONCORD$$,#{state_id_nsw},-33.841583,151.107502), - ($$2137$$,$$MORTLAKE$$,#{state_id_nsw},-33.841583,151.107502), - ($$2137$$,$$NORTH STRATHFIELD$$,#{state_id_nsw},-33.841583,151.107502), - ($$2138$$,$$CONCORD WEST$$,#{state_id_nsw},-33.848041,151.087326), - ($$2138$$,$$LIBERTY GROVE$$,#{state_id_nsw},-33.848041,151.087326), - ($$2138$$,$$RHODES$$,#{state_id_nsw},-33.848041,151.087326), - ($$2139$$,$$CONCORD REPATRIATION HOSPITAL$$,#{state_id_nsw},-33.837702,151.095046), - ($$2140$$,$$HOMEBUSH$$,#{state_id_nsw},-33.859654,151.081847), - ($$2140$$,$$HOMEBUSH SOUTH$$,#{state_id_nsw},-33.859654,151.081847), - ($$2140$$,$$HOMEBUSH WEST$$,#{state_id_nsw},-33.859654,151.081847), - ($$2141$$,$$BERALA$$,#{state_id_nsw},-33.871904,151.031033), - ($$2141$$,$$LIDCOMBE$$,#{state_id_nsw},-33.871904,151.031033), - ($$2141$$,$$LIDCOMBE NORTH$$,#{state_id_nsw},-33.871904,151.031033), - ($$2141$$,$$ROOKWOOD$$,#{state_id_nsw},-33.871904,151.031033), - ($$2142$$,$$BLAXCELL$$,#{state_id_nsw},-33.853251,151.008129), - ($$2142$$,$$CAMELLIA$$,#{state_id_nsw},-33.853251,151.008129), - ($$2142$$,$$CLYDE$$,#{state_id_nsw},-33.853251,151.008129), - ($$2142$$,$$GRANVILLE$$,#{state_id_nsw},-33.853251,151.008129), - ($$2142$$,$$HOLROYD$$,#{state_id_nsw},-33.853251,151.008129), - ($$2142$$,$$ROSEHILL$$,#{state_id_nsw},-33.853251,151.008129), - ($$2142$$,$$SOUTH GRANVILLE$$,#{state_id_nsw},-33.853251,151.008129), - ($$2143$$,$$BIRRONG$$,#{state_id_nsw},-33.89022,151.022398), - ($$2143$$,$$POTTS HILL$$,#{state_id_nsw},-33.89022,151.022398), - ($$2143$$,$$REGENTS PARK$$,#{state_id_nsw},-33.89022,151.022398), - ($$2144$$,$$AUBURN$$,#{state_id_nsw},-33.849322,151.033421), - ($$2145$$,$$CONSTITUTION HILL$$,#{state_id_nsw},-33.799843,150.947276), - ($$2145$$,$$GIRRAWEEN$$,#{state_id_nsw},-33.799843,150.947276), - ($$2145$$,$$GREYSTANES$$,#{state_id_nsw},-33.799843,150.947276), - ($$2145$$,$$MAYS HILL$$,#{state_id_nsw},-33.799843,150.947276), - ($$2145$$,$$PEMULWUY$$,#{state_id_nsw},-33.799843,150.947276), - ($$2145$$,$$PENDLE HILL$$,#{state_id_nsw},-33.799843,150.947276), - ($$2145$$,$$SOUTH WENTWORTHVILLE$$,#{state_id_nsw},-33.799843,150.947276), - ($$2145$$,$$WENTWORTHVILLE$$,#{state_id_nsw},-33.799843,150.947276), - ($$2145$$,$$WESTMEAD$$,#{state_id_nsw},-33.799843,150.947276), - ($$2146$$,$$OLD TOONGABBIE$$,#{state_id_nsw},-33.792713,150.974108), - ($$2146$$,$$TOONGABBIE$$,#{state_id_nsw},-33.792713,150.974108), - ($$2146$$,$$TOONGABBIE EAST$$,#{state_id_nsw},-33.792713,150.974108), - ($$2147$$,$$KINGS LANGLEY$$,#{state_id_nsw},-33.742079,150.922711), - ($$2147$$,$$LALOR PARK$$,#{state_id_nsw},-33.742079,150.922711), - ($$2147$$,$$SEVEN HILLS$$,#{state_id_nsw},-33.742079,150.922711), - ($$2147$$,$$SEVEN HILLS WEST$$,#{state_id_nsw},-33.742079,150.922711), - ($$2148$$,$$ARNDELL PARK$$,#{state_id_nsw},-33.787266,150.871959), - ($$2148$$,$$BLACKTOWN$$,#{state_id_nsw},-33.787266,150.871959), - ($$2148$$,$$BLACKTOWN WESTPOINT$$,#{state_id_nsw},-33.787266,150.871959), - ($$2148$$,$$HUNTINGWOOD$$,#{state_id_nsw},-33.787266,150.871959), - ($$2148$$,$$KINGS PARK$$,#{state_id_nsw},-33.787266,150.871959), - ($$2148$$,$$MARAYONG$$,#{state_id_nsw},-33.787266,150.871959), - ($$2148$$,$$PROSPECT$$,#{state_id_nsw},-33.787266,150.871959), - ($$2150$$,$$HARRIS PARK$$,#{state_id_nsw},-33.822427,151.008961), - ($$2150$$,$$PARRAMATTA$$,#{state_id_nsw},-33.822427,151.008961), - ($$2150$$,$$PARRAMATTA WESTFIELD$$,#{state_id_nsw},-33.822427,151.008961), - ($$2151$$,$$NORTH PARRAMATTA$$,#{state_id_nsw},-33.798043,151.010707), - ($$2151$$,$$NORTH ROCKS$$,#{state_id_nsw},-33.798043,151.010707), - ($$2152$$,$$NORTHMEAD$$,#{state_id_nsw},-33.783849,150.994336), - ($$2153$$,$$BAULKHAM HILLS$$,#{state_id_nsw},-33.758601,150.992887), - ($$2153$$,$$BELLA VISTA$$,#{state_id_nsw},-33.758601,150.992887), - ($$2153$$,$$WINSTON HILLS$$,#{state_id_nsw},-33.758601,150.992887), - ($$2154$$,$$CASTLE HILL$$,#{state_id_nsw},-33.732307,151.005616), - ($$2155$$,$$BEAUMONT HILLS$$,#{state_id_nsw},-33.703416,150.946875), - ($$2155$$,$$KELLYVILLE$$,#{state_id_nsw},-33.703416,150.946875), - ($$2155$$,$$KELLYVILLE RIDGE$$,#{state_id_nsw},-33.703416,150.946875), - ($$2155$$,$$ROUSE HILL$$,#{state_id_nsw},-33.703416,150.946875), - ($$2156$$,$$ANNANGROVE$$,#{state_id_nsw},-33.657772,150.943505), - ($$2156$$,$$GLENHAVEN$$,#{state_id_nsw},-33.657772,150.943505), - ($$2156$$,$$KENTHURST$$,#{state_id_nsw},-33.657772,150.943505), - ($$2157$$,$$CANOELANDS$$,#{state_id_nsw},-33.508714,151.061148), - ($$2157$$,$$FOREST GLEN$$,#{state_id_nsw},-33.508714,151.061148), - ($$2157$$,$$GLENORIE$$,#{state_id_nsw},-33.508714,151.061148), - ($$2158$$,$$DURAL$$,#{state_id_nsw},-33.681875,151.028435), - ($$2158$$,$$MIDDLE DURAL$$,#{state_id_nsw},-33.681875,151.028435), - ($$2158$$,$$ROUND CORNER$$,#{state_id_nsw},-33.681875,151.028435), - ($$2159$$,$$ARCADIA$$,#{state_id_nsw},-33.623101,151.052726), - ($$2159$$,$$BERRILEE$$,#{state_id_nsw},-33.623101,151.052726), - ($$2159$$,$$FIDDLETOWN$$,#{state_id_nsw},-33.623101,151.052726), - ($$2159$$,$$GALSTON$$,#{state_id_nsw},-33.623101,151.052726), - ($$2160$$,$$MERRYLANDS$$,#{state_id_nsw},-33.836381,150.989219), - ($$2160$$,$$MERRYLANDS WEST$$,#{state_id_nsw},-33.836381,150.989219), - ($$2161$$,$$GUILDFORD$$,#{state_id_nsw},-33.853984,150.985958), - ($$2161$$,$$GUILDFORD WEST$$,#{state_id_nsw},-33.853984,150.985958), - ($$2161$$,$$OLD GUILDFORD$$,#{state_id_nsw},-33.853984,150.985958), - ($$2161$$,$$YENNORA$$,#{state_id_nsw},-33.853984,150.985958), - ($$2162$$,$$CHESTER HILL$$,#{state_id_nsw},-33.883157,151.001185), - ($$2162$$,$$SEFTON$$,#{state_id_nsw},-33.883157,151.001185), - ($$2163$$,$$CARRAMAR$$,#{state_id_nsw},-33.884558,150.961884), - ($$2163$$,$$LANSDOWNE$$,#{state_id_nsw},-33.884558,150.961884), - ($$2163$$,$$VILLAWOOD$$,#{state_id_nsw},-33.884558,150.961884), - ($$2164$$,$$SMITHFIELD$$,#{state_id_nsw},-33.853546,150.940431), - ($$2164$$,$$SMITHFIELD WEST$$,#{state_id_nsw},-33.853546,150.940431), - ($$2164$$,$$WETHERILL PARK$$,#{state_id_nsw},-33.853546,150.940431), - ($$2164$$,$$WOODPARK$$,#{state_id_nsw},-33.853546,150.940431), - ($$2165$$,$$FAIRFIELD$$,#{state_id_nsw},-33.868529,150.955512), - ($$2165$$,$$FAIRFIELD EAST$$,#{state_id_nsw},-33.868529,150.955512), - ($$2165$$,$$FAIRFIELD HEIGHTS$$,#{state_id_nsw},-33.868529,150.955512), - ($$2165$$,$$FAIRFIELD WEST$$,#{state_id_nsw},-33.868529,150.955512), - ($$2166$$,$$CABRAMATTA$$,#{state_id_nsw},-33.89507,150.935889), - ($$2166$$,$$CABRAMATTA WEST$$,#{state_id_nsw},-33.89507,150.935889), - ($$2166$$,$$CANLEY HEIGHTS$$,#{state_id_nsw},-33.89507,150.935889), - ($$2166$$,$$CANLEY VALE$$,#{state_id_nsw},-33.89507,150.935889), - ($$2166$$,$$LANSVALE$$,#{state_id_nsw},-33.89507,150.935889), - ($$2167$$,$$GLENFIELD$$,#{state_id_nsw},-33.971305,150.894517), - ($$2168$$,$$ASHCROFT$$,#{state_id_nsw},-33.917587,150.899095), - ($$2168$$,$$BUSBY$$,#{state_id_nsw},-33.917587,150.899095), - ($$2168$$,$$CARTWRIGHT$$,#{state_id_nsw},-33.917587,150.899095), - ($$2168$$,$$GREEN VALLEY$$,#{state_id_nsw},-33.917587,150.899095), - ($$2168$$,$$HECKENBERG$$,#{state_id_nsw},-33.917587,150.899095), - ($$2168$$,$$HINCHINBROOK$$,#{state_id_nsw},-33.917587,150.899095), - ($$2168$$,$$MILLER$$,#{state_id_nsw},-33.917587,150.899095), - ($$2168$$,$$SADLEIR$$,#{state_id_nsw},-33.917587,150.899095), - ($$2170$$,$$CASULA$$,#{state_id_nsw},-33.94735,150.907753), - ($$2170$$,$$CASULA MALL$$,#{state_id_nsw},-33.94735,150.907753), - ($$2170$$,$$CHIPPING NORTON$$,#{state_id_nsw},-33.94735,150.907753), - ($$2170$$,$$HAMMONDVILLE$$,#{state_id_nsw},-33.94735,150.907753), - ($$2170$$,$$LIVERPOOL$$,#{state_id_nsw},-33.94735,150.907753), - ($$2170$$,$$LIVERPOOL SOUTH$$,#{state_id_nsw},-33.94735,150.907753), - ($$2170$$,$$LIVERPOOL WESTFIELD$$,#{state_id_nsw},-33.94735,150.907753), - ($$2170$$,$$LURNEA$$,#{state_id_nsw},-33.94735,150.907753), - ($$2170$$,$$MOOREBANK$$,#{state_id_nsw},-33.94735,150.907753), - ($$2170$$,$$MOUNT PRITCHARD$$,#{state_id_nsw},-33.94735,150.907753), - ($$2170$$,$$PRESTONS$$,#{state_id_nsw},-33.94735,150.907753), - ($$2170$$,$$WARWICK FARM$$,#{state_id_nsw},-33.94735,150.907753), - ($$2171$$,$$CECIL HILLS$$,#{state_id_nsw},-33.883696,150.853171), - ($$2171$$,$$ELIZABETH HILLS$$,#{state_id_nsw},-33.883696,150.853171), - ($$2171$$,$$HORNINGSEA PARK$$,#{state_id_nsw},-33.883696,150.853171), - ($$2171$$,$$HOXTON PARK$$,#{state_id_nsw},-33.883696,150.853171), - ($$2171$$,$$LEN WATERS ESTATE$$,#{state_id_nsw},-33.883696,150.853171), - ($$2171$$,$$MIDDLETON GRANGE$$,#{state_id_nsw},-33.883696,150.853171), - ($$2171$$,$$WEST HOXTON$$,#{state_id_nsw},-33.883696,150.853171), - ($$2172$$,$$PLEASURE POINT$$,#{state_id_nsw},-33.966909,150.98764), - ($$2172$$,$$SANDY POINT$$,#{state_id_nsw},-33.966909,150.98764), - ($$2172$$,$$VOYAGER POINT$$,#{state_id_nsw},-33.966909,150.98764), - ($$2173$$,$$HOLSWORTHY$$,#{state_id_nsw},-33.950403,150.949972), - ($$2173$$,$$WATTLE GROVE$$,#{state_id_nsw},-33.950403,150.949972), - ($$2174$$,$$EDMONDSON PARK$$,#{state_id_nsw},0.0,0.0), - ($$2174$$,$$INGLEBURN MILPO$$,#{state_id_nsw},0.0,0.0), - ($$2175$$,$$HORSLEY PARK$$,#{state_id_nsw},-33.845034,150.848192), - ($$2176$$,$$ABBOTSBURY$$,#{state_id_nsw},-33.877538,150.867768), - ($$2176$$,$$BOSSLEY PARK$$,#{state_id_nsw},-33.877538,150.867768), - ($$2176$$,$$EDENSOR PARK$$,#{state_id_nsw},-33.877538,150.867768), - ($$2176$$,$$GREENFIELD PARK$$,#{state_id_nsw},-33.877538,150.867768), - ($$2176$$,$$PRAIRIEWOOD$$,#{state_id_nsw},-33.877538,150.867768), - ($$2176$$,$$ST JOHNS PARK$$,#{state_id_nsw},-33.877538,150.867768), - ($$2176$$,$$WAKELEY$$,#{state_id_nsw},-33.877538,150.867768), - ($$2177$$,$$BONNYRIGG$$,#{state_id_nsw},-33.888801,150.886505), - ($$2177$$,$$BONNYRIGG HEIGHTS$$,#{state_id_nsw},-33.888801,150.886505), - ($$2178$$,$$CECIL PARK$$,#{state_id_nsw},-33.87478,150.838225), - ($$2178$$,$$KEMPS CREEK$$,#{state_id_nsw},-33.87478,150.838225), - ($$2178$$,$$MOUNT VERNON$$,#{state_id_nsw},-33.87478,150.838225), - ($$2179$$,$$AUSTRAL$$,#{state_id_nsw},-33.933109,150.812031), - ($$2179$$,$$LEPPINGTON$$,#{state_id_nsw},-33.933109,150.812031), - ($$2190$$,$$CHULLORA$$,#{state_id_nsw},-33.892441,151.055898), - ($$2190$$,$$GREENACRE$$,#{state_id_nsw},-33.892441,151.055898), - ($$2190$$,$$MOUNT LEWIS$$,#{state_id_nsw},-33.892441,151.055898), - ($$2191$$,$$BELFIELD$$,#{state_id_nsw},-33.902107,151.083553), - ($$2192$$,$$BELMORE$$,#{state_id_nsw},-33.916035,151.0875), - ($$2193$$,$$ASHBURY$$,#{state_id_nsw},-33.901897,151.11932), - ($$2193$$,$$CANTERBURY$$,#{state_id_nsw},-33.901897,151.11932), - ($$2193$$,$$HURLSTONE PARK$$,#{state_id_nsw},-33.901897,151.11932), - ($$2194$$,$$CAMPSIE$$,#{state_id_nsw},-33.914373,151.103465), - ($$2195$$,$$LAKEMBA$$,#{state_id_nsw},-33.920457,151.075921), - ($$2195$$,$$WILEY PARK$$,#{state_id_nsw},-33.920457,151.075921), - ($$2196$$,$$PUNCHBOWL$$,#{state_id_nsw},-33.925686,151.054635), - ($$2196$$,$$ROSELANDS$$,#{state_id_nsw},-33.925686,151.054635), - ($$2197$$,$$BASS HILL$$,#{state_id_nsw},-33.900608,150.992888), - ($$2198$$,$$GEORGES HALL$$,#{state_id_nsw},-33.912851,150.982469), - ($$2199$$,$$YAGOONA$$,#{state_id_nsw},-33.907725,151.026108), - ($$2199$$,$$YAGOONA WEST$$,#{state_id_nsw},-33.907725,151.026108), - ($$2200$$,$$BANKSTOWN$$,#{state_id_nsw},-33.919539,151.034909), - ($$2200$$,$$BANKSTOWN AERODROME$$,#{state_id_nsw},-33.919539,151.034909), - ($$2200$$,$$BANKSTOWN NORTH$$,#{state_id_nsw},-33.919539,151.034909), - ($$2200$$,$$BANKSTOWN SQUARE$$,#{state_id_nsw},-33.919539,151.034909), - ($$2200$$,$$CONDELL PARK$$,#{state_id_nsw},-33.919539,151.034909), - ($$2200$$,$$MANAHAN$$,#{state_id_nsw},-33.919539,151.034909), - ($$2200$$,$$MOUNT LEWIS$$,#{state_id_nsw},-33.919539,151.034909), - ($$2203$$,$$DULWICH HILL$$,#{state_id_nsw},-33.904689,151.138774), - ($$2204$$,$$MARRICKVILLE$$,#{state_id_nsw},-33.910923,151.157187), - ($$2204$$,$$MARRICKVILLE METRO$$,#{state_id_nsw},-33.910923,151.157187), - ($$2204$$,$$MARRICKVILLE SOUTH$$,#{state_id_nsw},-33.910923,151.157187), - ($$2205$$,$$ARNCLIFFE$$,#{state_id_nsw},-33.936592,151.146805), - ($$2205$$,$$TURRELLA$$,#{state_id_nsw},-33.936592,151.146805), - ($$2205$$,$$WOLLI CREEK$$,#{state_id_nsw},-33.936592,151.146805), - ($$2206$$,$$CLEMTON PARK$$,#{state_id_nsw},-33.925357,151.103282), - ($$2206$$,$$EARLWOOD$$,#{state_id_nsw},-33.925357,151.103282), - ($$2207$$,$$BARDWELL PARK$$,#{state_id_nsw},-33.93207,151.125594), - ($$2207$$,$$BARDWELL VALLEY$$,#{state_id_nsw},-33.93207,151.125594), - ($$2207$$,$$BEXLEY$$,#{state_id_nsw},-33.93207,151.125594), - ($$2207$$,$$BEXLEY NORTH$$,#{state_id_nsw},-33.93207,151.125594), - ($$2207$$,$$BEXLEY SOUTH$$,#{state_id_nsw},-33.93207,151.125594), - ($$2208$$,$$KINGSGROVE$$,#{state_id_nsw},-33.939481,151.098941), - ($$2208$$,$$KINGSWAY WEST$$,#{state_id_nsw},-33.939481,151.098941), - ($$2209$$,$$BEVERLY HILLS$$,#{state_id_nsw},-33.954218,151.076364), - ($$2209$$,$$NARWEE$$,#{state_id_nsw},-33.954218,151.076364), - ($$2210$$,$$LUGARNO$$,#{state_id_nsw},-33.982956,151.046942), - ($$2210$$,$$PEAKHURST$$,#{state_id_nsw},-33.982956,151.046942), - ($$2210$$,$$PEAKHURST HEIGHTS$$,#{state_id_nsw},-33.982956,151.046942), - ($$2210$$,$$RIVERWOOD$$,#{state_id_nsw},-33.982956,151.046942), - ($$2211$$,$$PADSTOW$$,#{state_id_nsw},-33.953915,151.038163), - ($$2211$$,$$PADSTOW HEIGHTS$$,#{state_id_nsw},-33.953915,151.038163), - ($$2212$$,$$REVESBY$$,#{state_id_nsw},-33.951507,151.017247), - ($$2212$$,$$REVESBY HEIGHTS$$,#{state_id_nsw},-33.951507,151.017247), - ($$2212$$,$$REVESBY NORTH$$,#{state_id_nsw},-33.951507,151.017247), - ($$2213$$,$$EAST HILLS$$,#{state_id_nsw},-33.963359,150.986695), - ($$2213$$,$$PANANIA$$,#{state_id_nsw},-33.963359,150.986695), - ($$2213$$,$$PICNIC POINT$$,#{state_id_nsw},-33.963359,150.986695), - ($$2214$$,$$MILPERRA$$,#{state_id_nsw},-33.937834,150.989263), - ($$2216$$,$$BANKSIA$$,#{state_id_nsw},-33.945237,151.140161), - ($$2216$$,$$BRIGHTON-LE-SANDS$$,#{state_id_nsw},-33.945237,151.140161), - ($$2216$$,$$KYEEMAGH$$,#{state_id_nsw},-33.945237,151.140161), - ($$2216$$,$$ROCKDALE$$,#{state_id_nsw},-33.945237,151.140161), - ($$2217$$,$$BEVERLEY PARK$$,#{state_id_nsw},-33.975135,151.131345), - ($$2217$$,$$KOGARAH$$,#{state_id_nsw},-33.975135,151.131345), - ($$2217$$,$$KOGARAH BAY$$,#{state_id_nsw},-33.975135,151.131345), - ($$2217$$,$$MONTEREY$$,#{state_id_nsw},-33.975135,151.131345), - ($$2217$$,$$RAMSGATE$$,#{state_id_nsw},-33.975135,151.131345), - ($$2217$$,$$RAMSGATE BEACH$$,#{state_id_nsw},-33.975135,151.131345), - ($$2218$$,$$ALLAWAH$$,#{state_id_nsw},-33.970018,151.114517), - ($$2218$$,$$CARLTON$$,#{state_id_nsw},-33.970018,151.114517), - ($$2219$$,$$DOLLS POINT$$,#{state_id_nsw},-33.993495,151.146801), - ($$2219$$,$$SANDRINGHAM$$,#{state_id_nsw},-33.993495,151.146801), - ($$2219$$,$$SANS SOUCI$$,#{state_id_nsw},-33.993495,151.146801), - ($$2220$$,$$HURSTVILLE$$,#{state_id_nsw},-33.965923,151.101184), - ($$2220$$,$$HURSTVILLE GROVE$$,#{state_id_nsw},-33.965923,151.101184), - ($$2220$$,$$HURSTVILLE WESTFIELD$$,#{state_id_nsw},-33.965923,151.101184), - ($$2221$$,$$BLAKEHURST$$,#{state_id_nsw},-33.988743,151.112314), - ($$2221$$,$$CARSS PARK$$,#{state_id_nsw},-33.988743,151.112314), - ($$2221$$,$$CONNELLS POINT$$,#{state_id_nsw},-33.988743,151.112314), - ($$2221$$,$$KYLE BAY$$,#{state_id_nsw},-33.988743,151.112314), - ($$2221$$,$$SOUTH HURSTVILLE$$,#{state_id_nsw},-33.988743,151.112314), - ($$2222$$,$$PENSHURST$$,#{state_id_nsw},-33.963346,151.086744), - ($$2223$$,$$MORTDALE$$,#{state_id_nsw},-33.972239,151.075391), - ($$2223$$,$$OATLEY$$,#{state_id_nsw},-33.972239,151.075391), - ($$2224$$,$$KANGAROO POINT$$,#{state_id_nsw},-33.997972,151.096235), - ($$2224$$,$$SYLVANIA$$,#{state_id_nsw},-33.997972,151.096235), - ($$2224$$,$$SYLVANIA SOUTHGATE$$,#{state_id_nsw},-33.997972,151.096235), - ($$2224$$,$$SYLVANIA WATERS$$,#{state_id_nsw},-33.997972,151.096235), - ($$2225$$,$$OYSTER BAY$$,#{state_id_nsw},-33.997441,151.087892), - ($$2226$$,$$BONNET BAY$$,#{state_id_nsw},-34.009518,151.054252), - ($$2226$$,$$COMO$$,#{state_id_nsw},-34.009518,151.054252), - ($$2226$$,$$JANNALI$$,#{state_id_nsw},-34.009518,151.054252), - ($$2227$$,$$GYMEA$$,#{state_id_nsw},-34.033142,151.085421), - ($$2227$$,$$GYMEA BAY$$,#{state_id_nsw},-34.033142,151.085421), - ($$2228$$,$$MIRANDA$$,#{state_id_nsw},-34.034014,151.100428), - ($$2228$$,$$YOWIE BAY$$,#{state_id_nsw},-34.034014,151.100428), - ($$2229$$,$$CARINGBAH$$,#{state_id_nsw},-34.04316,151.123102), - ($$2229$$,$$CARINGBAH SOUTH$$,#{state_id_nsw},-34.04316,151.123102), - ($$2229$$,$$DOLANS BAY$$,#{state_id_nsw},-34.04316,151.123102), - ($$2229$$,$$LILLI PILLI$$,#{state_id_nsw},-34.04316,151.123102), - ($$2229$$,$$PORT HACKING$$,#{state_id_nsw},-34.04316,151.123102), - ($$2229$$,$$TAREN POINT$$,#{state_id_nsw},-34.04316,151.123102), - ($$2230$$,$$BUNDEENA$$,#{state_id_nsw},-34.085064,151.151259), - ($$2230$$,$$BURRANEER$$,#{state_id_nsw},-34.085064,151.151259), - ($$2230$$,$$CRONULLA$$,#{state_id_nsw},-34.085064,151.151259), - ($$2230$$,$$MAIANBAR$$,#{state_id_nsw},-34.085064,151.151259), - ($$2230$$,$$WOOLOOWARE$$,#{state_id_nsw},-34.085064,151.151259), - ($$2231$$,$$KURNELL$$,#{state_id_nsw},-34.008487,151.204879), - ($$2232$$,$$AUDLEY$$,#{state_id_nsw},-34.075295,151.056519), - ($$2232$$,$$GARIE$$,#{state_id_nsw},-34.075295,151.056519), - ($$2232$$,$$GRAYS POINT$$,#{state_id_nsw},-34.075295,151.056519), - ($$2232$$,$$KAREELA$$,#{state_id_nsw},-34.075295,151.056519), - ($$2232$$,$$KIRRAWEE$$,#{state_id_nsw},-34.075295,151.056519), - ($$2232$$,$$LOFTUS$$,#{state_id_nsw},-34.075295,151.056519), - ($$2232$$,$$SUTHERLAND$$,#{state_id_nsw},-34.075295,151.056519), - ($$2232$$,$$WORONORA$$,#{state_id_nsw},-34.075295,151.056519), - ($$2233$$,$$ENGADINE$$,#{state_id_nsw},-34.065716,151.012663), - ($$2233$$,$$HEATHCOTE$$,#{state_id_nsw},-34.065716,151.012663), - ($$2233$$,$$WATERFALL$$,#{state_id_nsw},-34.065716,151.012663), - ($$2233$$,$$WORONORA HEIGHTS$$,#{state_id_nsw},-34.065716,151.012663), - ($$2233$$,$$YARRAWARRAH$$,#{state_id_nsw},-34.065716,151.012663), - ($$2234$$,$$ALFORDS POINT$$,#{state_id_nsw},-33.993303,151.024751), - ($$2234$$,$$BANGOR$$,#{state_id_nsw},-33.993303,151.024751), - ($$2234$$,$$BARDEN RIDGE$$,#{state_id_nsw},-33.993303,151.024751), - ($$2234$$,$$ILLAWONG$$,#{state_id_nsw},-33.993303,151.024751), - ($$2234$$,$$LUCAS HEIGHTS$$,#{state_id_nsw},-33.993303,151.024751), - ($$2234$$,$$MENAI$$,#{state_id_nsw},-33.993303,151.024751), - ($$2234$$,$$MENAI CENTRAL$$,#{state_id_nsw},-33.993303,151.024751), - ($$2250$$,$$BUCKETTY$$,#{state_id_nsw},-33.111273,151.138861), - ($$2250$$,$$CALGA$$,#{state_id_nsw},-33.111273,151.138861), - ($$2250$$,$$CENTRAL MANGROVE$$,#{state_id_nsw},-33.111273,151.138861), - ($$2250$$,$$EAST GOSFORD$$,#{state_id_nsw},-33.111273,151.138861), - ($$2250$$,$$ERINA$$,#{state_id_nsw},-33.111273,151.138861), - ($$2250$$,$$ERINA FAIR$$,#{state_id_nsw},-33.111273,151.138861), - ($$2250$$,$$GLENWORTH VALLEY$$,#{state_id_nsw},-33.111273,151.138861), - ($$2250$$,$$GOSFORD$$,#{state_id_nsw},-33.111273,151.138861), - ($$2250$$,$$GREENGROVE$$,#{state_id_nsw},-33.111273,151.138861), - ($$2250$$,$$HOLGATE$$,#{state_id_nsw},-33.111273,151.138861), - ($$2250$$,$$KARIONG$$,#{state_id_nsw},-33.111273,151.138861), - ($$2250$$,$$KULNURA$$,#{state_id_nsw},-33.111273,151.138861), - ($$2250$$,$$LISAROW$$,#{state_id_nsw},-33.111273,151.138861), - ($$2250$$,$$LOWER MANGROVE$$,#{state_id_nsw},-33.111273,151.138861), - ($$2250$$,$$MANGROVE CREEK$$,#{state_id_nsw},-33.111273,151.138861), - ($$2250$$,$$MANGROVE MOUNTAIN$$,#{state_id_nsw},-33.111273,151.138861), - ($$2250$$,$$MATCHAM$$,#{state_id_nsw},-33.111273,151.138861), - ($$2250$$,$$MOONEY MOONEY CREEK$$,#{state_id_nsw},-33.111273,151.138861), - ($$2250$$,$$MOUNT ELLIOT$$,#{state_id_nsw},-33.111273,151.138861), - ($$2250$$,$$MOUNT WHITE$$,#{state_id_nsw},-33.111273,151.138861), - ($$2250$$,$$NARARA$$,#{state_id_nsw},-33.111273,151.138861), - ($$2250$$,$$NIAGARA PARK$$,#{state_id_nsw},-33.111273,151.138861), - ($$2250$$,$$NORTH GOSFORD$$,#{state_id_nsw},-33.111273,151.138861), - ($$2250$$,$$PEATS RIDGE$$,#{state_id_nsw},-33.111273,151.138861), - ($$2250$$,$$POINT CLARE$$,#{state_id_nsw},-33.111273,151.138861), - ($$2250$$,$$POINT FREDERICK$$,#{state_id_nsw},-33.111273,151.138861), - ($$2250$$,$$SOMERSBY$$,#{state_id_nsw},-33.111273,151.138861), - ($$2250$$,$$SPRINGFIELD$$,#{state_id_nsw},-33.111273,151.138861), - ($$2250$$,$$TASCOTT$$,#{state_id_nsw},-33.111273,151.138861), - ($$2250$$,$$TEN MILE HOLLOW$$,#{state_id_nsw},-33.111273,151.138861), - ($$2250$$,$$UPPER MANGROVE$$,#{state_id_nsw},-33.111273,151.138861), - ($$2250$$,$$WENDOREE PARK$$,#{state_id_nsw},-33.111273,151.138861), - ($$2250$$,$$WEST GOSFORD$$,#{state_id_nsw},-33.111273,151.138861), - ($$2250$$,$$WYOMING$$,#{state_id_nsw},-33.111273,151.138861), - ($$2251$$,$$AVOCA BEACH$$,#{state_id_nsw},-33.464937,151.432387), - ($$2251$$,$$BENSVILLE$$,#{state_id_nsw},-33.464937,151.432387), - ($$2251$$,$$BOUDDI$$,#{state_id_nsw},-33.464937,151.432387), - ($$2251$$,$$COPACABANA$$,#{state_id_nsw},-33.464937,151.432387), - ($$2251$$,$$DAVISTOWN$$,#{state_id_nsw},-33.464937,151.432387), - ($$2251$$,$$GREEN POINT$$,#{state_id_nsw},-33.464937,151.432387), - ($$2251$$,$$KINCUMBER$$,#{state_id_nsw},-33.464937,151.432387), - ($$2251$$,$$KINCUMBER SOUTH$$,#{state_id_nsw},-33.464937,151.432387), - ($$2251$$,$$MACMASTERS BEACH$$,#{state_id_nsw},-33.464937,151.432387), - ($$2251$$,$$PICKETTS VALLEY$$,#{state_id_nsw},-33.464937,151.432387), - ($$2251$$,$$SARATOGA$$,#{state_id_nsw},-33.464937,151.432387), - ($$2251$$,$$YATTALUNGA$$,#{state_id_nsw},-33.464937,151.432387), - ($$2252$$,$$CENTRAL COAST MC$$,#{state_id_nsw},0.0,0.0), - ($$2256$$,$$BLACKWALL$$,#{state_id_nsw},-33.503434,151.327632), - ($$2256$$,$$HORSFIELD BAY$$,#{state_id_nsw},-33.503434,151.327632), - ($$2256$$,$$KOOLEWONG$$,#{state_id_nsw},-33.503434,151.327632), - ($$2256$$,$$LITTLE WOBBY$$,#{state_id_nsw},-33.503434,151.327632), - ($$2256$$,$$PATONGA$$,#{state_id_nsw},-33.503434,151.327632), - ($$2256$$,$$PEARL BEACH$$,#{state_id_nsw},-33.503434,151.327632), - ($$2256$$,$$PHEGANS BAY$$,#{state_id_nsw},-33.503434,151.327632), - ($$2256$$,$$WONDABYNE$$,#{state_id_nsw},-33.503434,151.327632), - ($$2256$$,$$WOY WOY$$,#{state_id_nsw},-33.503434,151.327632), - ($$2256$$,$$WOY WOY BAY$$,#{state_id_nsw},-33.503434,151.327632), - ($$2257$$,$$BOOKER BAY$$,#{state_id_nsw},-33.511566,151.34478), - ($$2257$$,$$BOX HEAD$$,#{state_id_nsw},-33.511566,151.34478), - ($$2257$$,$$DALEYS POINT$$,#{state_id_nsw},-33.511566,151.34478), - ($$2257$$,$$EMPIRE BAY$$,#{state_id_nsw},-33.511566,151.34478), - ($$2257$$,$$ETTALONG BEACH$$,#{state_id_nsw},-33.511566,151.34478), - ($$2257$$,$$HARDYS BAY$$,#{state_id_nsw},-33.511566,151.34478), - ($$2257$$,$$KILLCARE$$,#{state_id_nsw},-33.511566,151.34478), - ($$2257$$,$$KILLCARE HEIGHTS$$,#{state_id_nsw},-33.511566,151.34478), - ($$2257$$,$$PRETTY BEACH$$,#{state_id_nsw},-33.511566,151.34478), - ($$2257$$,$$ST HUBERTS ISLAND$$,#{state_id_nsw},-33.511566,151.34478), - ($$2257$$,$$UMINA BEACH$$,#{state_id_nsw},-33.511566,151.34478), - ($$2257$$,$$WAGSTAFFE$$,#{state_id_nsw},-33.511566,151.34478), - ($$2258$$,$$FOUNTAINDALE$$,#{state_id_nsw},-33.338303,151.392826), - ($$2258$$,$$KANGY ANGY$$,#{state_id_nsw},-33.338303,151.392826), - ($$2258$$,$$OURIMBAH$$,#{state_id_nsw},-33.338303,151.392826), - ($$2258$$,$$PALM GROVE$$,#{state_id_nsw},-33.338303,151.392826), - ($$2258$$,$$PALMDALE$$,#{state_id_nsw},-33.338303,151.392826), - ($$2259$$,$$ALISON$$,#{state_id_nsw},-33.265998,151.403858), - ($$2259$$,$$BUSHELLS RIDGE$$,#{state_id_nsw},-33.265998,151.403858), - ($$2259$$,$$CEDAR BRUSH CREEK$$,#{state_id_nsw},-33.265998,151.403858), - ($$2259$$,$$CHAIN VALLEY BAY$$,#{state_id_nsw},-33.265998,151.403858), - ($$2259$$,$$CRANGAN BAY$$,#{state_id_nsw},-33.265998,151.403858), - ($$2259$$,$$DOORALONG$$,#{state_id_nsw},-33.265998,151.403858), - ($$2259$$,$$DURREN DURREN$$,#{state_id_nsw},-33.265998,151.403858), - ($$2259$$,$$FRAZER PARK$$,#{state_id_nsw},-33.265998,151.403858), - ($$2259$$,$$FREEMANS$$,#{state_id_nsw},-33.265998,151.403858), - ($$2259$$,$$GWANDALAN$$,#{state_id_nsw},-33.265998,151.403858), - ($$2259$$,$$HALLORAN$$,#{state_id_nsw},-33.265998,151.403858), - ($$2259$$,$$HAMLYN TERRACE$$,#{state_id_nsw},-33.265998,151.403858), - ($$2259$$,$$JILLIBY$$,#{state_id_nsw},-33.265998,151.403858), - ($$2259$$,$$KANWAL$$,#{state_id_nsw},-33.265998,151.403858), - ($$2259$$,$$KIAR$$,#{state_id_nsw},-33.265998,151.403858), - ($$2259$$,$$KINGFISHER SHORES$$,#{state_id_nsw},-33.265998,151.403858), - ($$2259$$,$$LAKE MUNMORAH$$,#{state_id_nsw},-33.265998,151.403858), - ($$2259$$,$$LEMON TREE$$,#{state_id_nsw},-33.265998,151.403858), - ($$2259$$,$$LITTLE JILLIBY$$,#{state_id_nsw},-33.265998,151.403858), - ($$2259$$,$$MANNERING PARK$$,#{state_id_nsw},-33.265998,151.403858), - ($$2259$$,$$MARDI$$,#{state_id_nsw},-33.265998,151.403858), - ($$2259$$,$$MOONEE$$,#{state_id_nsw},-33.265998,151.403858), - ($$2259$$,$$POINT WOLSTONCROFT$$,#{state_id_nsw},-33.265998,151.403858), - ($$2259$$,$$RAVENSDALE$$,#{state_id_nsw},-33.265998,151.403858), - ($$2259$$,$$ROCKY POINT$$,#{state_id_nsw},-33.265998,151.403858), - ($$2259$$,$$SUMMERLAND POINT$$,#{state_id_nsw},-33.265998,151.403858), - ($$2259$$,$$TACOMA$$,#{state_id_nsw},-33.265998,151.403858), - ($$2259$$,$$TACOMA SOUTH$$,#{state_id_nsw},-33.265998,151.403858), - ($$2259$$,$$TUGGERAH$$,#{state_id_nsw},-33.265998,151.403858), - ($$2259$$,$$TUGGERAWONG$$,#{state_id_nsw},-33.265998,151.403858), - ($$2259$$,$$WADALBA$$,#{state_id_nsw},-33.265998,151.403858), - ($$2259$$,$$WALLARAH$$,#{state_id_nsw},-33.265998,151.403858), - ($$2259$$,$$WARNERVALE$$,#{state_id_nsw},-33.265998,151.403858), - ($$2259$$,$$WATANOBBI$$,#{state_id_nsw},-33.265998,151.403858), - ($$2259$$,$$WOONGARRAH$$,#{state_id_nsw},-33.265998,151.403858), - ($$2259$$,$$WYBUNG$$,#{state_id_nsw},-33.265998,151.403858), - ($$2259$$,$$WYEE$$,#{state_id_nsw},-33.265998,151.403858), - ($$2259$$,$$WYEE POINT$$,#{state_id_nsw},-33.265998,151.403858), - ($$2259$$,$$WYONG$$,#{state_id_nsw},-33.265998,151.403858), - ($$2259$$,$$WYONG CREEK$$,#{state_id_nsw},-33.265998,151.403858), - ($$2259$$,$$WYONGAH$$,#{state_id_nsw},-33.265998,151.403858), - ($$2259$$,$$YARRAMALONG$$,#{state_id_nsw},-33.265998,151.403858), - ($$2260$$,$$ERINA HEIGHTS$$,#{state_id_nsw},-33.426949,151.413211), - ($$2260$$,$$FORRESTERS BEACH$$,#{state_id_nsw},-33.426949,151.413211), - ($$2260$$,$$NORTH AVOCA$$,#{state_id_nsw},-33.426949,151.413211), - ($$2260$$,$$TERRIGAL$$,#{state_id_nsw},-33.426949,151.413211), - ($$2260$$,$$WAMBERAL$$,#{state_id_nsw},-33.426949,151.413211), - ($$2261$$,$$BATEAU BAY$$,#{state_id_nsw},-33.381213,151.479104), - ($$2261$$,$$BAY VILLAGE$$,#{state_id_nsw},-33.381213,151.479104), - ($$2261$$,$$BERKELEY VALE$$,#{state_id_nsw},-33.381213,151.479104), - ($$2261$$,$$BLUE BAY$$,#{state_id_nsw},-33.381213,151.479104), - ($$2261$$,$$CHITTAWAY BAY$$,#{state_id_nsw},-33.381213,151.479104), - ($$2261$$,$$CHITTAWAY POINT$$,#{state_id_nsw},-33.381213,151.479104), - ($$2261$$,$$GLENNING VALLEY$$,#{state_id_nsw},-33.381213,151.479104), - ($$2261$$,$$KILLARNEY VALE$$,#{state_id_nsw},-33.381213,151.479104), - ($$2261$$,$$LONG JETTY$$,#{state_id_nsw},-33.381213,151.479104), - ($$2261$$,$$MAGENTA$$,#{state_id_nsw},-33.381213,151.479104), - ($$2261$$,$$SHELLY BEACH$$,#{state_id_nsw},-33.381213,151.479104), - ($$2261$$,$$THE ENTRANCE$$,#{state_id_nsw},-33.381213,151.479104), - ($$2261$$,$$THE ENTRANCE NORTH$$,#{state_id_nsw},-33.381213,151.479104), - ($$2261$$,$$TOOWOON BAY$$,#{state_id_nsw},-33.381213,151.479104), - ($$2261$$,$$TUMBI UMBI$$,#{state_id_nsw},-33.381213,151.479104), - ($$2262$$,$$BLUE HAVEN$$,#{state_id_nsw},-33.207351,151.492751), - ($$2262$$,$$BUDGEWOI$$,#{state_id_nsw},-33.207351,151.492751), - ($$2262$$,$$BUDGEWOI PENINSULA$$,#{state_id_nsw},-33.207351,151.492751), - ($$2262$$,$$BUFF POINT$$,#{state_id_nsw},-33.207351,151.492751), - ($$2262$$,$$COLONGRA$$,#{state_id_nsw},-33.207351,151.492751), - ($$2262$$,$$DOYALSON$$,#{state_id_nsw},-33.207351,151.492751), - ($$2262$$,$$DOYALSON NORTH$$,#{state_id_nsw},-33.207351,151.492751), - ($$2262$$,$$HALEKULANI$$,#{state_id_nsw},-33.207351,151.492751), - ($$2262$$,$$SAN REMO$$,#{state_id_nsw},-33.207351,151.492751), - ($$2263$$,$$CANTON BEACH$$,#{state_id_nsw},-33.271922,151.544045), - ($$2263$$,$$CHARMHAVEN$$,#{state_id_nsw},-33.271922,151.544045), - ($$2263$$,$$GOROKAN$$,#{state_id_nsw},-33.271922,151.544045), - ($$2263$$,$$LAKE HAVEN$$,#{state_id_nsw},-33.271922,151.544045), - ($$2263$$,$$NORAH HEAD$$,#{state_id_nsw},-33.271922,151.544045), - ($$2263$$,$$NORAVILLE$$,#{state_id_nsw},-33.271922,151.544045), - ($$2263$$,$$TOUKLEY$$,#{state_id_nsw},-33.271922,151.544045), - ($$2264$$,$$BALCOLYN$$,#{state_id_nsw},-33.095642,151.553169), - ($$2264$$,$$BONNELLS BAY$$,#{state_id_nsw},-33.095642,151.553169), - ($$2264$$,$$BRIGHTWATERS$$,#{state_id_nsw},-33.095642,151.553169), - ($$2264$$,$$DORA CREEK$$,#{state_id_nsw},-33.095642,151.553169), - ($$2264$$,$$ERARING$$,#{state_id_nsw},-33.095642,151.553169), - ($$2264$$,$$MANDALONG$$,#{state_id_nsw},-33.095642,151.553169), - ($$2264$$,$$MIRRABOOKA$$,#{state_id_nsw},-33.095642,151.553169), - ($$2264$$,$$MORISSET$$,#{state_id_nsw},-33.095642,151.553169), - ($$2264$$,$$MORISSET PARK$$,#{state_id_nsw},-33.095642,151.553169), - ($$2264$$,$$MYUNA BAY$$,#{state_id_nsw},-33.095642,151.553169), - ($$2264$$,$$SILVERWATER$$,#{state_id_nsw},-33.095642,151.553169), - ($$2264$$,$$SUNSHINE$$,#{state_id_nsw},-33.095642,151.553169), - ($$2264$$,$$WINDERMERE PARK$$,#{state_id_nsw},-33.095642,151.553169), - ($$2264$$,$$YARRAWONGA PARK$$,#{state_id_nsw},-33.095642,151.553169), - ($$2265$$,$$COORANBONG$$,#{state_id_nsw},-33.076609,151.453989), - ($$2265$$,$$MARTINSVILLE$$,#{state_id_nsw},-33.076609,151.453989), - ($$2267$$,$$WANGI WANGI$$,#{state_id_nsw},-33.071491,151.584366), - ($$2278$$,$$BARNSLEY$$,#{state_id_nsw},-32.932412,151.590415), - ($$2278$$,$$KILLINGWORTH$$,#{state_id_nsw},-32.932412,151.590415), - ($$2278$$,$$WAKEFIELD$$,#{state_id_nsw},-32.932412,151.590415), - ($$2280$$,$$BELMONT$$,#{state_id_nsw},-33.036057,151.660563), - ($$2280$$,$$BELMONT NORTH$$,#{state_id_nsw},-33.036057,151.660563), - ($$2280$$,$$BELMONT SOUTH$$,#{state_id_nsw},-33.036057,151.660563), - ($$2280$$,$$CROUDACE BAY$$,#{state_id_nsw},-33.036057,151.660563), - ($$2280$$,$$FLORAVILLE$$,#{state_id_nsw},-33.036057,151.660563), - ($$2280$$,$$JEWELLS$$,#{state_id_nsw},-33.036057,151.660563), - ($$2280$$,$$MARKS POINT$$,#{state_id_nsw},-33.036057,151.660563), - ($$2280$$,$$VALENTINE$$,#{state_id_nsw},-33.036057,151.660563), - ($$2281$$,$$BLACKSMITHS$$,#{state_id_nsw},-33.077161,151.652305), - ($$2281$$,$$CAMS WHARF$$,#{state_id_nsw},-33.077161,151.652305), - ($$2281$$,$$CATHERINE HILL BAY$$,#{state_id_nsw},-33.077161,151.652305), - ($$2281$$,$$CAVES BEACH$$,#{state_id_nsw},-33.077161,151.652305), - ($$2281$$,$$LITTLE PELICAN$$,#{state_id_nsw},-33.077161,151.652305), - ($$2281$$,$$MURRAYS BEACH$$,#{state_id_nsw},-33.077161,151.652305), - ($$2281$$,$$NORDS WHARF$$,#{state_id_nsw},-33.077161,151.652305), - ($$2281$$,$$PELICAN$$,#{state_id_nsw},-33.077161,151.652305), - ($$2281$$,$$PINNY BEACH$$,#{state_id_nsw},-33.077161,151.652305), - ($$2281$$,$$SWANSEA$$,#{state_id_nsw},-33.077161,151.652305), - ($$2281$$,$$SWANSEA HEADS$$,#{state_id_nsw},-33.077161,151.652305), - ($$2282$$,$$ELEEBANA$$,#{state_id_nsw},-32.993626,151.635401), - ($$2282$$,$$LAKELANDS$$,#{state_id_nsw},-32.993626,151.635401), - ($$2282$$,$$WARNERS BAY$$,#{state_id_nsw},-32.993626,151.635401), - ($$2283$$,$$ARCADIA VALE$$,#{state_id_nsw},-33.060343,151.575815), - ($$2283$$,$$AWABA$$,#{state_id_nsw},-33.060343,151.575815), - ($$2283$$,$$BALMORAL$$,#{state_id_nsw},-33.060343,151.575815), - ($$2283$$,$$BLACKALLS PARK$$,#{state_id_nsw},-33.060343,151.575815), - ($$2283$$,$$BOLTON POINT$$,#{state_id_nsw},-33.060343,151.575815), - ($$2283$$,$$BUTTABA$$,#{state_id_nsw},-33.060343,151.575815), - ($$2283$$,$$CAREY BAY$$,#{state_id_nsw},-33.060343,151.575815), - ($$2283$$,$$COAL POINT$$,#{state_id_nsw},-33.060343,151.575815), - ($$2283$$,$$FASSIFERN$$,#{state_id_nsw},-33.060343,151.575815), - ($$2283$$,$$FENNELL BAY$$,#{state_id_nsw},-33.060343,151.575815), - ($$2283$$,$$FISHING POINT$$,#{state_id_nsw},-33.060343,151.575815), - ($$2283$$,$$KILABEN BAY$$,#{state_id_nsw},-33.060343,151.575815), - ($$2283$$,$$RATHMINES$$,#{state_id_nsw},-33.060343,151.575815), - ($$2283$$,$$RYHOPE$$,#{state_id_nsw},-33.060343,151.575815), - ($$2283$$,$$TORONTO$$,#{state_id_nsw},-33.060343,151.575815), - ($$2284$$,$$ARGENTON$$,#{state_id_nsw},-32.934813,151.630879), - ($$2284$$,$$BOOLAROO$$,#{state_id_nsw},-32.934813,151.630879), - ($$2284$$,$$BOORAGUL$$,#{state_id_nsw},-32.934813,151.630879), - ($$2284$$,$$MARMONG POINT$$,#{state_id_nsw},-32.934813,151.630879), - ($$2284$$,$$SPEERS POINT$$,#{state_id_nsw},-32.934813,151.630879), - ($$2284$$,$$TERALBA$$,#{state_id_nsw},-32.934813,151.630879), - ($$2284$$,$$WOODRISING$$,#{state_id_nsw},-32.934813,151.630879), - ($$2285$$,$$CAMERON PARK$$,#{state_id_nsw},-32.933952,151.655731), - ($$2285$$,$$CARDIFF$$,#{state_id_nsw},-32.933952,151.655731), - ($$2285$$,$$CARDIFF HEIGHTS$$,#{state_id_nsw},-32.933952,151.655731), - ($$2285$$,$$CARDIFF SOUTH$$,#{state_id_nsw},-32.933952,151.655731), - ($$2285$$,$$EDGEWORTH$$,#{state_id_nsw},-32.933952,151.655731), - ($$2285$$,$$GLENDALE$$,#{state_id_nsw},-32.933952,151.655731), - ($$2285$$,$$MACQUARIE HILLS$$,#{state_id_nsw},-32.933952,151.655731), - ($$2286$$,$$HOLMESVILLE$$,#{state_id_nsw},-32.913661,151.576847), - ($$2286$$,$$SEAHAMPTON$$,#{state_id_nsw},-32.913661,151.576847), - ($$2286$$,$$WEST WALLSEND$$,#{state_id_nsw},-32.913661,151.576847), - ($$2287$$,$$BIRMINGHAM GARDENS$$,#{state_id_nsw},-32.890844,151.690829), - ($$2287$$,$$ELERMORE VALE$$,#{state_id_nsw},-32.890844,151.690829), - ($$2287$$,$$FLETCHER$$,#{state_id_nsw},-32.890844,151.690829), - ($$2287$$,$$MARYLAND$$,#{state_id_nsw},-32.890844,151.690829), - ($$2287$$,$$MINMI$$,#{state_id_nsw},-32.890844,151.690829), - ($$2287$$,$$RANKIN PARK$$,#{state_id_nsw},-32.890844,151.690829), - ($$2287$$,$$WALLSEND$$,#{state_id_nsw},-32.890844,151.690829), - ($$2287$$,$$WALLSEND SOUTH$$,#{state_id_nsw},-32.890844,151.690829), - ($$2289$$,$$ADAMSTOWN$$,#{state_id_nsw},-32.932538,151.72625), - ($$2289$$,$$ADAMSTOWN HEIGHTS$$,#{state_id_nsw},-32.932538,151.72625), - ($$2289$$,$$GARDEN SUBURB$$,#{state_id_nsw},-32.932538,151.72625), - ($$2289$$,$$HIGHFIELDS$$,#{state_id_nsw},-32.932538,151.72625), - ($$2289$$,$$KOTARA$$,#{state_id_nsw},-32.932538,151.72625), - ($$2289$$,$$KOTARA FAIR$$,#{state_id_nsw},-32.932538,151.72625), - ($$2289$$,$$KOTARA SOUTH$$,#{state_id_nsw},-32.932538,151.72625), - ($$2290$$,$$BENNETTS GREEN$$,#{state_id_nsw},-32.995468,151.689084), - ($$2290$$,$$CHARLESTOWN$$,#{state_id_nsw},-32.995468,151.689084), - ($$2290$$,$$DUDLEY$$,#{state_id_nsw},-32.995468,151.689084), - ($$2290$$,$$GATESHEAD$$,#{state_id_nsw},-32.995468,151.689084), - ($$2290$$,$$HILLSBOROUGH$$,#{state_id_nsw},-32.995468,151.689084), - ($$2290$$,$$KAHIBAH$$,#{state_id_nsw},-32.995468,151.689084), - ($$2290$$,$$MOUNT HUTTON$$,#{state_id_nsw},-32.995468,151.689084), - ($$2290$$,$$REDHEAD$$,#{state_id_nsw},-32.995468,151.689084), - ($$2290$$,$$TINGIRA HEIGHTS$$,#{state_id_nsw},-32.995468,151.689084), - ($$2290$$,$$WHITEBRIDGE$$,#{state_id_nsw},-32.995468,151.689084), - ($$2291$$,$$MEREWETHER$$,#{state_id_nsw},-32.942237,151.751451), - ($$2291$$,$$MEREWETHER HEIGHTS$$,#{state_id_nsw},-32.942237,151.751451), - ($$2291$$,$$THE JUNCTION$$,#{state_id_nsw},-32.942237,151.751451), - ($$2292$$,$$BROADMEADOW$$,#{state_id_nsw},-32.924165,151.737829), - ($$2292$$,$$HAMILTON NORTH$$,#{state_id_nsw},-32.924165,151.737829), - ($$2293$$,$$MARYVILLE$$,#{state_id_nsw},-32.911844,151.753662), - ($$2293$$,$$WICKHAM$$,#{state_id_nsw},-32.911844,151.753662), - ($$2294$$,$$CARRINGTON$$,#{state_id_nsw},-32.916023,151.765725), - ($$2295$$,$$FERN BAY$$,#{state_id_nsw},-32.854436,151.810346), - ($$2295$$,$$STOCKTON$$,#{state_id_nsw},-32.854436,151.810346), - ($$2296$$,$$ISLINGTON$$,#{state_id_nsw},-32.911915,151.745721), - ($$2297$$,$$TIGHES HILL$$,#{state_id_nsw},-32.908014,151.751115), - ($$2298$$,$$GEORGETOWN$$,#{state_id_nsw},-32.907814,151.7286), - ($$2298$$,$$WARATAH$$,#{state_id_nsw},-32.907814,151.7286), - ($$2298$$,$$WARATAH WEST$$,#{state_id_nsw},-32.907814,151.7286), - ($$2299$$,$$JESMOND$$,#{state_id_nsw},-32.903131,151.690858), - ($$2299$$,$$LAMBTON$$,#{state_id_nsw},-32.903131,151.690858), - ($$2299$$,$$NORTH LAMBTON$$,#{state_id_nsw},-32.903131,151.690858), - ($$2300$$,$$BAR BEACH$$,#{state_id_nsw},-32.939962,151.768383), - ($$2300$$,$$COOKS HILL$$,#{state_id_nsw},-32.939962,151.768383), - ($$2300$$,$$NEWCASTLE$$,#{state_id_nsw},-32.939962,151.768383), - ($$2300$$,$$NEWCASTLE EAST$$,#{state_id_nsw},-32.939962,151.768383), - ($$2300$$,$$THE HILL$$,#{state_id_nsw},-32.939962,151.768383), - ($$2302$$,$$NEWCASTLE WEST$$,#{state_id_nsw},-32.924908,151.761141), - ($$2303$$,$$HAMILTON$$,#{state_id_nsw},-32.924042,151.746874), - ($$2303$$,$$HAMILTON EAST$$,#{state_id_nsw},-32.924042,151.746874), - ($$2303$$,$$HAMILTON SOUTH$$,#{state_id_nsw},-32.924042,151.746874), - ($$2304$$,$$KOORAGANG$$,#{state_id_nsw},-32.875728,151.74598), - ($$2304$$,$$MAYFIELD$$,#{state_id_nsw},-32.875728,151.74598), - ($$2304$$,$$MAYFIELD EAST$$,#{state_id_nsw},-32.875728,151.74598), - ($$2304$$,$$MAYFIELD NORTH$$,#{state_id_nsw},-32.875728,151.74598), - ($$2304$$,$$MAYFIELD WEST$$,#{state_id_nsw},-32.875728,151.74598), - ($$2304$$,$$SANDGATE$$,#{state_id_nsw},-32.875728,151.74598), - ($$2304$$,$$WARABROOK$$,#{state_id_nsw},-32.875728,151.74598), - ($$2305$$,$$KOTARA EAST$$,#{state_id_nsw},-32.934986,151.707216), - ($$2305$$,$$NEW LAMBTON$$,#{state_id_nsw},-32.934986,151.707216), - ($$2305$$,$$NEW LAMBTON HEIGHTS$$,#{state_id_nsw},-32.934986,151.707216), - ($$2306$$,$$WINDALE$$,#{state_id_nsw},-32.997694,151.681053), - ($$2307$$,$$SHORTLAND$$,#{state_id_nsw},-32.880873,151.691533), - ($$2308$$,$$CALLAGHAN$$,#{state_id_nsw},-35.125235,147.322357), - ($$2308$$,$$NEWCASTLE UNIVERSITY$$,#{state_id_nsw},-35.125235,147.322357), - ($$2309$$,$$DANGAR$$,#{state_id_nsw},-30.352158,148.890775), - ($$2310$$,$$HUNTER REGION MC$$,#{state_id_nsw},0.0,0.0), - ($$2311$$,$$ALLYNBROOK$$,#{state_id_nsw},-32.363343,151.536229), - ($$2311$$,$$BINGLEBURRA$$,#{state_id_nsw},-32.363343,151.536229), - ($$2311$$,$$CARRABOLLA$$,#{state_id_nsw},-32.363343,151.536229), - ($$2311$$,$$EAST GRESFORD$$,#{state_id_nsw},-32.363343,151.536229), - ($$2311$$,$$ECCLESTON$$,#{state_id_nsw},-32.363343,151.536229), - ($$2311$$,$$GRESFORD$$,#{state_id_nsw},-32.363343,151.536229), - ($$2311$$,$$HALTON$$,#{state_id_nsw},-32.363343,151.536229), - ($$2311$$,$$LEWINSBROOK$$,#{state_id_nsw},-32.363343,151.536229), - ($$2311$$,$$LOSTOCK$$,#{state_id_nsw},-32.363343,151.536229), - ($$2311$$,$$MOUNT RIVERS$$,#{state_id_nsw},-32.363343,151.536229), - ($$2311$$,$$UPPER ALLYN$$,#{state_id_nsw},-32.363343,151.536229), - ($$2312$$,$$MINIMBAH$$,#{state_id_nsw},-32.148267,152.361412), - ($$2312$$,$$NABIAC$$,#{state_id_nsw},-32.148267,152.361412), - ($$2314$$,$$WILLIAMTOWN RAAF$$,#{state_id_nsw},-32.797365,151.83699), - ($$2315$$,$$CORLETTE$$,#{state_id_nsw},-32.721158,152.106782), - ($$2315$$,$$FINGAL BAY$$,#{state_id_nsw},-32.721158,152.106782), - ($$2315$$,$$NELSON BAY$$,#{state_id_nsw},-32.721158,152.106782), - ($$2315$$,$$SHOAL BAY$$,#{state_id_nsw},-32.721158,152.106782), - ($$2316$$,$$ANNA BAY$$,#{state_id_nsw},-32.776919,152.083274), - ($$2316$$,$$BOAT HARBOUR$$,#{state_id_nsw},-32.776919,152.083274), - ($$2316$$,$$BOBS FARM$$,#{state_id_nsw},-32.776919,152.083274), - ($$2316$$,$$FISHERMANS BAY$$,#{state_id_nsw},-32.776919,152.083274), - ($$2316$$,$$ONE MILE$$,#{state_id_nsw},-32.776919,152.083274), - ($$2316$$,$$TAYLORS BEACH$$,#{state_id_nsw},-32.776919,152.083274), - ($$2317$$,$$SALAMANDER BAY$$,#{state_id_nsw},-32.720937,152.076399), - ($$2317$$,$$SOLDIERS POINT$$,#{state_id_nsw},-32.720937,152.076399), - ($$2318$$,$$CAMPVALE$$,#{state_id_nsw},-32.769906,151.851897), - ($$2318$$,$$FERODALE$$,#{state_id_nsw},-32.769906,151.851897), - ($$2318$$,$$FULLERTON COVE$$,#{state_id_nsw},-32.769906,151.851897), - ($$2318$$,$$MEDOWIE$$,#{state_id_nsw},-32.769906,151.851897), - ($$2318$$,$$OYSTER COVE$$,#{state_id_nsw},-32.769906,151.851897), - ($$2318$$,$$SALT ASH$$,#{state_id_nsw},-32.769906,151.851897), - ($$2318$$,$$WILLIAMTOWN$$,#{state_id_nsw},-32.769906,151.851897), - ($$2319$$,$$LEMON TREE PASSAGE$$,#{state_id_nsw},-32.730927,152.039551), - ($$2319$$,$$MALLABULA$$,#{state_id_nsw},-32.730927,152.039551), - ($$2319$$,$$TANILBA BAY$$,#{state_id_nsw},-32.730927,152.039551), - ($$2319$$,$$TILLIGERRY CREEK$$,#{state_id_nsw},-32.730927,152.039551), - ($$2320$$,$$ABERGLASSLYN$$,#{state_id_nsw},-32.694656,151.534607), - ($$2320$$,$$ALLANDALE$$,#{state_id_nsw},-32.694656,151.534607), - ($$2320$$,$$ANAMBAH$$,#{state_id_nsw},-32.694656,151.534607), - ($$2320$$,$$BOLWARRA$$,#{state_id_nsw},-32.694656,151.534607), - ($$2320$$,$$BOLWARRA HEIGHTS$$,#{state_id_nsw},-32.694656,151.534607), - ($$2320$$,$$FARLEY$$,#{state_id_nsw},-32.694656,151.534607), - ($$2320$$,$$GLEN OAK$$,#{state_id_nsw},-32.694656,151.534607), - ($$2320$$,$$GOSFORTH$$,#{state_id_nsw},-32.694656,151.534607), - ($$2320$$,$$HILLSBOROUGH$$,#{state_id_nsw},-32.694656,151.534607), - ($$2320$$,$$HORSESHOE BEND$$,#{state_id_nsw},-32.694656,151.534607), - ($$2320$$,$$KEINBAH$$,#{state_id_nsw},-32.694656,151.534607), - ($$2320$$,$$LARGS$$,#{state_id_nsw},-32.694656,151.534607), - ($$2320$$,$$LORN$$,#{state_id_nsw},-32.694656,151.534607), - ($$2320$$,$$LOUTH PARK$$,#{state_id_nsw},-32.694656,151.534607), - ($$2320$$,$$MAITLAND$$,#{state_id_nsw},-32.694656,151.534607), - ($$2320$$,$$MAITLAND NORTH$$,#{state_id_nsw},-32.694656,151.534607), - ($$2320$$,$$MAITLAND VALE$$,#{state_id_nsw},-32.694656,151.534607), - ($$2320$$,$$MELVILLE$$,#{state_id_nsw},-32.694656,151.534607), - ($$2320$$,$$MINDARIBBA$$,#{state_id_nsw},-32.694656,151.534607), - ($$2320$$,$$MOUNT DEE$$,#{state_id_nsw},-32.694656,151.534607), - ($$2320$$,$$OAKHAMPTON$$,#{state_id_nsw},-32.694656,151.534607), - ($$2320$$,$$OAKHAMPTON HEIGHTS$$,#{state_id_nsw},-32.694656,151.534607), - ($$2320$$,$$POKOLBIN$$,#{state_id_nsw},-32.694656,151.534607), - ($$2320$$,$$ROSEBROOK$$,#{state_id_nsw},-32.694656,151.534607), - ($$2320$$,$$ROTHBURY$$,#{state_id_nsw},-32.694656,151.534607), - ($$2320$$,$$RUTHERFORD$$,#{state_id_nsw},-32.694656,151.534607), - ($$2320$$,$$SOUTH MAITLAND$$,#{state_id_nsw},-32.694656,151.534607), - ($$2320$$,$$TELARAH$$,#{state_id_nsw},-32.694656,151.534607), - ($$2320$$,$$WALLALONG$$,#{state_id_nsw},-32.694656,151.534607), - ($$2320$$,$$WINDELLA$$,#{state_id_nsw},-32.694656,151.534607), - ($$2321$$,$$BERRY PARK$$,#{state_id_nsw},-32.755266,151.650248), - ($$2321$$,$$BUTTERWICK$$,#{state_id_nsw},-32.755266,151.650248), - ($$2321$$,$$CLARENCE TOWN$$,#{state_id_nsw},-32.755266,151.650248), - ($$2321$$,$$CLIFTLEIGH$$,#{state_id_nsw},-32.755266,151.650248), - ($$2321$$,$$DUCKENFIELD$$,#{state_id_nsw},-32.755266,151.650248), - ($$2321$$,$$DUNS CREEK$$,#{state_id_nsw},-32.755266,151.650248), - ($$2321$$,$$GILLIESTON HEIGHTS$$,#{state_id_nsw},-32.755266,151.650248), - ($$2321$$,$$GLEN MARTIN$$,#{state_id_nsw},-32.755266,151.650248), - ($$2321$$,$$GLEN WILLIAM$$,#{state_id_nsw},-32.755266,151.650248), - ($$2321$$,$$HARPERS HILL$$,#{state_id_nsw},-32.755266,151.650248), - ($$2321$$,$$HEDDON GRETA$$,#{state_id_nsw},-32.755266,151.650248), - ($$2321$$,$$HINTON$$,#{state_id_nsw},-32.755266,151.650248), - ($$2321$$,$$LOCHINVAR$$,#{state_id_nsw},-32.755266,151.650248), - ($$2321$$,$$LUSKINTYRE$$,#{state_id_nsw},-32.755266,151.650248), - ($$2321$$,$$MORPETH$$,#{state_id_nsw},-32.755266,151.650248), - ($$2321$$,$$OSWALD$$,#{state_id_nsw},-32.755266,151.650248), - ($$2321$$,$$PHOENIX PARK$$,#{state_id_nsw},-32.755266,151.650248), - ($$2321$$,$$RAWORTH$$,#{state_id_nsw},-32.755266,151.650248), - ($$2321$$,$$WINDERMERE$$,#{state_id_nsw},-32.755266,151.650248), - ($$2321$$,$$WOODVILLE$$,#{state_id_nsw},-32.755266,151.650248), - ($$2322$$,$$BERESFIELD$$,#{state_id_nsw},-32.801094,151.657881), - ($$2322$$,$$BLACK HILL$$,#{state_id_nsw},-32.801094,151.657881), - ($$2322$$,$$CHISHOLM$$,#{state_id_nsw},-32.801094,151.657881), - ($$2322$$,$$HEXHAM$$,#{state_id_nsw},-32.801094,151.657881), - ($$2322$$,$$LENAGHAN$$,#{state_id_nsw},-32.801094,151.657881), - ($$2322$$,$$STOCKRINGTON$$,#{state_id_nsw},-32.801094,151.657881), - ($$2322$$,$$TARRO$$,#{state_id_nsw},-32.801094,151.657881), - ($$2322$$,$$THORNTON$$,#{state_id_nsw},-32.801094,151.657881), - ($$2322$$,$$TOMAGO$$,#{state_id_nsw},-32.801094,151.657881), - ($$2322$$,$$WOODBERRY$$,#{state_id_nsw},-32.801094,151.657881), - ($$2323$$,$$ASHTONFIELD$$,#{state_id_nsw},-32.77382,151.601), - ($$2323$$,$$BRUNKERVILLE$$,#{state_id_nsw},-32.77382,151.601), - ($$2323$$,$$BUCHANAN$$,#{state_id_nsw},-32.77382,151.601), - ($$2323$$,$$BUTTAI$$,#{state_id_nsw},-32.77382,151.601), - ($$2323$$,$$EAST MAITLAND$$,#{state_id_nsw},-32.77382,151.601), - ($$2323$$,$$FOUR MILE CREEK$$,#{state_id_nsw},-32.77382,151.601), - ($$2323$$,$$FREEMANS WATERHOLE$$,#{state_id_nsw},-32.77382,151.601), - ($$2323$$,$$GREEN HILLS$$,#{state_id_nsw},-32.77382,151.601), - ($$2323$$,$$METFORD$$,#{state_id_nsw},-32.77382,151.601), - ($$2323$$,$$METFORD DC$$,#{state_id_nsw},-32.77382,151.601), - ($$2323$$,$$MOUNT VINCENT$$,#{state_id_nsw},-32.77382,151.601), - ($$2323$$,$$MULBRING$$,#{state_id_nsw},-32.77382,151.601), - ($$2323$$,$$PITNACREE$$,#{state_id_nsw},-32.77382,151.601), - ($$2323$$,$$RICHMOND VALE$$,#{state_id_nsw},-32.77382,151.601), - ($$2323$$,$$TENAMBIT$$,#{state_id_nsw},-32.77382,151.601), - ($$2324$$,$$BALICKERA$$,#{state_id_nsw},-32.673022,151.805367), - ($$2324$$,$$BRANDY HILL$$,#{state_id_nsw},-32.673022,151.805367), - ($$2324$$,$$BUNDABAH$$,#{state_id_nsw},-32.673022,151.805367), - ($$2324$$,$$CARRINGTON$$,#{state_id_nsw},-32.673022,151.805367), - ($$2324$$,$$EAGLETON$$,#{state_id_nsw},-32.673022,151.805367), - ($$2324$$,$$EAST SEAHAM$$,#{state_id_nsw},-32.673022,151.805367), - ($$2324$$,$$HAWKS NEST$$,#{state_id_nsw},-32.673022,151.805367), - ($$2324$$,$$HEATHERBRAE$$,#{state_id_nsw},-32.673022,151.805367), - ($$2324$$,$$KARUAH$$,#{state_id_nsw},-32.673022,151.805367), - ($$2324$$,$$LIMEBURNERS CREEK$$,#{state_id_nsw},-32.673022,151.805367), - ($$2324$$,$$MILLERS FOREST$$,#{state_id_nsw},-32.673022,151.805367), - ($$2324$$,$$NELSONS PLAINS$$,#{state_id_nsw},-32.673022,151.805367), - ($$2324$$,$$NORTH ARM COVE$$,#{state_id_nsw},-32.673022,151.805367), - ($$2324$$,$$OSTERLEY$$,#{state_id_nsw},-32.673022,151.805367), - ($$2324$$,$$PINDIMAR$$,#{state_id_nsw},-32.673022,151.805367), - ($$2324$$,$$RAYMOND TERRACE$$,#{state_id_nsw},-32.673022,151.805367), - ($$2324$$,$$RAYMOND TERRACE EAST$$,#{state_id_nsw},-32.673022,151.805367), - ($$2324$$,$$SEAHAM$$,#{state_id_nsw},-32.673022,151.805367), - ($$2324$$,$$SWAN BAY$$,#{state_id_nsw},-32.673022,151.805367), - ($$2324$$,$$TAHLEE$$,#{state_id_nsw},-32.673022,151.805367), - ($$2324$$,$$TEA GARDENS$$,#{state_id_nsw},-32.673022,151.805367), - ($$2324$$,$$TWELVE MILE CREEK$$,#{state_id_nsw},-32.673022,151.805367), - ($$2325$$,$$ABERDARE$$,#{state_id_nsw},-32.8442,151.376514), - ($$2325$$,$$ABERNETHY$$,#{state_id_nsw},-32.8442,151.376514), - ($$2325$$,$$BELLBIRD$$,#{state_id_nsw},-32.8442,151.376514), - ($$2325$$,$$BELLBIRD HEIGHTS$$,#{state_id_nsw},-32.8442,151.376514), - ($$2325$$,$$CEDAR CREEK$$,#{state_id_nsw},-32.8442,151.376514), - ($$2325$$,$$CESSNOCK$$,#{state_id_nsw},-32.8442,151.376514), - ($$2325$$,$$CESSNOCK WEST$$,#{state_id_nsw},-32.8442,151.376514), - ($$2325$$,$$CONGEWAI$$,#{state_id_nsw},-32.8442,151.376514), - ($$2325$$,$$CORRABARE$$,#{state_id_nsw},-32.8442,151.376514), - ($$2325$$,$$ELLALONG$$,#{state_id_nsw},-32.8442,151.376514), - ($$2325$$,$$ELRINGTON$$,#{state_id_nsw},-32.8442,151.376514), - ($$2325$$,$$GRETA MAIN$$,#{state_id_nsw},-32.8442,151.376514), - ($$2325$$,$$KEARSLEY$$,#{state_id_nsw},-32.8442,151.376514), - ($$2325$$,$$KITCHENER$$,#{state_id_nsw},-32.8442,151.376514), - ($$2325$$,$$LAGUNA$$,#{state_id_nsw},-32.8442,151.376514), - ($$2325$$,$$LOVEDALE$$,#{state_id_nsw},-32.8442,151.376514), - ($$2325$$,$$MILLFIELD$$,#{state_id_nsw},-32.8442,151.376514), - ($$2325$$,$$MORUBEN$$,#{state_id_nsw},-32.8442,151.376514), - ($$2325$$,$$MOUNT VIEW$$,#{state_id_nsw},-32.8442,151.376514), - ($$2325$$,$$NULKABA$$,#{state_id_nsw},-32.8442,151.376514), - ($$2325$$,$$OLNEY$$,#{state_id_nsw},-32.8442,151.376514), - ($$2325$$,$$PAXTON$$,#{state_id_nsw},-32.8442,151.376514), - ($$2325$$,$$PAYNES CROSSING$$,#{state_id_nsw},-32.8442,151.376514), - ($$2325$$,$$PELTON$$,#{state_id_nsw},-32.8442,151.376514), - ($$2325$$,$$QUORROBOLONG$$,#{state_id_nsw},-32.8442,151.376514), - ($$2325$$,$$SWEETMANS CREEK$$,#{state_id_nsw},-32.8442,151.376514), - ($$2325$$,$$WOLLOMBI$$,#{state_id_nsw},-32.8442,151.376514), - ($$2326$$,$$ABERMAIN$$,#{state_id_nsw},-32.810815,151.428668), - ($$2326$$,$$BISHOPS BRIDGE$$,#{state_id_nsw},-32.810815,151.428668), - ($$2326$$,$$LOXFORD$$,#{state_id_nsw},-32.810815,151.428668), - ($$2326$$,$$NEATH$$,#{state_id_nsw},-32.810815,151.428668), - ($$2326$$,$$SAWYERS GULLY$$,#{state_id_nsw},-32.810815,151.428668), - ($$2326$$,$$WESTON$$,#{state_id_nsw},-32.810815,151.428668), - ($$2327$$,$$KURRI KURRI$$,#{state_id_nsw},-32.817312,151.482952), - ($$2327$$,$$PELAW MAIN$$,#{state_id_nsw},-32.817312,151.482952), - ($$2327$$,$$STANFORD MERTHYR$$,#{state_id_nsw},-32.817312,151.482952), - ($$2328$$,$$BUREEN$$,#{state_id_nsw},-32.457341,150.741969), - ($$2328$$,$$DALSWINTON$$,#{state_id_nsw},-32.457341,150.741969), - ($$2328$$,$$DENMAN$$,#{state_id_nsw},-32.457341,150.741969), - ($$2328$$,$$GIANTS CREEK$$,#{state_id_nsw},-32.457341,150.741969), - ($$2328$$,$$HOLLYDEEN$$,#{state_id_nsw},-32.457341,150.741969), - ($$2328$$,$$KERRABEE$$,#{state_id_nsw},-32.457341,150.741969), - ($$2328$$,$$MANGOOLA$$,#{state_id_nsw},-32.457341,150.741969), - ($$2328$$,$$MARTINDALE$$,#{state_id_nsw},-32.457341,150.741969), - ($$2328$$,$$WIDDEN$$,#{state_id_nsw},-32.457341,150.741969), - ($$2328$$,$$YARRAWA$$,#{state_id_nsw},-32.457341,150.741969), - ($$2329$$,$$CASSILIS$$,#{state_id_nsw},-31.50643,150.642146), - ($$2329$$,$$MERRIWA$$,#{state_id_nsw},-31.50643,150.642146), - ($$2329$$,$$UARBRY$$,#{state_id_nsw},-31.50643,150.642146), - ($$2330$$,$$APPLETREE FLAT$$,#{state_id_nsw},-32.512305,150.864848), - ($$2330$$,$$BIG RIDGE$$,#{state_id_nsw},-32.512305,150.864848), - ($$2330$$,$$BIG YENGO$$,#{state_id_nsw},-32.512305,150.864848), - ($$2330$$,$$BOWMANS CREEK$$,#{state_id_nsw},-32.512305,150.864848), - ($$2330$$,$$BRIDGMAN$$,#{state_id_nsw},-32.512305,150.864848), - ($$2330$$,$$BROKE$$,#{state_id_nsw},-32.512305,150.864848), - ($$2330$$,$$BULGA$$,#{state_id_nsw},-32.512305,150.864848), - ($$2330$$,$$CAMBERWELL$$,#{state_id_nsw},-32.512305,150.864848), - ($$2330$$,$$CARROWBROOK$$,#{state_id_nsw},-32.512305,150.864848), - ($$2330$$,$$CLYDESDALE$$,#{state_id_nsw},-32.512305,150.864848), - ($$2330$$,$$COMBO$$,#{state_id_nsw},-32.512305,150.864848), - ($$2330$$,$$DARLINGTON$$,#{state_id_nsw},-32.512305,150.864848), - ($$2330$$,$$DOYLES CREEK$$,#{state_id_nsw},-32.512305,150.864848), - ($$2330$$,$$DUNOLLY$$,#{state_id_nsw},-32.512305,150.864848), - ($$2330$$,$$DURAL$$,#{state_id_nsw},-32.512305,150.864848), - ($$2330$$,$$DYRRING$$,#{state_id_nsw},-32.512305,150.864848), - ($$2330$$,$$FALBROOK$$,#{state_id_nsw},-32.512305,150.864848), - ($$2330$$,$$FERN GULLY$$,#{state_id_nsw},-32.512305,150.864848), - ($$2330$$,$$FORDWICH$$,#{state_id_nsw},-32.512305,150.864848), - ($$2330$$,$$GARLAND VALLEY$$,#{state_id_nsw},-32.512305,150.864848), - ($$2330$$,$$GLENDON$$,#{state_id_nsw},-32.512305,150.864848), - ($$2330$$,$$GLENDON BROOK$$,#{state_id_nsw},-32.512305,150.864848), - ($$2330$$,$$GLENNIES CREEK$$,#{state_id_nsw},-32.512305,150.864848), - ($$2330$$,$$GLENRIDDING$$,#{state_id_nsw},-32.512305,150.864848), - ($$2330$$,$$GOORANGOOLA$$,#{state_id_nsw},-32.512305,150.864848), - ($$2330$$,$$GOULDSVILLE$$,#{state_id_nsw},-32.512305,150.864848), - ($$2330$$,$$GOWRIE$$,#{state_id_nsw},-32.512305,150.864848), - ($$2330$$,$$GREENLANDS$$,#{state_id_nsw},-32.512305,150.864848), - ($$2330$$,$$HAMBLEDON HILL$$,#{state_id_nsw},-32.512305,150.864848), - ($$2330$$,$$HEBDEN$$,#{state_id_nsw},-32.512305,150.864848), - ($$2330$$,$$HOWES VALLEY$$,#{state_id_nsw},-32.512305,150.864848), - ($$2330$$,$$HOWICK$$,#{state_id_nsw},-32.512305,150.864848), - ($$2330$$,$$HUNTERVIEW$$,#{state_id_nsw},-32.512305,150.864848), - ($$2330$$,$$JERRYS PLAINS$$,#{state_id_nsw},-32.512305,150.864848), - ($$2330$$,$$LEMINGTON$$,#{state_id_nsw},-32.512305,150.864848), - ($$2330$$,$$LONG POINT$$,#{state_id_nsw},-32.512305,150.864848), - ($$2330$$,$$MAISON DIEU$$,#{state_id_nsw},-32.512305,150.864848), - ($$2330$$,$$MCDOUGALLS HILL$$,#{state_id_nsw},-32.512305,150.864848), - ($$2330$$,$$MIDDLE FALBROOK$$,#{state_id_nsw},-32.512305,150.864848), - ($$2330$$,$$MILBRODALE$$,#{state_id_nsw},-32.512305,150.864848), - ($$2330$$,$$MIRANNIE$$,#{state_id_nsw},-32.512305,150.864848), - ($$2330$$,$$MITCHELLS FLAT$$,#{state_id_nsw},-32.512305,150.864848), - ($$2330$$,$$MOUNT OLIVE$$,#{state_id_nsw},-32.512305,150.864848), - ($$2330$$,$$MOUNT ROYAL$$,#{state_id_nsw},-32.512305,150.864848), - ($$2330$$,$$MOUNT THORLEY$$,#{state_id_nsw},-32.512305,150.864848), - ($$2330$$,$$OBANVALE$$,#{state_id_nsw},-32.512305,150.864848), - ($$2330$$,$$PUTTY$$,#{state_id_nsw},-32.512305,150.864848), - ($$2330$$,$$RAVENSWORTH$$,#{state_id_nsw},-32.512305,150.864848), - ($$2330$$,$$REDBOURNBERRY$$,#{state_id_nsw},-32.512305,150.864848), - ($$2330$$,$$REEDY CREEK$$,#{state_id_nsw},-32.512305,150.864848), - ($$2330$$,$$RIXS CREEK$$,#{state_id_nsw},-32.512305,150.864848), - ($$2330$$,$$ROUGHIT$$,#{state_id_nsw},-32.512305,150.864848), - ($$2330$$,$$SCOTTS FLAT$$,#{state_id_nsw},-32.512305,150.864848), - ($$2330$$,$$SEDGEFIELD$$,#{state_id_nsw},-32.512305,150.864848), - ($$2330$$,$$SINGLETON$$,#{state_id_nsw},-32.512305,150.864848), - ($$2330$$,$$SINGLETON HEIGHTS$$,#{state_id_nsw},-32.512305,150.864848), - ($$2330$$,$$ST CLAIR$$,#{state_id_nsw},-32.512305,150.864848), - ($$2330$$,$$WARKWORTH$$,#{state_id_nsw},-32.512305,150.864848), - ($$2330$$,$$WATTLE PONDS$$,#{state_id_nsw},-32.512305,150.864848), - ($$2330$$,$$WESTBROOK$$,#{state_id_nsw},-32.512305,150.864848), - ($$2330$$,$$WHITTINGHAM$$,#{state_id_nsw},-32.512305,150.864848), - ($$2330$$,$$WOLLEMI$$,#{state_id_nsw},-32.512305,150.864848), - ($$2330$$,$$WYLIES FLAT$$,#{state_id_nsw},-32.512305,150.864848), - ($$2331$$,$$SINGLETON MILITARY AREA$$,#{state_id_nsw},-32.688569,151.180153), - ($$2331$$,$$SINGLETON MILPO$$,#{state_id_nsw},-32.688569,151.180153), - ($$2333$$,$$BAERAMI$$,#{state_id_nsw},-32.389105,150.470217), - ($$2333$$,$$BAERAMI CREEK$$,#{state_id_nsw},-32.389105,150.470217), - ($$2333$$,$$BENGALLA$$,#{state_id_nsw},-32.389105,150.470217), - ($$2333$$,$$CASTLE ROCK$$,#{state_id_nsw},-32.389105,150.470217), - ($$2333$$,$$EDDERTON$$,#{state_id_nsw},-32.389105,150.470217), - ($$2333$$,$$GUNGAL$$,#{state_id_nsw},-32.389105,150.470217), - ($$2333$$,$$KAYUGA$$,#{state_id_nsw},-32.389105,150.470217), - ($$2333$$,$$LIDDELL$$,#{state_id_nsw},-32.389105,150.470217), - ($$2333$$,$$MANOBALAI$$,#{state_id_nsw},-32.389105,150.470217), - ($$2333$$,$$MCCULLYS GAP$$,#{state_id_nsw},-32.389105,150.470217), - ($$2333$$,$$MUSCLE CREEK$$,#{state_id_nsw},-32.389105,150.470217), - ($$2333$$,$$MUSWELLBROOK$$,#{state_id_nsw},-32.389105,150.470217), - ($$2333$$,$$SANDY HOLLOW$$,#{state_id_nsw},-32.389105,150.470217), - ($$2333$$,$$WYBONG$$,#{state_id_nsw},-32.389105,150.470217), - ($$2334$$,$$GRETA$$,#{state_id_nsw},-32.677443,151.388741), - ($$2335$$,$$BELFORD$$,#{state_id_nsw},-32.653434,151.275212), - ($$2335$$,$$BRANXTON$$,#{state_id_nsw},-32.653434,151.275212), - ($$2335$$,$$DALWOOD$$,#{state_id_nsw},-32.653434,151.275212), - ($$2335$$,$$EAST BRANXTON$$,#{state_id_nsw},-32.653434,151.275212), - ($$2335$$,$$ELDERSLIE$$,#{state_id_nsw},-32.653434,151.275212), - ($$2335$$,$$LAMBS VALLEY$$,#{state_id_nsw},-32.653434,151.275212), - ($$2335$$,$$LECONFIELD$$,#{state_id_nsw},-32.653434,151.275212), - ($$2335$$,$$LOWER BELFORD$$,#{state_id_nsw},-32.653434,151.275212), - ($$2335$$,$$NORTH ROTHBURY$$,#{state_id_nsw},-32.653434,151.275212), - ($$2335$$,$$STANHOPE$$,#{state_id_nsw},-32.653434,151.275212), - ($$2336$$,$$ABERDEEN$$,#{state_id_nsw},-32.162396,150.890118), - ($$2336$$,$$DARTBROOK$$,#{state_id_nsw},-32.162396,150.890118), - ($$2336$$,$$DAVIS CREEK$$,#{state_id_nsw},-32.162396,150.890118), - ($$2336$$,$$ROSSGOLE$$,#{state_id_nsw},-32.162396,150.890118), - ($$2336$$,$$ROUCHEL$$,#{state_id_nsw},-32.162396,150.890118), - ($$2336$$,$$ROUCHEL BROOK$$,#{state_id_nsw},-32.162396,150.890118), - ($$2336$$,$$UPPER DARTBROOK$$,#{state_id_nsw},-32.162396,150.890118), - ($$2336$$,$$UPPER ROUCHEL$$,#{state_id_nsw},-32.162396,150.890118), - ($$2337$$,$$BELLTREES$$,#{state_id_nsw},-31.993284,151.124461), - ($$2337$$,$$BRAWBOY$$,#{state_id_nsw},-31.993284,151.124461), - ($$2337$$,$$BUNNAN$$,#{state_id_nsw},-31.993284,151.124461), - ($$2337$$,$$DRY CREEK$$,#{state_id_nsw},-31.993284,151.124461), - ($$2337$$,$$ELLERSTON$$,#{state_id_nsw},-31.993284,151.124461), - ($$2337$$,$$GLENBAWN$$,#{state_id_nsw},-31.993284,151.124461), - ($$2337$$,$$GLENROCK$$,#{state_id_nsw},-31.993284,151.124461), - ($$2337$$,$$GUNDY$$,#{state_id_nsw},-31.993284,151.124461), - ($$2337$$,$$KARS SPRINGS$$,#{state_id_nsw},-31.993284,151.124461), - ($$2337$$,$$MIDDLE BROOK$$,#{state_id_nsw},-31.993284,151.124461), - ($$2337$$,$$MOOBI$$,#{state_id_nsw},-31.993284,151.124461), - ($$2337$$,$$MOONAN BROOK$$,#{state_id_nsw},-31.993284,151.124461), - ($$2337$$,$$MOONAN FLAT$$,#{state_id_nsw},-31.993284,151.124461), - ($$2337$$,$$MURULLA$$,#{state_id_nsw},-31.993284,151.124461), - ($$2337$$,$$OMADALE$$,#{state_id_nsw},-31.993284,151.124461), - ($$2337$$,$$OWENS GAP$$,#{state_id_nsw},-31.993284,151.124461), - ($$2337$$,$$PAGES CREEK$$,#{state_id_nsw},-31.993284,151.124461), - ($$2337$$,$$PARKVILLE$$,#{state_id_nsw},-31.993284,151.124461), - ($$2337$$,$$SCONE$$,#{state_id_nsw},-31.993284,151.124461), - ($$2337$$,$$SEGENHOE$$,#{state_id_nsw},-31.993284,151.124461), - ($$2337$$,$$STEWARTS BROOK$$,#{state_id_nsw},-31.993284,151.124461), - ($$2337$$,$$TOMALLA$$,#{state_id_nsw},-31.993284,151.124461), - ($$2337$$,$$WAVERLY$$,#{state_id_nsw},-31.993284,151.124461), - ($$2337$$,$$WINGEN$$,#{state_id_nsw},-31.993284,151.124461), - ($$2337$$,$$WOOLOOMA$$,#{state_id_nsw},-31.993284,151.124461), - ($$2338$$,$$ARDGLEN$$,#{state_id_nsw},-31.734505,150.785588), - ($$2338$$,$$BLANDFORD$$,#{state_id_nsw},-31.734505,150.785588), - ($$2338$$,$$CRAWNEY$$,#{state_id_nsw},-31.734505,150.785588), - ($$2338$$,$$GREEN CREEK$$,#{state_id_nsw},-31.734505,150.785588), - ($$2338$$,$$MURRURUNDI$$,#{state_id_nsw},-31.734505,150.785588), - ($$2338$$,$$PAGES RIVER$$,#{state_id_nsw},-31.734505,150.785588), - ($$2338$$,$$SANDY CREEK$$,#{state_id_nsw},-31.734505,150.785588), - ($$2338$$,$$SCOTTS CREEK$$,#{state_id_nsw},-31.734505,150.785588), - ($$2338$$,$$TIMOR$$,#{state_id_nsw},-31.734505,150.785588), - ($$2339$$,$$BIG JACKS CREEK$$,#{state_id_nsw},-31.773148,150.615611), - ($$2339$$,$$BRAEFIELD$$,#{state_id_nsw},-31.773148,150.615611), - ($$2339$$,$$CATTLE CREEK$$,#{state_id_nsw},-31.773148,150.615611), - ($$2339$$,$$CHILCOTTS CREEK$$,#{state_id_nsw},-31.773148,150.615611), - ($$2339$$,$$LITTLE JACKS CREEK$$,#{state_id_nsw},-31.773148,150.615611), - ($$2339$$,$$MACDONALDS CREEK$$,#{state_id_nsw},-31.773148,150.615611), - ($$2339$$,$$PARRAWEENA$$,#{state_id_nsw},-31.773148,150.615611), - ($$2339$$,$$WARRAH$$,#{state_id_nsw},-31.773148,150.615611), - ($$2339$$,$$WARRAH CREEK$$,#{state_id_nsw},-31.773148,150.615611), - ($$2339$$,$$WILLOW TREE$$,#{state_id_nsw},-31.773148,150.615611), - ($$2340$$,$$APPLEBY$$,#{state_id_nsw},-30.964928,150.825148), - ($$2340$$,$$BARRY$$,#{state_id_nsw},-30.964928,150.825148), - ($$2340$$,$$BECTIVE$$,#{state_id_nsw},-30.964928,150.825148), - ($$2340$$,$$BITHRAMERE$$,#{state_id_nsw},-30.964928,150.825148), - ($$2340$$,$$BOWLING ALLEY POINT$$,#{state_id_nsw},-30.964928,150.825148), - ($$2340$$,$$CALALA$$,#{state_id_nsw},-30.964928,150.825148), - ($$2340$$,$$CARROLL$$,#{state_id_nsw},-30.964928,150.825148), - ($$2340$$,$$DARUKA$$,#{state_id_nsw},-30.964928,150.825148), - ($$2340$$,$$DUNCANS CREEK$$,#{state_id_nsw},-30.964928,150.825148), - ($$2340$$,$$DUNGOWAN$$,#{state_id_nsw},-30.964928,150.825148), - ($$2340$$,$$EAST TAMWORTH$$,#{state_id_nsw},-30.964928,150.825148), - ($$2340$$,$$GAROO$$,#{state_id_nsw},-30.964928,150.825148), - ($$2340$$,$$GIDLEY$$,#{state_id_nsw},-30.964928,150.825148), - ($$2340$$,$$GOONOO GOONOO$$,#{state_id_nsw},-30.964928,150.825148), - ($$2340$$,$$GOWRIE$$,#{state_id_nsw},-30.964928,150.825148), - ($$2340$$,$$HALLSVILLE$$,#{state_id_nsw},-30.964928,150.825148), - ($$2340$$,$$HANGING ROCK$$,#{state_id_nsw},-30.964928,150.825148), - ($$2340$$,$$HILLVUE$$,#{state_id_nsw},-30.964928,150.825148), - ($$2340$$,$$KEEPIT$$,#{state_id_nsw},-30.964928,150.825148), - ($$2340$$,$$KINGSWOOD$$,#{state_id_nsw},-30.964928,150.825148), - ($$2340$$,$$LOOMBERAH$$,#{state_id_nsw},-30.964928,150.825148), - ($$2340$$,$$MOORE CREEK$$,#{state_id_nsw},-30.964928,150.825148), - ($$2340$$,$$NEMINGHA$$,#{state_id_nsw},-30.964928,150.825148), - ($$2340$$,$$NORTH TAMWORTH$$,#{state_id_nsw},-30.964928,150.825148), - ($$2340$$,$$NUNDLE$$,#{state_id_nsw},-30.964928,150.825148), - ($$2340$$,$$OGUNBIL$$,#{state_id_nsw},-30.964928,150.825148), - ($$2340$$,$$OXLEY VALE$$,#{state_id_nsw},-30.964928,150.825148), - ($$2340$$,$$PIALLAMORE$$,#{state_id_nsw},-30.964928,150.825148), - ($$2340$$,$$SOMERTON$$,#{state_id_nsw},-30.964928,150.825148), - ($$2340$$,$$SOUTH TAMWORTH$$,#{state_id_nsw},-30.964928,150.825148), - ($$2340$$,$$TAMINDA$$,#{state_id_nsw},-30.964928,150.825148), - ($$2340$$,$$TAMWORTH$$,#{state_id_nsw},-30.964928,150.825148), - ($$2340$$,$$TIMBUMBURI$$,#{state_id_nsw},-30.964928,150.825148), - ($$2340$$,$$WALLAMORE$$,#{state_id_nsw},-30.964928,150.825148), - ($$2340$$,$$WARRAL$$,#{state_id_nsw},-30.964928,150.825148), - ($$2340$$,$$WEABONGA$$,#{state_id_nsw},-30.964928,150.825148), - ($$2340$$,$$WEST TAMWORTH$$,#{state_id_nsw},-30.964928,150.825148), - ($$2340$$,$$WESTDALE$$,#{state_id_nsw},-30.964928,150.825148), - ($$2340$$,$$WOOLOMIN$$,#{state_id_nsw},-30.964928,150.825148), - ($$2341$$,$$WERRIS CREEK$$,#{state_id_nsw},-31.345921,150.619915), - ($$2342$$,$$CURRABUBULA$$,#{state_id_nsw},-31.262722,150.734256), - ($$2342$$,$$PIALLAWAY$$,#{state_id_nsw},-31.262722,150.734256), - ($$2343$$,$$BLACKVILLE$$,#{state_id_nsw},-31.65821,150.30281), - ($$2343$$,$$BORAMBIL$$,#{state_id_nsw},-31.65821,150.30281), - ($$2343$$,$$BUNDELLA$$,#{state_id_nsw},-31.65821,150.30281), - ($$2343$$,$$CAROONA$$,#{state_id_nsw},-31.65821,150.30281), - ($$2343$$,$$COLLY BLUE$$,#{state_id_nsw},-31.65821,150.30281), - ($$2343$$,$$COOMOO COOMOO$$,#{state_id_nsw},-31.65821,150.30281), - ($$2343$$,$$PINE RIDGE$$,#{state_id_nsw},-31.65821,150.30281), - ($$2343$$,$$QUIPOLLY$$,#{state_id_nsw},-31.65821,150.30281), - ($$2343$$,$$QUIRINDI$$,#{state_id_nsw},-31.65821,150.30281), - ($$2343$$,$$SPRING RIDGE$$,#{state_id_nsw},-31.65821,150.30281), - ($$2343$$,$$WALLABADAH$$,#{state_id_nsw},-31.65821,150.30281), - ($$2343$$,$$WARRAH RIDGE$$,#{state_id_nsw},-31.65821,150.30281), - ($$2343$$,$$WINDY$$,#{state_id_nsw},-31.65821,150.30281), - ($$2343$$,$$YANNERGEE$$,#{state_id_nsw},-31.65821,150.30281), - ($$2343$$,$$YARRAMAN$$,#{state_id_nsw},-31.65821,150.30281), - ($$2344$$,$$DURI$$,#{state_id_nsw},-31.219024,150.819076), - ($$2344$$,$$WINTON$$,#{state_id_nsw},-31.219024,150.819076), - ($$2345$$,$$ATTUNGA$$,#{state_id_nsw},-30.930991,150.847933), - ($$2345$$,$$GARTHOWEN$$,#{state_id_nsw},-30.930991,150.847933), - ($$2346$$,$$BORAH CREEK$$,#{state_id_nsw},-30.609264,150.50534), - ($$2346$$,$$HALLS CREEK$$,#{state_id_nsw},-30.609264,150.50534), - ($$2346$$,$$KLORI$$,#{state_id_nsw},-30.609264,150.50534), - ($$2346$$,$$MANILLA$$,#{state_id_nsw},-30.609264,150.50534), - ($$2346$$,$$NAMOI RIVER$$,#{state_id_nsw},-30.609264,150.50534), - ($$2346$$,$$NEW MEXICO$$,#{state_id_nsw},-30.609264,150.50534), - ($$2346$$,$$RUSHES CREEK$$,#{state_id_nsw},-30.609264,150.50534), - ($$2346$$,$$UPPER MANILLA$$,#{state_id_nsw},-30.609264,150.50534), - ($$2346$$,$$WARRABAH$$,#{state_id_nsw},-30.609264,150.50534), - ($$2346$$,$$WIMBORNE$$,#{state_id_nsw},-30.609264,150.50534), - ($$2346$$,$$WONGO CREEK$$,#{state_id_nsw},-30.609264,150.50534), - ($$2347$$,$$BANOON$$,#{state_id_nsw},-30.527226,150.44311), - ($$2347$$,$$BARRABA$$,#{state_id_nsw},-30.527226,150.44311), - ($$2347$$,$$COBBADAH$$,#{state_id_nsw},-30.527226,150.44311), - ($$2347$$,$$GULF CREEK$$,#{state_id_nsw},-30.527226,150.44311), - ($$2347$$,$$GUNDAMULDA$$,#{state_id_nsw},-30.527226,150.44311), - ($$2347$$,$$IRONBARK$$,#{state_id_nsw},-30.527226,150.44311), - ($$2347$$,$$LINDESAY$$,#{state_id_nsw},-30.527226,150.44311), - ($$2347$$,$$LONGARM$$,#{state_id_nsw},-30.527226,150.44311), - ($$2347$$,$$MAYVALE$$,#{state_id_nsw},-30.527226,150.44311), - ($$2347$$,$$RED HILL$$,#{state_id_nsw},-30.527226,150.44311), - ($$2347$$,$$THIRLOENE$$,#{state_id_nsw},-30.527226,150.44311), - ($$2347$$,$$UPPER HORTON$$,#{state_id_nsw},-30.527226,150.44311), - ($$2347$$,$$WOODSREEF$$,#{state_id_nsw},-30.527226,150.44311), - ($$2348$$,$$NEW ENGLAND MC$$,#{state_id_nsw},0.0,0.0), - ($$2350$$,$$ABERFOYLE$$,#{state_id_nsw},-30.240605,152.012929), - ($$2350$$,$$ABINGTON$$,#{state_id_nsw},-30.240605,152.012929), - ($$2350$$,$$ARGYLE$$,#{state_id_nsw},-30.240605,152.012929), - ($$2350$$,$$ARMIDALE$$,#{state_id_nsw},-30.240605,152.012929), - ($$2350$$,$$BOOROLONG$$,#{state_id_nsw},-30.240605,152.012929), - ($$2350$$,$$CASTLE DOYLE$$,#{state_id_nsw},-30.240605,152.012929), - ($$2350$$,$$DANGARSLEIGH$$,#{state_id_nsw},-30.240605,152.012929), - ($$2350$$,$$DONALD CREEK$$,#{state_id_nsw},-30.240605,152.012929), - ($$2350$$,$$DUMARESQ$$,#{state_id_nsw},-30.240605,152.012929), - ($$2350$$,$$DUVAL$$,#{state_id_nsw},-30.240605,152.012929), - ($$2350$$,$$ENMORE$$,#{state_id_nsw},-30.240605,152.012929), - ($$2350$$,$$HILLGROVE$$,#{state_id_nsw},-30.240605,152.012929), - ($$2350$$,$$INVERGOWRIE$$,#{state_id_nsw},-30.240605,152.012929), - ($$2350$$,$$JEOGLA$$,#{state_id_nsw},-30.240605,152.012929), - ($$2350$$,$$KELLYS PLAINS$$,#{state_id_nsw},-30.240605,152.012929), - ($$2350$$,$$LYNDHURST$$,#{state_id_nsw},-30.240605,152.012929), - ($$2350$$,$$PUDDLEDOCK$$,#{state_id_nsw},-30.240605,152.012929), - ($$2350$$,$$SAUMAREZ$$,#{state_id_nsw},-30.240605,152.012929), - ($$2350$$,$$SAUMAREZ PONDS$$,#{state_id_nsw},-30.240605,152.012929), - ($$2350$$,$$THALGARRAH$$,#{state_id_nsw},-30.240605,152.012929), - ($$2350$$,$$TILBUSTER$$,#{state_id_nsw},-30.240605,152.012929), - ($$2350$$,$$WARDS MISTAKE$$,#{state_id_nsw},-30.240605,152.012929), - ($$2350$$,$$WEST ARMIDALE$$,#{state_id_nsw},-30.240605,152.012929), - ($$2350$$,$$WOLLOMOMBI$$,#{state_id_nsw},-30.240605,152.012929), - ($$2350$$,$$WONGWIBINDA$$,#{state_id_nsw},-30.240605,152.012929), - ($$2351$$,$$UNIVERSITY OF NEW ENGLAND$$,#{state_id_nsw},-30.49299,151.639714), - ($$2352$$,$$KOOTINGAL$$,#{state_id_nsw},-31.057413,151.054338), - ($$2352$$,$$LIMBRI$$,#{state_id_nsw},-31.057413,151.054338), - ($$2352$$,$$MULLA CREEK$$,#{state_id_nsw},-31.057413,151.054338), - ($$2352$$,$$TINTINHULL$$,#{state_id_nsw},-31.057413,151.054338), - ($$2353$$,$$MOONBI$$,#{state_id_nsw},-30.951431,151.045963), - ($$2354$$,$$KENTUCKY$$,#{state_id_nsw},-31.266104,151.549993), - ($$2354$$,$$KENTUCKY SOUTH$$,#{state_id_nsw},-31.266104,151.549993), - ($$2354$$,$$NIANGALA$$,#{state_id_nsw},-31.266104,151.549993), - ($$2354$$,$$NOWENDOC$$,#{state_id_nsw},-31.266104,151.549993), - ($$2354$$,$$WALCHA$$,#{state_id_nsw},-31.266104,151.549993), - ($$2354$$,$$WALCHA ROAD$$,#{state_id_nsw},-31.266104,151.549993), - ($$2354$$,$$WOLLUN$$,#{state_id_nsw},-31.266104,151.549993), - ($$2354$$,$$WOOLBROOK$$,#{state_id_nsw},-31.266104,151.549993), - ($$2354$$,$$YARROWITCH$$,#{state_id_nsw},-31.266104,151.549993), - ($$2355$$,$$BENDEMEER$$,#{state_id_nsw},-30.878399,151.159905), - ($$2355$$,$$RETREAT$$,#{state_id_nsw},-30.878399,151.159905), - ($$2355$$,$$WATSONS CREEK$$,#{state_id_nsw},-30.878399,151.159905), - ($$2356$$,$$GWABEGAR$$,#{state_id_nsw},-30.619798,148.96949), - ($$2357$$,$$BOMERA$$,#{state_id_nsw},-31.50933,149.793251), - ($$2357$$,$$BOX RIDGE$$,#{state_id_nsw},-31.50933,149.793251), - ($$2357$$,$$BUGALDIE$$,#{state_id_nsw},-31.50933,149.793251), - ($$2357$$,$$COONABARABRAN$$,#{state_id_nsw},-31.50933,149.793251), - ($$2357$$,$$DANDRY$$,#{state_id_nsw},-31.50933,149.793251), - ($$2357$$,$$GOWANG$$,#{state_id_nsw},-31.50933,149.793251), - ($$2357$$,$$PURLEWAUGH$$,#{state_id_nsw},-31.50933,149.793251), - ($$2357$$,$$ROCKY GLEN$$,#{state_id_nsw},-31.50933,149.793251), - ($$2357$$,$$TANNABAR$$,#{state_id_nsw},-31.50933,149.793251), - ($$2357$$,$$ULAMAMBRI$$,#{state_id_nsw},-31.50933,149.793251), - ($$2357$$,$$WATTLE SPRINGS$$,#{state_id_nsw},-31.50933,149.793251), - ($$2358$$,$$ARDING$$,#{state_id_nsw},-30.588547,151.556745), - ($$2358$$,$$BALALA$$,#{state_id_nsw},-30.588547,151.556745), - ($$2358$$,$$GOSTWYCK$$,#{state_id_nsw},-30.588547,151.556745), - ($$2358$$,$$KINGSTOWN$$,#{state_id_nsw},-30.588547,151.556745), - ($$2358$$,$$MIHI$$,#{state_id_nsw},-30.588547,151.556745), - ($$2358$$,$$ROCKY RIVER$$,#{state_id_nsw},-30.588547,151.556745), - ($$2358$$,$$SALISBURY PLAINS$$,#{state_id_nsw},-30.588547,151.556745), - ($$2358$$,$$TORRYBURN$$,#{state_id_nsw},-30.588547,151.556745), - ($$2358$$,$$URALLA$$,#{state_id_nsw},-30.588547,151.556745), - ($$2358$$,$$YARROWYCK$$,#{state_id_nsw},-30.588547,151.556745), - ($$2359$$,$$ABERDEEN$$,#{state_id_nsw},-29.996058,151.080554), - ($$2359$$,$$BAKERS CREEK$$,#{state_id_nsw},-29.996058,151.080554), - ($$2359$$,$$BUNDARRA$$,#{state_id_nsw},-29.996058,151.080554), - ($$2359$$,$$CAMERONS CREEK$$,#{state_id_nsw},-29.996058,151.080554), - ($$2360$$,$$AUBURN VALE$$,#{state_id_nsw},-29.83895,151.041719), - ($$2360$$,$$BRODIES PLAINS$$,#{state_id_nsw},-29.83895,151.041719), - ($$2360$$,$$BUKKULLA$$,#{state_id_nsw},-29.83895,151.041719), - ($$2360$$,$$CHERRY TREE HILL$$,#{state_id_nsw},-29.83895,151.041719), - ($$2360$$,$$COPETON$$,#{state_id_nsw},-29.83895,151.041719), - ($$2360$$,$$ELSMORE$$,#{state_id_nsw},-29.83895,151.041719), - ($$2360$$,$$GILGAI$$,#{state_id_nsw},-29.83895,151.041719), - ($$2360$$,$$GRAMAN$$,#{state_id_nsw},-29.83895,151.041719), - ($$2360$$,$$GUM FLAT$$,#{state_id_nsw},-29.83895,151.041719), - ($$2360$$,$$HOWELL$$,#{state_id_nsw},-29.83895,151.041719), - ($$2360$$,$$INVERELL$$,#{state_id_nsw},-29.83895,151.041719), - ($$2360$$,$$KINGS PLAINS$$,#{state_id_nsw},-29.83895,151.041719), - ($$2360$$,$$LITTLE PLAIN$$,#{state_id_nsw},-29.83895,151.041719), - ($$2360$$,$$LONG PLAIN$$,#{state_id_nsw},-29.83895,151.041719), - ($$2360$$,$$MOUNT RUSSELL$$,#{state_id_nsw},-29.83895,151.041719), - ($$2360$$,$$NEWSTEAD$$,#{state_id_nsw},-29.83895,151.041719), - ($$2360$$,$$NULLAMANNA$$,#{state_id_nsw},-29.83895,151.041719), - ($$2360$$,$$OAKWOOD$$,#{state_id_nsw},-29.83895,151.041719), - ($$2360$$,$$PARADISE$$,#{state_id_nsw},-29.83895,151.041719), - ($$2360$$,$$ROB ROY$$,#{state_id_nsw},-29.83895,151.041719), - ($$2360$$,$$SAPPHIRE$$,#{state_id_nsw},-29.83895,151.041719), - ($$2360$$,$$SPRING MOUNTAIN$$,#{state_id_nsw},-29.83895,151.041719), - ($$2360$$,$$STANBOROUGH$$,#{state_id_nsw},-29.83895,151.041719), - ($$2360$$,$$SWANBROOK$$,#{state_id_nsw},-29.83895,151.041719), - ($$2360$$,$$WALLANGRA$$,#{state_id_nsw},-29.83895,151.041719), - ($$2360$$,$$WANDERA$$,#{state_id_nsw},-29.83895,151.041719), - ($$2360$$,$$WOODSTOCK$$,#{state_id_nsw},-29.83895,151.041719), - ($$2361$$,$$ASHFORD$$,#{state_id_nsw},-29.321245,151.096081), - ($$2361$$,$$ATHOLWOOD$$,#{state_id_nsw},-29.321245,151.096081), - ($$2361$$,$$BONSHAW$$,#{state_id_nsw},-29.321245,151.096081), - ($$2361$$,$$LIMESTONE$$,#{state_id_nsw},-29.321245,151.096081), - ($$2361$$,$$PINDAROI$$,#{state_id_nsw},-29.321245,151.096081), - ($$2365$$,$$BACKWATER$$,#{state_id_nsw},-30.075367,151.880535), - ($$2365$$,$$BALD BLAIR$$,#{state_id_nsw},-30.075367,151.880535), - ($$2365$$,$$BALDERSLEIGH$$,#{state_id_nsw},-30.075367,151.880535), - ($$2365$$,$$BASSENDEAN$$,#{state_id_nsw},-30.075367,151.880535), - ($$2365$$,$$BEN LOMOND$$,#{state_id_nsw},-30.075367,151.880535), - ($$2365$$,$$BLACK MOUNTAIN$$,#{state_id_nsw},-30.075367,151.880535), - ($$2365$$,$$BRIARBROOK$$,#{state_id_nsw},-30.075367,151.880535), - ($$2365$$,$$BROCKLEY$$,#{state_id_nsw},-30.075367,151.880535), - ($$2365$$,$$BRUSHY CREEK$$,#{state_id_nsw},-30.075367,151.880535), - ($$2365$$,$$FALCONER$$,#{state_id_nsw},-30.075367,151.880535), - ($$2365$$,$$GEORGES CREEK$$,#{state_id_nsw},-30.075367,151.880535), - ($$2365$$,$$GLEN NEVIS$$,#{state_id_nsw},-30.075367,151.880535), - ($$2365$$,$$GLENCOE$$,#{state_id_nsw},-30.075367,151.880535), - ($$2365$$,$$GREEN HILLS$$,#{state_id_nsw},-30.075367,151.880535), - ($$2365$$,$$GUYRA$$,#{state_id_nsw},-30.075367,151.880535), - ($$2365$$,$$LLANGOTHLIN$$,#{state_id_nsw},-30.075367,151.880535), - ($$2365$$,$$MAYBOLE$$,#{state_id_nsw},-30.075367,151.880535), - ($$2365$$,$$MOUNT MITCHELL$$,#{state_id_nsw},-30.075367,151.880535), - ($$2365$$,$$NEW VALLEY$$,#{state_id_nsw},-30.075367,151.880535), - ($$2365$$,$$OBAN$$,#{state_id_nsw},-30.075367,151.880535), - ($$2365$$,$$SOUTH GUYRA$$,#{state_id_nsw},-30.075367,151.880535), - ($$2365$$,$$TENTERDEN$$,#{state_id_nsw},-30.075367,151.880535), - ($$2365$$,$$THE BASIN$$,#{state_id_nsw},-30.075367,151.880535), - ($$2365$$,$$THE GULF$$,#{state_id_nsw},-30.075367,151.880535), - ($$2365$$,$$TUBBAMURRA$$,#{state_id_nsw},-30.075367,151.880535), - ($$2365$$,$$WANDSWORTH$$,#{state_id_nsw},-30.075367,151.880535), - ($$2369$$,$$OLD MILL$$,#{state_id_nsw},-29.908381,151.205576), - ($$2369$$,$$STANNIFER$$,#{state_id_nsw},-29.908381,151.205576), - ($$2369$$,$$TINGHA$$,#{state_id_nsw},-29.908381,151.205576), - ($$2370$$,$$BALD NOB$$,#{state_id_nsw},-29.644588,151.962405), - ($$2370$$,$$DIEHARD$$,#{state_id_nsw},-29.644588,151.962405), - ($$2370$$,$$DUNDEE$$,#{state_id_nsw},-29.644588,151.962405), - ($$2370$$,$$FURRACABAD$$,#{state_id_nsw},-29.644588,151.962405), - ($$2370$$,$$GIBRALTAR RANGE$$,#{state_id_nsw},-29.644588,151.962405), - ($$2370$$,$$GLEN ELGIN$$,#{state_id_nsw},-29.644588,151.962405), - ($$2370$$,$$GLEN INNES$$,#{state_id_nsw},-29.644588,151.962405), - ($$2370$$,$$KINGSGATE$$,#{state_id_nsw},-29.644588,151.962405), - ($$2370$$,$$KOOKABOOKRA$$,#{state_id_nsw},-29.644588,151.962405), - ($$2370$$,$$LAMBS VALLEY$$,#{state_id_nsw},-29.644588,151.962405), - ($$2370$$,$$MATHESON$$,#{state_id_nsw},-29.644588,151.962405), - ($$2370$$,$$MOGGS SWAMP$$,#{state_id_nsw},-29.644588,151.962405), - ($$2370$$,$$MOOGEM$$,#{state_id_nsw},-29.644588,151.962405), - ($$2370$$,$$MORVEN$$,#{state_id_nsw},-29.644588,151.962405), - ($$2370$$,$$NEWTON BOYD$$,#{state_id_nsw},-29.644588,151.962405), - ($$2370$$,$$PINKETT$$,#{state_id_nsw},-29.644588,151.962405), - ($$2370$$,$$RANGERS VALLEY$$,#{state_id_nsw},-29.644588,151.962405), - ($$2370$$,$$RED RANGE$$,#{state_id_nsw},-29.644588,151.962405), - ($$2370$$,$$REDDESTONE$$,#{state_id_nsw},-29.644588,151.962405), - ($$2370$$,$$SHANNON VALE$$,#{state_id_nsw},-29.644588,151.962405), - ($$2370$$,$$STONEHENGE$$,#{state_id_nsw},-29.644588,151.962405), - ($$2370$$,$$SWAN VALE$$,#{state_id_nsw},-29.644588,151.962405), - ($$2370$$,$$WELLINGROVE$$,#{state_id_nsw},-29.644588,151.962405), - ($$2370$$,$$YARROWFORD$$,#{state_id_nsw},-29.644588,151.962405), - ($$2371$$,$$CAPOOMPETA$$,#{state_id_nsw},-29.393231,152.041527), - ($$2371$$,$$DEEPWATER$$,#{state_id_nsw},-29.393231,152.041527), - ($$2371$$,$$EMMAVILLE$$,#{state_id_nsw},-29.393231,152.041527), - ($$2371$$,$$ROCKY CREEK$$,#{state_id_nsw},-29.393231,152.041527), - ($$2371$$,$$STANNUM$$,#{state_id_nsw},-29.393231,152.041527), - ($$2371$$,$$THE GULF$$,#{state_id_nsw},-29.393231,152.041527), - ($$2371$$,$$TORRINGTON$$,#{state_id_nsw},-29.393231,152.041527), - ($$2371$$,$$WELLINGTON VALE$$,#{state_id_nsw},-29.393231,152.041527), - ($$2371$$,$$YELLOW DAM$$,#{state_id_nsw},-29.393231,152.041527), - ($$2372$$,$$BACK CREEK$$,#{state_id_nsw},-28.931739,151.702544), - ($$2372$$,$$BOLIVIA$$,#{state_id_nsw},-28.931739,151.702544), - ($$2372$$,$$BOONOO BOONOO$$,#{state_id_nsw},-28.931739,151.702544), - ($$2372$$,$$BOOROOK$$,#{state_id_nsw},-28.931739,151.702544), - ($$2372$$,$$CARROLLS CREEK$$,#{state_id_nsw},-28.931739,151.702544), - ($$2372$$,$$CULLENDORE$$,#{state_id_nsw},-28.931739,151.702544), - ($$2372$$,$$FOREST LAND$$,#{state_id_nsw},-28.931739,151.702544), - ($$2372$$,$$LISTON$$,#{state_id_nsw},-28.931739,151.702544), - ($$2372$$,$$MOLE RIVER$$,#{state_id_nsw},-28.931739,151.702544), - ($$2372$$,$$RIVERTREE$$,#{state_id_nsw},-28.931739,151.702544), - ($$2372$$,$$ROCKY RIVER$$,#{state_id_nsw},-28.931739,151.702544), - ($$2372$$,$$SANDY FLAT$$,#{state_id_nsw},-28.931739,151.702544), - ($$2372$$,$$SANDY HILL$$,#{state_id_nsw},-28.931739,151.702544), - ($$2372$$,$$SILENT GROVE$$,#{state_id_nsw},-28.931739,151.702544), - ($$2372$$,$$TARBAN$$,#{state_id_nsw},-28.931739,151.702544), - ($$2372$$,$$TENTERFIELD$$,#{state_id_nsw},-28.931739,151.702544), - ($$2372$$,$$TIMBARRA$$,#{state_id_nsw},-28.931739,151.702544), - ($$2372$$,$$WILLSONS DOWNFALL$$,#{state_id_nsw},-28.931739,151.702544), - ($$2372$$,$$WOODSIDE$$,#{state_id_nsw},-28.931739,151.702544), - ($$2372$$,$$WYLIE CREEK$$,#{state_id_nsw},-28.931739,151.702544), - ($$2379$$,$$GOOLHI$$,#{state_id_nsw},-31.067541,149.712049), - ($$2379$$,$$MULLALEY$$,#{state_id_nsw},-31.067541,149.712049), - ($$2379$$,$$NAPIER LANE$$,#{state_id_nsw},-31.067541,149.712049), - ($$2379$$,$$NOMBI$$,#{state_id_nsw},-31.067541,149.712049), - ($$2380$$,$$BLUE VALE$$,#{state_id_nsw},-30.798568,150.199631), - ($$2380$$,$$EMERALD HILL$$,#{state_id_nsw},-30.798568,150.199631), - ($$2380$$,$$GHOOLENDAADI$$,#{state_id_nsw},-30.798568,150.199631), - ($$2380$$,$$GUNNEDAH$$,#{state_id_nsw},-30.798568,150.199631), - ($$2380$$,$$KELVIN$$,#{state_id_nsw},-30.798568,150.199631), - ($$2380$$,$$MARYS MOUNT$$,#{state_id_nsw},-30.798568,150.199631), - ($$2380$$,$$MILROY$$,#{state_id_nsw},-30.798568,150.199631), - ($$2380$$,$$ORANGE GROVE$$,#{state_id_nsw},-30.798568,150.199631), - ($$2380$$,$$RANGARI$$,#{state_id_nsw},-30.798568,150.199631), - ($$2381$$,$$BREEZA$$,#{state_id_nsw},-31.244175,150.457901), - ($$2381$$,$$CURLEWIS$$,#{state_id_nsw},-31.244175,150.457901), - ($$2381$$,$$PREMER$$,#{state_id_nsw},-31.244175,150.457901), - ($$2381$$,$$TAMBAR SPRINGS$$,#{state_id_nsw},-31.244175,150.457901), - ($$2382$$,$$BOGGABRI$$,#{state_id_nsw},-30.704728,150.042508), - ($$2382$$,$$MAULES CREEK$$,#{state_id_nsw},-30.704728,150.042508), - ($$2382$$,$$WEAN$$,#{state_id_nsw},-30.704728,150.042508), - ($$2382$$,$$WILLALA$$,#{state_id_nsw},-30.704728,150.042508), - ($$2386$$,$$BURREN JUNCTION$$,#{state_id_nsw},-30.105176,148.965674), - ($$2386$$,$$DRILDOOL$$,#{state_id_nsw},-30.105176,148.965674), - ($$2386$$,$$NOWLEY$$,#{state_id_nsw},-30.105176,148.965674), - ($$2387$$,$$BULYEROI$$,#{state_id_nsw},-29.780297,149.090561), - ($$2387$$,$$ROWENA$$,#{state_id_nsw},-29.780297,149.090561), - ($$2388$$,$$BOOLCARROLL$$,#{state_id_nsw},-30.087425,149.438586), - ($$2388$$,$$CUTTABRI$$,#{state_id_nsw},-30.087425,149.438586), - ($$2388$$,$$JEWS LAGOON$$,#{state_id_nsw},-30.087425,149.438586), - ($$2388$$,$$MERAH NORTH$$,#{state_id_nsw},-30.087425,149.438586), - ($$2388$$,$$PILLIGA$$,#{state_id_nsw},-30.087425,149.438586), - ($$2388$$,$$SPRING PLAINS$$,#{state_id_nsw},-30.087425,149.438586), - ($$2388$$,$$THE PILLIGA$$,#{state_id_nsw},-30.087425,149.438586), - ($$2388$$,$$WEE WAA$$,#{state_id_nsw},-30.087425,149.438586), - ($$2388$$,$$YARRIE LAKE$$,#{state_id_nsw},-30.087425,149.438586), - ($$2390$$,$$BAAN BAA$$,#{state_id_nsw},-30.60138,149.966153), - ($$2390$$,$$BACK CREEK$$,#{state_id_nsw},-30.60138,149.966153), - ($$2390$$,$$BERRIGAL$$,#{state_id_nsw},-30.60138,149.966153), - ($$2390$$,$$BOHENA CREEK$$,#{state_id_nsw},-30.60138,149.966153), - ($$2390$$,$$BULLAWA CREEK$$,#{state_id_nsw},-30.60138,149.966153), - ($$2390$$,$$COURADDA$$,#{state_id_nsw},-30.60138,149.966153), - ($$2390$$,$$EDGEROI$$,#{state_id_nsw},-30.60138,149.966153), - ($$2390$$,$$EULAH CREEK$$,#{state_id_nsw},-30.60138,149.966153), - ($$2390$$,$$HARPARARY$$,#{state_id_nsw},-30.60138,149.966153), - ($$2390$$,$$JACKS CREEK$$,#{state_id_nsw},-30.60138,149.966153), - ($$2390$$,$$KAPUTAR$$,#{state_id_nsw},-30.60138,149.966153), - ($$2390$$,$$NARRABRI$$,#{state_id_nsw},-30.60138,149.966153), - ($$2390$$,$$NARRABRI WEST$$,#{state_id_nsw},-30.60138,149.966153), - ($$2390$$,$$ROCKY CREEK$$,#{state_id_nsw},-30.60138,149.966153), - ($$2390$$,$$TARRIARO$$,#{state_id_nsw},-30.60138,149.966153), - ($$2390$$,$$TURRAWAN$$,#{state_id_nsw},-30.60138,149.966153), - ($$2395$$,$$BINNAWAY$$,#{state_id_nsw},-31.552115,149.378497), - ($$2395$$,$$ROPERS ROAD$$,#{state_id_nsw},-31.552115,149.378497), - ($$2395$$,$$WEETALIBA$$,#{state_id_nsw},-31.552115,149.378497), - ($$2396$$,$$BARADINE$$,#{state_id_nsw},-30.943207,149.065815), - ($$2396$$,$$BARWON$$,#{state_id_nsw},-30.943207,149.065815), - ($$2396$$,$$GOORIANAWA$$,#{state_id_nsw},-30.943207,149.065815), - ($$2396$$,$$KENEBRI$$,#{state_id_nsw},-30.943207,149.065815), - ($$2397$$,$$BELLATA$$,#{state_id_nsw},-29.919624,149.790978), - ($$2397$$,$$MILLIE$$,#{state_id_nsw},-29.919624,149.790978), - ($$2398$$,$$GURLEY$$,#{state_id_nsw},-29.735601,149.79988), - ($$2399$$,$$BINIGUY$$,#{state_id_nsw},-29.580064,150.136946), - ($$2399$$,$$PALLAMALLAWA$$,#{state_id_nsw},-29.580064,150.136946), - ($$2400$$,$$ASHLEY$$,#{state_id_nsw},-29.317772,149.808064), - ($$2400$$,$$BULLARAH$$,#{state_id_nsw},-29.317772,149.808064), - ($$2400$$,$$CROOBLE$$,#{state_id_nsw},-29.317772,149.808064), - ($$2400$$,$$MALLOWA$$,#{state_id_nsw},-29.317772,149.808064), - ($$2400$$,$$MOREE$$,#{state_id_nsw},-29.317772,149.808064), - ($$2400$$,$$MOREE EAST$$,#{state_id_nsw},-29.317772,149.808064), - ($$2400$$,$$TERRY HIE HIE$$,#{state_id_nsw},-29.317772,149.808064), - ($$2400$$,$$TULLOONA$$,#{state_id_nsw},-29.317772,149.808064), - ($$2401$$,$$GRAVESEND$$,#{state_id_nsw},-29.582339,150.337609), - ($$2402$$,$$BALFOURS PEAK$$,#{state_id_nsw},-29.510948,150.751185), - ($$2402$$,$$COOLATAI$$,#{state_id_nsw},-29.510948,150.751185), - ($$2402$$,$$WARIALDA$$,#{state_id_nsw},-29.510948,150.751185), - ($$2402$$,$$WARIALDA RAIL$$,#{state_id_nsw},-29.510948,150.751185), - ($$2403$$,$$DELUNGRA$$,#{state_id_nsw},-29.652485,150.830948), - ($$2403$$,$$GRAGIN$$,#{state_id_nsw},-29.652485,150.830948), - ($$2403$$,$$MYALL CREEK$$,#{state_id_nsw},-29.652485,150.830948), - ($$2404$$,$$BANGHEET$$,#{state_id_nsw},-29.804159,150.478053), - ($$2404$$,$$BINGARA$$,#{state_id_nsw},-29.804159,150.478053), - ($$2404$$,$$DINOGA$$,#{state_id_nsw},-29.804159,150.478053), - ($$2404$$,$$ELCOMBE$$,#{state_id_nsw},-29.804159,150.478053), - ($$2404$$,$$GINEROI$$,#{state_id_nsw},-29.804159,150.478053), - ($$2404$$,$$KEERA$$,#{state_id_nsw},-29.804159,150.478053), - ($$2404$$,$$PALLAL$$,#{state_id_nsw},-29.804159,150.478053), - ($$2404$$,$$RIVERVIEW$$,#{state_id_nsw},-29.804159,150.478053), - ($$2404$$,$$UPPER BINGARA$$,#{state_id_nsw},-29.804159,150.478053), - ($$2405$$,$$BOOMI$$,#{state_id_nsw},-28.725412,149.57915), - ($$2405$$,$$GARAH$$,#{state_id_nsw},-28.725412,149.57915), - ($$2406$$,$$MUNGINDI$$,#{state_id_nsw},-28.999013,149.100731), - ($$2406$$,$$MUNGINDI$$,#{state_id_qld},-28.999013,149.100731), - ($$2406$$,$$WEEMELAH$$,#{state_id_nsw},-28.999013,149.100731), - ($$2408$$,$$BLUE NOBBY$$,#{state_id_nsw},-28.93259,150.391368), - ($$2408$$,$$NORTH STAR$$,#{state_id_nsw},-28.93259,150.391368), - ($$2408$$,$$YALLAROI$$,#{state_id_nsw},-28.93259,150.391368), - ($$2409$$,$$BOGGABILLA$$,#{state_id_nsw},-28.744821,150.415347), - ($$2409$$,$$BOONAL$$,#{state_id_nsw},-28.744821,150.415347), - ($$2410$$,$$TWIN RIVERS$$,#{state_id_nsw},-29.045063,150.644388), - ($$2410$$,$$YETMAN$$,#{state_id_nsw},-29.045063,150.644388), - ($$2411$$,$$CROPPA CREEK$$,#{state_id_nsw},-29.129441,150.381167), - ($$2415$$,$$MONKERAI$$,#{state_id_nsw},-32.292336,151.859784), - ($$2415$$,$$NOOROO$$,#{state_id_nsw},-32.292336,151.859784), - ($$2415$$,$$STROUD ROAD$$,#{state_id_nsw},-32.292336,151.859784), - ($$2415$$,$$UPPER KARUAH RIVER$$,#{state_id_nsw},-32.292336,151.859784), - ($$2415$$,$$WEISMANTELS$$,#{state_id_nsw},-32.292336,151.859784), - ($$2420$$,$$ALISON$$,#{state_id_nsw},-32.434801,151.771934), - ($$2420$$,$$BANDON GROVE$$,#{state_id_nsw},-32.434801,151.771934), - ($$2420$$,$$BENDOLBA$$,#{state_id_nsw},-32.434801,151.771934), - ($$2420$$,$$BROOKFIELD$$,#{state_id_nsw},-32.434801,151.771934), - ($$2420$$,$$CAMBRA$$,#{state_id_nsw},-32.434801,151.771934), - ($$2420$$,$$CHICHESTER$$,#{state_id_nsw},-32.434801,151.771934), - ($$2420$$,$$DUNGOG$$,#{state_id_nsw},-32.434801,151.771934), - ($$2420$$,$$FLAT TOPS$$,#{state_id_nsw},-32.434801,151.771934), - ($$2420$$,$$FOSTERTON$$,#{state_id_nsw},-32.434801,151.771934), - ($$2420$$,$$HANLEYS CREEK$$,#{state_id_nsw},-32.434801,151.771934), - ($$2420$$,$$HILLDALE$$,#{state_id_nsw},-32.434801,151.771934), - ($$2420$$,$$MAIN CREEK$$,#{state_id_nsw},-32.434801,151.771934), - ($$2420$$,$$MARSHDALE$$,#{state_id_nsw},-32.434801,151.771934), - ($$2420$$,$$MARTINS CREEK$$,#{state_id_nsw},-32.434801,151.771934), - ($$2420$$,$$MUNNI$$,#{state_id_nsw},-32.434801,151.771934), - ($$2420$$,$$SALISBURY$$,#{state_id_nsw},-32.434801,151.771934), - ($$2420$$,$$STROUD HILL$$,#{state_id_nsw},-32.434801,151.771934), - ($$2420$$,$$SUGARLOAF$$,#{state_id_nsw},-32.434801,151.771934), - ($$2420$$,$$TABBIL CREEK$$,#{state_id_nsw},-32.434801,151.771934), - ($$2420$$,$$UNDERBANK$$,#{state_id_nsw},-32.434801,151.771934), - ($$2420$$,$$WALLARINGA$$,#{state_id_nsw},-32.434801,151.771934), - ($$2420$$,$$WALLAROBBA$$,#{state_id_nsw},-32.434801,151.771934), - ($$2420$$,$$WIRRAGULLA$$,#{state_id_nsw},-32.434801,151.771934), - ($$2421$$,$$FISHERS HILL$$,#{state_id_nsw},-32.507287,151.532802), - ($$2421$$,$$PATERSON$$,#{state_id_nsw},-32.507287,151.532802), - ($$2421$$,$$SUMMER HILL$$,#{state_id_nsw},-32.507287,151.532802), - ($$2421$$,$$TOCAL$$,#{state_id_nsw},-32.507287,151.532802), - ($$2421$$,$$TORRYBURN$$,#{state_id_nsw},-32.507287,151.532802), - ($$2421$$,$$VACY$$,#{state_id_nsw},-32.507287,151.532802), - ($$2421$$,$$WEBBERS CREEK$$,#{state_id_nsw},-32.507287,151.532802), - ($$2422$$,$$BACK CREEK$$,#{state_id_nsw},-31.972237,152.065675), - ($$2422$$,$$BAKERS CREEK$$,#{state_id_nsw},-31.972237,152.065675), - ($$2422$$,$$BARRINGTON$$,#{state_id_nsw},-31.972237,152.065675), - ($$2422$$,$$BARRINGTON TOPS$$,#{state_id_nsw},-31.972237,152.065675), - ($$2422$$,$$BAXTERS RIDGE$$,#{state_id_nsw},-31.972237,152.065675), - ($$2422$$,$$BELBORA$$,#{state_id_nsw},-31.972237,152.065675), - ($$2422$$,$$BERRICO$$,#{state_id_nsw},-31.972237,152.065675), - ($$2422$$,$$BINDERA$$,#{state_id_nsw},-31.972237,152.065675), - ($$2422$$,$$BOWMAN$$,#{state_id_nsw},-31.972237,152.065675), - ($$2422$$,$$BOWMAN FARM$$,#{state_id_nsw},-31.972237,152.065675), - ($$2422$$,$$BRETTI$$,#{state_id_nsw},-31.972237,152.065675), - ($$2422$$,$$BULLIAC$$,#{state_id_nsw},-31.972237,152.065675), - ($$2422$$,$$BUNDOOK$$,#{state_id_nsw},-31.972237,152.065675), - ($$2422$$,$$CALLAGHANS CREEK$$,#{state_id_nsw},-31.972237,152.065675), - ($$2422$$,$$COBARK$$,#{state_id_nsw},-31.972237,152.065675), - ($$2422$$,$$CONEAC$$,#{state_id_nsw},-31.972237,152.065675), - ($$2422$$,$$COPELAND$$,#{state_id_nsw},-31.972237,152.065675), - ($$2422$$,$$CRAVEN$$,#{state_id_nsw},-31.972237,152.065675), - ($$2422$$,$$CRAVEN PLATEAU$$,#{state_id_nsw},-31.972237,152.065675), - ($$2422$$,$$CURRICABARK$$,#{state_id_nsw},-31.972237,152.065675), - ($$2422$$,$$DEWITT$$,#{state_id_nsw},-31.972237,152.065675), - ($$2422$$,$$FAULKLAND$$,#{state_id_nsw},-31.972237,152.065675), - ($$2422$$,$$FORBESDALE$$,#{state_id_nsw},-31.972237,152.065675), - ($$2422$$,$$GANGAT$$,#{state_id_nsw},-31.972237,152.065675), - ($$2422$$,$$GIRO$$,#{state_id_nsw},-31.972237,152.065675), - ($$2422$$,$$GLEN WARD$$,#{state_id_nsw},-31.972237,152.065675), - ($$2422$$,$$GLOUCESTER$$,#{state_id_nsw},-31.972237,152.065675), - ($$2422$$,$$GLOUCESTER TOPS$$,#{state_id_nsw},-31.972237,152.065675), - ($$2422$$,$$INVERGORDON$$,#{state_id_nsw},-31.972237,152.065675), - ($$2422$$,$$KIA ORA$$,#{state_id_nsw},-31.972237,152.065675), - ($$2422$$,$$MARES RUN$$,#{state_id_nsw},-31.972237,152.065675), - ($$2422$$,$$MERNOT$$,#{state_id_nsw},-31.972237,152.065675), - ($$2422$$,$$MOGRANI$$,#{state_id_nsw},-31.972237,152.065675), - ($$2422$$,$$MOPPY$$,#{state_id_nsw},-31.972237,152.065675), - ($$2422$$,$$RAWDON VALE$$,#{state_id_nsw},-31.972237,152.065675), - ($$2422$$,$$ROOKHURST$$,#{state_id_nsw},-31.972237,152.065675), - ($$2422$$,$$STRATFORD$$,#{state_id_nsw},-31.972237,152.065675), - ($$2422$$,$$TERREEL$$,#{state_id_nsw},-31.972237,152.065675), - ($$2422$$,$$TIBBUC$$,#{state_id_nsw},-31.972237,152.065675), - ($$2422$$,$$TITAATEE CREEK$$,#{state_id_nsw},-31.972237,152.065675), - ($$2422$$,$$TUGRABAKH$$,#{state_id_nsw},-31.972237,152.065675), - ($$2422$$,$$WALLANBAH$$,#{state_id_nsw},-31.972237,152.065675), - ($$2422$$,$$WARDS RIVER$$,#{state_id_nsw},-31.972237,152.065675), - ($$2422$$,$$WAUKIVORY$$,#{state_id_nsw},-31.972237,152.065675), - ($$2422$$,$$WOKO$$,#{state_id_nsw},-31.972237,152.065675), - ($$2423$$,$$BOMBAH POINT$$,#{state_id_nsw},-32.494001,152.276285), - ($$2423$$,$$BOOLAMBAYTE$$,#{state_id_nsw},-32.494001,152.276285), - ($$2423$$,$$BULAHDELAH$$,#{state_id_nsw},-32.494001,152.276285), - ($$2423$$,$$BUNGWAHL$$,#{state_id_nsw},-32.494001,152.276285), - ($$2423$$,$$COOLONGOLOOK$$,#{state_id_nsw},-32.494001,152.276285), - ($$2423$$,$$CRAWFORD RIVER$$,#{state_id_nsw},-32.494001,152.276285), - ($$2423$$,$$MARKWELL$$,#{state_id_nsw},-32.494001,152.276285), - ($$2423$$,$$MAYERS FLAT$$,#{state_id_nsw},-32.494001,152.276285), - ($$2423$$,$$MUNGO BRUSH$$,#{state_id_nsw},-32.494001,152.276285), - ($$2423$$,$$MYALL LAKE$$,#{state_id_nsw},-32.494001,152.276285), - ($$2423$$,$$NERONG$$,#{state_id_nsw},-32.494001,152.276285), - ($$2423$$,$$SEAL ROCKS$$,#{state_id_nsw},-32.494001,152.276285), - ($$2423$$,$$TOPI TOPI$$,#{state_id_nsw},-32.494001,152.276285), - ($$2423$$,$$UPPER MYALL$$,#{state_id_nsw},-32.494001,152.276285), - ($$2423$$,$$VIOLET HILL$$,#{state_id_nsw},-32.494001,152.276285), - ($$2423$$,$$WANG WAUK$$,#{state_id_nsw},-32.494001,152.276285), - ($$2423$$,$$WARRANULLA$$,#{state_id_nsw},-32.494001,152.276285), - ($$2423$$,$$WILLINA$$,#{state_id_nsw},-32.494001,152.276285), - ($$2423$$,$$WOOTTON$$,#{state_id_nsw},-32.494001,152.276285), - ($$2423$$,$$YAGON$$,#{state_id_nsw},-32.494001,152.276285), - ($$2424$$,$$CAFFREYS FLAT$$,#{state_id_nsw},-31.802185,152.068568), - ($$2424$$,$$CELLS RIVER$$,#{state_id_nsw},-31.802185,152.068568), - ($$2424$$,$$COOPLACURRIPA$$,#{state_id_nsw},-31.802185,152.068568), - ($$2424$$,$$CUNDLE FLAT$$,#{state_id_nsw},-31.802185,152.068568), - ($$2424$$,$$KNORRIT FLAT$$,#{state_id_nsw},-31.802185,152.068568), - ($$2424$$,$$KNORRIT FOREST$$,#{state_id_nsw},-31.802185,152.068568), - ($$2424$$,$$MOUNT GEORGE$$,#{state_id_nsw},-31.802185,152.068568), - ($$2424$$,$$NUMBER ONE$$,#{state_id_nsw},-31.802185,152.068568), - ($$2424$$,$$TIRI$$,#{state_id_nsw},-31.802185,152.068568), - ($$2425$$,$$ALLWORTH$$,#{state_id_nsw},-32.541688,151.960927), - ($$2425$$,$$BOORAL$$,#{state_id_nsw},-32.541688,151.960927), - ($$2425$$,$$GIRVAN$$,#{state_id_nsw},-32.541688,151.960927), - ($$2425$$,$$STROUD$$,#{state_id_nsw},-32.541688,151.960927), - ($$2425$$,$$THE BRANCH$$,#{state_id_nsw},-32.541688,151.960927), - ($$2425$$,$$WASHPOOL$$,#{state_id_nsw},-32.541688,151.960927), - ($$2426$$,$$COOPERNOOK$$,#{state_id_nsw},-31.826246,152.609896), - ($$2426$$,$$LANGLEY VALE$$,#{state_id_nsw},-31.826246,152.609896), - ($$2426$$,$$MOTO$$,#{state_id_nsw},-31.826246,152.609896), - ($$2427$$,$$CROWDY HEAD$$,#{state_id_nsw},-31.844821,152.738877), - ($$2427$$,$$HARRINGTON$$,#{state_id_nsw},-31.844821,152.738877), - ($$2428$$,$$BLUEYS BEACH$$,#{state_id_nsw},-32.347684,152.53525), - ($$2428$$,$$BOOMERANG BEACH$$,#{state_id_nsw},-32.347684,152.53525), - ($$2428$$,$$BOOTI BOOTI$$,#{state_id_nsw},-32.347684,152.53525), - ($$2428$$,$$CHARLOTTE BAY$$,#{state_id_nsw},-32.347684,152.53525), - ($$2428$$,$$COOMBA BAY$$,#{state_id_nsw},-32.347684,152.53525), - ($$2428$$,$$COOMBA PARK$$,#{state_id_nsw},-32.347684,152.53525), - ($$2428$$,$$DARAWANK$$,#{state_id_nsw},-32.347684,152.53525), - ($$2428$$,$$ELIZABETH BEACH$$,#{state_id_nsw},-32.347684,152.53525), - ($$2428$$,$$FORSTER$$,#{state_id_nsw},-32.347684,152.53525), - ($$2428$$,$$FORSTER SHOPPING VILLAGE$$,#{state_id_nsw},-32.347684,152.53525), - ($$2428$$,$$GREEN POINT$$,#{state_id_nsw},-32.347684,152.53525), - ($$2428$$,$$PACIFIC PALMS$$,#{state_id_nsw},-32.347684,152.53525), - ($$2428$$,$$SANDBAR$$,#{state_id_nsw},-32.347684,152.53525), - ($$2428$$,$$SHALLOW BAY$$,#{state_id_nsw},-32.347684,152.53525), - ($$2428$$,$$SMITHS LAKE$$,#{state_id_nsw},-32.347684,152.53525), - ($$2428$$,$$TARBUCK BAY$$,#{state_id_nsw},-32.347684,152.53525), - ($$2428$$,$$TIONA$$,#{state_id_nsw},-32.347684,152.53525), - ($$2428$$,$$TUNCURRY$$,#{state_id_nsw},-32.347684,152.53525), - ($$2428$$,$$WALLINGAT$$,#{state_id_nsw},-32.347684,152.53525), - ($$2428$$,$$WALLIS LAKE$$,#{state_id_nsw},-32.347684,152.53525), - ($$2428$$,$$WHOOTA$$,#{state_id_nsw},-32.347684,152.53525), - ($$2429$$,$$BOBIN$$,#{state_id_nsw},-31.726021,152.283882), - ($$2429$$,$$BOORGANNA$$,#{state_id_nsw},-31.726021,152.283882), - ($$2429$$,$$BUCCA WAUKA$$,#{state_id_nsw},-31.726021,152.283882), - ($$2429$$,$$BULGA FOREST$$,#{state_id_nsw},-31.726021,152.283882), - ($$2429$$,$$BUNYAH$$,#{state_id_nsw},-31.726021,152.283882), - ($$2429$$,$$BURRELL CREEK$$,#{state_id_nsw},-31.726021,152.283882), - ($$2429$$,$$CAPARRA$$,#{state_id_nsw},-31.726021,152.283882), - ($$2429$$,$$CEDAR PARTY$$,#{state_id_nsw},-31.726021,152.283882), - ($$2429$$,$$COMBOYNE$$,#{state_id_nsw},-31.726021,152.283882), - ($$2429$$,$$DINGO FOREST$$,#{state_id_nsw},-31.726021,152.283882), - ($$2429$$,$$DOLLYS FLAT$$,#{state_id_nsw},-31.726021,152.283882), - ($$2429$$,$$DYERS CROSSING$$,#{state_id_nsw},-31.726021,152.283882), - ($$2429$$,$$ELANDS$$,#{state_id_nsw},-31.726021,152.283882), - ($$2429$$,$$FIREFLY$$,#{state_id_nsw},-31.726021,152.283882), - ($$2429$$,$$INNES VIEW$$,#{state_id_nsw},-31.726021,152.283882), - ($$2429$$,$$KARAAK FLAT$$,#{state_id_nsw},-31.726021,152.283882), - ($$2429$$,$$KHATAMBUHL$$,#{state_id_nsw},-31.726021,152.283882), - ($$2429$$,$$KILLABAKH$$,#{state_id_nsw},-31.726021,152.283882), - ($$2429$$,$$KILLAWARRA$$,#{state_id_nsw},-31.726021,152.283882), - ($$2429$$,$$KIMBRIKI$$,#{state_id_nsw},-31.726021,152.283882), - ($$2429$$,$$KIPPAXS$$,#{state_id_nsw},-31.726021,152.283882), - ($$2429$$,$$KRAMBACH$$,#{state_id_nsw},-31.726021,152.283882), - ($$2429$$,$$KUNDIBAKH$$,#{state_id_nsw},-31.726021,152.283882), - ($$2429$$,$$MARLEE$$,#{state_id_nsw},-31.726021,152.283882), - ($$2429$$,$$MOORAL CREEK$$,#{state_id_nsw},-31.726021,152.283882), - ($$2429$$,$$STRATHCEDAR$$,#{state_id_nsw},-31.726021,152.283882), - ($$2429$$,$$THE BIGHT$$,#{state_id_nsw},-31.726021,152.283882), - ($$2429$$,$$TIPPERARY$$,#{state_id_nsw},-31.726021,152.283882), - ($$2429$$,$$WARRIWILLAH$$,#{state_id_nsw},-31.726021,152.283882), - ($$2429$$,$$WHERROL FLAT$$,#{state_id_nsw},-31.726021,152.283882), - ($$2429$$,$$WINGHAM$$,#{state_id_nsw},-31.726021,152.283882), - ($$2429$$,$$YARRATT FOREST$$,#{state_id_nsw},-31.726021,152.283882), - ($$2430$$,$$BLACK HEAD$$,#{state_id_nsw},-32.070894,152.543811), - ($$2430$$,$$BOHNOCK$$,#{state_id_nsw},-32.070894,152.543811), - ($$2430$$,$$BOOTAWA$$,#{state_id_nsw},-32.070894,152.543811), - ($$2430$$,$$BRIMBIN$$,#{state_id_nsw},-32.070894,152.543811), - ($$2430$$,$$CABBAGE TREE ISLAND$$,#{state_id_nsw},-32.070894,152.543811), - ($$2430$$,$$CHATHAM$$,#{state_id_nsw},-32.070894,152.543811), - ($$2430$$,$$CROKI$$,#{state_id_nsw},-32.070894,152.543811), - ($$2430$$,$$CUNDLETOWN$$,#{state_id_nsw},-32.070894,152.543811), - ($$2430$$,$$DIAMOND BEACH$$,#{state_id_nsw},-32.070894,152.543811), - ($$2430$$,$$DUMARESQ ISLAND$$,#{state_id_nsw},-32.070894,152.543811), - ($$2430$$,$$FAILFORD$$,#{state_id_nsw},-32.070894,152.543811), - ($$2430$$,$$GHINNI GHINNI$$,#{state_id_nsw},-32.070894,152.543811), - ($$2430$$,$$GLENTHORNE$$,#{state_id_nsw},-32.070894,152.543811), - ($$2430$$,$$HALLIDAYS POINT$$,#{state_id_nsw},-32.070894,152.543811), - ($$2430$$,$$HILLVILLE$$,#{state_id_nsw},-32.070894,152.543811), - ($$2430$$,$$JONES ISLAND$$,#{state_id_nsw},-32.070894,152.543811), - ($$2430$$,$$KIWARRAK$$,#{state_id_nsw},-32.070894,152.543811), - ($$2430$$,$$KOORAINGHAT$$,#{state_id_nsw},-32.070894,152.543811), - ($$2430$$,$$KUNDLE KUNDLE$$,#{state_id_nsw},-32.070894,152.543811), - ($$2430$$,$$LANSDOWNE$$,#{state_id_nsw},-32.070894,152.543811), - ($$2430$$,$$LANSDOWNE FOREST$$,#{state_id_nsw},-32.070894,152.543811), - ($$2430$$,$$MANNING POINT$$,#{state_id_nsw},-32.070894,152.543811), - ($$2430$$,$$MELINGA$$,#{state_id_nsw},-32.070894,152.543811), - ($$2430$$,$$MITCHELLS ISLAND$$,#{state_id_nsw},-32.070894,152.543811), - ($$2430$$,$$MONDROOK$$,#{state_id_nsw},-32.070894,152.543811), - ($$2430$$,$$OLD BAR$$,#{state_id_nsw},-32.070894,152.543811), - ($$2430$$,$$OXLEY ISLAND$$,#{state_id_nsw},-32.070894,152.543811), - ($$2430$$,$$PAMPOOLAH$$,#{state_id_nsw},-32.070894,152.543811), - ($$2430$$,$$POSSUM BRUSH$$,#{state_id_nsw},-32.070894,152.543811), - ($$2430$$,$$PURFLEET$$,#{state_id_nsw},-32.070894,152.543811), - ($$2430$$,$$RAINBOW FLAT$$,#{state_id_nsw},-32.070894,152.543811), - ($$2430$$,$$RED HEAD$$,#{state_id_nsw},-32.070894,152.543811), - ($$2430$$,$$SALTWATER$$,#{state_id_nsw},-32.070894,152.543811), - ($$2430$$,$$TALLWOODS VILLAGE$$,#{state_id_nsw},-32.070894,152.543811), - ($$2430$$,$$TAREE$$,#{state_id_nsw},-32.070894,152.543811), - ($$2430$$,$$TAREE SOUTH$$,#{state_id_nsw},-32.070894,152.543811), - ($$2430$$,$$TINONEE$$,#{state_id_nsw},-32.070894,152.543811), - ($$2430$$,$$UPPER LANSDOWNE$$,#{state_id_nsw},-32.070894,152.543811), - ($$2430$$,$$WALLABI POINT$$,#{state_id_nsw},-32.070894,152.543811), - ($$2431$$,$$ARAKOON$$,#{state_id_nsw},-30.888231,153.066955), - ($$2431$$,$$JERSEYVILLE$$,#{state_id_nsw},-30.888231,153.066955), - ($$2431$$,$$SOUTH WEST ROCKS$$,#{state_id_nsw},-30.888231,153.066955), - ($$2439$$,$$BATAR CREEK$$,#{state_id_nsw},-31.658604,152.679031), - ($$2439$$,$$BLACK CREEK$$,#{state_id_nsw},-31.658604,152.679031), - ($$2439$$,$$KENDALL$$,#{state_id_nsw},-31.658604,152.679031), - ($$2439$$,$$KEREWONG$$,#{state_id_nsw},-31.658604,152.679031), - ($$2439$$,$$KEW$$,#{state_id_nsw},-31.658604,152.679031), - ($$2439$$,$$LOGANS CROSSING$$,#{state_id_nsw},-31.658604,152.679031), - ($$2439$$,$$LORNE$$,#{state_id_nsw},-31.658604,152.679031), - ($$2439$$,$$ROSSGLEN$$,#{state_id_nsw},-31.658604,152.679031), - ($$2439$$,$$SWANS CROSSING$$,#{state_id_nsw},-31.658604,152.679031), - ($$2439$$,$$UPSALLS CREEK$$,#{state_id_nsw},-31.658604,152.679031), - ($$2440$$,$$ALDAVILLA$$,#{state_id_nsw},-31.058949,152.768142), - ($$2440$$,$$AUSTRAL EDEN$$,#{state_id_nsw},-31.058949,152.768142), - ($$2440$$,$$BELLBROOK$$,#{state_id_nsw},-31.058949,152.768142), - ($$2440$$,$$BELLIMBOPINNI$$,#{state_id_nsw},-31.058949,152.768142), - ($$2440$$,$$BELMORE RIVER$$,#{state_id_nsw},-31.058949,152.768142), - ($$2440$$,$$BURNT BRIDGE$$,#{state_id_nsw},-31.058949,152.768142), - ($$2440$$,$$CARRAI$$,#{state_id_nsw},-31.058949,152.768142), - ($$2440$$,$$CLYBUCCA$$,#{state_id_nsw},-31.058949,152.768142), - ($$2440$$,$$COLLOMBATTI$$,#{state_id_nsw},-31.058949,152.768142), - ($$2440$$,$$COMARA$$,#{state_id_nsw},-31.058949,152.768142), - ($$2440$$,$$CORANGULA$$,#{state_id_nsw},-31.058949,152.768142), - ($$2440$$,$$CRESCENT HEAD$$,#{state_id_nsw},-31.058949,152.768142), - ($$2440$$,$$DEEP CREEK$$,#{state_id_nsw},-31.058949,152.768142), - ($$2440$$,$$DONDINGALONG$$,#{state_id_nsw},-31.058949,152.768142), - ($$2440$$,$$EAST KEMPSEY$$,#{state_id_nsw},-31.058949,152.768142), - ($$2440$$,$$EUROKA$$,#{state_id_nsw},-31.058949,152.768142), - ($$2440$$,$$FREDERICKTON$$,#{state_id_nsw},-31.058949,152.768142), - ($$2440$$,$$GLADSTONE$$,#{state_id_nsw},-31.058949,152.768142), - ($$2440$$,$$GREENHILL$$,#{state_id_nsw},-31.058949,152.768142), - ($$2440$$,$$HAMPDEN HALL$$,#{state_id_nsw},-31.058949,152.768142), - ($$2440$$,$$HAT HEAD$$,#{state_id_nsw},-31.058949,152.768142), - ($$2440$$,$$HICKEYS CREEK$$,#{state_id_nsw},-31.058949,152.768142), - ($$2440$$,$$KEMPSEY$$,#{state_id_nsw},-31.058949,152.768142), - ($$2440$$,$$KINCHELA$$,#{state_id_nsw},-31.058949,152.768142), - ($$2440$$,$$LOWER CREEK$$,#{state_id_nsw},-31.058949,152.768142), - ($$2440$$,$$MILLBANK$$,#{state_id_nsw},-31.058949,152.768142), - ($$2440$$,$$MOONEBA$$,#{state_id_nsw},-31.058949,152.768142), - ($$2440$$,$$MOPARRABAH$$,#{state_id_nsw},-31.058949,152.768142), - ($$2440$$,$$MUNGAY CREEK$$,#{state_id_nsw},-31.058949,152.768142), - ($$2440$$,$$OLD STATION$$,#{state_id_nsw},-31.058949,152.768142), - ($$2440$$,$$POLA CREEK$$,#{state_id_nsw},-31.058949,152.768142), - ($$2440$$,$$RAINBOW REACH$$,#{state_id_nsw},-31.058949,152.768142), - ($$2440$$,$$SEVEN OAKS$$,#{state_id_nsw},-31.058949,152.768142), - ($$2440$$,$$SHERWOOD$$,#{state_id_nsw},-31.058949,152.768142), - ($$2440$$,$$SKILLION FLAT$$,#{state_id_nsw},-31.058949,152.768142), - ($$2440$$,$$SMITHTOWN$$,#{state_id_nsw},-31.058949,152.768142), - ($$2440$$,$$SOUTH KEMPSEY$$,#{state_id_nsw},-31.058949,152.768142), - ($$2440$$,$$SUMMER ISLAND$$,#{state_id_nsw},-31.058949,152.768142), - ($$2440$$,$$TEMAGOG$$,#{state_id_nsw},-31.058949,152.768142), - ($$2440$$,$$TOOROOKA$$,#{state_id_nsw},-31.058949,152.768142), - ($$2440$$,$$TURNERS FLAT$$,#{state_id_nsw},-31.058949,152.768142), - ($$2440$$,$$VERGES CREEK$$,#{state_id_nsw},-31.058949,152.768142), - ($$2440$$,$$WEST KEMPSEY$$,#{state_id_nsw},-31.058949,152.768142), - ($$2440$$,$$WILLAWARRIN$$,#{state_id_nsw},-31.058949,152.768142), - ($$2440$$,$$WILLI WILLI$$,#{state_id_nsw},-31.058949,152.768142), - ($$2440$$,$$WITTITRIN$$,#{state_id_nsw},-31.058949,152.768142), - ($$2440$$,$$YARRAVEL$$,#{state_id_nsw},-31.058949,152.768142), - ($$2440$$,$$YESSABAH$$,#{state_id_nsw},-31.058949,152.768142), - ($$2441$$,$$ALLGOMERA$$,#{state_id_nsw},-30.816097,152.831369), - ($$2441$$,$$BALLENGARRA$$,#{state_id_nsw},-30.816097,152.831369), - ($$2441$$,$$BARRAGANYATTI$$,#{state_id_nsw},-30.816097,152.831369), - ($$2441$$,$$BONVILLE$$,#{state_id_nsw},-30.816097,152.831369), - ($$2441$$,$$BRIL BRIL$$,#{state_id_nsw},-30.816097,152.831369), - ($$2441$$,$$BRINERVILLE$$,#{state_id_nsw},-30.816097,152.831369), - ($$2441$$,$$COOPERABUNG$$,#{state_id_nsw},-30.816097,152.831369), - ($$2441$$,$$EUNGAI CREEK$$,#{state_id_nsw},-30.816097,152.831369), - ($$2441$$,$$EUNGAI RAIL$$,#{state_id_nsw},-30.816097,152.831369), - ($$2441$$,$$FISHERMANS REACH$$,#{state_id_nsw},-30.816097,152.831369), - ($$2441$$,$$GRASSY HEAD$$,#{state_id_nsw},-30.816097,152.831369), - ($$2441$$,$$GUM SCRUB$$,#{state_id_nsw},-30.816097,152.831369), - ($$2441$$,$$HACKS FERRY$$,#{state_id_nsw},-30.816097,152.831369), - ($$2441$$,$$KIPPARA$$,#{state_id_nsw},-30.816097,152.831369), - ($$2441$$,$$KUNDABUNG$$,#{state_id_nsw},-30.816097,152.831369), - ($$2441$$,$$ROLLANDS PLAINS$$,#{state_id_nsw},-30.816097,152.831369), - ($$2441$$,$$STUARTS POINT$$,#{state_id_nsw},-30.816097,152.831369), - ($$2441$$,$$TAMBAN$$,#{state_id_nsw},-30.816097,152.831369), - ($$2441$$,$$TELEGRAPH POINT$$,#{state_id_nsw},-30.816097,152.831369), - ($$2441$$,$$UPPER ROLLANDS PLAINS$$,#{state_id_nsw},-30.816097,152.831369), - ($$2441$$,$$YARRAHAPINNI$$,#{state_id_nsw},-30.816097,152.831369), - ($$2442$$,$$KEMPSEY MSC$$,#{state_id_nsw},0.0,0.0), - ($$2442$$,$$MID NORTH COAST MSC$$,#{state_id_nsw},0.0,0.0), - ($$2443$$,$$BOBS CREEK$$,#{state_id_nsw},-31.584673,152.744357), - ($$2443$$,$$CAMDEN HEAD$$,#{state_id_nsw},-31.584673,152.744357), - ($$2443$$,$$CORALVILLE$$,#{state_id_nsw},-31.584673,152.744357), - ($$2443$$,$$CROWDY BAY NATIONAL PARK$$,#{state_id_nsw},-31.584673,152.744357), - ($$2443$$,$$DEAUVILLE$$,#{state_id_nsw},-31.584673,152.744357), - ($$2443$$,$$DIAMOND HEAD$$,#{state_id_nsw},-31.584673,152.744357), - ($$2443$$,$$DUNBOGAN$$,#{state_id_nsw},-31.584673,152.744357), - ($$2443$$,$$HANNAM VALE$$,#{state_id_nsw},-31.584673,152.744357), - ($$2443$$,$$HERONS CREEK$$,#{state_id_nsw},-31.584673,152.744357), - ($$2443$$,$$JOHNS RIVER$$,#{state_id_nsw},-31.584673,152.744357), - ($$2443$$,$$LAKEWOOD$$,#{state_id_nsw},-31.584673,152.744357), - ($$2443$$,$$LAURIETON$$,#{state_id_nsw},-31.584673,152.744357), - ($$2443$$,$$MIDDLE BROTHER$$,#{state_id_nsw},-31.584673,152.744357), - ($$2443$$,$$MOORLAND$$,#{state_id_nsw},-31.584673,152.744357), - ($$2443$$,$$NORTH BROTHER$$,#{state_id_nsw},-31.584673,152.744357), - ($$2443$$,$$NORTH HAVEN$$,#{state_id_nsw},-31.584673,152.744357), - ($$2443$$,$$STEWARTS RIVER$$,#{state_id_nsw},-31.584673,152.744357), - ($$2443$$,$$WAITUI$$,#{state_id_nsw},-31.584673,152.744357), - ($$2443$$,$$WEST HAVEN$$,#{state_id_nsw},-31.584673,152.744357), - ($$2444$$,$$BLACKMANS POINT$$,#{state_id_nsw},-31.400666,152.851836), - ($$2444$$,$$FERNBANK CREEK$$,#{state_id_nsw},-31.400666,152.851836), - ($$2444$$,$$FLYNNS BEACH$$,#{state_id_nsw},-31.400666,152.851836), - ($$2444$$,$$LIGHTHOUSE BEACH$$,#{state_id_nsw},-31.400666,152.851836), - ($$2444$$,$$LIMEBURNERS CREEK$$,#{state_id_nsw},-31.400666,152.851836), - ($$2444$$,$$NORTH SHORE$$,#{state_id_nsw},-31.400666,152.851836), - ($$2444$$,$$PORT MACQUARIE$$,#{state_id_nsw},-31.400666,152.851836), - ($$2444$$,$$PORT MACQUARIE BC$$,#{state_id_nsw},-31.400666,152.851836), - ($$2444$$,$$RIVERSIDE$$,#{state_id_nsw},-31.400666,152.851836), - ($$2444$$,$$SETTLEMENT CITY$$,#{state_id_nsw},-31.400666,152.851836), - ($$2444$$,$$THE HATCH$$,#{state_id_nsw},-31.400666,152.851836), - ($$2444$$,$$THRUMSTER$$,#{state_id_nsw},-31.400666,152.851836), - ($$2445$$,$$BONNY HILLS$$,#{state_id_nsw},-31.594981,152.840605), - ($$2445$$,$$GRANTS BEACH$$,#{state_id_nsw},-31.594981,152.840605), - ($$2445$$,$$JOLLY NOSE$$,#{state_id_nsw},-31.594981,152.840605), - ($$2445$$,$$LAKE CATHIE$$,#{state_id_nsw},-31.594981,152.840605), - ($$2446$$,$$BAGNOO$$,#{state_id_nsw},-31.463639,152.533221), - ($$2446$$,$$BAGO$$,#{state_id_nsw},-31.463639,152.533221), - ($$2446$$,$$BANDA BANDA$$,#{state_id_nsw},-31.463639,152.533221), - ($$2446$$,$$BEECHWOOD$$,#{state_id_nsw},-31.463639,152.533221), - ($$2446$$,$$BELLANGRY$$,#{state_id_nsw},-31.463639,152.533221), - ($$2446$$,$$BIRDWOOD$$,#{state_id_nsw},-31.463639,152.533221), - ($$2446$$,$$BROMBIN$$,#{state_id_nsw},-31.463639,152.533221), - ($$2446$$,$$BYABARRA$$,#{state_id_nsw},-31.463639,152.533221), - ($$2446$$,$$CAIRNCROSS$$,#{state_id_nsw},-31.463639,152.533221), - ($$2446$$,$$CROSSLANDS$$,#{state_id_nsw},-31.463639,152.533221), - ($$2446$$,$$DEBENHAM$$,#{state_id_nsw},-31.463639,152.533221), - ($$2446$$,$$DOYLES RIVER$$,#{state_id_nsw},-31.463639,152.533221), - ($$2446$$,$$ELLENBOROUGH$$,#{state_id_nsw},-31.463639,152.533221), - ($$2446$$,$$FORBES RIVER$$,#{state_id_nsw},-31.463639,152.533221), - ($$2446$$,$$FRAZERS CREEK$$,#{state_id_nsw},-31.463639,152.533221), - ($$2446$$,$$GEARYS FLAT$$,#{state_id_nsw},-31.463639,152.533221), - ($$2446$$,$$HARTYS PLAINS$$,#{state_id_nsw},-31.463639,152.533221), - ($$2446$$,$$HOLLISDALE$$,#{state_id_nsw},-31.463639,152.533221), - ($$2446$$,$$HUNTINGDON$$,#{state_id_nsw},-31.463639,152.533221), - ($$2446$$,$$HYNDMANS CREEK$$,#{state_id_nsw},-31.463639,152.533221), - ($$2446$$,$$KINDEE$$,#{state_id_nsw},-31.463639,152.533221), - ($$2446$$,$$KING CREEK$$,#{state_id_nsw},-31.463639,152.533221), - ($$2446$$,$$LAKE INNES$$,#{state_id_nsw},-31.463639,152.533221), - ($$2446$$,$$LONG FLAT$$,#{state_id_nsw},-31.463639,152.533221), - ($$2446$$,$$LOWER PAPPINBARRA$$,#{state_id_nsw},-31.463639,152.533221), - ($$2446$$,$$MARLO MERRICAN$$,#{state_id_nsw},-31.463639,152.533221), - ($$2446$$,$$MORTONS CREEK$$,#{state_id_nsw},-31.463639,152.533221), - ($$2446$$,$$MOUNT SEAVIEW$$,#{state_id_nsw},-31.463639,152.533221), - ($$2446$$,$$PAPPINBARRA$$,#{state_id_nsw},-31.463639,152.533221), - ($$2446$$,$$PEMBROOKE$$,#{state_id_nsw},-31.463639,152.533221), - ($$2446$$,$$PIPECLAY$$,#{state_id_nsw},-31.463639,152.533221), - ($$2446$$,$$RAWDON ISLAND$$,#{state_id_nsw},-31.463639,152.533221), - ($$2446$$,$$REDBANK$$,#{state_id_nsw},-31.463639,152.533221), - ($$2446$$,$$ROSEWOOD$$,#{state_id_nsw},-31.463639,152.533221), - ($$2446$$,$$SANCROX$$,#{state_id_nsw},-31.463639,152.533221), - ($$2446$$,$$TOMS CREEK$$,#{state_id_nsw},-31.463639,152.533221), - ($$2446$$,$$UPPER PAPPINBARRA$$,#{state_id_nsw},-31.463639,152.533221), - ($$2446$$,$$WAUCHOPE$$,#{state_id_nsw},-31.463639,152.533221), - ($$2446$$,$$WERRIKIMBE$$,#{state_id_nsw},-31.463639,152.533221), - ($$2446$$,$$YARRAS$$,#{state_id_nsw},-31.463639,152.533221), - ($$2446$$,$$YIPPIN CREEK$$,#{state_id_nsw},-31.463639,152.533221), - ($$2447$$,$$BAKERS CREEK$$,#{state_id_nsw},-30.803716,152.726106), - ($$2447$$,$$BURRAPINE$$,#{state_id_nsw},-30.803716,152.726106), - ($$2447$$,$$CONGARINNI$$,#{state_id_nsw},-30.803716,152.726106), - ($$2447$$,$$CONGARINNI NORTH$$,#{state_id_nsw},-30.803716,152.726106), - ($$2447$$,$$DONNELLYVILLE$$,#{state_id_nsw},-30.803716,152.726106), - ($$2447$$,$$GUMMA$$,#{state_id_nsw},-30.803716,152.726106), - ($$2447$$,$$MACKSVILLE$$,#{state_id_nsw},-30.803716,152.726106), - ($$2447$$,$$NEWEE CREEK$$,#{state_id_nsw},-30.803716,152.726106), - ($$2447$$,$$NORTH MACKSVILLE$$,#{state_id_nsw},-30.803716,152.726106), - ($$2447$$,$$SCOTTS HEAD$$,#{state_id_nsw},-30.803716,152.726106), - ($$2447$$,$$TALARM$$,#{state_id_nsw},-30.803716,152.726106), - ($$2447$$,$$TAYLORS ARM$$,#{state_id_nsw},-30.803716,152.726106), - ($$2447$$,$$THUMB CREEK$$,#{state_id_nsw},-30.803716,152.726106), - ($$2447$$,$$UPPER TAYLORS ARM$$,#{state_id_nsw},-30.803716,152.726106), - ($$2447$$,$$UTUNGUN$$,#{state_id_nsw},-30.803716,152.726106), - ($$2447$$,$$WARRELL CREEK$$,#{state_id_nsw},-30.803716,152.726106), - ($$2447$$,$$WAY WAY$$,#{state_id_nsw},-30.803716,152.726106), - ($$2447$$,$$WIRRIMBI$$,#{state_id_nsw},-30.803716,152.726106), - ($$2447$$,$$YARRANBELLA$$,#{state_id_nsw},-30.803716,152.726106), - ($$2448$$,$$HYLAND PARK$$,#{state_id_nsw},-30.615511,152.999909), - ($$2448$$,$$NAMBUCCA HEADS$$,#{state_id_nsw},-30.615511,152.999909), - ($$2448$$,$$VALLA$$,#{state_id_nsw},-30.615511,152.999909), - ($$2448$$,$$VALLA BEACH$$,#{state_id_nsw},-30.615511,152.999909), - ($$2449$$,$$ARGENTS HILL$$,#{state_id_nsw},-30.621952,152.746326), - ($$2449$$,$$BOWRAVILLE$$,#{state_id_nsw},-30.621952,152.746326), - ($$2449$$,$$BUCKRA BENDINNI$$,#{state_id_nsw},-30.621952,152.746326), - ($$2449$$,$$GIRRALONG$$,#{state_id_nsw},-30.621952,152.746326), - ($$2449$$,$$KENNAICLE CREEK$$,#{state_id_nsw},-30.621952,152.746326), - ($$2449$$,$$KILLIEKRANKIE$$,#{state_id_nsw},-30.621952,152.746326), - ($$2449$$,$$MISSABOTTI$$,#{state_id_nsw},-30.621952,152.746326), - ($$2449$$,$$SOUTH ARM$$,#{state_id_nsw},-30.621952,152.746326), - ($$2449$$,$$TEWINGA$$,#{state_id_nsw},-30.621952,152.746326), - ($$2450$$,$$BOAMBEE$$,#{state_id_nsw},-30.337186,153.069748), - ($$2450$$,$$BROOKLANA$$,#{state_id_nsw},-30.337186,153.069748), - ($$2450$$,$$BUCCA$$,#{state_id_nsw},-30.337186,153.069748), - ($$2450$$,$$COFFS HARBOUR$$,#{state_id_nsw},-30.337186,153.069748), - ($$2450$$,$$COFFS HARBOUR JETTY$$,#{state_id_nsw},-30.337186,153.069748), - ($$2450$$,$$COFFS HARBOUR PLAZA$$,#{state_id_nsw},-30.337186,153.069748), - ($$2450$$,$$CORAMBA$$,#{state_id_nsw},-30.337186,153.069748), - ($$2450$$,$$GLENREAGH$$,#{state_id_nsw},-30.337186,153.069748), - ($$2450$$,$$KARANGI$$,#{state_id_nsw},-30.337186,153.069748), - ($$2450$$,$$KORORA$$,#{state_id_nsw},-30.337186,153.069748), - ($$2450$$,$$LOWANNA$$,#{state_id_nsw},-30.337186,153.069748), - ($$2450$$,$$MOONEE BEACH$$,#{state_id_nsw},-30.337186,153.069748), - ($$2450$$,$$NANA GLEN$$,#{state_id_nsw},-30.337186,153.069748), - ($$2450$$,$$NORTH BOAMBEE VALLEY$$,#{state_id_nsw},-30.337186,153.069748), - ($$2450$$,$$SAPPHIRE BEACH$$,#{state_id_nsw},-30.337186,153.069748), - ($$2450$$,$$SHERWOOD$$,#{state_id_nsw},-30.337186,153.069748), - ($$2450$$,$$ULONG$$,#{state_id_nsw},-30.337186,153.069748), - ($$2450$$,$$UPPER ORARA$$,#{state_id_nsw},-30.337186,153.069748), - ($$2452$$,$$BOAMBEE EAST$$,#{state_id_nsw},-30.34062,153.084224), - ($$2452$$,$$SAWTELL$$,#{state_id_nsw},-30.34062,153.084224), - ($$2452$$,$$TOORMINA$$,#{state_id_nsw},-30.34062,153.084224), - ($$2453$$,$$BIELSDOWN HILLS$$,#{state_id_nsw},-30.137937,152.599482), - ($$2453$$,$$BILLYS CREEK$$,#{state_id_nsw},-30.137937,152.599482), - ($$2453$$,$$BOSTOBRICK$$,#{state_id_nsw},-30.137937,152.599482), - ($$2453$$,$$CASCADE$$,#{state_id_nsw},-30.137937,152.599482), - ($$2453$$,$$CLOUDS CREEK$$,#{state_id_nsw},-30.137937,152.599482), - ($$2453$$,$$DEER VALE$$,#{state_id_nsw},-30.137937,152.599482), - ($$2453$$,$$DORRIGO$$,#{state_id_nsw},-30.137937,152.599482), - ($$2453$$,$$DORRIGO MOUNTAIN$$,#{state_id_nsw},-30.137937,152.599482), - ($$2453$$,$$DUNDURRABIN$$,#{state_id_nsw},-30.137937,152.599482), - ($$2453$$,$$EBOR$$,#{state_id_nsw},-30.137937,152.599482), - ($$2453$$,$$FERNBROOK$$,#{state_id_nsw},-30.137937,152.599482), - ($$2453$$,$$HERNANI$$,#{state_id_nsw},-30.137937,152.599482), - ($$2453$$,$$MARENGO$$,#{state_id_nsw},-30.137937,152.599482), - ($$2453$$,$$MEGAN$$,#{state_id_nsw},-30.137937,152.599482), - ($$2453$$,$$MOONPAR$$,#{state_id_nsw},-30.137937,152.599482), - ($$2453$$,$$NEVER NEVER$$,#{state_id_nsw},-30.137937,152.599482), - ($$2453$$,$$NORTH DORRIGO$$,#{state_id_nsw},-30.137937,152.599482), - ($$2453$$,$$TALLOWWOOD RIDGE$$,#{state_id_nsw},-30.137937,152.599482), - ($$2453$$,$$TYRINGHAM$$,#{state_id_nsw},-30.137937,152.599482), - ($$2453$$,$$WILD CATTLE CREEK$$,#{state_id_nsw},-30.137937,152.599482), - ($$2454$$,$$BELLINGEN$$,#{state_id_nsw},-30.452388,152.898147), - ($$2454$$,$$BRIERFIELD$$,#{state_id_nsw},-30.452388,152.898147), - ($$2454$$,$$BUNDAGEN$$,#{state_id_nsw},-30.452388,152.898147), - ($$2454$$,$$DARKWOOD$$,#{state_id_nsw},-30.452388,152.898147), - ($$2454$$,$$FERNMOUNT$$,#{state_id_nsw},-30.452388,152.898147), - ($$2454$$,$$GLENIFFER$$,#{state_id_nsw},-30.452388,152.898147), - ($$2454$$,$$KALANG$$,#{state_id_nsw},-30.452388,152.898147), - ($$2454$$,$$MYLESTOM$$,#{state_id_nsw},-30.452388,152.898147), - ($$2454$$,$$RALEIGH$$,#{state_id_nsw},-30.452388,152.898147), - ($$2454$$,$$REPTON$$,#{state_id_nsw},-30.452388,152.898147), - ($$2454$$,$$SPICKETTS CREEK$$,#{state_id_nsw},-30.452388,152.898147), - ($$2454$$,$$THORA$$,#{state_id_nsw},-30.452388,152.898147), - ($$2454$$,$$VALERY$$,#{state_id_nsw},-30.452388,152.898147), - ($$2455$$,$$URUNGA$$,#{state_id_nsw},-30.522855,152.975538), - ($$2456$$,$$ARRAWARRA$$,#{state_id_nsw},-30.059596,153.18789), - ($$2456$$,$$ARRAWARRA HEADLAND$$,#{state_id_nsw},-30.059596,153.18789), - ($$2456$$,$$CORINDI BEACH$$,#{state_id_nsw},-30.059596,153.18789), - ($$2456$$,$$EMERALD BEACH$$,#{state_id_nsw},-30.059596,153.18789), - ($$2456$$,$$MULLAWAY$$,#{state_id_nsw},-30.059596,153.18789), - ($$2456$$,$$RED ROCK$$,#{state_id_nsw},-30.059596,153.18789), - ($$2456$$,$$SAFETY BEACH$$,#{state_id_nsw},-30.059596,153.18789), - ($$2456$$,$$SANDY BEACH$$,#{state_id_nsw},-30.059596,153.18789), - ($$2456$$,$$UPPER CORINDI$$,#{state_id_nsw},-30.059596,153.18789), - ($$2456$$,$$WOOLGOOLGA$$,#{state_id_nsw},-30.059596,153.18789), - ($$2460$$,$$ALUMY CREEK$$,#{state_id_nsw},-29.050238,152.587447), - ($$2460$$,$$BARCOONGERE$$,#{state_id_nsw},-29.050238,152.587447), - ($$2460$$,$$BARRETTS CREEK$$,#{state_id_nsw},-29.050238,152.587447), - ($$2460$$,$$BARYULGIL$$,#{state_id_nsw},-29.050238,152.587447), - ($$2460$$,$$BLAXLANDS CREEK$$,#{state_id_nsw},-29.050238,152.587447), - ($$2460$$,$$BOM BOM$$,#{state_id_nsw},-29.050238,152.587447), - ($$2460$$,$$BOOKRAM$$,#{state_id_nsw},-29.050238,152.587447), - ($$2460$$,$$BRAUNSTONE$$,#{state_id_nsw},-29.050238,152.587447), - ($$2460$$,$$BRUSHGROVE$$,#{state_id_nsw},-29.050238,152.587447), - ($$2460$$,$$BUCCARUMBI$$,#{state_id_nsw},-29.050238,152.587447), - ($$2460$$,$$CALAMIA$$,#{state_id_nsw},-29.050238,152.587447), - ($$2460$$,$$CANGAI$$,#{state_id_nsw},-29.050238,152.587447), - ($$2460$$,$$CARNHAM$$,#{state_id_nsw},-29.050238,152.587447), - ($$2460$$,$$CARRS CREEK$$,#{state_id_nsw},-29.050238,152.587447), - ($$2460$$,$$CARRS ISLAND$$,#{state_id_nsw},-29.050238,152.587447), - ($$2460$$,$$CARRS PENINSULAR$$,#{state_id_nsw},-29.050238,152.587447), - ($$2460$$,$$CHAELUNDI$$,#{state_id_nsw},-29.050238,152.587447), - ($$2460$$,$$CHAMBIGNE$$,#{state_id_nsw},-29.050238,152.587447), - ($$2460$$,$$CLARENZA$$,#{state_id_nsw},-29.050238,152.587447), - ($$2460$$,$$CLIFDEN$$,#{state_id_nsw},-29.050238,152.587447), - ($$2460$$,$$COALDALE$$,#{state_id_nsw},-29.050238,152.587447), - ($$2460$$,$$COLLUM COLLUM$$,#{state_id_nsw},-29.050238,152.587447), - ($$2460$$,$$COOMBADJHA$$,#{state_id_nsw},-29.050238,152.587447), - ($$2460$$,$$COPMANHURST$$,#{state_id_nsw},-29.050238,152.587447), - ($$2460$$,$$COUTTS CROSSING$$,#{state_id_nsw},-29.050238,152.587447), - ($$2460$$,$$COWPER$$,#{state_id_nsw},-29.050238,152.587447), - ($$2460$$,$$CROWTHER ISLAND$$,#{state_id_nsw},-29.050238,152.587447), - ($$2460$$,$$DALMORTON$$,#{state_id_nsw},-29.050238,152.587447), - ($$2460$$,$$DEEP CREEK$$,#{state_id_nsw},-29.050238,152.587447), - ($$2460$$,$$DILKOON$$,#{state_id_nsw},-29.050238,152.587447), - ($$2460$$,$$DIRTY CREEK$$,#{state_id_nsw},-29.050238,152.587447), - ($$2460$$,$$DUMBUDGERY$$,#{state_id_nsw},-29.050238,152.587447), - ($$2460$$,$$EATONSVILLE$$,#{state_id_nsw},-29.050238,152.587447), - ($$2460$$,$$EIGHTEEN MILE$$,#{state_id_nsw},-29.050238,152.587447), - ($$2460$$,$$ELLAND$$,#{state_id_nsw},-29.050238,152.587447), - ($$2460$$,$$FINE FLOWER$$,#{state_id_nsw},-29.050238,152.587447), - ($$2460$$,$$FORTIS CREEK$$,#{state_id_nsw},-29.050238,152.587447), - ($$2460$$,$$GLENUGIE$$,#{state_id_nsw},-29.050238,152.587447), - ($$2460$$,$$GRAFTON$$,#{state_id_nsw},-29.050238,152.587447), - ($$2460$$,$$GRAFTON WEST$$,#{state_id_nsw},-29.050238,152.587447), - ($$2460$$,$$GREAT MARLOW$$,#{state_id_nsw},-29.050238,152.587447), - ($$2460$$,$$GURRANANG$$,#{state_id_nsw},-29.050238,152.587447), - ($$2460$$,$$HALFWAY CREEK$$,#{state_id_nsw},-29.050238,152.587447), - ($$2460$$,$$HEIFER STATION$$,#{state_id_nsw},-29.050238,152.587447), - ($$2460$$,$$JACKADGERY$$,#{state_id_nsw},-29.050238,152.587447), - ($$2460$$,$$JUNCTION HILL$$,#{state_id_nsw},-29.050238,152.587447), - ($$2460$$,$$KANGAROO CREEK$$,#{state_id_nsw},-29.050238,152.587447), - ($$2460$$,$$KOOLKHAN$$,#{state_id_nsw},-29.050238,152.587447), - ($$2460$$,$$KREMNOS$$,#{state_id_nsw},-29.050238,152.587447), - ($$2460$$,$$KUNGALA$$,#{state_id_nsw},-29.050238,152.587447), - ($$2460$$,$$KYARRAN$$,#{state_id_nsw},-29.050238,152.587447), - ($$2460$$,$$LANITZA$$,#{state_id_nsw},-29.050238,152.587447), - ($$2460$$,$$LAWRENCE$$,#{state_id_nsw},-29.050238,152.587447), - ($$2460$$,$$LEVENSTRATH$$,#{state_id_nsw},-29.050238,152.587447), - ($$2460$$,$$LILYDALE$$,#{state_id_nsw},-29.050238,152.587447), - ($$2460$$,$$LIONSVILLE$$,#{state_id_nsw},-29.050238,152.587447), - ($$2460$$,$$LOWER SOUTHGATE$$,#{state_id_nsw},-29.050238,152.587447), - ($$2460$$,$$MALABUGILMAH$$,#{state_id_nsw},-29.050238,152.587447), - ($$2460$$,$$MOLEVILLE CREEK$$,#{state_id_nsw},-29.050238,152.587447), - ($$2460$$,$$MOUNTAIN VIEW$$,#{state_id_nsw},-29.050238,152.587447), - ($$2460$$,$$MYLNEFORD$$,#{state_id_nsw},-29.050238,152.587447), - ($$2460$$,$$NEWBOLD$$,#{state_id_nsw},-29.050238,152.587447), - ($$2460$$,$$NYMBOIDA$$,#{state_id_nsw},-29.050238,152.587447), - ($$2460$$,$$PULGANBAR$$,#{state_id_nsw},-29.050238,152.587447), - ($$2460$$,$$PUNCHBOWL$$,#{state_id_nsw},-29.050238,152.587447), - ($$2460$$,$$RAMORNIE$$,#{state_id_nsw},-29.050238,152.587447), - ($$2460$$,$$RUSHFORTH$$,#{state_id_nsw},-29.050238,152.587447), - ($$2460$$,$$SANDY CROSSING$$,#{state_id_nsw},-29.050238,152.587447), - ($$2460$$,$$SEELANDS$$,#{state_id_nsw},-29.050238,152.587447), - ($$2460$$,$$SHANNONDALE$$,#{state_id_nsw},-29.050238,152.587447), - ($$2460$$,$$SMITHS CREEK$$,#{state_id_nsw},-29.050238,152.587447), - ($$2460$$,$$SOUTH ARM$$,#{state_id_nsw},-29.050238,152.587447), - ($$2460$$,$$SOUTH GRAFTON$$,#{state_id_nsw},-29.050238,152.587447), - ($$2460$$,$$SOUTHAMPTON$$,#{state_id_nsw},-29.050238,152.587447), - ($$2460$$,$$SOUTHGATE$$,#{state_id_nsw},-29.050238,152.587447), - ($$2460$$,$$STOCKYARD CREEK$$,#{state_id_nsw},-29.050238,152.587447), - ($$2460$$,$$THE PINNACLES$$,#{state_id_nsw},-29.050238,152.587447), - ($$2460$$,$$THE WHITEMAN$$,#{state_id_nsw},-29.050238,152.587447), - ($$2460$$,$$TOWALLUM$$,#{state_id_nsw},-29.050238,152.587447), - ($$2460$$,$$TRENAYR$$,#{state_id_nsw},-29.050238,152.587447), - ($$2460$$,$$TYNDALE$$,#{state_id_nsw},-29.050238,152.587447), - ($$2460$$,$$UPPER COPMANHURST$$,#{state_id_nsw},-29.050238,152.587447), - ($$2460$$,$$UPPER FINE FLOWER$$,#{state_id_nsw},-29.050238,152.587447), - ($$2460$$,$$WARRAGAI CREEK$$,#{state_id_nsw},-29.050238,152.587447), - ($$2460$$,$$WASHPOOL$$,#{state_id_nsw},-29.050238,152.587447), - ($$2460$$,$$WATERVIEW$$,#{state_id_nsw},-29.050238,152.587447), - ($$2460$$,$$WATERVIEW HEIGHTS$$,#{state_id_nsw},-29.050238,152.587447), - ($$2460$$,$$WELLS CROSSING$$,#{state_id_nsw},-29.050238,152.587447), - ($$2460$$,$$WHITEMAN CREEK$$,#{state_id_nsw},-29.050238,152.587447), - ($$2460$$,$$WINEGROVE$$,#{state_id_nsw},-29.050238,152.587447), - ($$2460$$,$$WOMBAT CREEK$$,#{state_id_nsw},-29.050238,152.587447), - ($$2462$$,$$CALLIOPE$$,#{state_id_nsw},-29.618586,153.07136), - ($$2462$$,$$COLDSTREAM$$,#{state_id_nsw},-29.618586,153.07136), - ($$2462$$,$$DIGGERS CAMP$$,#{state_id_nsw},-29.618586,153.07136), - ($$2462$$,$$GILLETTS RIDGE$$,#{state_id_nsw},-29.618586,153.07136), - ($$2462$$,$$LAKE HIAWATHA$$,#{state_id_nsw},-29.618586,153.07136), - ($$2462$$,$$LAVADIA$$,#{state_id_nsw},-29.618586,153.07136), - ($$2462$$,$$MINNIE WATER$$,#{state_id_nsw},-29.618586,153.07136), - ($$2462$$,$$PILLAR VALLEY$$,#{state_id_nsw},-29.618586,153.07136), - ($$2462$$,$$SWAN CREEK$$,#{state_id_nsw},-29.618586,153.07136), - ($$2462$$,$$TUCABIA$$,#{state_id_nsw},-29.618586,153.07136), - ($$2462$$,$$ULMARRA$$,#{state_id_nsw},-29.618586,153.07136), - ($$2462$$,$$WOOLI$$,#{state_id_nsw},-29.618586,153.07136), - ($$2463$$,$$ASHBY$$,#{state_id_nsw},-29.423925,153.187448), - ($$2463$$,$$ASHBY HEIGHTS$$,#{state_id_nsw},-29.423925,153.187448), - ($$2463$$,$$ASHBY ISLAND$$,#{state_id_nsw},-29.423925,153.187448), - ($$2463$$,$$BROOMS HEAD$$,#{state_id_nsw},-29.423925,153.187448), - ($$2463$$,$$GULMARRAD$$,#{state_id_nsw},-29.423925,153.187448), - ($$2463$$,$$ILARWILL$$,#{state_id_nsw},-29.423925,153.187448), - ($$2463$$,$$JACKY BULBIN FLAT$$,#{state_id_nsw},-29.423925,153.187448), - ($$2463$$,$$JAMES CREEK$$,#{state_id_nsw},-29.423925,153.187448), - ($$2463$$,$$MACLEAN$$,#{state_id_nsw},-29.423925,153.187448), - ($$2463$$,$$PALMERS CHANNEL$$,#{state_id_nsw},-29.423925,153.187448), - ($$2463$$,$$PALMERS ISLAND$$,#{state_id_nsw},-29.423925,153.187448), - ($$2463$$,$$SANDON$$,#{state_id_nsw},-29.423925,153.187448), - ($$2463$$,$$SHARK CREEK$$,#{state_id_nsw},-29.423925,153.187448), - ($$2463$$,$$TALOUMBI$$,#{state_id_nsw},-29.423925,153.187448), - ($$2463$$,$$THE SANDON$$,#{state_id_nsw},-29.423925,153.187448), - ($$2463$$,$$TOWNSEND$$,#{state_id_nsw},-29.423925,153.187448), - ($$2463$$,$$TULLYMORGAN$$,#{state_id_nsw},-29.423925,153.187448), - ($$2463$$,$$WOODFORD ISLAND$$,#{state_id_nsw},-29.423925,153.187448), - ($$2464$$,$$ANGOURIE$$,#{state_id_nsw},-29.481012,153.359964), - ($$2464$$,$$FREEBURN ISLAND$$,#{state_id_nsw},-29.481012,153.359964), - ($$2464$$,$$MICALO ISLAND$$,#{state_id_nsw},-29.481012,153.359964), - ($$2464$$,$$WOOLOWEYAH$$,#{state_id_nsw},-29.481012,153.359964), - ($$2464$$,$$YAMBA$$,#{state_id_nsw},-29.481012,153.359964), - ($$2464$$,$$YURAYGIR$$,#{state_id_nsw},-29.481012,153.359964), - ($$2465$$,$$HARWOOD$$,#{state_id_nsw},-29.418833,153.240867), - ($$2466$$,$$ILUKA$$,#{state_id_nsw},-29.407475,153.350886), - ($$2466$$,$$THE FRESHWATER$$,#{state_id_nsw},-29.407475,153.350886), - ($$2466$$,$$WOODY HEAD$$,#{state_id_nsw},-29.407475,153.350886), - ($$2469$$,$$ALICE$$,#{state_id_nsw},-29.338403,153.002402), - ($$2469$$,$$BANYABBA$$,#{state_id_nsw},-29.338403,153.002402), - ($$2469$$,$$BEAN CREEK$$,#{state_id_nsw},-29.338403,153.002402), - ($$2469$$,$$BINGEEBEEBRA CREEK$$,#{state_id_nsw},-29.338403,153.002402), - ($$2469$$,$$BONALBO$$,#{state_id_nsw},-29.338403,153.002402), - ($$2469$$,$$BOTTLE CREEK$$,#{state_id_nsw},-29.338403,153.002402), - ($$2469$$,$$BULLDOG$$,#{state_id_nsw},-29.338403,153.002402), - ($$2469$$,$$BUNGAWALBIN$$,#{state_id_nsw},-29.338403,153.002402), - ($$2469$$,$$BUSBYS FLAT$$,#{state_id_nsw},-29.338403,153.002402), - ($$2469$$,$$CAMBRIDGE PLATEAU$$,#{state_id_nsw},-29.338403,153.002402), - ($$2469$$,$$CAMIRA$$,#{state_id_nsw},-29.338403,153.002402), - ($$2469$$,$$CAPEEN CREEK$$,#{state_id_nsw},-29.338403,153.002402), - ($$2469$$,$$CHATSWORTH$$,#{state_id_nsw},-29.338403,153.002402), - ($$2469$$,$$CLEARFIELD$$,#{state_id_nsw},-29.338403,153.002402), - ($$2469$$,$$COONGBAR$$,#{state_id_nsw},-29.338403,153.002402), - ($$2469$$,$$CULMARAN CREEK$$,#{state_id_nsw},-29.338403,153.002402), - ($$2469$$,$$DEEP CREEK$$,#{state_id_nsw},-29.338403,153.002402), - ($$2469$$,$$DRAKE$$,#{state_id_nsw},-29.338403,153.002402), - ($$2469$$,$$DRAKE VILLAGE$$,#{state_id_nsw},-29.338403,153.002402), - ($$2469$$,$$DUCK CREEK$$,#{state_id_nsw},-29.338403,153.002402), - ($$2469$$,$$EWINGAR$$,#{state_id_nsw},-29.338403,153.002402), - ($$2469$$,$$GIBBERAGEE$$,#{state_id_nsw},-29.338403,153.002402), - ($$2469$$,$$GOODWOOD ISLAND$$,#{state_id_nsw},-29.338403,153.002402), - ($$2469$$,$$GORGE CREEK$$,#{state_id_nsw},-29.338403,153.002402), - ($$2469$$,$$HAYSTACK$$,#{state_id_nsw},-29.338403,153.002402), - ($$2469$$,$$HOGARTH RANGE$$,#{state_id_nsw},-29.338403,153.002402), - ($$2469$$,$$JACKSONS FLAT$$,#{state_id_nsw},-29.338403,153.002402), - ($$2469$$,$$JOES BOX$$,#{state_id_nsw},-29.338403,153.002402), - ($$2469$$,$$KEYBARBIN$$,#{state_id_nsw},-29.338403,153.002402), - ($$2469$$,$$KIPPENDUFF$$,#{state_id_nsw},-29.338403,153.002402), - ($$2469$$,$$LOUISA CREEK$$,#{state_id_nsw},-29.338403,153.002402), - ($$2469$$,$$LOWER BOTTLE CREEK$$,#{state_id_nsw},-29.338403,153.002402), - ($$2469$$,$$LOWER DUCK CREEK$$,#{state_id_nsw},-29.338403,153.002402), - ($$2469$$,$$LOWER PEACOCK$$,#{state_id_nsw},-29.338403,153.002402), - ($$2469$$,$$MALLANGANEE$$,#{state_id_nsw},-29.338403,153.002402), - ($$2469$$,$$MOOKIMA WYBRA$$,#{state_id_nsw},-29.338403,153.002402), - ($$2469$$,$$MORORO$$,#{state_id_nsw},-29.338403,153.002402), - ($$2469$$,$$MOUNT MARSH$$,#{state_id_nsw},-29.338403,153.002402), - ($$2469$$,$$MUMMULGUM$$,#{state_id_nsw},-29.338403,153.002402), - ($$2469$$,$$MYRTLE CREEK$$,#{state_id_nsw},-29.338403,153.002402), - ($$2469$$,$$OLD BONALBO$$,#{state_id_nsw},-29.338403,153.002402), - ($$2469$$,$$PADDYS FLAT$$,#{state_id_nsw},-29.338403,153.002402), - ($$2469$$,$$PAGANS FLAT$$,#{state_id_nsw},-29.338403,153.002402), - ($$2469$$,$$PEACOCK CREEK$$,#{state_id_nsw},-29.338403,153.002402), - ($$2469$$,$$PIKAPENE$$,#{state_id_nsw},-29.338403,153.002402), - ($$2469$$,$$RAPPVILLE$$,#{state_id_nsw},-29.338403,153.002402), - ($$2469$$,$$SANDILANDS$$,#{state_id_nsw},-29.338403,153.002402), - ($$2469$$,$$SIMPKINS CREEK$$,#{state_id_nsw},-29.338403,153.002402), - ($$2469$$,$$SIX MILE SWAMP$$,#{state_id_nsw},-29.338403,153.002402), - ($$2469$$,$$TABULAM$$,#{state_id_nsw},-29.338403,153.002402), - ($$2469$$,$$THERESA CREEK$$,#{state_id_nsw},-29.338403,153.002402), - ($$2469$$,$$TUNGLEBUNG$$,#{state_id_nsw},-29.338403,153.002402), - ($$2469$$,$$UPPER DUCK CREEK$$,#{state_id_nsw},-29.338403,153.002402), - ($$2469$$,$$WARREGAH ISLAND$$,#{state_id_nsw},-29.338403,153.002402), - ($$2469$$,$$WHIPORIE$$,#{state_id_nsw},-29.338403,153.002402), - ($$2469$$,$$WOOMBAH$$,#{state_id_nsw},-29.338403,153.002402), - ($$2469$$,$$WYAN$$,#{state_id_nsw},-29.338403,153.002402), - ($$2469$$,$$YABBRA$$,#{state_id_nsw},-29.338403,153.002402), - ($$2470$$,$$BABYL CREEK$$,#{state_id_nsw},-28.730383,152.795939), - ($$2470$$,$$BACKMEDE$$,#{state_id_nsw},-28.730383,152.795939), - ($$2470$$,$$CASINO$$,#{state_id_nsw},-28.730383,152.795939), - ($$2470$$,$$COOMBELL$$,#{state_id_nsw},-28.730383,152.795939), - ($$2470$$,$$DOBIES BIGHT$$,#{state_id_nsw},-28.730383,152.795939), - ($$2470$$,$$DOUBTFUL CREEK$$,#{state_id_nsw},-28.730383,152.795939), - ($$2470$$,$$DYRAABA$$,#{state_id_nsw},-28.730383,152.795939), - ($$2470$$,$$ELLANGOWAN$$,#{state_id_nsw},-28.730383,152.795939), - ($$2470$$,$$FAIRY HILL$$,#{state_id_nsw},-28.730383,152.795939), - ($$2470$$,$$IRVINGTON$$,#{state_id_nsw},-28.730383,152.795939), - ($$2470$$,$$LEEVILLE$$,#{state_id_nsw},-28.730383,152.795939), - ($$2470$$,$$LOWER DYRAABA$$,#{state_id_nsw},-28.730383,152.795939), - ($$2470$$,$$MONGOGARIE$$,#{state_id_nsw},-28.730383,152.795939), - ($$2470$$,$$NAUGHTONS GAP$$,#{state_id_nsw},-28.730383,152.795939), - ($$2470$$,$$NORTH CASINO$$,#{state_id_nsw},-28.730383,152.795939), - ($$2470$$,$$PIORA$$,#{state_id_nsw},-28.730383,152.795939), - ($$2470$$,$$SEXTONVILLE$$,#{state_id_nsw},-28.730383,152.795939), - ($$2470$$,$$SHANNON BROOK$$,#{state_id_nsw},-28.730383,152.795939), - ($$2470$$,$$SPRING GROVE$$,#{state_id_nsw},-28.730383,152.795939), - ($$2470$$,$$STRATHEDEN$$,#{state_id_nsw},-28.730383,152.795939), - ($$2470$$,$$UPPER MONGOGARIE$$,#{state_id_nsw},-28.730383,152.795939), - ($$2470$$,$$WOODVIEW$$,#{state_id_nsw},-28.730383,152.795939), - ($$2470$$,$$WOOLNERS ARM$$,#{state_id_nsw},-28.730383,152.795939), - ($$2470$$,$$YORKLEA$$,#{state_id_nsw},-28.730383,152.795939), - ($$2471$$,$$BORA RIDGE$$,#{state_id_nsw},-29.045091,153.226754), - ($$2471$$,$$CODRINGTON$$,#{state_id_nsw},-29.045091,153.226754), - ($$2471$$,$$CORAKI$$,#{state_id_nsw},-29.045091,153.226754), - ($$2471$$,$$EAST CORAKI$$,#{state_id_nsw},-29.045091,153.226754), - ($$2471$$,$$GREEN FOREST$$,#{state_id_nsw},-29.045091,153.226754), - ($$2471$$,$$GREENRIDGE$$,#{state_id_nsw},-29.045091,153.226754), - ($$2471$$,$$NORTH WOODBURN$$,#{state_id_nsw},-29.045091,153.226754), - ($$2471$$,$$SWAN BAY$$,#{state_id_nsw},-29.045091,153.226754), - ($$2471$$,$$TATHAM$$,#{state_id_nsw},-29.045091,153.226754), - ($$2471$$,$$WEST CORAKI$$,#{state_id_nsw},-29.045091,153.226754), - ($$2472$$,$$BROADWATER$$,#{state_id_nsw},-29.010351,153.435446), - ($$2472$$,$$BUCKENDOON$$,#{state_id_nsw},-29.010351,153.435446), - ($$2472$$,$$ESK$$,#{state_id_nsw},-29.010351,153.435446), - ($$2472$$,$$KILGIN$$,#{state_id_nsw},-29.010351,153.435446), - ($$2472$$,$$MOONEM$$,#{state_id_nsw},-29.010351,153.435446), - ($$2472$$,$$NEW ITALY$$,#{state_id_nsw},-29.010351,153.435446), - ($$2472$$,$$RILEYS HILL$$,#{state_id_nsw},-29.010351,153.435446), - ($$2472$$,$$TABBIMOBLE$$,#{state_id_nsw},-29.010351,153.435446), - ($$2472$$,$$THE GAP$$,#{state_id_nsw},-29.010351,153.435446), - ($$2472$$,$$TRUSTUMS HILL$$,#{state_id_nsw},-29.010351,153.435446), - ($$2472$$,$$WOODBURN$$,#{state_id_nsw},-29.010351,153.435446), - ($$2473$$,$$BUNDJALUNG$$,#{state_id_nsw},-29.086621,153.375933), - ($$2473$$,$$DOONBAH$$,#{state_id_nsw},-29.086621,153.375933), - ($$2473$$,$$EVANS HEAD$$,#{state_id_nsw},-29.086621,153.375933), - ($$2473$$,$$IRON GATES$$,#{state_id_nsw},-29.086621,153.375933), - ($$2473$$,$$SOUTH EVANS HEAD$$,#{state_id_nsw},-29.086621,153.375933), - ($$2474$$,$$AFTERLEE$$,#{state_id_nsw},-28.593876,152.825754), - ($$2474$$,$$BARKERS VALE$$,#{state_id_nsw},-28.593876,152.825754), - ($$2474$$,$$BORDER RANGES$$,#{state_id_nsw},-28.593876,152.825754), - ($$2474$$,$$CAWONGLA$$,#{state_id_nsw},-28.593876,152.825754), - ($$2474$$,$$CEDAR POINT$$,#{state_id_nsw},-28.593876,152.825754), - ($$2474$$,$$COLLINS CREEK$$,#{state_id_nsw},-28.593876,152.825754), - ($$2474$$,$$COUGAL$$,#{state_id_nsw},-28.593876,152.825754), - ($$2474$$,$$DAIRY FLAT$$,#{state_id_nsw},-28.593876,152.825754), - ($$2474$$,$$EDEN CREEK$$,#{state_id_nsw},-28.593876,152.825754), - ($$2474$$,$$EDENVILLE$$,#{state_id_nsw},-28.593876,152.825754), - ($$2474$$,$$ETTRICK$$,#{state_id_nsw},-28.593876,152.825754), - ($$2474$$,$$FAWCETTS PLAIN$$,#{state_id_nsw},-28.593876,152.825754), - ($$2474$$,$$FINDON CREEK$$,#{state_id_nsw},-28.593876,152.825754), - ($$2474$$,$$GENEVA$$,#{state_id_nsw},-28.593876,152.825754), - ($$2474$$,$$GHINNI GHI$$,#{state_id_nsw},-28.593876,152.825754), - ($$2474$$,$$GRADYS CREEK$$,#{state_id_nsw},-28.593876,152.825754), - ($$2474$$,$$GREEN PIGEON$$,#{state_id_nsw},-28.593876,152.825754), - ($$2474$$,$$GREVILLIA$$,#{state_id_nsw},-28.593876,152.825754), - ($$2474$$,$$HOMELEIGH$$,#{state_id_nsw},-28.593876,152.825754), - ($$2474$$,$$HORSE STATION CREEK$$,#{state_id_nsw},-28.593876,152.825754), - ($$2474$$,$$HORSESHOE CREEK$$,#{state_id_nsw},-28.593876,152.825754), - ($$2474$$,$$IRON POT CREEK$$,#{state_id_nsw},-28.593876,152.825754), - ($$2474$$,$$KILGRA$$,#{state_id_nsw},-28.593876,152.825754), - ($$2474$$,$$KYOGLE$$,#{state_id_nsw},-28.593876,152.825754), - ($$2474$$,$$LITTLE BACK CREEK$$,#{state_id_nsw},-28.593876,152.825754), - ($$2474$$,$$LOADSTONE$$,#{state_id_nsw},-28.593876,152.825754), - ($$2474$$,$$LYNCHS CREEK$$,#{state_id_nsw},-28.593876,152.825754), - ($$2474$$,$$NEW PARK$$,#{state_id_nsw},-28.593876,152.825754), - ($$2474$$,$$OLD GREVILLIA$$,#{state_id_nsw},-28.593876,152.825754), - ($$2474$$,$$ROSEBERRY$$,#{state_id_nsw},-28.593876,152.825754), - ($$2474$$,$$ROSEBERRY CREEK$$,#{state_id_nsw},-28.593876,152.825754), - ($$2474$$,$$RUKENVALE$$,#{state_id_nsw},-28.593876,152.825754), - ($$2474$$,$$SAWPIT CREEK$$,#{state_id_nsw},-28.593876,152.825754), - ($$2474$$,$$SHERWOOD$$,#{state_id_nsw},-28.593876,152.825754), - ($$2474$$,$$SMITHS CREEK$$,#{state_id_nsw},-28.593876,152.825754), - ($$2474$$,$$TERRACE CREEK$$,#{state_id_nsw},-28.593876,152.825754), - ($$2474$$,$$THE RISK$$,#{state_id_nsw},-28.593876,152.825754), - ($$2474$$,$$TOONUMBAR$$,#{state_id_nsw},-28.593876,152.825754), - ($$2474$$,$$UNUMGAR$$,#{state_id_nsw},-28.593876,152.825754), - ($$2474$$,$$UPPER EDEN CREEK$$,#{state_id_nsw},-28.593876,152.825754), - ($$2474$$,$$UPPER HORSESHOE CREEK$$,#{state_id_nsw},-28.593876,152.825754), - ($$2474$$,$$WADEVILLE$$,#{state_id_nsw},-28.593876,152.825754), - ($$2474$$,$$WARRAZAMBIL CREEK$$,#{state_id_nsw},-28.593876,152.825754), - ($$2474$$,$$WEST WIANGAREE$$,#{state_id_nsw},-28.593876,152.825754), - ($$2474$$,$$WIANGAREE$$,#{state_id_nsw},-28.593876,152.825754), - ($$2474$$,$$WYNEDEN$$,#{state_id_nsw},-28.593876,152.825754), - ($$2475$$,$$TOOLOOM$$,#{state_id_nsw},-28.622045,152.420365), - ($$2475$$,$$UPPER TOOLOOM$$,#{state_id_nsw},-28.622045,152.420365), - ($$2475$$,$$URBENVILLE$$,#{state_id_nsw},-28.622045,152.420365), - ($$2476$$,$$BOOMI CREEK$$,#{state_id_nsw},-28.373331,152.320879), - ($$2476$$,$$BRUMBY PLAINS$$,#{state_id_nsw},-28.373331,152.320879), - ($$2476$$,$$KOREELAH$$,#{state_id_nsw},-28.373331,152.320879), - ($$2476$$,$$LEGUME$$,#{state_id_nsw},-28.373331,152.320879), - ($$2476$$,$$LINDESAY CREEK$$,#{state_id_nsw},-28.373331,152.320879), - ($$2476$$,$$MULI MULI$$,#{state_id_nsw},-28.373331,152.320879), - ($$2476$$,$$OLD KOREELAH$$,#{state_id_nsw},-28.373331,152.320879), - ($$2476$$,$$THE GLEN$$,#{state_id_nsw},-28.373331,152.320879), - ($$2476$$,$$WOODENBONG$$,#{state_id_nsw},-28.373331,152.320879), - ($$2477$$,$$ALSTONVALE$$,#{state_id_nsw},-28.805607,153.445477), - ($$2477$$,$$ALSTONVILLE$$,#{state_id_nsw},-28.805607,153.445477), - ($$2477$$,$$BAGOTVILLE$$,#{state_id_nsw},-28.805607,153.445477), - ($$2477$$,$$CABBAGE TREE ISLAND$$,#{state_id_nsw},-28.805607,153.445477), - ($$2477$$,$$DALWOOD$$,#{state_id_nsw},-28.805607,153.445477), - ($$2477$$,$$EAST WARDELL$$,#{state_id_nsw},-28.805607,153.445477), - ($$2477$$,$$GOAT ISLAND$$,#{state_id_nsw},-28.805607,153.445477), - ($$2477$$,$$LYNWOOD$$,#{state_id_nsw},-28.805607,153.445477), - ($$2477$$,$$MEERSCHAUM VALE$$,#{state_id_nsw},-28.805607,153.445477), - ($$2477$$,$$PEARCES CREEK$$,#{state_id_nsw},-28.805607,153.445477), - ($$2477$$,$$ROUS$$,#{state_id_nsw},-28.805607,153.445477), - ($$2477$$,$$ROUS MILL$$,#{state_id_nsw},-28.805607,153.445477), - ($$2477$$,$$TUCKOMBIL$$,#{state_id_nsw},-28.805607,153.445477), - ($$2477$$,$$URALBA$$,#{state_id_nsw},-28.805607,153.445477), - ($$2477$$,$$WARDELL$$,#{state_id_nsw},-28.805607,153.445477), - ($$2477$$,$$WOLLONGBAR$$,#{state_id_nsw},-28.805607,153.445477), - ($$2478$$,$$BALLINA$$,#{state_id_nsw},-28.869984,153.559167), - ($$2478$$,$$COOLGARDIE$$,#{state_id_nsw},-28.869984,153.559167), - ($$2478$$,$$CUMBALUM$$,#{state_id_nsw},-28.869984,153.559167), - ($$2478$$,$$EAST BALLINA$$,#{state_id_nsw},-28.869984,153.559167), - ($$2478$$,$$EMPIRE VALE$$,#{state_id_nsw},-28.869984,153.559167), - ($$2478$$,$$KEITH HALL$$,#{state_id_nsw},-28.869984,153.559167), - ($$2478$$,$$LENNOX HEAD$$,#{state_id_nsw},-28.869984,153.559167), - ($$2478$$,$$PATCHS BEACH$$,#{state_id_nsw},-28.869984,153.559167), - ($$2478$$,$$PIMLICO$$,#{state_id_nsw},-28.869984,153.559167), - ($$2478$$,$$PIMLICO ISLAND$$,#{state_id_nsw},-28.869984,153.559167), - ($$2478$$,$$SKENNARS HEAD$$,#{state_id_nsw},-28.869984,153.559167), - ($$2478$$,$$SOUTH BALLINA$$,#{state_id_nsw},-28.869984,153.559167), - ($$2478$$,$$TEVEN$$,#{state_id_nsw},-28.869984,153.559167), - ($$2478$$,$$TINTENBAR$$,#{state_id_nsw},-28.869984,153.559167), - ($$2478$$,$$WEST BALLINA$$,#{state_id_nsw},-28.869984,153.559167), - ($$2479$$,$$BANGALOW$$,#{state_id_nsw},-28.686356,153.524792), - ($$2479$$,$$BINNA BURRA$$,#{state_id_nsw},-28.686356,153.524792), - ($$2479$$,$$BROOKLET$$,#{state_id_nsw},-28.686356,153.524792), - ($$2479$$,$$COOPERS SHOOT$$,#{state_id_nsw},-28.686356,153.524792), - ($$2479$$,$$COORABELL$$,#{state_id_nsw},-28.686356,153.524792), - ($$2479$$,$$FERNLEIGH$$,#{state_id_nsw},-28.686356,153.524792), - ($$2479$$,$$KNOCKROW$$,#{state_id_nsw},-28.686356,153.524792), - ($$2479$$,$$MCLEODS SHOOT$$,#{state_id_nsw},-28.686356,153.524792), - ($$2479$$,$$NASHUA$$,#{state_id_nsw},-28.686356,153.524792), - ($$2479$$,$$NEWRYBAR$$,#{state_id_nsw},-28.686356,153.524792), - ($$2479$$,$$POSSUM CREEK$$,#{state_id_nsw},-28.686356,153.524792), - ($$2480$$,$$BENTLEY$$,#{state_id_nsw},-28.61462,153.037662), - ($$2480$$,$$BEXHILL$$,#{state_id_nsw},-28.61462,153.037662), - ($$2480$$,$$BLAKEBROOK$$,#{state_id_nsw},-28.61462,153.037662), - ($$2480$$,$$BLUE KNOB$$,#{state_id_nsw},-28.61462,153.037662), - ($$2480$$,$$BOAT HARBOUR$$,#{state_id_nsw},-28.61462,153.037662), - ($$2480$$,$$BOOERIE CREEK$$,#{state_id_nsw},-28.61462,153.037662), - ($$2480$$,$$BOORABEE PARK$$,#{state_id_nsw},-28.61462,153.037662), - ($$2480$$,$$BOOYONG$$,#{state_id_nsw},-28.61462,153.037662), - ($$2480$$,$$BUNGABBEE$$,#{state_id_nsw},-28.61462,153.037662), - ($$2480$$,$$CANIABA$$,#{state_id_nsw},-28.61462,153.037662), - ($$2480$$,$$CHILCOTTS GRASS$$,#{state_id_nsw},-28.61462,153.037662), - ($$2480$$,$$CLOVASS$$,#{state_id_nsw},-28.61462,153.037662), - ($$2480$$,$$CLUNES$$,#{state_id_nsw},-28.61462,153.037662), - ($$2480$$,$$COFFEE CAMP$$,#{state_id_nsw},-28.61462,153.037662), - ($$2480$$,$$CORNDALE$$,#{state_id_nsw},-28.61462,153.037662), - ($$2480$$,$$DORROUGHBY$$,#{state_id_nsw},-28.61462,153.037662), - ($$2480$$,$$DUNGARUBBA$$,#{state_id_nsw},-28.61462,153.037662), - ($$2480$$,$$DUNOON$$,#{state_id_nsw},-28.61462,153.037662), - ($$2480$$,$$EAST LISMORE$$,#{state_id_nsw},-28.61462,153.037662), - ($$2480$$,$$ELTHAM$$,#{state_id_nsw},-28.61462,153.037662), - ($$2480$$,$$EUREKA$$,#{state_id_nsw},-28.61462,153.037662), - ($$2480$$,$$FEDERAL$$,#{state_id_nsw},-28.61462,153.037662), - ($$2480$$,$$FERNSIDE$$,#{state_id_nsw},-28.61462,153.037662), - ($$2480$$,$$GEORGICA$$,#{state_id_nsw},-28.61462,153.037662), - ($$2480$$,$$GIRARDS HILL$$,#{state_id_nsw},-28.61462,153.037662), - ($$2480$$,$$GOOLMANGAR$$,#{state_id_nsw},-28.61462,153.037662), - ($$2480$$,$$GOONELLABAH$$,#{state_id_nsw},-28.61462,153.037662), - ($$2480$$,$$HOWARDS GRASS$$,#{state_id_nsw},-28.61462,153.037662), - ($$2480$$,$$JIGGI$$,#{state_id_nsw},-28.61462,153.037662), - ($$2480$$,$$KEERRONG$$,#{state_id_nsw},-28.61462,153.037662), - ($$2480$$,$$KOONORIGAN$$,#{state_id_nsw},-28.61462,153.037662), - ($$2480$$,$$LAGOON GRASS$$,#{state_id_nsw},-28.61462,153.037662), - ($$2480$$,$$LARNOOK$$,#{state_id_nsw},-28.61462,153.037662), - ($$2480$$,$$LEYCESTER$$,#{state_id_nsw},-28.61462,153.037662), - ($$2480$$,$$LILLIAN ROCK$$,#{state_id_nsw},-28.61462,153.037662), - ($$2480$$,$$LINDENDALE$$,#{state_id_nsw},-28.61462,153.037662), - ($$2480$$,$$LISMORE$$,#{state_id_nsw},-28.61462,153.037662), - ($$2480$$,$$LISMORE HEIGHTS$$,#{state_id_nsw},-28.61462,153.037662), - ($$2480$$,$$LOFTVILLE$$,#{state_id_nsw},-28.61462,153.037662), - ($$2480$$,$$MAROM CREEK$$,#{state_id_nsw},-28.61462,153.037662), - ($$2480$$,$$MCKEES HILL$$,#{state_id_nsw},-28.61462,153.037662), - ($$2480$$,$$MCLEANS RIDGES$$,#{state_id_nsw},-28.61462,153.037662), - ($$2480$$,$$MODANVILLE$$,#{state_id_nsw},-28.61462,153.037662), - ($$2480$$,$$MONALTRIE$$,#{state_id_nsw},-28.61462,153.037662), - ($$2480$$,$$MOUNTAIN TOP$$,#{state_id_nsw},-28.61462,153.037662), - ($$2480$$,$$NIGHTCAP$$,#{state_id_nsw},-28.61462,153.037662), - ($$2480$$,$$NIMBIN$$,#{state_id_nsw},-28.61462,153.037662), - ($$2480$$,$$NORTH LISMORE$$,#{state_id_nsw},-28.61462,153.037662), - ($$2480$$,$$NUMULGI$$,#{state_id_nsw},-28.61462,153.037662), - ($$2480$$,$$REPENTANCE CREEK$$,#{state_id_nsw},-28.61462,153.037662), - ($$2480$$,$$RICHMOND HILL$$,#{state_id_nsw},-28.61462,153.037662), - ($$2480$$,$$ROCK VALLEY$$,#{state_id_nsw},-28.61462,153.037662), - ($$2480$$,$$ROSEBANK$$,#{state_id_nsw},-28.61462,153.037662), - ($$2480$$,$$RUTHVEN$$,#{state_id_nsw},-28.61462,153.037662), - ($$2480$$,$$SOUTH GUNDURIMBA$$,#{state_id_nsw},-28.61462,153.037662), - ($$2480$$,$$SOUTH LISMORE$$,#{state_id_nsw},-28.61462,153.037662), - ($$2480$$,$$STONY CHUTE$$,#{state_id_nsw},-28.61462,153.037662), - ($$2480$$,$$TERANIA CREEK$$,#{state_id_nsw},-28.61462,153.037662), - ($$2480$$,$$THE CHANNON$$,#{state_id_nsw},-28.61462,153.037662), - ($$2480$$,$$TREGEAGLE$$,#{state_id_nsw},-28.61462,153.037662), - ($$2480$$,$$TUCKI TUCKI$$,#{state_id_nsw},-28.61462,153.037662), - ($$2480$$,$$TUCKURIMBA$$,#{state_id_nsw},-28.61462,153.037662), - ($$2480$$,$$TULLERA$$,#{state_id_nsw},-28.61462,153.037662), - ($$2480$$,$$TUNCESTER$$,#{state_id_nsw},-28.61462,153.037662), - ($$2480$$,$$TUNTABLE CREEK$$,#{state_id_nsw},-28.61462,153.037662), - ($$2480$$,$$WHIAN WHIAN$$,#{state_id_nsw},-28.61462,153.037662), - ($$2480$$,$$WOODLAWN$$,#{state_id_nsw},-28.61462,153.037662), - ($$2480$$,$$WYRALLAH$$,#{state_id_nsw},-28.61462,153.037662), - ($$2481$$,$$BROKEN HEAD$$,#{state_id_nsw},-28.717235,153.592296), - ($$2481$$,$$BYRON BAY$$,#{state_id_nsw},-28.717235,153.592296), - ($$2481$$,$$EWINGSDALE$$,#{state_id_nsw},-28.717235,153.592296), - ($$2481$$,$$HAYTERS HILL$$,#{state_id_nsw},-28.717235,153.592296), - ($$2481$$,$$MYOCUM$$,#{state_id_nsw},-28.717235,153.592296), - ($$2481$$,$$SKINNERS SHOOT$$,#{state_id_nsw},-28.717235,153.592296), - ($$2481$$,$$SUFFOLK PARK$$,#{state_id_nsw},-28.717235,153.592296), - ($$2481$$,$$TALOFA$$,#{state_id_nsw},-28.717235,153.592296), - ($$2481$$,$$TYAGARAH$$,#{state_id_nsw},-28.717235,153.592296), - ($$2482$$,$$GOONENGERRY$$,#{state_id_nsw},-28.61079,153.439674), - ($$2482$$,$$HUONBROOK$$,#{state_id_nsw},-28.61079,153.439674), - ($$2482$$,$$KOONYUM RANGE$$,#{state_id_nsw},-28.61079,153.439674), - ($$2482$$,$$MAIN ARM$$,#{state_id_nsw},-28.61079,153.439674), - ($$2482$$,$$MONTECOLLUM$$,#{state_id_nsw},-28.61079,153.439674), - ($$2482$$,$$MULLUMBIMBY$$,#{state_id_nsw},-28.61079,153.439674), - ($$2482$$,$$MULLUMBIMBY CREEK$$,#{state_id_nsw},-28.61079,153.439674), - ($$2482$$,$$PALMWOODS$$,#{state_id_nsw},-28.61079,153.439674), - ($$2482$$,$$UPPER COOPERS CREEK$$,#{state_id_nsw},-28.61079,153.439674), - ($$2482$$,$$UPPER MAIN ARM$$,#{state_id_nsw},-28.61079,153.439674), - ($$2482$$,$$UPPER WILSONS CREEK$$,#{state_id_nsw},-28.61079,153.439674), - ($$2482$$,$$WANGANUI$$,#{state_id_nsw},-28.61079,153.439674), - ($$2482$$,$$WILSONS CREEK$$,#{state_id_nsw},-28.61079,153.439674), - ($$2483$$,$$BILLINUDGEL$$,#{state_id_nsw},-28.504114,153.528274), - ($$2483$$,$$BRUNSWICK HEADS$$,#{state_id_nsw},-28.504114,153.528274), - ($$2483$$,$$BURRINGBAR$$,#{state_id_nsw},-28.504114,153.528274), - ($$2483$$,$$CRABBES CREEK$$,#{state_id_nsw},-28.504114,153.528274), - ($$2483$$,$$MIDDLE POCKET$$,#{state_id_nsw},-28.504114,153.528274), - ($$2483$$,$$MOOBALL$$,#{state_id_nsw},-28.504114,153.528274), - ($$2483$$,$$NEW BRIGHTON$$,#{state_id_nsw},-28.504114,153.528274), - ($$2483$$,$$OCEAN SHORES$$,#{state_id_nsw},-28.504114,153.528274), - ($$2483$$,$$SLEEPY HOLLOW$$,#{state_id_nsw},-28.504114,153.528274), - ($$2483$$,$$SOUTH GOLDEN BEACH$$,#{state_id_nsw},-28.504114,153.528274), - ($$2483$$,$$THE POCKET$$,#{state_id_nsw},-28.504114,153.528274), - ($$2483$$,$$UPPER BURRINGBAR$$,#{state_id_nsw},-28.504114,153.528274), - ($$2483$$,$$WOOYUNG$$,#{state_id_nsw},-28.504114,153.528274), - ($$2483$$,$$YELGUN$$,#{state_id_nsw},-28.504114,153.528274), - ($$2484$$,$$BACK CREEK$$,#{state_id_nsw},-28.40351,153.171821), - ($$2484$$,$$BRAY PARK$$,#{state_id_nsw},-28.40351,153.171821), - ($$2484$$,$$BRAYS CREEK$$,#{state_id_nsw},-28.40351,153.171821), - ($$2484$$,$$BYANGUM$$,#{state_id_nsw},-28.40351,153.171821), - ($$2484$$,$$BYRRILL CREEK$$,#{state_id_nsw},-28.40351,153.171821), - ($$2484$$,$$CEDAR CREEK$$,#{state_id_nsw},-28.40351,153.171821), - ($$2484$$,$$CHILLINGHAM$$,#{state_id_nsw},-28.40351,153.171821), - ($$2484$$,$$CHOWAN CREEK$$,#{state_id_nsw},-28.40351,153.171821), - ($$2484$$,$$CLOTHIERS CREEK$$,#{state_id_nsw},-28.40351,153.171821), - ($$2484$$,$$COMMISSIONERS CREEK$$,#{state_id_nsw},-28.40351,153.171821), - ($$2484$$,$$CONDONG$$,#{state_id_nsw},-28.40351,153.171821), - ($$2484$$,$$CRYSTAL CREEK$$,#{state_id_nsw},-28.40351,153.171821), - ($$2484$$,$$CUDGERA CREEK$$,#{state_id_nsw},-28.40351,153.171821), - ($$2484$$,$$DOON DOON$$,#{state_id_nsw},-28.40351,153.171821), - ($$2484$$,$$DULGUIGAN$$,#{state_id_nsw},-28.40351,153.171821), - ($$2484$$,$$DUM DUM$$,#{state_id_nsw},-28.40351,153.171821), - ($$2484$$,$$DUNBIBLE$$,#{state_id_nsw},-28.40351,153.171821), - ($$2484$$,$$DUNGAY$$,#{state_id_nsw},-28.40351,153.171821), - ($$2484$$,$$EUNGELLA$$,#{state_id_nsw},-28.40351,153.171821), - ($$2484$$,$$EVIRON$$,#{state_id_nsw},-28.40351,153.171821), - ($$2484$$,$$FARRANTS HILL$$,#{state_id_nsw},-28.40351,153.171821), - ($$2484$$,$$FERNVALE$$,#{state_id_nsw},-28.40351,153.171821), - ($$2484$$,$$HOPKINS CREEK$$,#{state_id_nsw},-28.40351,153.171821), - ($$2484$$,$$KIELVALE$$,#{state_id_nsw},-28.40351,153.171821), - ($$2484$$,$$KUNGHUR$$,#{state_id_nsw},-28.40351,153.171821), - ($$2484$$,$$KUNGHUR CREEK$$,#{state_id_nsw},-28.40351,153.171821), - ($$2484$$,$$KYNNUMBOON$$,#{state_id_nsw},-28.40351,153.171821), - ($$2484$$,$$LIMPINWOOD$$,#{state_id_nsw},-28.40351,153.171821), - ($$2484$$,$$MEBBIN$$,#{state_id_nsw},-28.40351,153.171821), - ($$2484$$,$$MIDGINBIL$$,#{state_id_nsw},-28.40351,153.171821), - ($$2484$$,$$MOUNT BURRELL$$,#{state_id_nsw},-28.40351,153.171821), - ($$2484$$,$$MOUNT WARNING$$,#{state_id_nsw},-28.40351,153.171821), - ($$2484$$,$$MURWILLUMBAH$$,#{state_id_nsw},-28.40351,153.171821), - ($$2484$$,$$MURWILLUMBAH SOUTH$$,#{state_id_nsw},-28.40351,153.171821), - ($$2484$$,$$NOBBYS CREEK$$,#{state_id_nsw},-28.40351,153.171821), - ($$2484$$,$$NORTH ARM$$,#{state_id_nsw},-28.40351,153.171821), - ($$2484$$,$$NUMINBAH$$,#{state_id_nsw},-28.40351,153.171821), - ($$2484$$,$$NUNDERI$$,#{state_id_nsw},-28.40351,153.171821), - ($$2484$$,$$PALMVALE$$,#{state_id_nsw},-28.40351,153.171821), - ($$2484$$,$$PUMPENBIL$$,#{state_id_nsw},-28.40351,153.171821), - ($$2484$$,$$RESERVE CREEK$$,#{state_id_nsw},-28.40351,153.171821), - ($$2484$$,$$ROUND MOUNTAIN$$,#{state_id_nsw},-28.40351,153.171821), - ($$2484$$,$$ROWLANDS CREEK$$,#{state_id_nsw},-28.40351,153.171821), - ($$2484$$,$$SMITHS CREEK$$,#{state_id_nsw},-28.40351,153.171821), - ($$2484$$,$$SOUTH MURWILLUMBAH$$,#{state_id_nsw},-28.40351,153.171821), - ($$2484$$,$$STOKERS SIDING$$,#{state_id_nsw},-28.40351,153.171821), - ($$2484$$,$$TERRAGON$$,#{state_id_nsw},-28.40351,153.171821), - ($$2484$$,$$TOMEWIN$$,#{state_id_nsw},-28.40351,153.171821), - ($$2484$$,$$TYALGUM$$,#{state_id_nsw},-28.40351,153.171821), - ($$2484$$,$$TYALGUM CREEK$$,#{state_id_nsw},-28.40351,153.171821), - ($$2484$$,$$TYGALGAH$$,#{state_id_nsw},-28.40351,153.171821), - ($$2484$$,$$UKI$$,#{state_id_nsw},-28.40351,153.171821), - ($$2484$$,$$UPPER CRYSTAL CREEK$$,#{state_id_nsw},-28.40351,153.171821), - ($$2484$$,$$URLIUP$$,#{state_id_nsw},-28.40351,153.171821), - ($$2484$$,$$WARDROP VALLEY$$,#{state_id_nsw},-28.40351,153.171821), - ($$2484$$,$$ZARA$$,#{state_id_nsw},-28.40351,153.171821), - ($$2485$$,$$TWEED HEADS$$,#{state_id_nsw},-28.177537,153.538538), - ($$2485$$,$$TWEED HEADS WEST$$,#{state_id_nsw},-28.177537,153.538538), - ($$2486$$,$$BANORA POINT$$,#{state_id_nsw},-28.213363,153.535999), - ($$2486$$,$$BILAMBIL$$,#{state_id_nsw},-28.213363,153.535999), - ($$2486$$,$$BILAMBIL HEIGHTS$$,#{state_id_nsw},-28.213363,153.535999), - ($$2486$$,$$BUNGALORA$$,#{state_id_nsw},-28.213363,153.535999), - ($$2486$$,$$CAROOL$$,#{state_id_nsw},-28.213363,153.535999), - ($$2486$$,$$COBAKI$$,#{state_id_nsw},-28.213363,153.535999), - ($$2486$$,$$COBAKI LAKES$$,#{state_id_nsw},-28.213363,153.535999), - ($$2486$$,$$DUROBY$$,#{state_id_nsw},-28.213363,153.535999), - ($$2486$$,$$GLENGARRIE$$,#{state_id_nsw},-28.213363,153.535999), - ($$2486$$,$$PIGGABEEN$$,#{state_id_nsw},-28.213363,153.535999), - ($$2486$$,$$TERRANORA$$,#{state_id_nsw},-28.213363,153.535999), - ($$2486$$,$$TWEED HEADS SOUTH$$,#{state_id_nsw},-28.213363,153.535999), - ($$2486$$,$$UPPER DUROBY$$,#{state_id_nsw},-28.213363,153.535999), - ($$2487$$,$$CASUARINA$$,#{state_id_nsw},-28.296652,153.571274), - ($$2487$$,$$CHINDERAH$$,#{state_id_nsw},-28.296652,153.571274), - ($$2487$$,$$CUDGEN$$,#{state_id_nsw},-28.296652,153.571274), - ($$2487$$,$$DURANBAH$$,#{state_id_nsw},-28.296652,153.571274), - ($$2487$$,$$FINGAL HEAD$$,#{state_id_nsw},-28.296652,153.571274), - ($$2487$$,$$KINGS FOREST$$,#{state_id_nsw},-28.296652,153.571274), - ($$2487$$,$$KINGSCLIFF$$,#{state_id_nsw},-28.296652,153.571274), - ($$2487$$,$$STOTTS CREEK$$,#{state_id_nsw},-28.296652,153.571274), - ($$2488$$,$$BOGANGAR$$,#{state_id_nsw},-28.332381,153.542241), - ($$2488$$,$$CABARITA BEACH$$,#{state_id_nsw},-28.332381,153.542241), - ($$2488$$,$$TANGLEWOOD$$,#{state_id_nsw},-28.332381,153.542241), - ($$2489$$,$$HASTINGS POINT$$,#{state_id_nsw},-28.361966,153.576246), - ($$2489$$,$$POTTSVILLE$$,#{state_id_nsw},-28.361966,153.576246), - ($$2489$$,$$POTTSVILLE BEACH$$,#{state_id_nsw},-28.361966,153.576246), - ($$2490$$,$$NORTH TUMBULGUM$$,#{state_id_nsw},-28.26807,153.470406), - ($$2490$$,$$TUMBULGUM$$,#{state_id_nsw},-28.26807,153.470406), - ($$2500$$,$$CONISTON$$,#{state_id_nsw},-34.436545,150.885559), - ($$2500$$,$$GWYNNEVILLE$$,#{state_id_nsw},-34.436545,150.885559), - ($$2500$$,$$KEIRAVILLE$$,#{state_id_nsw},-34.436545,150.885559), - ($$2500$$,$$MANGERTON$$,#{state_id_nsw},-34.436545,150.885559), - ($$2500$$,$$MOUNT KEIRA$$,#{state_id_nsw},-34.436545,150.885559), - ($$2500$$,$$MOUNT SAINT THOMAS$$,#{state_id_nsw},-34.436545,150.885559), - ($$2500$$,$$NORTH WOLLONGONG$$,#{state_id_nsw},-34.436545,150.885559), - ($$2500$$,$$SPRING HILL$$,#{state_id_nsw},-34.436545,150.885559), - ($$2500$$,$$WEST WOLLONGONG$$,#{state_id_nsw},-34.436545,150.885559), - ($$2500$$,$$WOLLONGONG$$,#{state_id_nsw},-34.436545,150.885559), - ($$2500$$,$$WOLLONGONG DC$$,#{state_id_nsw},-34.436545,150.885559), - ($$2500$$,$$WOLLONGONG WEST$$,#{state_id_nsw},-34.436545,150.885559), - ($$2502$$,$$CRINGILA$$,#{state_id_nsw},-34.471575,150.871375), - ($$2502$$,$$LAKE HEIGHTS$$,#{state_id_nsw},-34.471575,150.871375), - ($$2502$$,$$PRIMBEE$$,#{state_id_nsw},-34.471575,150.871375), - ($$2502$$,$$WARRAWONG$$,#{state_id_nsw},-34.471575,150.871375), - ($$2505$$,$$PORT KEMBLA$$,#{state_id_nsw},-34.493252,150.892832), - ($$2506$$,$$BERKELEY$$,#{state_id_nsw},-34.481408,150.844147), - ($$2508$$,$$COALCLIFF$$,#{state_id_nsw},-34.243453,150.976081), - ($$2508$$,$$DARKES FOREST$$,#{state_id_nsw},-34.243453,150.976081), - ($$2508$$,$$HELENSBURGH$$,#{state_id_nsw},-34.243453,150.976081), - ($$2508$$,$$LILYVALE$$,#{state_id_nsw},-34.243453,150.976081), - ($$2508$$,$$MADDENS PLAINS$$,#{state_id_nsw},-34.243453,150.976081), - ($$2508$$,$$OTFORD$$,#{state_id_nsw},-34.243453,150.976081), - ($$2508$$,$$STANWELL PARK$$,#{state_id_nsw},-34.243453,150.976081), - ($$2508$$,$$STANWELL TOPS$$,#{state_id_nsw},-34.243453,150.976081), - ($$2508$$,$$WORONORA DAM$$,#{state_id_nsw},-34.243453,150.976081), - ($$2515$$,$$AUSTINMER$$,#{state_id_nsw},-34.306283,150.934563), - ($$2515$$,$$CLIFTON$$,#{state_id_nsw},-34.306283,150.934563), - ($$2515$$,$$COLEDALE$$,#{state_id_nsw},-34.306283,150.934563), - ($$2515$$,$$SCARBOROUGH$$,#{state_id_nsw},-34.306283,150.934563), - ($$2515$$,$$THIRROUL$$,#{state_id_nsw},-34.306283,150.934563), - ($$2515$$,$$WOMBARRA$$,#{state_id_nsw},-34.306283,150.934563), - ($$2516$$,$$BULLI$$,#{state_id_nsw},-34.333861,150.913281), - ($$2517$$,$$RUSSELL VALE$$,#{state_id_nsw},-34.358093,150.900783), - ($$2517$$,$$WOONONA$$,#{state_id_nsw},-34.358093,150.900783), - ($$2517$$,$$WOONONA EAST$$,#{state_id_nsw},-34.358093,150.900783), - ($$2518$$,$$BELLAMBI$$,#{state_id_nsw},-34.365911,150.910756), - ($$2518$$,$$CORRIMAL$$,#{state_id_nsw},-34.365911,150.910756), - ($$2518$$,$$CORRIMAL EAST$$,#{state_id_nsw},-34.365911,150.910756), - ($$2518$$,$$EAST CORRIMAL$$,#{state_id_nsw},-34.365911,150.910756), - ($$2518$$,$$TARRAWANNA$$,#{state_id_nsw},-34.365911,150.910756), - ($$2518$$,$$TOWRADGI$$,#{state_id_nsw},-34.365911,150.910756), - ($$2519$$,$$BALGOWNIE$$,#{state_id_nsw},-34.38859,150.877689), - ($$2519$$,$$FAIRY MEADOW$$,#{state_id_nsw},-34.38859,150.877689), - ($$2519$$,$$FERNHILL$$,#{state_id_nsw},-34.38859,150.877689), - ($$2519$$,$$MOUNT OUSLEY$$,#{state_id_nsw},-34.38859,150.877689), - ($$2519$$,$$MOUNT PLEASANT$$,#{state_id_nsw},-34.38859,150.877689), - ($$2520$$,$$WOLLONGONG$$,#{state_id_nsw},-33.937789,151.139594), - ($$2522$$,$$UNIVERSITY OF WOLLONGONG$$,#{state_id_nsw},-34.405103,150.877805), - ($$2525$$,$$FIGTREE$$,#{state_id_nsw},-34.435686,150.861241), - ($$2526$$,$$CORDEAUX$$,#{state_id_nsw},-34.380765,150.77681), - ($$2526$$,$$CORDEAUX HEIGHTS$$,#{state_id_nsw},-34.380765,150.77681), - ($$2526$$,$$DOMBARTON$$,#{state_id_nsw},-34.380765,150.77681), - ($$2526$$,$$FARMBOROUGH HEIGHTS$$,#{state_id_nsw},-34.380765,150.77681), - ($$2526$$,$$KEMBLA GRANGE$$,#{state_id_nsw},-34.380765,150.77681), - ($$2526$$,$$KEMBLA HEIGHTS$$,#{state_id_nsw},-34.380765,150.77681), - ($$2526$$,$$MOUNT KEMBLA$$,#{state_id_nsw},-34.380765,150.77681), - ($$2526$$,$$UNANDERRA$$,#{state_id_nsw},-34.380765,150.77681), - ($$2526$$,$$UNANDERRA DC$$,#{state_id_nsw},-34.380765,150.77681), - ($$2527$$,$$ALBION PARK$$,#{state_id_nsw},-34.570722,150.775031), - ($$2527$$,$$ALBION PARK RAIL$$,#{state_id_nsw},-34.570722,150.775031), - ($$2527$$,$$CALDERWOOD$$,#{state_id_nsw},-34.570722,150.775031), - ($$2527$$,$$CROOM$$,#{state_id_nsw},-34.570722,150.775031), - ($$2527$$,$$NORTH MACQUARIE$$,#{state_id_nsw},-34.570722,150.775031), - ($$2527$$,$$TONGARRA$$,#{state_id_nsw},-34.570722,150.775031), - ($$2527$$,$$TULLIMBAR$$,#{state_id_nsw},-34.570722,150.775031), - ($$2527$$,$$YELLOW ROCK$$,#{state_id_nsw},-34.570722,150.775031), - ($$2528$$,$$BARRACK HEIGHTS$$,#{state_id_nsw},-34.565203,150.857066), - ($$2528$$,$$BARRACK POINT$$,#{state_id_nsw},-34.565203,150.857066), - ($$2528$$,$$LAKE ILLAWARRA$$,#{state_id_nsw},-34.565203,150.857066), - ($$2528$$,$$MOUNT WARRIGAL$$,#{state_id_nsw},-34.565203,150.857066), - ($$2528$$,$$WARILLA$$,#{state_id_nsw},-34.565203,150.857066), - ($$2528$$,$$WINDANG$$,#{state_id_nsw},-34.565203,150.857066), - ($$2529$$,$$BLACKBUTT$$,#{state_id_nsw},-34.56896,150.834695), - ($$2529$$,$$DUNMORE$$,#{state_id_nsw},-34.56896,150.834695), - ($$2529$$,$$FLINDERS$$,#{state_id_nsw},-34.56896,150.834695), - ($$2529$$,$$OAK FLATS$$,#{state_id_nsw},-34.56896,150.834695), - ($$2529$$,$$OAK FLATS DC$$,#{state_id_nsw},-34.56896,150.834695), - ($$2529$$,$$SHELL COVE$$,#{state_id_nsw},-34.56896,150.834695), - ($$2529$$,$$SHELLHARBOUR$$,#{state_id_nsw},-34.56896,150.834695), - ($$2529$$,$$SHELLHARBOUR CITY CENTRE$$,#{state_id_nsw},-34.56896,150.834695), - ($$2530$$,$$AVONDALE$$,#{state_id_nsw},-34.514177,150.733774), - ($$2530$$,$$BROWNSVILLE$$,#{state_id_nsw},-34.514177,150.733774), - ($$2530$$,$$CLEVELAND$$,#{state_id_nsw},-34.514177,150.733774), - ($$2530$$,$$DAPTO$$,#{state_id_nsw},-34.514177,150.733774), - ($$2530$$,$$HAYWARDS BAY$$,#{state_id_nsw},-34.514177,150.733774), - ($$2530$$,$$HORSLEY$$,#{state_id_nsw},-34.514177,150.733774), - ($$2530$$,$$HUNTLEY$$,#{state_id_nsw},-34.514177,150.733774), - ($$2530$$,$$KANAHOOKA$$,#{state_id_nsw},-34.514177,150.733774), - ($$2530$$,$$KOONAWARRA$$,#{state_id_nsw},-34.514177,150.733774), - ($$2530$$,$$MARSHALL MOUNT$$,#{state_id_nsw},-34.514177,150.733774), - ($$2530$$,$$PENROSE$$,#{state_id_nsw},-34.514177,150.733774), - ($$2530$$,$$WONGAWILLI$$,#{state_id_nsw},-34.514177,150.733774), - ($$2530$$,$$YALLAH$$,#{state_id_nsw},-34.514177,150.733774), - ($$2533$$,$$BOMBO$$,#{state_id_nsw},-34.656396,150.854028), - ($$2533$$,$$CURRAMORE$$,#{state_id_nsw},-34.656396,150.854028), - ($$2533$$,$$JAMBEROO$$,#{state_id_nsw},-34.656396,150.854028), - ($$2533$$,$$JERRARA$$,#{state_id_nsw},-34.656396,150.854028), - ($$2533$$,$$KIAMA$$,#{state_id_nsw},-34.656396,150.854028), - ($$2533$$,$$KIAMA DOWNS$$,#{state_id_nsw},-34.656396,150.854028), - ($$2533$$,$$KIAMA HEIGHTS$$,#{state_id_nsw},-34.656396,150.854028), - ($$2533$$,$$MINNAMURRA$$,#{state_id_nsw},-34.656396,150.854028), - ($$2533$$,$$SADDLEBACK MOUNTAIN$$,#{state_id_nsw},-34.656396,150.854028), - ($$2534$$,$$BROUGHTON VILLAGE$$,#{state_id_nsw},-34.727084,150.768404), - ($$2534$$,$$FOXGROUND$$,#{state_id_nsw},-34.727084,150.768404), - ($$2534$$,$$GERRINGONG$$,#{state_id_nsw},-34.727084,150.768404), - ($$2534$$,$$GERROA$$,#{state_id_nsw},-34.727084,150.768404), - ($$2534$$,$$ROSE VALLEY$$,#{state_id_nsw},-34.727084,150.768404), - ($$2534$$,$$TOOLIJOOA$$,#{state_id_nsw},-34.727084,150.768404), - ($$2534$$,$$WERRI BEACH$$,#{state_id_nsw},-34.727084,150.768404), - ($$2534$$,$$WILLOW VALE$$,#{state_id_nsw},-34.727084,150.768404), - ($$2535$$,$$BACK FOREST$$,#{state_id_nsw},-34.853655,150.678507), - ($$2535$$,$$BELLAWONGARAH$$,#{state_id_nsw},-34.853655,150.678507), - ($$2535$$,$$BERRY$$,#{state_id_nsw},-34.853655,150.678507), - ($$2535$$,$$BERRY MOUNTAIN$$,#{state_id_nsw},-34.853655,150.678507), - ($$2535$$,$$BROGERS CREEK$$,#{state_id_nsw},-34.853655,150.678507), - ($$2535$$,$$BROUGHTON$$,#{state_id_nsw},-34.853655,150.678507), - ($$2535$$,$$BROUGHTON VALE$$,#{state_id_nsw},-34.853655,150.678507), - ($$2535$$,$$BUDDEROO$$,#{state_id_nsw},-34.853655,150.678507), - ($$2535$$,$$BUNDEWALLAH$$,#{state_id_nsw},-34.853655,150.678507), - ($$2535$$,$$COOLANGATTA$$,#{state_id_nsw},-34.853655,150.678507), - ($$2535$$,$$FAR MEADOW$$,#{state_id_nsw},-34.853655,150.678507), - ($$2535$$,$$JASPERS BRUSH$$,#{state_id_nsw},-34.853655,150.678507), - ($$2535$$,$$SHOALHAVEN HEADS$$,#{state_id_nsw},-34.853655,150.678507), - ($$2535$$,$$WATTAMOLLA$$,#{state_id_nsw},-34.853655,150.678507), - ($$2535$$,$$WOODHILL$$,#{state_id_nsw},-34.853655,150.678507), - ($$2536$$,$$BATEHAVEN$$,#{state_id_nsw},-35.73211,150.199539), - ($$2536$$,$$BATEMANS BAY$$,#{state_id_nsw},-35.73211,150.199539), - ($$2536$$,$$BENANDARAH$$,#{state_id_nsw},-35.73211,150.199539), - ($$2536$$,$$BIMBIMBIE$$,#{state_id_nsw},-35.73211,150.199539), - ($$2536$$,$$BUCKENBOWRA$$,#{state_id_nsw},-35.73211,150.199539), - ($$2536$$,$$CATALINA$$,#{state_id_nsw},-35.73211,150.199539), - ($$2536$$,$$CURROWAN$$,#{state_id_nsw},-35.73211,150.199539), - ($$2536$$,$$DENHAMS BEACH$$,#{state_id_nsw},-35.73211,150.199539), - ($$2536$$,$$DEPOT BEACH$$,#{state_id_nsw},-35.73211,150.199539), - ($$2536$$,$$DURRAS NORTH$$,#{state_id_nsw},-35.73211,150.199539), - ($$2536$$,$$EAST LYNNE$$,#{state_id_nsw},-35.73211,150.199539), - ($$2536$$,$$GUERILLA BAY$$,#{state_id_nsw},-35.73211,150.199539), - ($$2536$$,$$JEREMADRA$$,#{state_id_nsw},-35.73211,150.199539), - ($$2536$$,$$LILLI PILLI$$,#{state_id_nsw},-35.73211,150.199539), - ($$2536$$,$$LONG BEACH$$,#{state_id_nsw},-35.73211,150.199539), - ($$2536$$,$$MALONEYS BEACH$$,#{state_id_nsw},-35.73211,150.199539), - ($$2536$$,$$MALUA BAY$$,#{state_id_nsw},-35.73211,150.199539), - ($$2536$$,$$MOGO$$,#{state_id_nsw},-35.73211,150.199539), - ($$2536$$,$$NELLIGEN$$,#{state_id_nsw},-35.73211,150.199539), - ($$2536$$,$$NORTH BATEMANS BAY$$,#{state_id_nsw},-35.73211,150.199539), - ($$2536$$,$$PEBBLY BEACH$$,#{state_id_nsw},-35.73211,150.199539), - ($$2536$$,$$ROSEDALE$$,#{state_id_nsw},-35.73211,150.199539), - ($$2536$$,$$RUNNYFORD$$,#{state_id_nsw},-35.73211,150.199539), - ($$2536$$,$$SOUTH DURRAS$$,#{state_id_nsw},-35.73211,150.199539), - ($$2536$$,$$SUNSHINE BAY$$,#{state_id_nsw},-35.73211,150.199539), - ($$2536$$,$$SURF BEACH$$,#{state_id_nsw},-35.73211,150.199539), - ($$2536$$,$$SURFSIDE$$,#{state_id_nsw},-35.73211,150.199539), - ($$2536$$,$$WOODLANDS$$,#{state_id_nsw},-35.73211,150.199539), - ($$2537$$,$$BERGALIA$$,#{state_id_nsw},-35.981261,150.105175), - ($$2537$$,$$BINGIE$$,#{state_id_nsw},-35.981261,150.105175), - ($$2537$$,$$BROULEE$$,#{state_id_nsw},-35.981261,150.105175), - ($$2537$$,$$COILA$$,#{state_id_nsw},-35.981261,150.105175), - ($$2537$$,$$CONGO$$,#{state_id_nsw},-35.981261,150.105175), - ($$2537$$,$$DEUA$$,#{state_id_nsw},-35.981261,150.105175), - ($$2537$$,$$DEUA RIVER VALLEY$$,#{state_id_nsw},-35.981261,150.105175), - ($$2537$$,$$KIORA$$,#{state_id_nsw},-35.981261,150.105175), - ($$2537$$,$$MERINGO$$,#{state_id_nsw},-35.981261,150.105175), - ($$2537$$,$$MOGENDOURA$$,#{state_id_nsw},-35.981261,150.105175), - ($$2537$$,$$MORUYA$$,#{state_id_nsw},-35.981261,150.105175), - ($$2537$$,$$MORUYA HEADS$$,#{state_id_nsw},-35.981261,150.105175), - ($$2537$$,$$MOSSY POINT$$,#{state_id_nsw},-35.981261,150.105175), - ($$2537$$,$$TOMAKIN$$,#{state_id_nsw},-35.981261,150.105175), - ($$2537$$,$$TURLINJAH$$,#{state_id_nsw},-35.981261,150.105175), - ($$2537$$,$$TUROSS HEAD$$,#{state_id_nsw},-35.981261,150.105175), - ($$2537$$,$$WAMBAN$$,#{state_id_nsw},-35.981261,150.105175), - ($$2538$$,$$BROOMAN$$,#{state_id_nsw},-35.48985,150.233827), - ($$2538$$,$$LITTLE FOREST$$,#{state_id_nsw},-35.48985,150.233827), - ($$2538$$,$$MILTON$$,#{state_id_nsw},-35.48985,150.233827), - ($$2538$$,$$MOGOOD$$,#{state_id_nsw},-35.48985,150.233827), - ($$2538$$,$$MORTON$$,#{state_id_nsw},-35.48985,150.233827), - ($$2538$$,$$PORTERS CREEK$$,#{state_id_nsw},-35.48985,150.233827), - ($$2538$$,$$WOODBURN$$,#{state_id_nsw},-35.48985,150.233827), - ($$2538$$,$$WOODSTOCK$$,#{state_id_nsw},-35.48985,150.233827), - ($$2539$$,$$BAWLEY POINT$$,#{state_id_nsw},-35.522387,150.393202), - ($$2539$$,$$BENDALONG$$,#{state_id_nsw},-35.522387,150.393202), - ($$2539$$,$$BERRINGER LAKE$$,#{state_id_nsw},-35.522387,150.393202), - ($$2539$$,$$BURRILL LAKE$$,#{state_id_nsw},-35.522387,150.393202), - ($$2539$$,$$COCKWHY$$,#{state_id_nsw},-35.522387,150.393202), - ($$2539$$,$$CONJOLA$$,#{state_id_nsw},-35.522387,150.393202), - ($$2539$$,$$CONJOLA PARK$$,#{state_id_nsw},-35.522387,150.393202), - ($$2539$$,$$CROOBYAR$$,#{state_id_nsw},-35.522387,150.393202), - ($$2539$$,$$CUNJURONG POINT$$,#{state_id_nsw},-35.522387,150.393202), - ($$2539$$,$$DOLPHIN POINT$$,#{state_id_nsw},-35.522387,150.393202), - ($$2539$$,$$FISHERMANS PARADISE$$,#{state_id_nsw},-35.522387,150.393202), - ($$2539$$,$$KINGS POINT$$,#{state_id_nsw},-35.522387,150.393202), - ($$2539$$,$$KIOLOA$$,#{state_id_nsw},-35.522387,150.393202), - ($$2539$$,$$LAKE CONJOLA$$,#{state_id_nsw},-35.522387,150.393202), - ($$2539$$,$$LAKE TABOURIE$$,#{state_id_nsw},-35.522387,150.393202), - ($$2539$$,$$MANYANA$$,#{state_id_nsw},-35.522387,150.393202), - ($$2539$$,$$MOLLYMOOK$$,#{state_id_nsw},-35.522387,150.393202), - ($$2539$$,$$MOLLYMOOK BEACH$$,#{state_id_nsw},-35.522387,150.393202), - ($$2539$$,$$MOUNT KINGIMAN$$,#{state_id_nsw},-35.522387,150.393202), - ($$2539$$,$$NARRAWALLEE$$,#{state_id_nsw},-35.522387,150.393202), - ($$2539$$,$$POINTER MOUNTAIN$$,#{state_id_nsw},-35.522387,150.393202), - ($$2539$$,$$PRETTY BEACH$$,#{state_id_nsw},-35.522387,150.393202), - ($$2539$$,$$TERMEIL$$,#{state_id_nsw},-35.522387,150.393202), - ($$2539$$,$$ULLADULLA$$,#{state_id_nsw},-35.522387,150.393202), - ($$2539$$,$$YADBORO$$,#{state_id_nsw},-35.522387,150.393202), - ($$2539$$,$$YATTE YATTAH$$,#{state_id_nsw},-35.522387,150.393202), - ($$2540$$,$$BAMARANG$$,#{state_id_nsw},-34.894362,150.534464), - ($$2540$$,$$BARRINGELLA$$,#{state_id_nsw},-34.894362,150.534464), - ($$2540$$,$$BASIN VIEW$$,#{state_id_nsw},-34.894362,150.534464), - ($$2540$$,$$BEECROFT PENINSULA$$,#{state_id_nsw},-34.894362,150.534464), - ($$2540$$,$$BERRARA$$,#{state_id_nsw},-34.894362,150.534464), - ($$2540$$,$$BEWONG$$,#{state_id_nsw},-34.894362,150.534464), - ($$2540$$,$$BOLONG$$,#{state_id_nsw},-34.894362,150.534464), - ($$2540$$,$$BOOLIJAH$$,#{state_id_nsw},-34.894362,150.534464), - ($$2540$$,$$BREAM BEACH$$,#{state_id_nsw},-34.894362,150.534464), - ($$2540$$,$$BROWNS MOUNTAIN$$,#{state_id_nsw},-34.894362,150.534464), - ($$2540$$,$$BRUNDEE$$,#{state_id_nsw},-34.894362,150.534464), - ($$2540$$,$$BUANGLA$$,#{state_id_nsw},-34.894362,150.534464), - ($$2540$$,$$BURRIER$$,#{state_id_nsw},-34.894362,150.534464), - ($$2540$$,$$CALLALA BAY$$,#{state_id_nsw},-34.894362,150.534464), - ($$2540$$,$$CALLALA BEACH$$,#{state_id_nsw},-34.894362,150.534464), - ($$2540$$,$$CAMBEWARRA$$,#{state_id_nsw},-34.894362,150.534464), - ($$2540$$,$$CAMBEWARRA VILLAGE$$,#{state_id_nsw},-34.894362,150.534464), - ($$2540$$,$$COMBERTON$$,#{state_id_nsw},-34.894362,150.534464), - ($$2540$$,$$COMERONG ISLAND$$,#{state_id_nsw},-34.894362,150.534464), - ($$2540$$,$$CUDMIRRAH$$,#{state_id_nsw},-34.894362,150.534464), - ($$2540$$,$$CULBURRA BEACH$$,#{state_id_nsw},-34.894362,150.534464), - ($$2540$$,$$CURRARONG$$,#{state_id_nsw},-34.894362,150.534464), - ($$2540$$,$$EROWAL BAY$$,#{state_id_nsw},-34.894362,150.534464), - ($$2540$$,$$ETTREMA$$,#{state_id_nsw},-34.894362,150.534464), - ($$2540$$,$$FALLS CREEK$$,#{state_id_nsw},-34.894362,150.534464), - ($$2540$$,$$GREENWELL POINT$$,#{state_id_nsw},-34.894362,150.534464), - ($$2540$$,$$HMAS ALBATROSS$$,#{state_id_nsw},-34.894362,150.534464), - ($$2540$$,$$HMAS CRESWELL$$,#{state_id_act},-34.894362,150.534464), - ($$2540$$,$$HUSKISSON$$,#{state_id_nsw},-34.894362,150.534464), - ($$2540$$,$$HYAMS BEACH$$,#{state_id_nsw},-34.894362,150.534464), - ($$2540$$,$$ILLAROO$$,#{state_id_nsw},-34.894362,150.534464), - ($$2540$$,$$JERRAWANGALA$$,#{state_id_nsw},-34.894362,150.534464), - ($$2540$$,$$JERVIS BAY$$,#{state_id_act},-34.894362,150.534464), - ($$2540$$,$$KINGHORNE$$,#{state_id_nsw},-34.894362,150.534464), - ($$2540$$,$$LONGREACH$$,#{state_id_nsw},-34.894362,150.534464), - ($$2540$$,$$MAYFIELD$$,#{state_id_nsw},-34.894362,150.534464), - ($$2540$$,$$MEROO MEADOW$$,#{state_id_nsw},-34.894362,150.534464), - ($$2540$$,$$MONDAYONG$$,#{state_id_nsw},-34.894362,150.534464), - ($$2540$$,$$MOOLLATTOO$$,#{state_id_nsw},-34.894362,150.534464), - ($$2540$$,$$MUNDAMIA$$,#{state_id_nsw},-34.894362,150.534464), - ($$2540$$,$$MYOLA$$,#{state_id_nsw},-34.894362,150.534464), - ($$2540$$,$$NOWRA HILL$$,#{state_id_nsw},-34.894362,150.534464), - ($$2540$$,$$NOWRA NAVAL PO$$,#{state_id_nsw},-34.894362,150.534464), - ($$2540$$,$$NUMBAA$$,#{state_id_nsw},-34.894362,150.534464), - ($$2540$$,$$OLD EROWAL BAY$$,#{state_id_nsw},-34.894362,150.534464), - ($$2540$$,$$ORIENT POINT$$,#{state_id_nsw},-34.894362,150.534464), - ($$2540$$,$$PARMA$$,#{state_id_nsw},-34.894362,150.534464), - ($$2540$$,$$PYREE$$,#{state_id_nsw},-34.894362,150.534464), - ($$2540$$,$$SANCTUARY POINT$$,#{state_id_nsw},-34.894362,150.534464), - ($$2540$$,$$ST GEORGES BASIN$$,#{state_id_nsw},-34.894362,150.534464), - ($$2540$$,$$SUSSEX INLET$$,#{state_id_nsw},-34.894362,150.534464), - ($$2540$$,$$SWANHAVEN$$,#{state_id_nsw},-34.894362,150.534464), - ($$2540$$,$$TALLOWAL$$,#{state_id_nsw},-34.894362,150.534464), - ($$2540$$,$$TAPITALLEE$$,#{state_id_nsw},-34.894362,150.534464), - ($$2540$$,$$TERARA$$,#{state_id_nsw},-34.894362,150.534464), - ($$2540$$,$$TOMERONG$$,#{state_id_nsw},-34.894362,150.534464), - ($$2540$$,$$TULLARWALLA$$,#{state_id_nsw},-34.894362,150.534464), - ($$2540$$,$$TWELVE MILE PEG$$,#{state_id_nsw},-34.894362,150.534464), - ($$2540$$,$$VINCENTIA$$,#{state_id_nsw},-34.894362,150.534464), - ($$2540$$,$$WANDANDIAN$$,#{state_id_nsw},-34.894362,150.534464), - ($$2540$$,$$WATERSLEIGH$$,#{state_id_nsw},-34.894362,150.534464), - ($$2540$$,$$WOLLUMBOOLA$$,#{state_id_nsw},-34.894362,150.534464), - ($$2540$$,$$WOOLLAMIA$$,#{state_id_nsw},-34.894362,150.534464), - ($$2540$$,$$WORRIGEE$$,#{state_id_nsw},-34.894362,150.534464), - ($$2540$$,$$WORROWING HEIGHTS$$,#{state_id_nsw},-34.894362,150.534464), - ($$2540$$,$$WRIGHTS BEACH$$,#{state_id_nsw},-34.894362,150.534464), - ($$2540$$,$$YALWAL$$,#{state_id_nsw},-34.894362,150.534464), - ($$2540$$,$$YERRIYONG$$,#{state_id_nsw},-34.894362,150.534464), - ($$2541$$,$$BANGALEE$$,#{state_id_nsw},-34.843976,150.570381), - ($$2541$$,$$BOMADERRY$$,#{state_id_nsw},-34.843976,150.570381), - ($$2541$$,$$NORTH NOWRA$$,#{state_id_nsw},-34.843976,150.570381), - ($$2541$$,$$NOWRA$$,#{state_id_nsw},-34.843976,150.570381), - ($$2541$$,$$NOWRA DC$$,#{state_id_nsw},-34.843976,150.570381), - ($$2541$$,$$NOWRA EAST$$,#{state_id_nsw},-34.843976,150.570381), - ($$2541$$,$$NOWRA NORTH$$,#{state_id_nsw},-34.843976,150.570381), - ($$2541$$,$$SOUTH NOWRA$$,#{state_id_nsw},-34.843976,150.570381), - ($$2541$$,$$WEST NOWRA$$,#{state_id_nsw},-34.843976,150.570381), - ($$2545$$,$$BELOWRA$$,#{state_id_nsw},-36.148482,149.705058), - ($$2545$$,$$BODALLA$$,#{state_id_nsw},-36.148482,149.705058), - ($$2545$$,$$CADGEE$$,#{state_id_nsw},-36.148482,149.705058), - ($$2545$$,$$EUROBODALLA$$,#{state_id_nsw},-36.148482,149.705058), - ($$2545$$,$$NERRIGUNDAH$$,#{state_id_nsw},-36.148482,149.705058), - ($$2545$$,$$POTATO POINT$$,#{state_id_nsw},-36.148482,149.705058), - ($$2546$$,$$AKOLELE$$,#{state_id_nsw},-36.355798,150.07685), - ($$2546$$,$$BARRAGGA BAY$$,#{state_id_nsw},-36.355798,150.07685), - ($$2546$$,$$BERMAGUI$$,#{state_id_nsw},-36.355798,150.07685), - ($$2546$$,$$CENTRAL TILBA$$,#{state_id_nsw},-36.355798,150.07685), - ($$2546$$,$$CORUNNA$$,#{state_id_nsw},-36.355798,150.07685), - ($$2546$$,$$CUTTAGEE$$,#{state_id_nsw},-36.355798,150.07685), - ($$2546$$,$$DALMENY$$,#{state_id_nsw},-36.355798,150.07685), - ($$2546$$,$$DIGNAMS CREEK$$,#{state_id_nsw},-36.355798,150.07685), - ($$2546$$,$$KIANGA$$,#{state_id_nsw},-36.355798,150.07685), - ($$2546$$,$$MURRAH$$,#{state_id_nsw},-36.355798,150.07685), - ($$2546$$,$$MYSTERY BAY$$,#{state_id_nsw},-36.355798,150.07685), - ($$2546$$,$$NAROOMA$$,#{state_id_nsw},-36.355798,150.07685), - ($$2546$$,$$NORTH NAROOMA$$,#{state_id_nsw},-36.355798,150.07685), - ($$2546$$,$$TILBA TILBA$$,#{state_id_nsw},-36.355798,150.07685), - ($$2546$$,$$TINPOT$$,#{state_id_nsw},-36.355798,150.07685), - ($$2546$$,$$WADBILLIGA$$,#{state_id_nsw},-36.355798,150.07685), - ($$2546$$,$$WALLAGA LAKE$$,#{state_id_nsw},-36.355798,150.07685), - ($$2548$$,$$BERRAMBOOL$$,#{state_id_nsw},-36.8788,149.917402), - ($$2548$$,$$BOURNDA$$,#{state_id_nsw},-36.8788,149.917402), - ($$2548$$,$$MERIMBULA$$,#{state_id_nsw},-36.8788,149.917402), - ($$2548$$,$$MIRADOR$$,#{state_id_nsw},-36.8788,149.917402), - ($$2548$$,$$TURA BEACH$$,#{state_id_nsw},-36.8788,149.917402), - ($$2548$$,$$YELLOW PINCH$$,#{state_id_nsw},-36.8788,149.917402), - ($$2549$$,$$BALD HILLS$$,#{state_id_nsw},-36.912886,149.847662), - ($$2549$$,$$BROADWATER$$,#{state_id_nsw},-36.912886,149.847662), - ($$2549$$,$$GREIGS FLAT$$,#{state_id_nsw},-36.912886,149.847662), - ($$2549$$,$$LOCHIEL$$,#{state_id_nsw},-36.912886,149.847662), - ($$2549$$,$$MILLINGANDI$$,#{state_id_nsw},-36.912886,149.847662), - ($$2549$$,$$NETHERCOTE$$,#{state_id_nsw},-36.912886,149.847662), - ($$2549$$,$$PAMBULA$$,#{state_id_nsw},-36.912886,149.847662), - ($$2549$$,$$PAMBULA BEACH$$,#{state_id_nsw},-36.912886,149.847662), - ($$2549$$,$$SOUTH PAMBULA$$,#{state_id_nsw},-36.912886,149.847662), - ($$2550$$,$$ANGLEDALE$$,#{state_id_nsw},-36.63575,149.857154), - ($$2550$$,$$BEGA$$,#{state_id_nsw},-36.63575,149.857154), - ($$2550$$,$$BEMBOKA$$,#{state_id_nsw},-36.63575,149.857154), - ($$2550$$,$$BLACK RANGE$$,#{state_id_nsw},-36.63575,149.857154), - ($$2550$$,$$BROGO$$,#{state_id_nsw},-36.63575,149.857154), - ($$2550$$,$$BUCKAJO$$,#{state_id_nsw},-36.63575,149.857154), - ($$2550$$,$$BURRAGATE$$,#{state_id_nsw},-36.63575,149.857154), - ($$2550$$,$$CANDELO$$,#{state_id_nsw},-36.63575,149.857154), - ($$2550$$,$$CHINNOCK$$,#{state_id_nsw},-36.63575,149.857154), - ($$2550$$,$$COBARGO$$,#{state_id_nsw},-36.63575,149.857154), - ($$2550$$,$$COOLAGOLITE$$,#{state_id_nsw},-36.63575,149.857154), - ($$2550$$,$$COOLANGUBRA$$,#{state_id_nsw},-36.63575,149.857154), - ($$2550$$,$$COOPERS GULLY$$,#{state_id_nsw},-36.63575,149.857154), - ($$2550$$,$$DEVILS HOLE$$,#{state_id_nsw},-36.63575,149.857154), - ($$2550$$,$$DOCTOR GEORGE MOUNTAIN$$,#{state_id_nsw},-36.63575,149.857154), - ($$2550$$,$$FROGS HOLLOW$$,#{state_id_nsw},-36.63575,149.857154), - ($$2550$$,$$GREENDALE$$,#{state_id_nsw},-36.63575,149.857154), - ($$2550$$,$$JELLAT JELLAT$$,#{state_id_nsw},-36.63575,149.857154), - ($$2550$$,$$KALARU$$,#{state_id_nsw},-36.63575,149.857154), - ($$2550$$,$$KAMERUKA$$,#{state_id_nsw},-36.63575,149.857154), - ($$2550$$,$$KANOONA$$,#{state_id_nsw},-36.63575,149.857154), - ($$2550$$,$$KINGSWOOD$$,#{state_id_nsw},-36.63575,149.857154), - ($$2550$$,$$MOGAREEKA$$,#{state_id_nsw},-36.63575,149.857154), - ($$2550$$,$$MOGILLA$$,#{state_id_nsw},-36.63575,149.857154), - ($$2550$$,$$MORANS CROSSING$$,#{state_id_nsw},-36.63575,149.857154), - ($$2550$$,$$MUMBULLA MOUNTAIN$$,#{state_id_nsw},-36.63575,149.857154), - ($$2550$$,$$MYRTLE MOUNTAIN$$,#{state_id_nsw},-36.63575,149.857154), - ($$2550$$,$$NELSON$$,#{state_id_nsw},-36.63575,149.857154), - ($$2550$$,$$NEW BUILDINGS$$,#{state_id_nsw},-36.63575,149.857154), - ($$2550$$,$$NUMBUGGA$$,#{state_id_nsw},-36.63575,149.857154), - ($$2550$$,$$PERICOE$$,#{state_id_nsw},-36.63575,149.857154), - ($$2550$$,$$QUAAMA$$,#{state_id_nsw},-36.63575,149.857154), - ($$2550$$,$$REEDY SWAMP$$,#{state_id_nsw},-36.63575,149.857154), - ($$2550$$,$$ROCKY HALL$$,#{state_id_nsw},-36.63575,149.857154), - ($$2550$$,$$SOUTH WOLUMLA$$,#{state_id_nsw},-36.63575,149.857154), - ($$2550$$,$$STONY CREEK$$,#{state_id_nsw},-36.63575,149.857154), - ($$2550$$,$$TANJA$$,#{state_id_nsw},-36.63575,149.857154), - ($$2550$$,$$TANTAWANGALO$$,#{state_id_nsw},-36.63575,149.857154), - ($$2550$$,$$TARRAGANDA$$,#{state_id_nsw},-36.63575,149.857154), - ($$2550$$,$$TATHRA$$,#{state_id_nsw},-36.63575,149.857154), - ($$2550$$,$$TOOTHDALE$$,#{state_id_nsw},-36.63575,149.857154), - ($$2550$$,$$TOWAMBA$$,#{state_id_nsw},-36.63575,149.857154), - ($$2550$$,$$VERONA$$,#{state_id_nsw},-36.63575,149.857154), - ($$2550$$,$$WALLAGOOT$$,#{state_id_nsw},-36.63575,149.857154), - ($$2550$$,$$WANDELLA$$,#{state_id_nsw},-36.63575,149.857154), - ($$2550$$,$$WAPENGO$$,#{state_id_nsw},-36.63575,149.857154), - ($$2550$$,$$WOG WOG$$,#{state_id_nsw},-36.63575,149.857154), - ($$2550$$,$$WOLUMLA$$,#{state_id_nsw},-36.63575,149.857154), - ($$2550$$,$$WYNDHAM$$,#{state_id_nsw},-36.63575,149.857154), - ($$2550$$,$$YAMBULLA$$,#{state_id_nsw},-36.63575,149.857154), - ($$2550$$,$$YANKEES CREEK$$,#{state_id_nsw},-36.63575,149.857154), - ($$2550$$,$$YOWRIE$$,#{state_id_nsw},-36.63575,149.857154), - ($$2551$$,$$BOYDTOWN$$,#{state_id_nsw},-37.10475,149.879014), - ($$2551$$,$$EDEN$$,#{state_id_nsw},-37.10475,149.879014), - ($$2551$$,$$EDROM$$,#{state_id_nsw},-37.10475,149.879014), - ($$2551$$,$$GREEN CAPE$$,#{state_id_nsw},-37.10475,149.879014), - ($$2551$$,$$KIAH$$,#{state_id_nsw},-37.10475,149.879014), - ($$2551$$,$$NADGEE$$,#{state_id_nsw},-37.10475,149.879014), - ($$2551$$,$$NARRABARBA$$,#{state_id_nsw},-37.10475,149.879014), - ($$2551$$,$$NULLICA$$,#{state_id_nsw},-37.10475,149.879014), - ($$2551$$,$$NUNGATTA$$,#{state_id_nsw},-37.10475,149.879014), - ($$2551$$,$$NUNGATTA SOUTH$$,#{state_id_nsw},-37.10475,149.879014), - ($$2551$$,$$TIMBILLICA$$,#{state_id_nsw},-37.10475,149.879014), - ($$2551$$,$$WONBOYN$$,#{state_id_nsw},-37.10475,149.879014), - ($$2551$$,$$WONBOYN LAKE$$,#{state_id_nsw},-37.10475,149.879014), - ($$2551$$,$$WONBOYN NORTH$$,#{state_id_nsw},-37.10475,149.879014), - ($$2555$$,$$BADGERYS CREEK$$,#{state_id_nsw},-33.883376,150.741351), - ($$2556$$,$$BRINGELLY$$,#{state_id_nsw},-33.945707,150.725207), - ($$2557$$,$$CATHERINE FIELD$$,#{state_id_nsw},-33.993545,150.774858), - ($$2557$$,$$GREGORY HILLS$$,#{state_id_nsw},-33.993545,150.774858), - ($$2557$$,$$ROSSMORE$$,#{state_id_nsw},-33.993545,150.774858), - ($$2558$$,$$EAGLE VALE$$,#{state_id_nsw},-34.037882,150.814153), - ($$2558$$,$$ESCHOL PARK$$,#{state_id_nsw},-34.037882,150.814153), - ($$2558$$,$$KEARNS$$,#{state_id_nsw},-34.037882,150.814153), - ($$2559$$,$$BLAIRMOUNT$$,#{state_id_nsw},-34.049485,150.799611), - ($$2559$$,$$CLAYMORE$$,#{state_id_nsw},-34.049485,150.799611), - ($$2560$$,$$AIRDS$$,#{state_id_nsw},-34.084468,150.829041), - ($$2560$$,$$AMBARVALE$$,#{state_id_nsw},-34.084468,150.829041), - ($$2560$$,$$APPIN$$,#{state_id_nsw},-34.084468,150.829041), - ($$2560$$,$$BLAIR ATHOL$$,#{state_id_nsw},-34.084468,150.829041), - ($$2560$$,$$BRADBURY$$,#{state_id_nsw},-34.084468,150.829041), - ($$2560$$,$$CAMPBELLTOWN$$,#{state_id_nsw},-34.084468,150.829041), - ($$2560$$,$$CAMPBELLTOWN NORTH$$,#{state_id_nsw},-34.084468,150.829041), - ($$2560$$,$$CATARACT$$,#{state_id_nsw},-34.084468,150.829041), - ($$2560$$,$$ENGLORIE PARK$$,#{state_id_nsw},-34.084468,150.829041), - ($$2560$$,$$GILEAD$$,#{state_id_nsw},-34.084468,150.829041), - ($$2560$$,$$GLEN ALPINE$$,#{state_id_nsw},-34.084468,150.829041), - ($$2560$$,$$KENTLYN$$,#{state_id_nsw},-34.084468,150.829041), - ($$2560$$,$$LEUMEAH$$,#{state_id_nsw},-34.084468,150.829041), - ($$2560$$,$$MACARTHUR SQUARE$$,#{state_id_nsw},-34.084468,150.829041), - ($$2560$$,$$ROSEMEADOW$$,#{state_id_nsw},-34.084468,150.829041), - ($$2560$$,$$RUSE$$,#{state_id_nsw},-34.084468,150.829041), - ($$2560$$,$$ST HELENS PARK$$,#{state_id_nsw},-34.084468,150.829041), - ($$2560$$,$$WEDDERBURN$$,#{state_id_nsw},-34.084468,150.829041), - ($$2560$$,$$WOODBINE$$,#{state_id_nsw},-34.084468,150.829041), - ($$2563$$,$$MENANGLE PARK$$,#{state_id_nsw},-34.100121,150.757016), - ($$2564$$,$$GLENQUARIE$$,#{state_id_nsw},-33.986032,150.89171), - ($$2564$$,$$LONG POINT$$,#{state_id_nsw},-33.986032,150.89171), - ($$2564$$,$$MACQUARIE FIELDS$$,#{state_id_nsw},-33.986032,150.89171), - ($$2565$$,$$BARDIA$$,#{state_id_nsw},-33.990081,150.844653), - ($$2565$$,$$DENHAM COURT$$,#{state_id_nsw},-33.990081,150.844653), - ($$2565$$,$$INGLEBURN$$,#{state_id_nsw},-33.990081,150.844653), - ($$2565$$,$$MACQUARIE LINKS$$,#{state_id_nsw},-33.990081,150.844653), - ($$2566$$,$$BOW BOWING$$,#{state_id_nsw},-34.015342,150.83682), - ($$2566$$,$$MINTO$$,#{state_id_nsw},-34.015342,150.83682), - ($$2566$$,$$MINTO DC$$,#{state_id_nsw},-34.015342,150.83682), - ($$2566$$,$$MINTO HEIGHTS$$,#{state_id_nsw},-34.015342,150.83682), - ($$2566$$,$$RABY$$,#{state_id_nsw},-34.015342,150.83682), - ($$2566$$,$$ST ANDREWS$$,#{state_id_nsw},-34.015342,150.83682), - ($$2566$$,$$VARROVILLE$$,#{state_id_nsw},-34.015342,150.83682), - ($$2567$$,$$CURRANS HILL$$,#{state_id_nsw},-34.045179,150.764007), - ($$2567$$,$$HARRINGTON PARK$$,#{state_id_nsw},-34.045179,150.764007), - ($$2567$$,$$MOUNT ANNAN$$,#{state_id_nsw},-34.045179,150.764007), - ($$2567$$,$$NARELLAN$$,#{state_id_nsw},-34.045179,150.764007), - ($$2567$$,$$NARELLAN DC$$,#{state_id_nsw},-34.045179,150.764007), - ($$2567$$,$$NARELLAN VALE$$,#{state_id_nsw},-34.045179,150.764007), - ($$2567$$,$$SMEATON GRANGE$$,#{state_id_nsw},-34.045179,150.764007), - ($$2568$$,$$MENANGLE$$,#{state_id_nsw},-34.108654,150.749149), - ($$2569$$,$$DOUGLAS PARK$$,#{state_id_nsw},-34.193696,150.712878), - ($$2570$$,$$BELIMBLA PARK$$,#{state_id_nsw},-34.075888,150.543085), - ($$2570$$,$$BICKLEY VALE$$,#{state_id_nsw},-34.075888,150.543085), - ($$2570$$,$$BROWNLOW HILL$$,#{state_id_nsw},-34.075888,150.543085), - ($$2570$$,$$CAMDEN$$,#{state_id_nsw},-34.075888,150.543085), - ($$2570$$,$$CAMDEN PARK$$,#{state_id_nsw},-34.075888,150.543085), - ($$2570$$,$$CAMDEN SOUTH$$,#{state_id_nsw},-34.075888,150.543085), - ($$2570$$,$$CAWDOR$$,#{state_id_nsw},-34.075888,150.543085), - ($$2570$$,$$COBBITTY$$,#{state_id_nsw},-34.075888,150.543085), - ($$2570$$,$$ELDERSLIE$$,#{state_id_nsw},-34.075888,150.543085), - ($$2570$$,$$ELLIS LANE$$,#{state_id_nsw},-34.075888,150.543085), - ($$2570$$,$$GLENMORE$$,#{state_id_nsw},-34.075888,150.543085), - ($$2570$$,$$GRASMERE$$,#{state_id_nsw},-34.075888,150.543085), - ($$2570$$,$$KIRKHAM$$,#{state_id_nsw},-34.075888,150.543085), - ($$2570$$,$$MOUNT HUNTER$$,#{state_id_nsw},-34.075888,150.543085), - ($$2570$$,$$NATTAI$$,#{state_id_nsw},-34.075888,150.543085), - ($$2570$$,$$OAKDALE$$,#{state_id_nsw},-34.075888,150.543085), - ($$2570$$,$$ORAN PARK$$,#{state_id_nsw},-34.075888,150.543085), - ($$2570$$,$$ORANGEVILLE$$,#{state_id_nsw},-34.075888,150.543085), - ($$2570$$,$$SPRING FARM$$,#{state_id_nsw},-34.075888,150.543085), - ($$2570$$,$$THE OAKS$$,#{state_id_nsw},-34.075888,150.543085), - ($$2570$$,$$THERESA PARK$$,#{state_id_nsw},-34.075888,150.543085), - ($$2570$$,$$WEROMBI$$,#{state_id_nsw},-34.075888,150.543085), - ($$2571$$,$$BALMORAL$$,#{state_id_nsw},-34.294422,150.525259), - ($$2571$$,$$BUXTON$$,#{state_id_nsw},-34.294422,150.525259), - ($$2571$$,$$COURIDJAH$$,#{state_id_nsw},-34.294422,150.525259), - ($$2571$$,$$MALDON$$,#{state_id_nsw},-34.294422,150.525259), - ($$2571$$,$$MOWBRAY PARK$$,#{state_id_nsw},-34.294422,150.525259), - ($$2571$$,$$PICTON$$,#{state_id_nsw},-34.294422,150.525259), - ($$2571$$,$$RAZORBACK$$,#{state_id_nsw},-34.294422,150.525259), - ($$2571$$,$$WILTON$$,#{state_id_nsw},-34.294422,150.525259), - ($$2572$$,$$LAKESLAND$$,#{state_id_nsw},-34.18087,150.526834), - ($$2572$$,$$THIRLMERE$$,#{state_id_nsw},-34.18087,150.526834), - ($$2573$$,$$TAHMOOR$$,#{state_id_nsw},-34.22291,150.593447), - ($$2574$$,$$AVON$$,#{state_id_nsw},-34.352071,150.634745), - ($$2574$$,$$BARGO$$,#{state_id_nsw},-34.352071,150.634745), - ($$2574$$,$$PHEASANTS NEST$$,#{state_id_nsw},-34.352071,150.634745), - ($$2574$$,$$YANDERRA$$,#{state_id_nsw},-34.352071,150.634745), - ($$2575$$,$$ALPINE$$,#{state_id_nsw},-34.409517,150.523957), - ($$2575$$,$$AYLMERTON$$,#{state_id_nsw},-34.409517,150.523957), - ($$2575$$,$$BRAEMAR$$,#{state_id_nsw},-34.409517,150.523957), - ($$2575$$,$$BULLIO$$,#{state_id_nsw},-34.409517,150.523957), - ($$2575$$,$$COLO VALE$$,#{state_id_nsw},-34.409517,150.523957), - ($$2575$$,$$HIGH RANGE$$,#{state_id_nsw},-34.409517,150.523957), - ($$2575$$,$$HILL TOP$$,#{state_id_nsw},-34.409517,150.523957), - ($$2575$$,$$JOADJA$$,#{state_id_nsw},-34.409517,150.523957), - ($$2575$$,$$MITTAGONG$$,#{state_id_nsw},-34.409517,150.523957), - ($$2575$$,$$MOUNT LINDSEY$$,#{state_id_nsw},-34.409517,150.523957), - ($$2575$$,$$WATTLE RIDGE$$,#{state_id_nsw},-34.409517,150.523957), - ($$2575$$,$$WELBY$$,#{state_id_nsw},-34.409517,150.523957), - ($$2575$$,$$WILLOW VALE$$,#{state_id_nsw},-34.409517,150.523957), - ($$2575$$,$$WOODLANDS$$,#{state_id_nsw},-34.409517,150.523957), - ($$2575$$,$$YERRINBOOL$$,#{state_id_nsw},-34.409517,150.523957), - ($$2576$$,$$BOWRAL$$,#{state_id_nsw},-34.537116,150.390855), - ($$2576$$,$$BURRADOO$$,#{state_id_nsw},-34.537116,150.390855), - ($$2576$$,$$EAST BOWRAL$$,#{state_id_nsw},-34.537116,150.390855), - ($$2576$$,$$EAST KANGALOON$$,#{state_id_nsw},-34.537116,150.390855), - ($$2576$$,$$GLENQUARRY$$,#{state_id_nsw},-34.537116,150.390855), - ($$2576$$,$$KANGALOON$$,#{state_id_nsw},-34.537116,150.390855), - ($$2577$$,$$AVOCA$$,#{state_id_nsw},-34.613522,150.479323), - ($$2577$$,$$BANGADILLY$$,#{state_id_nsw},-34.613522,150.479323), - ($$2577$$,$$BARREN GROUNDS$$,#{state_id_nsw},-34.613522,150.479323), - ($$2577$$,$$BARRENGARRY$$,#{state_id_nsw},-34.613522,150.479323), - ($$2577$$,$$BEAUMONT$$,#{state_id_nsw},-34.613522,150.479323), - ($$2577$$,$$BELANGLO$$,#{state_id_nsw},-34.613522,150.479323), - ($$2577$$,$$BERRIMA$$,#{state_id_nsw},-34.613522,150.479323), - ($$2577$$,$$BUDGONG$$,#{state_id_nsw},-34.613522,150.479323), - ($$2577$$,$$BURRAWANG$$,#{state_id_nsw},-34.613522,150.479323), - ($$2577$$,$$CANYONLEIGH$$,#{state_id_nsw},-34.613522,150.479323), - ($$2577$$,$$CARRINGTON FALLS$$,#{state_id_nsw},-34.613522,150.479323), - ($$2577$$,$$FITZROY FALLS$$,#{state_id_nsw},-34.613522,150.479323), - ($$2577$$,$$HANGING ROCK$$,#{state_id_nsw},-34.613522,150.479323), - ($$2577$$,$$KANGAROO VALLEY$$,#{state_id_nsw},-34.613522,150.479323), - ($$2577$$,$$KNIGHTS HILL$$,#{state_id_nsw},-34.613522,150.479323), - ($$2577$$,$$MACQUARIE PASS$$,#{state_id_nsw},-34.613522,150.479323), - ($$2577$$,$$MANCHESTER SQUARE$$,#{state_id_nsw},-34.613522,150.479323), - ($$2577$$,$$MEDWAY$$,#{state_id_nsw},-34.613522,150.479323), - ($$2577$$,$$MERYLA$$,#{state_id_nsw},-34.613522,150.479323), - ($$2577$$,$$MOSS VALE$$,#{state_id_nsw},-34.613522,150.479323), - ($$2577$$,$$MOUNT MURRAY$$,#{state_id_nsw},-34.613522,150.479323), - ($$2577$$,$$NEW BERRIMA$$,#{state_id_nsw},-34.613522,150.479323), - ($$2577$$,$$PADDYS RIVER$$,#{state_id_nsw},-34.613522,150.479323), - ($$2577$$,$$RED ROCKS$$,#{state_id_nsw},-34.613522,150.479323), - ($$2577$$,$$ROBERTSON$$,#{state_id_nsw},-34.613522,150.479323), - ($$2577$$,$$SUTTON FOREST$$,#{state_id_nsw},-34.613522,150.479323), - ($$2577$$,$$UPPER KANGAROO RIVER$$,#{state_id_nsw},-34.613522,150.479323), - ($$2577$$,$$UPPER KANGAROO VALLEY$$,#{state_id_nsw},-34.613522,150.479323), - ($$2577$$,$$WERAI$$,#{state_id_nsw},-34.613522,150.479323), - ($$2577$$,$$WILDES MEADOW$$,#{state_id_nsw},-34.613522,150.479323), - ($$2578$$,$$BUNDANOON$$,#{state_id_nsw},-34.632598,150.321575), - ($$2579$$,$$BIG HILL$$,#{state_id_nsw},-34.562935,149.987083), - ($$2579$$,$$BRAYTON$$,#{state_id_nsw},-34.562935,149.987083), - ($$2579$$,$$EXETER$$,#{state_id_nsw},-34.562935,149.987083), - ($$2579$$,$$MARULAN$$,#{state_id_nsw},-34.562935,149.987083), - ($$2579$$,$$PENROSE$$,#{state_id_nsw},-34.562935,149.987083), - ($$2579$$,$$TALLONG$$,#{state_id_nsw},-34.562935,149.987083), - ($$2579$$,$$WINGELLO$$,#{state_id_nsw},-34.562935,149.987083), - ($$2580$$,$$BANNABY$$,#{state_id_nsw},-34.439785,149.961474), - ($$2580$$,$$BANNISTER$$,#{state_id_nsw},-34.439785,149.961474), - ($$2580$$,$$BAW BAW$$,#{state_id_nsw},-34.439785,149.961474), - ($$2580$$,$$BOXERS CREEK$$,#{state_id_nsw},-34.439785,149.961474), - ($$2580$$,$$BRISBANE GROVE$$,#{state_id_nsw},-34.439785,149.961474), - ($$2580$$,$$BUNGONIA$$,#{state_id_nsw},-34.439785,149.961474), - ($$2580$$,$$CARRICK$$,#{state_id_nsw},-34.439785,149.961474), - ($$2580$$,$$CHATSBURY$$,#{state_id_nsw},-34.439785,149.961474), - ($$2580$$,$$CURRAWANG$$,#{state_id_nsw},-34.439785,149.961474), - ($$2580$$,$$CURRAWEELA$$,#{state_id_nsw},-34.439785,149.961474), - ($$2580$$,$$GOLSPIE$$,#{state_id_nsw},-34.439785,149.961474), - ($$2580$$,$$GOULBURN$$,#{state_id_nsw},-34.439785,149.961474), - ($$2580$$,$$GOULBURN DC$$,#{state_id_nsw},-34.439785,149.961474), - ($$2580$$,$$GOULBURN NORTH$$,#{state_id_nsw},-34.439785,149.961474), - ($$2580$$,$$GREENWICH PARK$$,#{state_id_nsw},-34.439785,149.961474), - ($$2580$$,$$GUNDARY$$,#{state_id_nsw},-34.439785,149.961474), - ($$2580$$,$$JERRONG$$,#{state_id_nsw},-34.439785,149.961474), - ($$2580$$,$$KINGSDALE$$,#{state_id_nsw},-34.439785,149.961474), - ($$2580$$,$$LAKE BATHURST$$,#{state_id_nsw},-34.439785,149.961474), - ($$2580$$,$$LOWER BORO$$,#{state_id_nsw},-34.439785,149.961474), - ($$2580$$,$$MAYFIELD$$,#{state_id_nsw},-34.439785,149.961474), - ($$2580$$,$$MIDDLE ARM$$,#{state_id_nsw},-34.439785,149.961474), - ($$2580$$,$$MOUNT FAIRY$$,#{state_id_nsw},-34.439785,149.961474), - ($$2580$$,$$MUMMEL$$,#{state_id_nsw},-34.439785,149.961474), - ($$2580$$,$$MYRTLEVILLE$$,#{state_id_nsw},-34.439785,149.961474), - ($$2580$$,$$PALING YARDS$$,#{state_id_nsw},-34.439785,149.961474), - ($$2580$$,$$PARKESBOURNE$$,#{state_id_nsw},-34.439785,149.961474), - ($$2580$$,$$POMEROY$$,#{state_id_nsw},-34.439785,149.961474), - ($$2580$$,$$QUIALIGO$$,#{state_id_nsw},-34.439785,149.961474), - ($$2580$$,$$RICHLANDS$$,#{state_id_nsw},-34.439785,149.961474), - ($$2580$$,$$ROSLYN$$,#{state_id_nsw},-34.439785,149.961474), - ($$2580$$,$$RUN-O-WATERS$$,#{state_id_nsw},-34.439785,149.961474), - ($$2580$$,$$STONEQUARRY$$,#{state_id_nsw},-34.439785,149.961474), - ($$2580$$,$$TARAGO$$,#{state_id_nsw},-34.439785,149.961474), - ($$2580$$,$$TARALGA$$,#{state_id_nsw},-34.439785,149.961474), - ($$2580$$,$$TARLO$$,#{state_id_nsw},-34.439785,149.961474), - ($$2580$$,$$TIRRANNAVILLE$$,#{state_id_nsw},-34.439785,149.961474), - ($$2580$$,$$TOWRANG$$,#{state_id_nsw},-34.439785,149.961474), - ($$2580$$,$$WAYO$$,#{state_id_nsw},-34.439785,149.961474), - ($$2580$$,$$WIARBOROUGH$$,#{state_id_nsw},-34.439785,149.961474), - ($$2580$$,$$WINDELLAMA$$,#{state_id_nsw},-34.439785,149.961474), - ($$2580$$,$$WOMBEYAN CAVES$$,#{state_id_nsw},-34.439785,149.961474), - ($$2580$$,$$WOODHOUSELEE$$,#{state_id_nsw},-34.439785,149.961474), - ($$2580$$,$$YALBRAITH$$,#{state_id_nsw},-34.439785,149.961474), - ($$2580$$,$$YARRA$$,#{state_id_nsw},-34.439785,149.961474), - ($$2581$$,$$BELLMOUNT FOREST$$,#{state_id_nsw},-34.917864,149.24271), - ($$2581$$,$$BEVENDALE$$,#{state_id_nsw},-34.917864,149.24271), - ($$2581$$,$$BIALA$$,#{state_id_nsw},-34.917864,149.24271), - ($$2581$$,$$BLAKNEY CREEK$$,#{state_id_nsw},-34.917864,149.24271), - ($$2581$$,$$BREADALBANE$$,#{state_id_nsw},-34.917864,149.24271), - ($$2581$$,$$BROADWAY$$,#{state_id_nsw},-34.917864,149.24271), - ($$2581$$,$$COLLECTOR$$,#{state_id_nsw},-34.917864,149.24271), - ($$2581$$,$$CULLERIN$$,#{state_id_nsw},-34.917864,149.24271), - ($$2581$$,$$DALTON$$,#{state_id_nsw},-34.917864,149.24271), - ($$2581$$,$$GUNNING$$,#{state_id_nsw},-34.917864,149.24271), - ($$2581$$,$$GURRUNDAH$$,#{state_id_nsw},-34.917864,149.24271), - ($$2581$$,$$LADE VALE$$,#{state_id_nsw},-34.917864,149.24271), - ($$2581$$,$$LAKE GEORGE$$,#{state_id_nsw},-34.917864,149.24271), - ($$2581$$,$$LERIDA$$,#{state_id_nsw},-34.917864,149.24271), - ($$2581$$,$$MERRILL$$,#{state_id_nsw},-34.917864,149.24271), - ($$2581$$,$$OOLONG$$,#{state_id_nsw},-34.917864,149.24271), - ($$2581$$,$$WOLLOGORANG$$,#{state_id_nsw},-34.917864,149.24271), - ($$2582$$,$$BANGO$$,#{state_id_nsw},-34.745475,148.956495), - ($$2582$$,$$BERREMANGRA$$,#{state_id_nsw},-34.745475,148.956495), - ($$2582$$,$$BOAMBOLO$$,#{state_id_nsw},-34.745475,148.956495), - ($$2582$$,$$BOOKHAM$$,#{state_id_nsw},-34.745475,148.956495), - ($$2582$$,$$BOWNING$$,#{state_id_nsw},-34.745475,148.956495), - ($$2582$$,$$BURRINJUCK$$,#{state_id_nsw},-34.745475,148.956495), - ($$2582$$,$$CAVAN$$,#{state_id_nsw},-34.745475,148.956495), - ($$2582$$,$$GOOD HOPE$$,#{state_id_nsw},-34.745475,148.956495), - ($$2582$$,$$JEIR$$,#{state_id_nsw},-34.745475,148.956495), - ($$2582$$,$$JERRAWA$$,#{state_id_nsw},-34.745475,148.956495), - ($$2582$$,$$KANGIARA$$,#{state_id_nsw},-34.745475,148.956495), - ($$2582$$,$$LAVERSTOCK$$,#{state_id_nsw},-34.745475,148.956495), - ($$2582$$,$$MANTON$$,#{state_id_nsw},-34.745475,148.956495), - ($$2582$$,$$MARCHMONT$$,#{state_id_nsw},-34.745475,148.956495), - ($$2582$$,$$MULLION$$,#{state_id_nsw},-34.745475,148.956495), - ($$2582$$,$$MURRUMBATEMAN$$,#{state_id_nsw},-34.745475,148.956495), - ($$2582$$,$$NARRANGULLEN$$,#{state_id_nsw},-34.745475,148.956495), - ($$2582$$,$$WEE JASPER$$,#{state_id_nsw},-34.745475,148.956495), - ($$2582$$,$$WOOLGARLO$$,#{state_id_nsw},-34.745475,148.956495), - ($$2582$$,$$YASS$$,#{state_id_nsw},-34.745475,148.956495), - ($$2582$$,$$YASS RIVER$$,#{state_id_nsw},-34.745475,148.956495), - ($$2583$$,$$BIGGA$$,#{state_id_nsw},-34.084776,149.151361), - ($$2583$$,$$BINDA$$,#{state_id_nsw},-34.084776,149.151361), - ($$2583$$,$$CROOKED CORNER$$,#{state_id_nsw},-34.084776,149.151361), - ($$2583$$,$$CROOKWELL$$,#{state_id_nsw},-34.084776,149.151361), - ($$2583$$,$$FULLERTON$$,#{state_id_nsw},-34.084776,149.151361), - ($$2583$$,$$GRABBEN GULLEN$$,#{state_id_nsw},-34.084776,149.151361), - ($$2583$$,$$GRABINE$$,#{state_id_nsw},-34.084776,149.151361), - ($$2583$$,$$LAGGAN$$,#{state_id_nsw},-34.084776,149.151361), - ($$2583$$,$$LIMERICK$$,#{state_id_nsw},-34.084776,149.151361), - ($$2583$$,$$LOST RIVER$$,#{state_id_nsw},-34.084776,149.151361), - ($$2583$$,$$NARRAWA$$,#{state_id_nsw},-34.084776,149.151361), - ($$2583$$,$$PEELWOOD$$,#{state_id_nsw},-34.084776,149.151361), - ($$2583$$,$$PEJAR$$,#{state_id_nsw},-34.084776,149.151361), - ($$2583$$,$$RUGBY$$,#{state_id_nsw},-34.084776,149.151361), - ($$2583$$,$$TUENA$$,#{state_id_nsw},-34.084776,149.151361), - ($$2583$$,$$WHEEO$$,#{state_id_nsw},-34.084776,149.151361), - ($$2584$$,$$BINALONG$$,#{state_id_nsw},-34.670932,148.628209), - ($$2585$$,$$GALONG$$,#{state_id_nsw},-34.601586,148.556895), - ($$2586$$,$$BOOROWA$$,#{state_id_nsw},-34.438598,148.716326), - ($$2586$$,$$FROGMORE$$,#{state_id_nsw},-34.438598,148.716326), - ($$2586$$,$$GODFREYS CREEK$$,#{state_id_nsw},-34.438598,148.716326), - ($$2586$$,$$MURRINGO$$,#{state_id_nsw},-34.438598,148.716326), - ($$2586$$,$$REIDS FLAT$$,#{state_id_nsw},-34.438598,148.716326), - ($$2586$$,$$RYE PARK$$,#{state_id_nsw},-34.438598,148.716326), - ($$2586$$,$$TAYLORS FLAT$$,#{state_id_nsw},-34.438598,148.716326), - ($$2587$$,$$HARDEN$$,#{state_id_nsw},-34.547823,148.370154), - ($$2587$$,$$KINGSVALE$$,#{state_id_nsw},-34.547823,148.370154), - ($$2587$$,$$MCMAHONS REEF$$,#{state_id_nsw},-34.547823,148.370154), - ($$2587$$,$$MURRUMBURRAH$$,#{state_id_nsw},-34.547823,148.370154), - ($$2587$$,$$NUBBA$$,#{state_id_nsw},-34.547823,148.370154), - ($$2587$$,$$WOMBAT$$,#{state_id_nsw},-34.547823,148.370154), - ($$2588$$,$$WALLENDBEEN$$,#{state_id_nsw},-34.524319,148.16016), - ($$2590$$,$$BETHUNGRA$$,#{state_id_nsw},-34.762894,147.852565), - ($$2590$$,$$COOTAMUNDRA$$,#{state_id_nsw},-34.762894,147.852565), - ($$2590$$,$$ILLABO$$,#{state_id_nsw},-34.762894,147.852565), - ($$2594$$,$$BERTHONG$$,#{state_id_nsw},-34.425659,148.065745), - ($$2594$$,$$BRIBBAREE$$,#{state_id_nsw},-34.425659,148.065745), - ($$2594$$,$$BULLA CREEK$$,#{state_id_nsw},-34.425659,148.065745), - ($$2594$$,$$BURRANGONG$$,#{state_id_nsw},-34.425659,148.065745), - ($$2594$$,$$KIKIAMAH$$,#{state_id_nsw},-34.425659,148.065745), - ($$2594$$,$$MAIMURU$$,#{state_id_nsw},-34.425659,148.065745), - ($$2594$$,$$MEMAGONG$$,#{state_id_nsw},-34.425659,148.065745), - ($$2594$$,$$MILVALE$$,#{state_id_nsw},-34.425659,148.065745), - ($$2594$$,$$MONTEAGLE$$,#{state_id_nsw},-34.425659,148.065745), - ($$2594$$,$$THUDDUNGRA$$,#{state_id_nsw},-34.425659,148.065745), - ($$2594$$,$$TUBBUL$$,#{state_id_nsw},-34.425659,148.065745), - ($$2594$$,$$WEEDALLION$$,#{state_id_nsw},-34.425659,148.065745), - ($$2594$$,$$YOUNG$$,#{state_id_nsw},-34.425659,148.065745), - ($$2600$$,$$BARTON$$,#{state_id_act},-35.314348,149.137033), - ($$2600$$,$$CANBERRA$$,#{state_id_act},-35.314348,149.137033), - ($$2600$$,$$CAPITAL HILL$$,#{state_id_act},-35.314348,149.137033), - ($$2600$$,$$DEAKIN$$,#{state_id_act},-35.314348,149.137033), - ($$2600$$,$$DEAKIN WEST$$,#{state_id_act},-35.314348,149.137033), - ($$2600$$,$$DUNTROON$$,#{state_id_act},-35.314348,149.137033), - ($$2600$$,$$HARMAN$$,#{state_id_act},-35.314348,149.137033), - ($$2600$$,$$HMAS HARMAN$$,#{state_id_act},-35.314348,149.137033), - ($$2600$$,$$PARKES$$,#{state_id_act},-35.314348,149.137033), - ($$2600$$,$$PARLIAMENT HOUSE$$,#{state_id_act},-35.314348,149.137033), - ($$2600$$,$$RUSSELL$$,#{state_id_act},-35.314348,149.137033), - ($$2600$$,$$RUSSELL HILL$$,#{state_id_act},-35.314348,149.137033), - ($$2600$$,$$YARRALUMLA$$,#{state_id_act},-35.314348,149.137033), - ($$2601$$,$$ACTON$$,#{state_id_act},-35.282087,149.108716), - ($$2601$$,$$BLACK MOUNTAIN$$,#{state_id_act},-35.282087,149.108716), - ($$2601$$,$$CANBERRA$$,#{state_id_act},-35.282087,149.108716), - ($$2601$$,$$CITY$$,#{state_id_act},-35.282087,149.108716), - ($$2602$$,$$AINSLIE$$,#{state_id_act},-35.262153,149.145893), - ($$2602$$,$$DICKSON$$,#{state_id_act},-35.262153,149.145893), - ($$2602$$,$$DOWNER$$,#{state_id_act},-35.262153,149.145893), - ($$2602$$,$$HACKETT$$,#{state_id_act},-35.262153,149.145893), - ($$2602$$,$$LYNEHAM$$,#{state_id_act},-35.262153,149.145893), - ($$2602$$,$$O'CONNOR$$,#{state_id_act},-35.262153,149.145893), - ($$2602$$,$$WATSON$$,#{state_id_act},-35.262153,149.145893), - ($$2603$$,$$FORREST$$,#{state_id_act},-35.31848,149.124096), - ($$2603$$,$$GRIFFITH$$,#{state_id_act},-35.31848,149.124096), - ($$2603$$,$$MANUKA$$,#{state_id_act},-35.31848,149.124096), - ($$2603$$,$$RED HILL$$,#{state_id_act},-35.31848,149.124096), - ($$2604$$,$$CAUSEWAY$$,#{state_id_act},-35.317703,149.150133), - ($$2604$$,$$KINGSTON$$,#{state_id_act},-35.317703,149.150133), - ($$2604$$,$$NARRABUNDAH$$,#{state_id_act},-35.317703,149.150133), - ($$2605$$,$$CURTIN$$,#{state_id_act},-35.32454,149.075667), - ($$2605$$,$$GARRAN$$,#{state_id_act},-35.32454,149.075667), - ($$2605$$,$$HUGHES$$,#{state_id_act},-35.32454,149.075667), - ($$2606$$,$$CHIFLEY$$,#{state_id_act},-35.353521,149.079546), - ($$2606$$,$$LYONS$$,#{state_id_act},-35.353521,149.079546), - ($$2606$$,$$O'MALLEY$$,#{state_id_act},-35.353521,149.079546), - ($$2606$$,$$PHILLIP$$,#{state_id_act},-35.353521,149.079546), - ($$2606$$,$$PHILLIP DC$$,#{state_id_act},-35.353521,149.079546), - ($$2606$$,$$SWINGER HILL$$,#{state_id_act},-35.353521,149.079546), - ($$2606$$,$$WODEN$$,#{state_id_act},-35.353521,149.079546), - ($$2607$$,$$FARRER$$,#{state_id_act},-35.375443,149.10095), - ($$2607$$,$$ISAACS$$,#{state_id_act},-35.375443,149.10095), - ($$2607$$,$$MAWSON$$,#{state_id_act},-35.375443,149.10095), - ($$2607$$,$$PEARCE$$,#{state_id_act},-35.375443,149.10095), - ($$2607$$,$$TORRENS$$,#{state_id_act},-35.375443,149.10095), - ($$2608$$,$$CIVIC SQUARE$$,#{state_id_act},-35.282868,149.129372), - ($$2609$$,$$CANBERRA INTERNATIONAL AIRPORT$$,#{state_id_act},-35.303411,149.194007), - ($$2609$$,$$FYSHWICK$$,#{state_id_act},-35.303411,149.194007), - ($$2609$$,$$MAJURA$$,#{state_id_act},-35.303411,149.194007), - ($$2609$$,$$PIALLIGO$$,#{state_id_act},-35.303411,149.194007), - ($$2609$$,$$SYMONSTON$$,#{state_id_act},-35.303411,149.194007), - ($$2610$$,$$CANBERRA BC$$,#{state_id_act},0.0,0.0), - ($$2610$$,$$CANBERRA MC$$,#{state_id_act},0.0,0.0), - ($$2611$$,$$BIMBERI$$,#{state_id_nsw},-35.560416,148.624957), - ($$2611$$,$$BRINDABELLA$$,#{state_id_nsw},-35.560416,148.624957), - ($$2611$$,$$CHAPMAN$$,#{state_id_act},-35.560416,148.624957), - ($$2611$$,$$COOLEMAN$$,#{state_id_nsw},-35.560416,148.624957), - ($$2611$$,$$DUFFY$$,#{state_id_act},-35.560416,148.624957), - ($$2611$$,$$FISHER$$,#{state_id_act},-35.560416,148.624957), - ($$2611$$,$$HOLDER$$,#{state_id_act},-35.560416,148.624957), - ($$2611$$,$$MOUNT STROMLO$$,#{state_id_act},-35.560416,148.624957), - ($$2611$$,$$PIERCES CREEK$$,#{state_id_act},-35.560416,148.624957), - ($$2611$$,$$RIVETT$$,#{state_id_act},-35.560416,148.624957), - ($$2611$$,$$STIRLING$$,#{state_id_act},-35.560416,148.624957), - ($$2611$$,$$URIARRA$$,#{state_id_act},-35.560416,148.624957), - ($$2611$$,$$URIARRA$$,#{state_id_nsw},-35.560416,148.624957), - ($$2611$$,$$URIARRA FOREST$$,#{state_id_act},-35.560416,148.624957), - ($$2611$$,$$WARAMANGA$$,#{state_id_act},-35.560416,148.624957), - ($$2611$$,$$WESTON$$,#{state_id_act},-35.560416,148.624957), - ($$2611$$,$$WESTON CREEK$$,#{state_id_act},-35.560416,148.624957), - ($$2612$$,$$BRADDON$$,#{state_id_act},-35.270615,149.133208), - ($$2612$$,$$CAMPBELL$$,#{state_id_act},-35.270615,149.133208), - ($$2612$$,$$REID$$,#{state_id_act},-35.270615,149.133208), - ($$2612$$,$$TURNER$$,#{state_id_act},-35.270615,149.133208), - ($$2614$$,$$ARANDA$$,#{state_id_act},-35.257964,149.075648), - ($$2614$$,$$COOK$$,#{state_id_act},-35.257964,149.075648), - ($$2614$$,$$HAWKER$$,#{state_id_act},-35.257964,149.075648), - ($$2614$$,$$JAMISON CENTRE$$,#{state_id_act},-35.257964,149.075648), - ($$2614$$,$$MACQUARIE$$,#{state_id_act},-35.257964,149.075648), - ($$2614$$,$$PAGE$$,#{state_id_act},-35.257964,149.075648), - ($$2614$$,$$SCULLIN$$,#{state_id_act},-35.257964,149.075648), - ($$2614$$,$$WEETANGERA$$,#{state_id_act},-35.257964,149.075648), - ($$2615$$,$$CHARNWOOD$$,#{state_id_act},-35.199345,149.030062), - ($$2615$$,$$DUNLOP$$,#{state_id_act},-35.199345,149.030062), - ($$2615$$,$$FLOREY$$,#{state_id_act},-35.199345,149.030062), - ($$2615$$,$$FLYNN$$,#{state_id_act},-35.199345,149.030062), - ($$2615$$,$$FRASER$$,#{state_id_act},-35.199345,149.030062), - ($$2615$$,$$HIGGINS$$,#{state_id_act},-35.199345,149.030062), - ($$2615$$,$$HOLT$$,#{state_id_act},-35.199345,149.030062), - ($$2615$$,$$KIPPAX$$,#{state_id_act},-35.199345,149.030062), - ($$2615$$,$$LATHAM$$,#{state_id_act},-35.199345,149.030062), - ($$2615$$,$$MACGREGOR$$,#{state_id_act},-35.199345,149.030062), - ($$2615$$,$$MELBA$$,#{state_id_act},-35.199345,149.030062), - ($$2615$$,$$SPENCE$$,#{state_id_act},-35.199345,149.030062), - ($$2616$$,$$BELCONNEN$$,#{state_id_act},-35.248442,149.070336), - ($$2617$$,$$BELCONNEN$$,#{state_id_act},-35.236234,149.067347), - ($$2617$$,$$BELCONNEN DC$$,#{state_id_act},-35.236234,149.067347), - ($$2617$$,$$BRUCE$$,#{state_id_act},-35.236234,149.067347), - ($$2617$$,$$EVATT$$,#{state_id_act},-35.236234,149.067347), - ($$2617$$,$$GIRALANG$$,#{state_id_act},-35.236234,149.067347), - ($$2617$$,$$KALEEN$$,#{state_id_act},-35.236234,149.067347), - ($$2617$$,$$LAWSON$$,#{state_id_act},-35.236234,149.067347), - ($$2617$$,$$MCKELLAR$$,#{state_id_act},-35.236234,149.067347), - ($$2617$$,$$UNIVERSITY OF CANBERRA$$,#{state_id_act},-35.236234,149.067347), - ($$2618$$,$$HALL$$,#{state_id_act},-35.522639,149.08098), - ($$2618$$,$$NANIMA$$,#{state_id_nsw},-35.522639,149.08098), - ($$2618$$,$$SPRINGRANGE$$,#{state_id_nsw},-35.522639,149.08098), - ($$2618$$,$$WALLAROO$$,#{state_id_nsw},-35.522639,149.08098), - ($$2619$$,$$JERRABOMBERRA$$,#{state_id_nsw},-35.384458,149.199053), - ($$2620$$,$$BEARD$$,#{state_id_act},-35.576088,149.227469), - ($$2620$$,$$BURRA$$,#{state_id_nsw},-35.576088,149.227469), - ($$2620$$,$$CARWOOLA$$,#{state_id_nsw},-35.576088,149.227469), - ($$2620$$,$$CLEAR RANGE$$,#{state_id_nsw},-35.576088,149.227469), - ($$2620$$,$$CRESTWOOD$$,#{state_id_nsw},-35.576088,149.227469), - ($$2620$$,$$ENVIRONA$$,#{state_id_nsw},-35.576088,149.227469), - ($$2620$$,$$GOOGONG$$,#{state_id_nsw},-35.576088,149.227469), - ($$2620$$,$$GREENLEIGH$$,#{state_id_nsw},-35.576088,149.227469), - ($$2620$$,$$GUNDAROO$$,#{state_id_nsw},-35.576088,149.227469), - ($$2620$$,$$HUME$$,#{state_id_act},-35.576088,149.227469), - ($$2620$$,$$KARABAR$$,#{state_id_nsw},-35.576088,149.227469), - ($$2620$$,$$KOWEN FOREST$$,#{state_id_act},-35.576088,149.227469), - ($$2620$$,$$MICHELAGO$$,#{state_id_nsw},-35.576088,149.227469), - ($$2620$$,$$OAKS ESTATE$$,#{state_id_act},-35.576088,149.227469), - ($$2620$$,$$QUEANBEYAN$$,#{state_id_nsw},-35.576088,149.227469), - ($$2620$$,$$QUEANBEYAN DC$$,#{state_id_nsw},-35.576088,149.227469), - ($$2620$$,$$QUEANBEYAN EAST$$,#{state_id_nsw},-35.576088,149.227469), - ($$2620$$,$$QUEANBEYAN WEST$$,#{state_id_nsw},-35.576088,149.227469), - ($$2620$$,$$ROYALLA$$,#{state_id_nsw},-35.576088,149.227469), - ($$2620$$,$$SUTTON$$,#{state_id_nsw},-35.576088,149.227469), - ($$2620$$,$$THARWA$$,#{state_id_act},-35.576088,149.227469), - ($$2620$$,$$THE ANGLE$$,#{state_id_nsw},-35.576088,149.227469), - ($$2620$$,$$THE RIDGEWAY$$,#{state_id_nsw},-35.576088,149.227469), - ($$2620$$,$$TINDERRY$$,#{state_id_nsw},-35.576088,149.227469), - ($$2620$$,$$TOP NAAS$$,#{state_id_act},-35.576088,149.227469), - ($$2620$$,$$TRALEE$$,#{state_id_nsw},-35.576088,149.227469), - ($$2620$$,$$URILA$$,#{state_id_nsw},-35.576088,149.227469), - ($$2620$$,$$WAMBOIN$$,#{state_id_nsw},-35.576088,149.227469), - ($$2620$$,$$WILLIAMSDALE$$,#{state_id_nsw},-35.576088,149.227469), - ($$2620$$,$$WILLIAMSDALE$$,#{state_id_act},-35.576088,149.227469), - ($$2620$$,$$YARROW$$,#{state_id_nsw},-35.576088,149.227469), - ($$2621$$,$$ANEMBO$$,#{state_id_nsw},-35.805448,149.42833), - ($$2621$$,$$BUNGENDORE$$,#{state_id_nsw},-35.805448,149.42833), - ($$2621$$,$$BYWONG$$,#{state_id_nsw},-35.805448,149.42833), - ($$2621$$,$$FORBES CREEK$$,#{state_id_nsw},-35.805448,149.42833), - ($$2621$$,$$HOSKINSTOWN$$,#{state_id_nsw},-35.805448,149.42833), - ($$2621$$,$$PRIMROSE VALLEY$$,#{state_id_nsw},-35.805448,149.42833), - ($$2621$$,$$ROSSI$$,#{state_id_nsw},-35.805448,149.42833), - ($$2622$$,$$ARALUEN$$,#{state_id_nsw},-35.64693,149.812149), - ($$2622$$,$$BACK CREEK$$,#{state_id_nsw},-35.64693,149.812149), - ($$2622$$,$$BALLALABA$$,#{state_id_nsw},-35.64693,149.812149), - ($$2622$$,$$BENDOURA$$,#{state_id_nsw},-35.64693,149.812149), - ($$2622$$,$$BERLANG$$,#{state_id_nsw},-35.64693,149.812149), - ($$2622$$,$$BOMBAY$$,#{state_id_nsw},-35.64693,149.812149), - ($$2622$$,$$BORO$$,#{state_id_nsw},-35.64693,149.812149), - ($$2622$$,$$BRAIDWOOD$$,#{state_id_nsw},-35.64693,149.812149), - ($$2622$$,$$BUDAWANG$$,#{state_id_nsw},-35.64693,149.812149), - ($$2622$$,$$BULEE$$,#{state_id_nsw},-35.64693,149.812149), - ($$2622$$,$$CHARLEYS FOREST$$,#{state_id_nsw},-35.64693,149.812149), - ($$2622$$,$$COOLUMBURRA$$,#{state_id_nsw},-35.64693,149.812149), - ($$2622$$,$$CORANG$$,#{state_id_nsw},-35.64693,149.812149), - ($$2622$$,$$DURRAN DURRA$$,#{state_id_nsw},-35.64693,149.812149), - ($$2622$$,$$ENDRICK$$,#{state_id_nsw},-35.64693,149.812149), - ($$2622$$,$$FARRINGDON$$,#{state_id_nsw},-35.64693,149.812149), - ($$2622$$,$$HAROLDS CROSS$$,#{state_id_nsw},-35.64693,149.812149), - ($$2622$$,$$HEREFORD HALL$$,#{state_id_nsw},-35.64693,149.812149), - ($$2622$$,$$JEMBAICUMBENE$$,#{state_id_nsw},-35.64693,149.812149), - ($$2622$$,$$JERRABATTGULLA$$,#{state_id_nsw},-35.64693,149.812149), - ($$2622$$,$$JINDEN$$,#{state_id_nsw},-35.64693,149.812149), - ($$2622$$,$$JINGERA$$,#{state_id_nsw},-35.64693,149.812149), - ($$2622$$,$$KINDERVALE$$,#{state_id_nsw},-35.64693,149.812149), - ($$2622$$,$$KRAWARREE$$,#{state_id_nsw},-35.64693,149.812149), - ($$2622$$,$$LARBERT$$,#{state_id_nsw},-35.64693,149.812149), - ($$2622$$,$$MAJORS CREEK$$,#{state_id_nsw},-35.64693,149.812149), - ($$2622$$,$$MANAR$$,#{state_id_nsw},-35.64693,149.812149), - ($$2622$$,$$MARLOWE$$,#{state_id_nsw},-35.64693,149.812149), - ($$2622$$,$$MERRICUMBENE$$,#{state_id_nsw},-35.64693,149.812149), - ($$2622$$,$$MONGA$$,#{state_id_nsw},-35.64693,149.812149), - ($$2622$$,$$MONGARLOWE$$,#{state_id_nsw},-35.64693,149.812149), - ($$2622$$,$$MULLOON$$,#{state_id_nsw},-35.64693,149.812149), - ($$2622$$,$$MURRENGENBURG$$,#{state_id_nsw},-35.64693,149.812149), - ($$2622$$,$$NERINGLA$$,#{state_id_nsw},-35.64693,149.812149), - ($$2622$$,$$NERRIGA$$,#{state_id_nsw},-35.64693,149.812149), - ($$2622$$,$$NORTHANGERA$$,#{state_id_nsw},-35.64693,149.812149), - ($$2622$$,$$OALLEN$$,#{state_id_nsw},-35.64693,149.812149), - ($$2622$$,$$PALERANG$$,#{state_id_nsw},-35.64693,149.812149), - ($$2622$$,$$QUIERA$$,#{state_id_nsw},-35.64693,149.812149), - ($$2622$$,$$REIDSDALE$$,#{state_id_nsw},-35.64693,149.812149), - ($$2622$$,$$SASSAFRAS$$,#{state_id_nsw},-35.64693,149.812149), - ($$2622$$,$$SNOWBALL$$,#{state_id_nsw},-35.64693,149.812149), - ($$2622$$,$$ST GEORGE$$,#{state_id_nsw},-35.64693,149.812149), - ($$2622$$,$$TIANJARA$$,#{state_id_nsw},-35.64693,149.812149), - ($$2622$$,$$TOLWONG$$,#{state_id_nsw},-35.64693,149.812149), - ($$2622$$,$$TOMBOYE$$,#{state_id_nsw},-35.64693,149.812149), - ($$2622$$,$$TOUGA$$,#{state_id_nsw},-35.64693,149.812149), - ($$2622$$,$$WARRI$$,#{state_id_nsw},-35.64693,149.812149), - ($$2622$$,$$WOG WOG$$,#{state_id_nsw},-35.64693,149.812149), - ($$2622$$,$$WYANBENE$$,#{state_id_nsw},-35.64693,149.812149), - ($$2623$$,$$CAPTAINS FLAT$$,#{state_id_nsw},-35.552827,149.445083), - ($$2624$$,$$PERISHER VALLEY$$,#{state_id_nsw},-36.180818,148.441281), - ($$2625$$,$$THREDBO$$,#{state_id_nsw},-36.50661,148.301005), - ($$2626$$,$$BREDBO$$,#{state_id_nsw},-35.959129,149.150191), - ($$2626$$,$$BUMBALONG$$,#{state_id_nsw},-35.959129,149.150191), - ($$2626$$,$$COLINTON$$,#{state_id_nsw},-35.959129,149.150191), - ($$2627$$,$$CRACKENBACK$$,#{state_id_nsw},-36.441153,148.511421), - ($$2627$$,$$EAST JINDABYNE$$,#{state_id_nsw},-36.441153,148.511421), - ($$2627$$,$$GROSSES PLAIN$$,#{state_id_nsw},-36.441153,148.511421), - ($$2627$$,$$INGEBIRAH$$,#{state_id_nsw},-36.441153,148.511421), - ($$2627$$,$$JINDABYNE$$,#{state_id_nsw},-36.441153,148.511421), - ($$2627$$,$$KALKITE$$,#{state_id_nsw},-36.441153,148.511421), - ($$2627$$,$$KOSCIUSZKO$$,#{state_id_nsw},-36.441153,148.511421), - ($$2627$$,$$KOSCIUSZKO NATIONAL PARK$$,#{state_id_nsw},-36.441153,148.511421), - ($$2627$$,$$MOONBAH$$,#{state_id_nsw},-36.441153,148.511421), - ($$2627$$,$$PILOT WILDERNESS$$,#{state_id_nsw},-36.441153,148.511421), - ($$2628$$,$$AVONSIDE$$,#{state_id_nsw},-36.438997,148.701689), - ($$2628$$,$$BELOKA$$,#{state_id_nsw},-36.438997,148.701689), - ($$2628$$,$$BERRIDALE$$,#{state_id_nsw},-36.438997,148.701689), - ($$2628$$,$$BRAEMAR BAY$$,#{state_id_nsw},-36.438997,148.701689), - ($$2628$$,$$BYADBO WILDERNESS$$,#{state_id_nsw},-36.438997,148.701689), - ($$2628$$,$$COOTRALANTRA$$,#{state_id_nsw},-36.438997,148.701689), - ($$2628$$,$$DALGETY$$,#{state_id_nsw},-36.438997,148.701689), - ($$2628$$,$$EUCUMBENE$$,#{state_id_nsw},-36.438997,148.701689), - ($$2628$$,$$HILL TOP$$,#{state_id_nsw},-36.438997,148.701689), - ($$2628$$,$$NIMMO$$,#{state_id_nsw},-36.438997,148.701689), - ($$2628$$,$$NUMBLA VALE$$,#{state_id_nsw},-36.438997,148.701689), - ($$2628$$,$$PAUPONG$$,#{state_id_nsw},-36.438997,148.701689), - ($$2628$$,$$ROCKY PLAIN$$,#{state_id_nsw},-36.438997,148.701689), - ($$2628$$,$$SNOWY PLAIN$$,#{state_id_nsw},-36.438997,148.701689), - ($$2629$$,$$ADAMINABY$$,#{state_id_nsw},-35.997349,148.769744), - ($$2629$$,$$ANGLERS REACH$$,#{state_id_nsw},-35.997349,148.769744), - ($$2629$$,$$BOLARO$$,#{state_id_nsw},-35.997349,148.769744), - ($$2629$$,$$CABRAMURRA$$,#{state_id_nsw},-35.997349,148.769744), - ($$2629$$,$$LONG PLAIN$$,#{state_id_nsw},-35.997349,148.769744), - ($$2629$$,$$OLD ADAMINABY$$,#{state_id_nsw},-35.997349,148.769744), - ($$2629$$,$$PROVIDENCE PORTAL$$,#{state_id_nsw},-35.997349,148.769744), - ($$2629$$,$$TANTANGARA$$,#{state_id_nsw},-35.997349,148.769744), - ($$2629$$,$$YAOUK$$,#{state_id_nsw},-35.997349,148.769744), - ($$2630$$,$$ARABLE$$,#{state_id_nsw},-36.367368,148.945534), - ($$2630$$,$$BADJA$$,#{state_id_nsw},-36.367368,148.945534), - ($$2630$$,$$BILLILINGRA$$,#{state_id_nsw},-36.367368,148.945534), - ($$2630$$,$$BINJURA$$,#{state_id_nsw},-36.367368,148.945534), - ($$2630$$,$$BOBUNDARA$$,#{state_id_nsw},-36.367368,148.945534), - ($$2630$$,$$BUCKENDERRA$$,#{state_id_nsw},-36.367368,148.945534), - ($$2630$$,$$BUNGARBY$$,#{state_id_nsw},-36.367368,148.945534), - ($$2630$$,$$BUNYAN$$,#{state_id_nsw},-36.367368,148.945534), - ($$2630$$,$$CARLAMINDA$$,#{state_id_nsw},-36.367368,148.945534), - ($$2630$$,$$CHAKOLA$$,#{state_id_nsw},-36.367368,148.945534), - ($$2630$$,$$COOLRINGDON$$,#{state_id_nsw},-36.367368,148.945534), - ($$2630$$,$$COOMA$$,#{state_id_nsw},-36.367368,148.945534), - ($$2630$$,$$COOMA NORTH$$,#{state_id_nsw},-36.367368,148.945534), - ($$2630$$,$$COUNTEGANY$$,#{state_id_nsw},-36.367368,148.945534), - ($$2630$$,$$DAIRYMANS PLAINS$$,#{state_id_nsw},-36.367368,148.945534), - ($$2630$$,$$DANGELONG$$,#{state_id_nsw},-36.367368,148.945534), - ($$2630$$,$$DRY PLAIN$$,#{state_id_nsw},-36.367368,148.945534), - ($$2630$$,$$FRYING PAN$$,#{state_id_nsw},-36.367368,148.945534), - ($$2630$$,$$GLEN FERGUS$$,#{state_id_nsw},-36.367368,148.945534), - ($$2630$$,$$IRONMUNGY$$,#{state_id_nsw},-36.367368,148.945534), - ($$2630$$,$$JERANGLE$$,#{state_id_nsw},-36.367368,148.945534), - ($$2630$$,$$JIMENBUEN$$,#{state_id_nsw},-36.367368,148.945534), - ($$2630$$,$$MAFFRA$$,#{state_id_nsw},-36.367368,148.945534), - ($$2630$$,$$MIDDLE FLAT$$,#{state_id_nsw},-36.367368,148.945534), - ($$2630$$,$$MIDDLINGBANK$$,#{state_id_nsw},-36.367368,148.945534), - ($$2630$$,$$MURRUMBUCCA$$,#{state_id_nsw},-36.367368,148.945534), - ($$2630$$,$$MYALLA$$,#{state_id_nsw},-36.367368,148.945534), - ($$2630$$,$$NUMERALLA$$,#{state_id_nsw},-36.367368,148.945534), - ($$2630$$,$$PEAK VIEW$$,#{state_id_nsw},-36.367368,148.945534), - ($$2630$$,$$PINE VALLEY$$,#{state_id_nsw},-36.367368,148.945534), - ($$2630$$,$$POLO FLAT$$,#{state_id_nsw},-36.367368,148.945534), - ($$2630$$,$$RHINE FALLS$$,#{state_id_nsw},-36.367368,148.945534), - ($$2630$$,$$ROCK FLAT$$,#{state_id_nsw},-36.367368,148.945534), - ($$2630$$,$$ROSE VALLEY$$,#{state_id_nsw},-36.367368,148.945534), - ($$2630$$,$$SHANNONS FLAT$$,#{state_id_nsw},-36.367368,148.945534), - ($$2630$$,$$SPRINGFIELD$$,#{state_id_nsw},-36.367368,148.945534), - ($$2630$$,$$THE BROTHERS$$,#{state_id_nsw},-36.367368,148.945534), - ($$2630$$,$$TUROSS$$,#{state_id_nsw},-36.367368,148.945534), - ($$2630$$,$$WAMBROOK$$,#{state_id_nsw},-36.367368,148.945534), - ($$2631$$,$$ANDO$$,#{state_id_nsw},-36.739931,149.261211), - ($$2631$$,$$BOCO$$,#{state_id_nsw},-36.739931,149.261211), - ($$2631$$,$$CREEWAH$$,#{state_id_nsw},-36.739931,149.261211), - ($$2631$$,$$GLEN ALLEN$$,#{state_id_nsw},-36.739931,149.261211), - ($$2631$$,$$GREENLANDS$$,#{state_id_nsw},-36.739931,149.261211), - ($$2631$$,$$HOLTS FLAT$$,#{state_id_nsw},-36.739931,149.261211), - ($$2631$$,$$JINCUMBILLY$$,#{state_id_nsw},-36.739931,149.261211), - ($$2631$$,$$KYBEYAN$$,#{state_id_nsw},-36.739931,149.261211), - ($$2631$$,$$MOUNT COOPER$$,#{state_id_nsw},-36.739931,149.261211), - ($$2631$$,$$NIMMITABEL$$,#{state_id_nsw},-36.739931,149.261211), - ($$2631$$,$$STEEPLE FLAT$$,#{state_id_nsw},-36.739931,149.261211), - ($$2631$$,$$WINIFRED$$,#{state_id_nsw},-36.739931,149.261211), - ($$2632$$,$$BIBBENLUKE$$,#{state_id_nsw},-36.815805,149.283408), - ($$2632$$,$$BOMBALA$$,#{state_id_nsw},-36.815805,149.283408), - ($$2632$$,$$BONDI FOREST$$,#{state_id_nsw},-36.815805,149.283408), - ($$2632$$,$$BUKALONG$$,#{state_id_nsw},-36.815805,149.283408), - ($$2632$$,$$CAMBALONG$$,#{state_id_nsw},-36.815805,149.283408), - ($$2632$$,$$CATHCART$$,#{state_id_nsw},-36.815805,149.283408), - ($$2632$$,$$COOLUMBOOKA$$,#{state_id_nsw},-36.815805,149.283408), - ($$2632$$,$$CRAIGIE$$,#{state_id_nsw},-36.815805,149.283408), - ($$2632$$,$$GUNNINGRAH$$,#{state_id_nsw},-36.815805,149.283408), - ($$2632$$,$$LORDS HILL$$,#{state_id_nsw},-36.815805,149.283408), - ($$2632$$,$$MERRIANGAAH$$,#{state_id_nsw},-36.815805,149.283408), - ($$2632$$,$$MILA$$,#{state_id_nsw},-36.815805,149.283408), - ($$2632$$,$$MOUNT DARRAGH$$,#{state_id_nsw},-36.815805,149.283408), - ($$2632$$,$$PADDYS FLAT$$,#{state_id_nsw},-36.815805,149.283408), - ($$2632$$,$$PALARANG$$,#{state_id_nsw},-36.815805,149.283408), - ($$2632$$,$$QUIDONG$$,#{state_id_nsw},-36.815805,149.283408), - ($$2632$$,$$ROCKTON$$,#{state_id_nsw},-36.815805,149.283408), - ($$2632$$,$$ROSEMEATH$$,#{state_id_nsw},-36.815805,149.283408), - ($$2633$$,$$CORROWONG$$,#{state_id_nsw},-36.933875,148.826148), - ($$2633$$,$$DELEGATE$$,#{state_id_nsw},-36.933875,148.826148), - ($$2633$$,$$TOMBONG$$,#{state_id_nsw},-36.933875,148.826148), - ($$2640$$,$$ALBURY$$,#{state_id_nsw},-36.082137,146.910174), - ($$2640$$,$$BUNGOWANNAH$$,#{state_id_nsw},-36.082137,146.910174), - ($$2640$$,$$EAST ALBURY$$,#{state_id_nsw},-36.082137,146.910174), - ($$2640$$,$$ETTAMOGAH$$,#{state_id_nsw},-36.082137,146.910174), - ($$2640$$,$$GLENROY$$,#{state_id_nsw},-36.082137,146.910174), - ($$2640$$,$$LAVINGTON DC$$,#{state_id_nsw},-36.082137,146.910174), - ($$2640$$,$$MOORWATHA$$,#{state_id_nsw},-36.082137,146.910174), - ($$2640$$,$$NORTH ALBURY$$,#{state_id_nsw},-36.082137,146.910174), - ($$2640$$,$$OURNIE$$,#{state_id_nsw},-36.082137,146.910174), - ($$2640$$,$$SOUTH ALBURY$$,#{state_id_nsw},-36.082137,146.910174), - ($$2640$$,$$SPLITTERS CREEK$$,#{state_id_nsw},-36.082137,146.910174), - ($$2640$$,$$TABLE TOP$$,#{state_id_nsw},-36.082137,146.910174), - ($$2640$$,$$TALMALMO$$,#{state_id_nsw},-36.082137,146.910174), - ($$2640$$,$$THURGOONA$$,#{state_id_nsw},-36.082137,146.910174), - ($$2640$$,$$WEST ALBURY$$,#{state_id_nsw},-36.082137,146.910174), - ($$2640$$,$$WIRLINGA$$,#{state_id_nsw},-36.082137,146.910174), - ($$2640$$,$$WYMAH$$,#{state_id_nsw},-36.082137,146.910174), - ($$2641$$,$$HAMILTON VALLEY$$,#{state_id_nsw},-36.037497,146.919729), - ($$2641$$,$$LAVINGTON$$,#{state_id_nsw},-36.037497,146.919729), - ($$2641$$,$$SPRINGDALE HEIGHTS$$,#{state_id_nsw},-36.037497,146.919729), - ($$2642$$,$$BIDGEEMIA$$,#{state_id_nsw},-35.437209,146.439875), - ($$2642$$,$$BROCKLESBY$$,#{state_id_nsw},-35.437209,146.439875), - ($$2642$$,$$BURRUMBUTTOCK$$,#{state_id_nsw},-35.437209,146.439875), - ($$2642$$,$$GEEHI$$,#{state_id_nsw},-35.437209,146.439875), - ($$2642$$,$$GEROGERY$$,#{state_id_nsw},-35.437209,146.439875), - ($$2642$$,$$GLENELLEN$$,#{state_id_nsw},-35.437209,146.439875), - ($$2642$$,$$GREG GREG$$,#{state_id_nsw},-35.437209,146.439875), - ($$2642$$,$$INDI$$,#{state_id_nsw},-35.437209,146.439875), - ($$2642$$,$$JAGUMBA$$,#{state_id_nsw},-35.437209,146.439875), - ($$2642$$,$$JAGUNGAL WILDERNESS$$,#{state_id_nsw},-35.437209,146.439875), - ($$2642$$,$$JINDERA$$,#{state_id_nsw},-35.437209,146.439875), - ($$2642$$,$$JINGELLIC$$,#{state_id_nsw},-35.437209,146.439875), - ($$2642$$,$$KHANCOBAN$$,#{state_id_nsw},-35.437209,146.439875), - ($$2642$$,$$MURRAY GORGE$$,#{state_id_nsw},-35.437209,146.439875), - ($$2642$$,$$RAND$$,#{state_id_nsw},-35.437209,146.439875), - ($$2642$$,$$TOOMA$$,#{state_id_nsw},-35.437209,146.439875), - ($$2642$$,$$WALBUNDRIE$$,#{state_id_nsw},-35.437209,146.439875), - ($$2642$$,$$WELAREGANG$$,#{state_id_nsw},-35.437209,146.439875), - ($$2642$$,$$WRATHALL$$,#{state_id_nsw},-35.437209,146.439875), - ($$2642$$,$$YERONG CREEK$$,#{state_id_nsw},-35.437209,146.439875), - ($$2643$$,$$HOWLONG$$,#{state_id_nsw},-35.958568,146.60586), - ($$2644$$,$$BOWNA$$,#{state_id_nsw},-35.964984,147.130856), - ($$2644$$,$$COPPABELLA$$,#{state_id_nsw},-35.964984,147.130856), - ($$2644$$,$$HOLBROOK$$,#{state_id_nsw},-35.964984,147.130856), - ($$2644$$,$$LANKEYS CREEK$$,#{state_id_nsw},-35.964984,147.130856), - ($$2644$$,$$LITTLE BILLABONG$$,#{state_id_nsw},-35.964984,147.130856), - ($$2644$$,$$MOUNTAIN CREEK$$,#{state_id_nsw},-35.964984,147.130856), - ($$2644$$,$$MULLENGANDRA$$,#{state_id_nsw},-35.964984,147.130856), - ($$2644$$,$$WANTAGONG$$,#{state_id_nsw},-35.964984,147.130856), - ($$2644$$,$$WOOMARGAMA$$,#{state_id_nsw},-35.964984,147.130856), - ($$2644$$,$$YARARA$$,#{state_id_nsw},-35.964984,147.130856), - ($$2645$$,$$CULLIVEL$$,#{state_id_nsw},-35.129938,146.125254), - ($$2645$$,$$URANA$$,#{state_id_nsw},-35.129938,146.125254), - ($$2646$$,$$BALLDALE$$,#{state_id_nsw},-35.845724,146.518358), - ($$2646$$,$$COLLENDINA$$,#{state_id_nsw},-35.845724,146.518358), - ($$2646$$,$$COREEN$$,#{state_id_nsw},-35.845724,146.518358), - ($$2646$$,$$COROWA$$,#{state_id_nsw},-35.845724,146.518358), - ($$2646$$,$$DAYSDALE$$,#{state_id_nsw},-35.845724,146.518358), - ($$2646$$,$$GOOMBARGANA$$,#{state_id_nsw},-35.845724,146.518358), - ($$2646$$,$$HOPEFIELD$$,#{state_id_nsw},-35.845724,146.518358), - ($$2646$$,$$LOWESDALE$$,#{state_id_nsw},-35.845724,146.518358), - ($$2646$$,$$NYORA$$,#{state_id_nsw},-35.845724,146.518358), - ($$2646$$,$$OAKLANDS$$,#{state_id_nsw},-35.845724,146.518358), - ($$2646$$,$$REDLANDS$$,#{state_id_nsw},-35.845724,146.518358), - ($$2646$$,$$RENNIE$$,#{state_id_nsw},-35.845724,146.518358), - ($$2646$$,$$RINGWOOD$$,#{state_id_nsw},-35.845724,146.518358), - ($$2646$$,$$SANGER$$,#{state_id_nsw},-35.845724,146.518358), - ($$2646$$,$$SAVERNAKE$$,#{state_id_nsw},-35.845724,146.518358), - ($$2647$$,$$MULWALA$$,#{state_id_nsw},-35.954159,145.963942), - ($$2648$$,$$ANABRANCH$$,#{state_id_nsw},-34.152621,142.090252), - ($$2648$$,$$CURLWAA$$,#{state_id_nsw},-34.152621,142.090252), - ($$2648$$,$$PAN BAN$$,#{state_id_nsw},-34.152621,142.090252), - ($$2648$$,$$POONCARIE$$,#{state_id_nsw},-34.152621,142.090252), - ($$2648$$,$$RUFUS RIVER$$,#{state_id_nsw},-34.152621,142.090252), - ($$2648$$,$$SCOTIA$$,#{state_id_nsw},-34.152621,142.090252), - ($$2648$$,$$WENTWORTH$$,#{state_id_nsw},-34.152621,142.090252), - ($$2649$$,$$LAUREL HILL$$,#{state_id_nsw},-35.60039,148.0931), - ($$2649$$,$$NURENMERENMONG$$,#{state_id_nsw},-35.60039,148.0931), - ($$2650$$,$$ALFREDTOWN$$,#{state_id_nsw},-35.161474,147.512382), - ($$2650$$,$$ASHMONT$$,#{state_id_nsw},-35.161474,147.512382), - ($$2650$$,$$BELFRAYDEN$$,#{state_id_nsw},-35.161474,147.512382), - ($$2650$$,$$BIG SPRINGS$$,#{state_id_nsw},-35.161474,147.512382), - ($$2650$$,$$BOMEN$$,#{state_id_nsw},-35.161474,147.512382), - ($$2650$$,$$BOOK BOOK$$,#{state_id_nsw},-35.161474,147.512382), - ($$2650$$,$$BOOROOMA$$,#{state_id_nsw},-35.161474,147.512382), - ($$2650$$,$$BORAMBOLA$$,#{state_id_nsw},-35.161474,147.512382), - ($$2650$$,$$BOURKELANDS$$,#{state_id_nsw},-35.161474,147.512382), - ($$2650$$,$$BRUCEDALE$$,#{state_id_nsw},-35.161474,147.512382), - ($$2650$$,$$BULGARY$$,#{state_id_nsw},-35.161474,147.512382), - ($$2650$$,$$BURRANDANA$$,#{state_id_nsw},-35.161474,147.512382), - ($$2650$$,$$CARABOST$$,#{state_id_nsw},-35.161474,147.512382), - ($$2650$$,$$CARTWRIGHTS HILL$$,#{state_id_nsw},-35.161474,147.512382), - ($$2650$$,$$COLLINGULLIE$$,#{state_id_nsw},-35.161474,147.512382), - ($$2650$$,$$COOKARDINIA$$,#{state_id_nsw},-35.161474,147.512382), - ($$2650$$,$$CURRAWARNA$$,#{state_id_nsw},-35.161474,147.512382), - ($$2650$$,$$DOWNSIDE$$,#{state_id_nsw},-35.161474,147.512382), - ($$2650$$,$$EAST WAGGA WAGGA$$,#{state_id_nsw},-35.161474,147.512382), - ($$2650$$,$$ESTELLA$$,#{state_id_nsw},-35.161474,147.512382), - ($$2650$$,$$EUBERTA$$,#{state_id_nsw},-35.161474,147.512382), - ($$2650$$,$$EUNANOREENYA$$,#{state_id_nsw},-35.161474,147.512382), - ($$2650$$,$$GALORE$$,#{state_id_nsw},-35.161474,147.512382), - ($$2650$$,$$GELSTON PARK$$,#{state_id_nsw},-35.161474,147.512382), - ($$2650$$,$$GLENFIELD PARK$$,#{state_id_nsw},-35.161474,147.512382), - ($$2650$$,$$GOBBAGOMBALIN$$,#{state_id_nsw},-35.161474,147.512382), - ($$2650$$,$$GREGADOO$$,#{state_id_nsw},-35.161474,147.512382), - ($$2650$$,$$HAREFIELD$$,#{state_id_nsw},-35.161474,147.512382), - ($$2650$$,$$HILLGROVE$$,#{state_id_nsw},-35.161474,147.512382), - ($$2650$$,$$KOORINGAL$$,#{state_id_nsw},-35.161474,147.512382), - ($$2650$$,$$KYEAMBA$$,#{state_id_nsw},-35.161474,147.512382), - ($$2650$$,$$LAKE ALBERT$$,#{state_id_nsw},-35.161474,147.512382), - ($$2650$$,$$LLOYD$$,#{state_id_nsw},-35.161474,147.512382), - ($$2650$$,$$MAXWELL$$,#{state_id_nsw},-35.161474,147.512382), - ($$2650$$,$$MOORONG$$,#{state_id_nsw},-35.161474,147.512382), - ($$2650$$,$$MOUNT AUSTIN$$,#{state_id_nsw},-35.161474,147.512382), - ($$2650$$,$$NORTH WAGGA WAGGA$$,#{state_id_nsw},-35.161474,147.512382), - ($$2650$$,$$OBERNE CREEK$$,#{state_id_nsw},-35.161474,147.512382), - ($$2650$$,$$OURA$$,#{state_id_nsw},-35.161474,147.512382), - ($$2650$$,$$PULLETOP$$,#{state_id_nsw},-35.161474,147.512382), - ($$2650$$,$$ROWAN$$,#{state_id_nsw},-35.161474,147.512382), - ($$2650$$,$$SAN ISIDORE$$,#{state_id_nsw},-35.161474,147.512382), - ($$2650$$,$$SPRINGVALE$$,#{state_id_nsw},-35.161474,147.512382), - ($$2650$$,$$TATTON$$,#{state_id_nsw},-35.161474,147.512382), - ($$2650$$,$$THE GAP$$,#{state_id_nsw},-35.161474,147.512382), - ($$2650$$,$$TOLLAND$$,#{state_id_nsw},-35.161474,147.512382), - ($$2650$$,$$TURVEY PARK$$,#{state_id_nsw},-35.161474,147.512382), - ($$2650$$,$$WAGGA WAGGA$$,#{state_id_nsw},-35.161474,147.512382), - ($$2650$$,$$WAGGA WAGGA BC$$,#{state_id_nsw},-35.161474,147.512382), - ($$2650$$,$$WALLACETOWN$$,#{state_id_nsw},-35.161474,147.512382), - ($$2650$$,$$WANTABADGERY$$,#{state_id_nsw},-35.161474,147.512382), - ($$2650$$,$$YARRAGUNDRY$$,#{state_id_nsw},-35.161474,147.512382), - ($$2650$$,$$YATHELLA$$,#{state_id_nsw},-35.161474,147.512382), - ($$2651$$,$$FOREST HILL$$,#{state_id_nsw},-32.707981,151.55001), - ($$2651$$,$$WAGGA WAGGA RAAF$$,#{state_id_nsw},-32.707981,151.55001), - ($$2652$$,$$BOORGA$$,#{state_id_nsw},-34.039439,146.030172), - ($$2652$$,$$BOREE CREEK$$,#{state_id_nsw},-34.039439,146.030172), - ($$2652$$,$$GOOLGOWI$$,#{state_id_nsw},-34.039439,146.030172), - ($$2652$$,$$GRONG GRONG$$,#{state_id_nsw},-34.039439,146.030172), - ($$2652$$,$$GUMLY GUMLY$$,#{state_id_nsw},-34.039439,146.030172), - ($$2652$$,$$HUMULA$$,#{state_id_nsw},-34.039439,146.030172), - ($$2652$$,$$LADYSMITH$$,#{state_id_nsw},-34.039439,146.030172), - ($$2652$$,$$LANDERVALE$$,#{state_id_nsw},-34.039439,146.030172), - ($$2652$$,$$MANGOPLAH$$,#{state_id_nsw},-34.039439,146.030172), - ($$2652$$,$$MARRAR$$,#{state_id_nsw},-34.039439,146.030172), - ($$2652$$,$$MATONG$$,#{state_id_nsw},-34.039439,146.030172), - ($$2652$$,$$MERRIWAGGA$$,#{state_id_nsw},-34.039439,146.030172), - ($$2652$$,$$MURRULEBALE$$,#{state_id_nsw},-34.039439,146.030172), - ($$2652$$,$$OLD JUNEE$$,#{state_id_nsw},-34.039439,146.030172), - ($$2652$$,$$ROSEWOOD$$,#{state_id_nsw},-34.039439,146.030172), - ($$2652$$,$$TABBITA$$,#{state_id_nsw},-34.039439,146.030172), - ($$2652$$,$$TARCUTTA$$,#{state_id_nsw},-34.039439,146.030172), - ($$2652$$,$$URANQUINTY$$,#{state_id_nsw},-34.039439,146.030172), - ($$2653$$,$$BURRA$$,#{state_id_nsw},-35.829468,148.068724), - ($$2653$$,$$COURABYRA$$,#{state_id_nsw},-35.829468,148.068724), - ($$2653$$,$$GLENROY$$,#{state_id_nsw},-35.829468,148.068724), - ($$2653$$,$$MANNUS$$,#{state_id_nsw},-35.829468,148.068724), - ($$2653$$,$$MARAGLE$$,#{state_id_nsw},-35.829468,148.068724), - ($$2653$$,$$MUNDEROO$$,#{state_id_nsw},-35.829468,148.068724), - ($$2653$$,$$PADDYS RIVER$$,#{state_id_nsw},-35.829468,148.068724), - ($$2653$$,$$TARADALE$$,#{state_id_nsw},-35.829468,148.068724), - ($$2653$$,$$TUMBARUMBA$$,#{state_id_nsw},-35.829468,148.068724), - ($$2653$$,$$WESTDALE$$,#{state_id_nsw},-35.829468,148.068724), - ($$2653$$,$$WILLIGOBUNG$$,#{state_id_nsw},-35.829468,148.068724), - ($$2655$$,$$FRENCH PARK$$,#{state_id_nsw},-35.319006,147.189768), - ($$2655$$,$$KUBURA$$,#{state_id_nsw},-35.319006,147.189768), - ($$2655$$,$$THE ROCK$$,#{state_id_nsw},-35.319006,147.189768), - ($$2655$$,$$TOOTOOL$$,#{state_id_nsw},-35.319006,147.189768), - ($$2656$$,$$BROOKDALE$$,#{state_id_nsw},-35.124957,147.001238), - ($$2656$$,$$BROOKONG$$,#{state_id_nsw},-35.124957,147.001238), - ($$2656$$,$$FARGUNYAH$$,#{state_id_nsw},-35.124957,147.001238), - ($$2656$$,$$LOCKHART$$,#{state_id_nsw},-35.124957,147.001238), - ($$2656$$,$$MILBRULONG$$,#{state_id_nsw},-35.124957,147.001238), - ($$2656$$,$$OSBORNE$$,#{state_id_nsw},-35.124957,147.001238), - ($$2656$$,$$URANGELINE$$,#{state_id_nsw},-35.124957,147.001238), - ($$2656$$,$$URANGELINE EAST$$,#{state_id_nsw},-35.124957,147.001238), - ($$2658$$,$$HENTY$$,#{state_id_nsw},-35.43665,146.989498), - ($$2658$$,$$MUNYABLA$$,#{state_id_nsw},-35.43665,146.989498), - ($$2658$$,$$PLEASANT HILLS$$,#{state_id_nsw},-35.43665,146.989498), - ($$2658$$,$$RYAN$$,#{state_id_nsw},-35.43665,146.989498), - ($$2659$$,$$ALMA PARK$$,#{state_id_nsw},-35.604129,146.791693), - ($$2659$$,$$WALLA WALLA$$,#{state_id_nsw},-35.604129,146.791693), - ($$2660$$,$$CULCAIRN$$,#{state_id_nsw},-35.670726,146.999996), - ($$2660$$,$$MORVEN$$,#{state_id_nsw},-35.670726,146.999996), - ($$2661$$,$$KAPOOKA$$,#{state_id_nsw},-35.14779,147.295748), - ($$2663$$,$$COWABBIE$$,#{state_id_nsw},-35.149379,145.978867), - ($$2663$$,$$ERIN VALE$$,#{state_id_nsw},-35.149379,145.978867), - ($$2663$$,$$EURONGILLY$$,#{state_id_nsw},-35.149379,145.978867), - ($$2663$$,$$JUNEE$$,#{state_id_nsw},-35.149379,145.978867), - ($$2663$$,$$MARINNA$$,#{state_id_nsw},-35.149379,145.978867), - ($$2663$$,$$WANTIOOL$$,#{state_id_nsw},-35.149379,145.978867), - ($$2665$$,$$ARDLETHAN$$,#{state_id_nsw},-34.357355,146.903401), - ($$2665$$,$$ARIAH PARK$$,#{state_id_nsw},-34.357355,146.903401), - ($$2665$$,$$BARELLAN$$,#{state_id_nsw},-34.357355,146.903401), - ($$2665$$,$$BECKOM$$,#{state_id_nsw},-34.357355,146.903401), - ($$2665$$,$$BECTRIC$$,#{state_id_nsw},-34.357355,146.903401), - ($$2665$$,$$BINYA$$,#{state_id_nsw},-34.357355,146.903401), - ($$2665$$,$$KAMARAH$$,#{state_id_nsw},-34.357355,146.903401), - ($$2665$$,$$MIRROOL$$,#{state_id_nsw},-34.357355,146.903401), - ($$2665$$,$$MOOMBOOLDOOL$$,#{state_id_nsw},-34.357355,146.903401), - ($$2665$$,$$QUANDARY$$,#{state_id_nsw},-34.357355,146.903401), - ($$2665$$,$$TARA$$,#{state_id_nsw},-34.357355,146.903401), - ($$2665$$,$$WALLEROOBIE$$,#{state_id_nsw},-34.357355,146.903401), - ($$2666$$,$$COMBANING$$,#{state_id_nsw},-34.458856,147.68243), - ($$2666$$,$$DIRNASEER$$,#{state_id_nsw},-34.458856,147.68243), - ($$2666$$,$$GIDGINBUNG$$,#{state_id_nsw},-34.458856,147.68243), - ($$2666$$,$$GROGAN$$,#{state_id_nsw},-34.458856,147.68243), - ($$2666$$,$$JUNEE REEFS$$,#{state_id_nsw},-34.458856,147.68243), - ($$2666$$,$$MIMOSA$$,#{state_id_nsw},-34.458856,147.68243), - ($$2666$$,$$MORANGARELL$$,#{state_id_nsw},-34.458856,147.68243), - ($$2666$$,$$NARRABURRA$$,#{state_id_nsw},-34.458856,147.68243), - ($$2666$$,$$PUCAWAN$$,#{state_id_nsw},-34.458856,147.68243), - ($$2666$$,$$REEFTON$$,#{state_id_nsw},-34.458856,147.68243), - ($$2666$$,$$SEBASTOPOL$$,#{state_id_nsw},-34.458856,147.68243), - ($$2666$$,$$SPRINGDALE$$,#{state_id_nsw},-34.458856,147.68243), - ($$2666$$,$$TEMORA$$,#{state_id_nsw},-34.458856,147.68243), - ($$2666$$,$$TRUNGLEY HALL$$,#{state_id_nsw},-34.458856,147.68243), - ($$2668$$,$$BARMEDMAN$$,#{state_id_nsw},-34.184676,147.406754), - ($$2669$$,$$ERIGOLIA$$,#{state_id_nsw},-33.498371,146.803615), - ($$2669$$,$$GIRRAL$$,#{state_id_nsw},-33.498371,146.803615), - ($$2669$$,$$KIKOIRA$$,#{state_id_nsw},-33.498371,146.803615), - ($$2669$$,$$MELBERGEN$$,#{state_id_nsw},-33.498371,146.803615), - ($$2669$$,$$NARADHAN$$,#{state_id_nsw},-33.498371,146.803615), - ($$2669$$,$$RANKINS SPRINGS$$,#{state_id_nsw},-33.498371,146.803615), - ($$2669$$,$$TALLIMBA$$,#{state_id_nsw},-33.498371,146.803615), - ($$2669$$,$$TULLIBIGEAL$$,#{state_id_nsw},-33.498371,146.803615), - ($$2669$$,$$UNGARIE$$,#{state_id_nsw},-33.498371,146.803615), - ($$2669$$,$$WEETHALLE$$,#{state_id_nsw},-33.498371,146.803615), - ($$2671$$,$$ALLEENA$$,#{state_id_nsw},-34.109459,147.140966), - ($$2671$$,$$BACK CREEK$$,#{state_id_nsw},-34.109459,147.140966), - ($$2671$$,$$BURCHER$$,#{state_id_nsw},-34.109459,147.140966), - ($$2671$$,$$LAKE COWAL$$,#{state_id_nsw},-34.109459,147.140966), - ($$2671$$,$$NORTH YALGOGRIN$$,#{state_id_nsw},-34.109459,147.140966), - ($$2671$$,$$WEST WYALONG$$,#{state_id_nsw},-34.109459,147.140966), - ($$2671$$,$$WYALONG$$,#{state_id_nsw},-34.109459,147.140966), - ($$2672$$,$$CURLEW WATERS$$,#{state_id_nsw},-33.387503,146.579788), - ($$2672$$,$$LAKE CARGELLIGO$$,#{state_id_nsw},-33.387503,146.579788), - ($$2672$$,$$MURRIN BRIDGE$$,#{state_id_nsw},-33.387503,146.579788), - ($$2675$$,$$HILLSTON$$,#{state_id_nsw},-33.481365,145.534656), - ($$2675$$,$$LAKE BREWSTER$$,#{state_id_nsw},-33.481365,145.534656), - ($$2675$$,$$MONIA GAP$$,#{state_id_nsw},-33.481365,145.534656), - ($$2675$$,$$ROTO$$,#{state_id_nsw},-33.481365,145.534656), - ($$2675$$,$$WALLANTHERY$$,#{state_id_nsw},-33.481365,145.534656), - ($$2678$$,$$CHARLES STURT UNIVERSITY$$,#{state_id_nsw},-35.059334,147.351953), - ($$2678$$,$$RIVERINA MSC$$,#{state_id_nsw},-35.059334,147.351953), - ($$2680$$,$$BEELBANGERA$$,#{state_id_nsw},-34.257097,146.100092), - ($$2680$$,$$BENEREMBAH$$,#{state_id_nsw},-34.257097,146.100092), - ($$2680$$,$$BILBUL$$,#{state_id_nsw},-34.257097,146.100092), - ($$2680$$,$$GRIFFITH$$,#{state_id_nsw},-34.257097,146.100092), - ($$2680$$,$$GRIFFITH DC$$,#{state_id_nsw},-34.257097,146.100092), - ($$2680$$,$$GRIFFITH EAST$$,#{state_id_nsw},-34.257097,146.100092), - ($$2680$$,$$HANWOOD$$,#{state_id_nsw},-34.257097,146.100092), - ($$2680$$,$$KOOBA$$,#{state_id_nsw},-34.257097,146.100092), - ($$2680$$,$$LAKE WYANGAN$$,#{state_id_nsw},-34.257097,146.100092), - ($$2680$$,$$NERICON$$,#{state_id_nsw},-34.257097,146.100092), - ($$2680$$,$$THARBOGANG$$,#{state_id_nsw},-34.257097,146.100092), - ($$2680$$,$$WARBURN$$,#{state_id_nsw},-34.257097,146.100092), - ($$2680$$,$$WARRAWIDGEE$$,#{state_id_nsw},-34.257097,146.100092), - ($$2680$$,$$WIDGELLI$$,#{state_id_nsw},-34.257097,146.100092), - ($$2680$$,$$WILLBRIGGIE$$,#{state_id_nsw},-34.257097,146.100092), - ($$2680$$,$$YOOGALI$$,#{state_id_nsw},-34.257097,146.100092), - ($$2681$$,$$MYALL PARK$$,#{state_id_nsw},-34.177539,146.108252), - ($$2681$$,$$YENDA$$,#{state_id_nsw},-34.177539,146.108252), - ($$2700$$,$$BUNDURE$$,#{state_id_nsw},-35.031701,146.612603), - ($$2700$$,$$COLINROOBIE$$,#{state_id_nsw},-35.031701,146.612603), - ($$2700$$,$$COROBIMILLA$$,#{state_id_nsw},-35.031701,146.612603), - ($$2700$$,$$CUDGEL$$,#{state_id_nsw},-35.031701,146.612603), - ($$2700$$,$$EUROLEY$$,#{state_id_nsw},-35.031701,146.612603), - ($$2700$$,$$GILLENBAH$$,#{state_id_nsw},-35.031701,146.612603), - ($$2700$$,$$KYWONG$$,#{state_id_nsw},-35.031701,146.612603), - ($$2700$$,$$MORUNDAH$$,#{state_id_nsw},-35.031701,146.612603), - ($$2700$$,$$NARRANDERA$$,#{state_id_nsw},-35.031701,146.612603), - ($$2700$$,$$SANDIGO$$,#{state_id_nsw},-35.031701,146.612603), - ($$2701$$,$$BERRY JERRY$$,#{state_id_nsw},-34.815539,147.200085), - ($$2701$$,$$COOLAMON$$,#{state_id_nsw},-34.815539,147.200085), - ($$2701$$,$$METHUL$$,#{state_id_nsw},-34.815539,147.200085), - ($$2701$$,$$RANNOCK$$,#{state_id_nsw},-34.815539,147.200085), - ($$2702$$,$$GANMAIN$$,#{state_id_nsw},-34.794899,147.038823), - ($$2703$$,$$YANCO$$,#{state_id_nsw},-34.630843,146.40345), - ($$2705$$,$$BROBENAH$$,#{state_id_nsw},-34.48697,146.433213), - ($$2705$$,$$CORBIE HILL$$,#{state_id_nsw},-34.48697,146.433213), - ($$2705$$,$$GOGELDRIE$$,#{state_id_nsw},-34.48697,146.433213), - ($$2705$$,$$LEETON$$,#{state_id_nsw},-34.48697,146.433213), - ($$2705$$,$$MERUNGLE HILL$$,#{state_id_nsw},-34.48697,146.433213), - ($$2705$$,$$MURRAMI$$,#{state_id_nsw},-34.48697,146.433213), - ($$2705$$,$$STANBRIDGE$$,#{state_id_nsw},-34.48697,146.433213), - ($$2705$$,$$WHITTON$$,#{state_id_nsw},-34.48697,146.433213), - ($$2706$$,$$DARLINGTON POINT$$,#{state_id_nsw},-34.557729,146.010496), - ($$2707$$,$$ARGOON$$,#{state_id_nsw},-34.858279,145.674146), - ($$2707$$,$$COLEAMBALLY$$,#{state_id_nsw},-34.858279,145.674146), - ($$2708$$,$$ALBURY MSC$$,#{state_id_nsw},0.0,0.0), - ($$2708$$,$$MURRAY REGION MC$$,#{state_id_nsw},0.0,0.0), - ($$2710$$,$$BARRATTA$$,#{state_id_nsw},-35.228183,144.49432), - ($$2710$$,$$BIRGANBIGIL$$,#{state_id_nsw},-35.228183,144.49432), - ($$2710$$,$$BOOROORBAN$$,#{state_id_nsw},-35.228183,144.49432), - ($$2710$$,$$BULLATALE$$,#{state_id_nsw},-35.228183,144.49432), - ($$2710$$,$$CALDWELL$$,#{state_id_nsw},-35.228183,144.49432), - ($$2710$$,$$CALIMO$$,#{state_id_nsw},-35.228183,144.49432), - ($$2710$$,$$CONARGO$$,#{state_id_nsw},-35.228183,144.49432), - ($$2710$$,$$COREE$$,#{state_id_nsw},-35.228183,144.49432), - ($$2710$$,$$DENILIQUIN$$,#{state_id_nsw},-35.228183,144.49432), - ($$2710$$,$$HARTWOOD$$,#{state_id_nsw},-35.228183,144.49432), - ($$2710$$,$$LINDIFFERON$$,#{state_id_nsw},-35.228183,144.49432), - ($$2710$$,$$MATHOURA$$,#{state_id_nsw},-35.228183,144.49432), - ($$2710$$,$$MAYRUNG$$,#{state_id_nsw},-35.228183,144.49432), - ($$2710$$,$$MOONBRIA$$,#{state_id_nsw},-35.228183,144.49432), - ($$2710$$,$$MORAGO$$,#{state_id_nsw},-35.228183,144.49432), - ($$2710$$,$$PRETTY PINE$$,#{state_id_nsw},-35.228183,144.49432), - ($$2710$$,$$STEAM PLAINS$$,#{state_id_nsw},-35.228183,144.49432), - ($$2710$$,$$STUD PARK$$,#{state_id_nsw},-35.228183,144.49432), - ($$2710$$,$$WAKOOL$$,#{state_id_nsw},-35.228183,144.49432), - ($$2710$$,$$WANDOOK$$,#{state_id_nsw},-35.228183,144.49432), - ($$2710$$,$$WANGANELLA$$,#{state_id_nsw},-35.228183,144.49432), - ($$2710$$,$$WARRAGOON$$,#{state_id_nsw},-35.228183,144.49432), - ($$2710$$,$$WILLURAH$$,#{state_id_nsw},-35.228183,144.49432), - ($$2711$$,$$BOOLIGAL$$,#{state_id_nsw},-33.679442,144.749504), - ($$2711$$,$$CARRATHOOL$$,#{state_id_nsw},-33.679442,144.749504), - ($$2711$$,$$CLARE$$,#{state_id_nsw},-33.679442,144.749504), - ($$2711$$,$$CORRONG$$,#{state_id_nsw},-33.679442,144.749504), - ($$2711$$,$$GUNBAR$$,#{state_id_nsw},-33.679442,144.749504), - ($$2711$$,$$HAY$$,#{state_id_nsw},-33.679442,144.749504), - ($$2711$$,$$HAY SOUTH$$,#{state_id_nsw},-33.679442,144.749504), - ($$2711$$,$$KERI KERI$$,#{state_id_nsw},-33.679442,144.749504), - ($$2711$$,$$MAUDE$$,#{state_id_nsw},-33.679442,144.749504), - ($$2711$$,$$ONE TREE$$,#{state_id_nsw},-33.679442,144.749504), - ($$2711$$,$$OXLEY$$,#{state_id_nsw},-33.679442,144.749504), - ($$2711$$,$$WAUGORAH$$,#{state_id_nsw},-33.679442,144.749504), - ($$2711$$,$$YANGA$$,#{state_id_nsw},-33.679442,144.749504), - ($$2712$$,$$BERRIGAN$$,#{state_id_nsw},-35.65743,145.812641), - ($$2712$$,$$BOOMANOOMANA$$,#{state_id_nsw},-35.65743,145.812641), - ($$2713$$,$$BLIGHTY$$,#{state_id_nsw},-35.591687,145.285731), - ($$2713$$,$$FINLEY$$,#{state_id_nsw},-35.591687,145.285731), - ($$2713$$,$$LOGIE BRAE$$,#{state_id_nsw},-35.591687,145.285731), - ($$2713$$,$$MYRTLE PARK$$,#{state_id_nsw},-35.591687,145.285731), - ($$2714$$,$$ARATULA$$,#{state_id_nsw},-35.511748,145.051193), - ($$2714$$,$$PINE LODGE$$,#{state_id_nsw},-35.511748,145.051193), - ($$2714$$,$$TOCUMWAL$$,#{state_id_nsw},-35.511748,145.051193), - ($$2714$$,$$TUPPAL$$,#{state_id_nsw},-35.511748,145.051193), - ($$2715$$,$$ARUMPO$$,#{state_id_nsw},-33.873621,142.885264), - ($$2715$$,$$BALRANALD$$,#{state_id_nsw},-33.873621,142.885264), - ($$2715$$,$$HATFIELD$$,#{state_id_nsw},-33.873621,142.885264), - ($$2715$$,$$MUNGO$$,#{state_id_nsw},-33.873621,142.885264), - ($$2716$$,$$FOUR CORNERS$$,#{state_id_nsw},-35.354272,145.529572), - ($$2716$$,$$GALA VALE$$,#{state_id_nsw},-35.354272,145.529572), - ($$2716$$,$$JERILDERIE$$,#{state_id_nsw},-35.354272,145.529572), - ($$2716$$,$$MABINS WELL$$,#{state_id_nsw},-35.354272,145.529572), - ($$2716$$,$$MAIRJIMMY$$,#{state_id_nsw},-35.354272,145.529572), - ($$2717$$,$$DARETON$$,#{state_id_nsw},-34.091641,142.042284), - ($$2720$$,$$ARGALONG$$,#{state_id_nsw},-35.315496,148.453868), - ($$2720$$,$$BLOWERING$$,#{state_id_nsw},-35.315496,148.453868), - ($$2720$$,$$BOGONG PEAKS WILDERNESS$$,#{state_id_nsw},-35.315496,148.453868), - ($$2720$$,$$BOMBOWLEE$$,#{state_id_nsw},-35.315496,148.453868), - ($$2720$$,$$BOMBOWLEE CREEK$$,#{state_id_nsw},-35.315496,148.453868), - ($$2720$$,$$BUDDONG$$,#{state_id_nsw},-35.315496,148.453868), - ($$2720$$,$$COURAGAGO$$,#{state_id_nsw},-35.315496,148.453868), - ($$2720$$,$$GADARA$$,#{state_id_nsw},-35.315496,148.453868), - ($$2720$$,$$GILMORE$$,#{state_id_nsw},-35.315496,148.453868), - ($$2720$$,$$GOCUP$$,#{state_id_nsw},-35.315496,148.453868), - ($$2720$$,$$GOOBARRAGANDRA$$,#{state_id_nsw},-35.315496,148.453868), - ($$2720$$,$$JONES BRIDGE$$,#{state_id_nsw},-35.315496,148.453868), - ($$2720$$,$$KILLIMICAT$$,#{state_id_nsw},-35.315496,148.453868), - ($$2720$$,$$LACMALAC$$,#{state_id_nsw},-35.315496,148.453868), - ($$2720$$,$$LITTLE RIVER$$,#{state_id_nsw},-35.315496,148.453868), - ($$2720$$,$$MINJARY$$,#{state_id_nsw},-35.315496,148.453868), - ($$2720$$,$$MUNDONGO$$,#{state_id_nsw},-35.315496,148.453868), - ($$2720$$,$$PINBEYAN$$,#{state_id_nsw},-35.315496,148.453868), - ($$2720$$,$$RED HILL$$,#{state_id_nsw},-35.315496,148.453868), - ($$2720$$,$$TALBINGO$$,#{state_id_nsw},-35.315496,148.453868), - ($$2720$$,$$TUMORRAMA$$,#{state_id_nsw},-35.315496,148.453868), - ($$2720$$,$$TUMUT$$,#{state_id_nsw},-35.315496,148.453868), - ($$2720$$,$$TUMUT PLAINS$$,#{state_id_nsw},-35.315496,148.453868), - ($$2720$$,$$WEREBOLDERA$$,#{state_id_nsw},-35.315496,148.453868), - ($$2720$$,$$WERMATONG$$,#{state_id_nsw},-35.315496,148.453868), - ($$2720$$,$$WINDOWIE$$,#{state_id_nsw},-35.315496,148.453868), - ($$2720$$,$$WYANGLE$$,#{state_id_nsw},-35.315496,148.453868), - ($$2720$$,$$YARRANGOBILLY$$,#{state_id_nsw},-35.315496,148.453868), - ($$2721$$,$$BLAND$$,#{state_id_nsw},-33.994749,147.678307), - ($$2721$$,$$QUANDIALLA$$,#{state_id_nsw},-33.994749,147.678307), - ($$2722$$,$$BRUNGLE$$,#{state_id_nsw},-34.820143,148.084812), - ($$2722$$,$$BRUNGLE CREEK$$,#{state_id_nsw},-34.820143,148.084812), - ($$2722$$,$$BURRA CREEK$$,#{state_id_nsw},-34.820143,148.084812), - ($$2722$$,$$DARBALARA$$,#{state_id_nsw},-34.820143,148.084812), - ($$2722$$,$$GUNDAGAI$$,#{state_id_nsw},-34.820143,148.084812), - ($$2722$$,$$JONES CREEK$$,#{state_id_nsw},-34.820143,148.084812), - ($$2722$$,$$MUTTAMA$$,#{state_id_nsw},-34.820143,148.084812), - ($$2722$$,$$NANGUS$$,#{state_id_nsw},-34.820143,148.084812), - ($$2722$$,$$SOUTH GUNDAGAI$$,#{state_id_nsw},-34.820143,148.084812), - ($$2725$$,$$STOCKINBINGAL$$,#{state_id_nsw},-34.506023,147.879961), - ($$2726$$,$$JUGIONG$$,#{state_id_nsw},-34.906288,148.320014), - ($$2727$$,$$ADJUNGBILLY$$,#{state_id_nsw},-35.081463,148.40992), - ($$2727$$,$$COOLAC$$,#{state_id_nsw},-35.081463,148.40992), - ($$2727$$,$$GOBARRALONG$$,#{state_id_nsw},-35.081463,148.40992), - ($$2729$$,$$ADELONG$$,#{state_id_nsw},-35.307947,148.063682), - ($$2729$$,$$BANGADANG$$,#{state_id_nsw},-35.307947,148.063682), - ($$2729$$,$$BLACK CREEK$$,#{state_id_nsw},-35.307947,148.063682), - ($$2729$$,$$CALIFAT$$,#{state_id_nsw},-35.307947,148.063682), - ($$2729$$,$$COOLEYS CREEK$$,#{state_id_nsw},-35.307947,148.063682), - ($$2729$$,$$DARLOW$$,#{state_id_nsw},-35.307947,148.063682), - ($$2729$$,$$ELLERSLIE$$,#{state_id_nsw},-35.307947,148.063682), - ($$2729$$,$$GRAHAMSTOWN$$,#{state_id_nsw},-35.307947,148.063682), - ($$2729$$,$$MOUNT ADRAH$$,#{state_id_nsw},-35.307947,148.063682), - ($$2729$$,$$MOUNT HOREB$$,#{state_id_nsw},-35.307947,148.063682), - ($$2729$$,$$MUNDARLO$$,#{state_id_nsw},-35.307947,148.063682), - ($$2729$$,$$SANDY GULLY$$,#{state_id_nsw},-35.307947,148.063682), - ($$2729$$,$$SHARPS CREEK$$,#{state_id_nsw},-35.307947,148.063682), - ($$2729$$,$$TUMBLONG$$,#{state_id_nsw},-35.307947,148.063682), - ($$2729$$,$$WESTWOOD$$,#{state_id_nsw},-35.307947,148.063682), - ($$2729$$,$$WONDALGA$$,#{state_id_nsw},-35.307947,148.063682), - ($$2729$$,$$YAVEN CREEK$$,#{state_id_nsw},-35.307947,148.063682), - ($$2730$$,$$BATLOW$$,#{state_id_nsw},-31.488259,152.660113), - ($$2730$$,$$GREEN HILLS$$,#{state_id_nsw},-31.488259,152.660113), - ($$2730$$,$$KUNAMA$$,#{state_id_nsw},-31.488259,152.660113), - ($$2730$$,$$LOWER BAGO$$,#{state_id_nsw},-31.488259,152.660113), - ($$2731$$,$$BUNNALOO$$,#{state_id_nsw},-35.791187,144.629848), - ($$2731$$,$$MOAMA$$,#{state_id_nsw},-35.791187,144.629848), - ($$2731$$,$$TANTONAN$$,#{state_id_nsw},-35.791187,144.629848), - ($$2731$$,$$THYRA$$,#{state_id_nsw},-35.791187,144.629848), - ($$2731$$,$$WOMBOOTA$$,#{state_id_nsw},-35.791187,144.629848), - ($$2732$$,$$BARHAM$$,#{state_id_nsw},-35.630473,144.130423), - ($$2732$$,$$BURRABOI$$,#{state_id_nsw},-35.630473,144.130423), - ($$2732$$,$$COBRAMUNGA$$,#{state_id_nsw},-35.630473,144.130423), - ($$2732$$,$$GONN$$,#{state_id_nsw},-35.630473,144.130423), - ($$2732$$,$$NOORONG$$,#{state_id_nsw},-35.630473,144.130423), - ($$2732$$,$$THULE$$,#{state_id_nsw},-35.630473,144.130423), - ($$2732$$,$$TULLAKOOL$$,#{state_id_nsw},-35.630473,144.130423), - ($$2733$$,$$DHURAGOON$$,#{state_id_nsw},-35.194233,144.155278), - ($$2733$$,$$MOULAMEIN$$,#{state_id_nsw},-35.194233,144.155278), - ($$2733$$,$$NIEMUR$$,#{state_id_nsw},-35.194233,144.155278), - ($$2734$$,$$CUNNINYEUK$$,#{state_id_nsw},-35.247256,143.89834), - ($$2734$$,$$DILPURRA$$,#{state_id_nsw},-35.247256,143.89834), - ($$2734$$,$$KYALITE$$,#{state_id_nsw},-35.247256,143.89834), - ($$2734$$,$$MELLOOL$$,#{state_id_nsw},-35.247256,143.89834), - ($$2734$$,$$MOOLPA$$,#{state_id_nsw},-35.247256,143.89834), - ($$2734$$,$$STONY CROSSING$$,#{state_id_nsw},-35.247256,143.89834), - ($$2734$$,$$TOORANIE$$,#{state_id_nsw},-35.247256,143.89834), - ($$2734$$,$$WETUPPA$$,#{state_id_nsw},-35.247256,143.89834), - ($$2735$$,$$KORALEIGH$$,#{state_id_nsw},-35.658614,144.136534), - ($$2735$$,$$SPEEWA$$,#{state_id_nsw},-35.658614,144.136534), - ($$2736$$,$$GOODNIGHT$$,#{state_id_nsw},-34.958682,143.33744), - ($$2736$$,$$TOOLEYBUC$$,#{state_id_nsw},-34.958682,143.33744), - ($$2737$$,$$EUSTON$$,#{state_id_nsw},-34.513995,142.848756), - ($$2738$$,$$GOL GOL$$,#{state_id_nsw},-34.180087,142.219531), - ($$2738$$,$$MONAK$$,#{state_id_nsw},-34.180087,142.219531), - ($$2739$$,$$BURONGA$$,#{state_id_nsw},-34.171443,142.182794), - ($$2745$$,$$GLENMORE PARK$$,#{state_id_nsw},-33.790683,150.6693), - ($$2745$$,$$GREENDALE$$,#{state_id_nsw},-33.790683,150.6693), - ($$2745$$,$$LUDDENHAM$$,#{state_id_nsw},-33.790683,150.6693), - ($$2745$$,$$MULGOA$$,#{state_id_nsw},-33.790683,150.6693), - ($$2745$$,$$REGENTVILLE$$,#{state_id_nsw},-33.790683,150.6693), - ($$2745$$,$$WALLACIA$$,#{state_id_nsw},-33.790683,150.6693), - ($$2747$$,$$CAMBRIDGE GARDENS$$,#{state_id_nsw},-33.735558,150.721712), - ($$2747$$,$$CAMBRIDGE PARK$$,#{state_id_nsw},-33.735558,150.721712), - ($$2747$$,$$CLAREMONT MEADOWS$$,#{state_id_nsw},-33.735558,150.721712), - ($$2747$$,$$KINGSWOOD$$,#{state_id_nsw},-33.735558,150.721712), - ($$2747$$,$$LLANDILO$$,#{state_id_nsw},-33.735558,150.721712), - ($$2747$$,$$SHANES PARK$$,#{state_id_nsw},-33.735558,150.721712), - ($$2747$$,$$WERRINGTON$$,#{state_id_nsw},-33.735558,150.721712), - ($$2747$$,$$WERRINGTON COUNTY$$,#{state_id_nsw},-33.735558,150.721712), - ($$2747$$,$$WERRINGTON DOWNS$$,#{state_id_nsw},-33.735558,150.721712), - ($$2748$$,$$ORCHARD HILLS$$,#{state_id_nsw},-33.779331,150.716312), - ($$2749$$,$$CASTLEREAGH$$,#{state_id_nsw},-33.668796,150.67655), - ($$2749$$,$$CRANEBROOK$$,#{state_id_nsw},-33.668796,150.67655), - ($$2750$$,$$EMU HEIGHTS$$,#{state_id_nsw},-33.735636,150.650321), - ($$2750$$,$$EMU PLAINS$$,#{state_id_nsw},-33.735636,150.650321), - ($$2750$$,$$JAMISONTOWN$$,#{state_id_nsw},-33.735636,150.650321), - ($$2750$$,$$LEONAY$$,#{state_id_nsw},-33.735636,150.650321), - ($$2750$$,$$PENRITH$$,#{state_id_nsw},-33.735636,150.650321), - ($$2750$$,$$PENRITH PLAZA$$,#{state_id_nsw},-33.735636,150.650321), - ($$2750$$,$$PENRITH SOUTH$$,#{state_id_nsw},-33.735636,150.650321), - ($$2750$$,$$SOUTH PENRITH$$,#{state_id_nsw},-33.735636,150.650321), - ($$2751$$,$$PENRITH$$,#{state_id_nsw},-33.732127,151.280352), - ($$2752$$,$$SILVERDALE$$,#{state_id_nsw},-33.942212,150.580102), - ($$2752$$,$$WARRAGAMBA$$,#{state_id_nsw},-33.942212,150.580102), - ($$2753$$,$$AGNES BANKS$$,#{state_id_nsw},-33.618877,150.707372), - ($$2753$$,$$BOWEN MOUNTAIN$$,#{state_id_nsw},-33.618877,150.707372), - ($$2753$$,$$GROSE VALE$$,#{state_id_nsw},-33.618877,150.707372), - ($$2753$$,$$GROSE WOLD$$,#{state_id_nsw},-33.618877,150.707372), - ($$2753$$,$$HOBARTVILLE$$,#{state_id_nsw},-33.618877,150.707372), - ($$2753$$,$$LONDONDERRY$$,#{state_id_nsw},-33.618877,150.707372), - ($$2753$$,$$RICHMOND$$,#{state_id_nsw},-33.618877,150.707372), - ($$2753$$,$$RICHMOND LOWLANDS$$,#{state_id_nsw},-33.618877,150.707372), - ($$2753$$,$$YARRAMUNDI$$,#{state_id_nsw},-33.618877,150.707372), - ($$2754$$,$$NORTH RICHMOND$$,#{state_id_nsw},-33.582355,150.721891), - ($$2754$$,$$TENNYSON$$,#{state_id_nsw},-33.582355,150.721891), - ($$2754$$,$$THE SLOPES$$,#{state_id_nsw},-33.582355,150.721891), - ($$2755$$,$$RICHMOND RAAF$$,#{state_id_nsw},-33.604378,150.796234), - ($$2756$$,$$BLIGH PARK$$,#{state_id_nsw},-33.63765,150.79458), - ($$2756$$,$$CATTAI$$,#{state_id_nsw},-33.63765,150.79458), - ($$2756$$,$$CENTRAL COLO$$,#{state_id_nsw},-33.63765,150.79458), - ($$2756$$,$$CLARENDON$$,#{state_id_nsw},-33.63765,150.79458), - ($$2756$$,$$COLO$$,#{state_id_nsw},-33.63765,150.79458), - ($$2756$$,$$COLO HEIGHTS$$,#{state_id_nsw},-33.63765,150.79458), - ($$2756$$,$$CORNWALLIS$$,#{state_id_nsw},-33.63765,150.79458), - ($$2756$$,$$CUMBERLAND REACH$$,#{state_id_nsw},-33.63765,150.79458), - ($$2756$$,$$EBENEZER$$,#{state_id_nsw},-33.63765,150.79458), - ($$2756$$,$$FREEMANS REACH$$,#{state_id_nsw},-33.63765,150.79458), - ($$2756$$,$$GLOSSODIA$$,#{state_id_nsw},-33.63765,150.79458), - ($$2756$$,$$LOWER PORTLAND$$,#{state_id_nsw},-33.63765,150.79458), - ($$2756$$,$$MAROOTA$$,#{state_id_nsw},-33.63765,150.79458), - ($$2756$$,$$MCGRATHS HILL$$,#{state_id_nsw},-33.63765,150.79458), - ($$2756$$,$$MELLONG$$,#{state_id_nsw},-33.63765,150.79458), - ($$2756$$,$$MULGRAVE$$,#{state_id_nsw},-33.63765,150.79458), - ($$2756$$,$$PITT TOWN$$,#{state_id_nsw},-33.63765,150.79458), - ($$2756$$,$$PITT TOWN BOTTOMS$$,#{state_id_nsw},-33.63765,150.79458), - ($$2756$$,$$SACKVILLE$$,#{state_id_nsw},-33.63765,150.79458), - ($$2756$$,$$SACKVILLE NORTH$$,#{state_id_nsw},-33.63765,150.79458), - ($$2756$$,$$SCHEYVILLE$$,#{state_id_nsw},-33.63765,150.79458), - ($$2756$$,$$SOUTH MAROOTA$$,#{state_id_nsw},-33.63765,150.79458), - ($$2756$$,$$SOUTH WINDSOR$$,#{state_id_nsw},-33.63765,150.79458), - ($$2756$$,$$UPPER COLO$$,#{state_id_nsw},-33.63765,150.79458), - ($$2756$$,$$WILBERFORCE$$,#{state_id_nsw},-33.63765,150.79458), - ($$2756$$,$$WINDSOR$$,#{state_id_nsw},-33.63765,150.79458), - ($$2756$$,$$WINDSOR DOWNS$$,#{state_id_nsw},-33.63765,150.79458), - ($$2756$$,$$WOMERAH$$,#{state_id_nsw},-33.63765,150.79458), - ($$2757$$,$$KURMOND$$,#{state_id_nsw},-33.549452,150.701116), - ($$2758$$,$$BERAMBING$$,#{state_id_nsw},-33.53648,150.442845), - ($$2758$$,$$BILPIN$$,#{state_id_nsw},-33.53648,150.442845), - ($$2758$$,$$BLAXLANDS RIDGE$$,#{state_id_nsw},-33.53648,150.442845), - ($$2758$$,$$EAST KURRAJONG$$,#{state_id_nsw},-33.53648,150.442845), - ($$2758$$,$$KURRAJONG$$,#{state_id_nsw},-33.53648,150.442845), - ($$2758$$,$$KURRAJONG HEIGHTS$$,#{state_id_nsw},-33.53648,150.442845), - ($$2758$$,$$KURRAJONG HILLS$$,#{state_id_nsw},-33.53648,150.442845), - ($$2758$$,$$MOUNT TOMAH$$,#{state_id_nsw},-33.53648,150.442845), - ($$2758$$,$$MOUNTAIN LAGOON$$,#{state_id_nsw},-33.53648,150.442845), - ($$2758$$,$$THE DEVILS WILDERNESS$$,#{state_id_nsw},-33.53648,150.442845), - ($$2758$$,$$WHEENY CREEK$$,#{state_id_nsw},-33.53648,150.442845), - ($$2759$$,$$ERSKINE PARK$$,#{state_id_nsw},-33.807618,150.789313), - ($$2759$$,$$ST CLAIR$$,#{state_id_nsw},-33.807618,150.789313), - ($$2760$$,$$COLYTON$$,#{state_id_nsw},-33.776657,150.793433), - ($$2760$$,$$NORTH ST MARYS$$,#{state_id_nsw},-33.776657,150.793433), - ($$2760$$,$$OXLEY PARK$$,#{state_id_nsw},-33.776657,150.793433), - ($$2760$$,$$ROPES CROSSING$$,#{state_id_nsw},-33.776657,150.793433), - ($$2760$$,$$ST MARYS$$,#{state_id_nsw},-33.776657,150.793433), - ($$2760$$,$$ST MARYS EAST$$,#{state_id_nsw},-33.776657,150.793433), - ($$2760$$,$$ST MARYS SOUTH$$,#{state_id_nsw},-33.776657,150.793433), - ($$2761$$,$$COLEBEE$$,#{state_id_nsw},-33.730647,150.866046), - ($$2761$$,$$DEAN PARK$$,#{state_id_nsw},-33.730647,150.866046), - ($$2761$$,$$GLENDENNING$$,#{state_id_nsw},-33.730647,150.866046), - ($$2761$$,$$HASSALL GROVE$$,#{state_id_nsw},-33.730647,150.866046), - ($$2761$$,$$OAKHURST$$,#{state_id_nsw},-33.730647,150.866046), - ($$2761$$,$$PLUMPTON$$,#{state_id_nsw},-33.730647,150.866046), - ($$2762$$,$$SCHOFIELDS$$,#{state_id_nsw},-33.697217,150.888428), - ($$2763$$,$$ACACIA GARDENS$$,#{state_id_nsw},-33.730077,150.906502), - ($$2763$$,$$QUAKERS HILL$$,#{state_id_nsw},-33.730077,150.906502), - ($$2765$$,$$BERKSHIRE PARK$$,#{state_id_nsw},-33.672237,150.79576), - ($$2765$$,$$BOX HILL$$,#{state_id_nsw},-33.672237,150.79576), - ($$2765$$,$$MARAYLYA$$,#{state_id_nsw},-33.672237,150.79576), - ($$2765$$,$$MARSDEN PARK$$,#{state_id_nsw},-33.672237,150.79576), - ($$2765$$,$$NELSON$$,#{state_id_nsw},-33.672237,150.79576), - ($$2765$$,$$OAKVILLE$$,#{state_id_nsw},-33.672237,150.79576), - ($$2765$$,$$RIVERSTONE$$,#{state_id_nsw},-33.672237,150.79576), - ($$2765$$,$$VINEYARD$$,#{state_id_nsw},-33.672237,150.79576), - ($$2766$$,$$EASTERN CREEK$$,#{state_id_nsw},-33.803114,150.852192), - ($$2766$$,$$ROOTY HILL$$,#{state_id_nsw},-33.803114,150.852192), - ($$2767$$,$$DOONSIDE$$,#{state_id_nsw},-33.765071,150.86929), - ($$2767$$,$$WOODCROFT$$,#{state_id_nsw},-33.765071,150.86929), - ($$2768$$,$$GLENWOOD$$,#{state_id_nsw},-33.737863,150.922732), - ($$2768$$,$$PARKLEA$$,#{state_id_nsw},-33.737863,150.922732), - ($$2768$$,$$STANHOPE GARDENS$$,#{state_id_nsw},-33.737863,150.922732), - ($$2769$$,$$THE PONDS$$,#{state_id_nsw},-34.054326,150.753104), - ($$2770$$,$$BIDWILL$$,#{state_id_nsw},-33.73024,150.822765), - ($$2770$$,$$BLACKETT$$,#{state_id_nsw},-33.73024,150.822765), - ($$2770$$,$$DHARRUK$$,#{state_id_nsw},-33.73024,150.822765), - ($$2770$$,$$EMERTON$$,#{state_id_nsw},-33.73024,150.822765), - ($$2770$$,$$HEBERSHAM$$,#{state_id_nsw},-33.73024,150.822765), - ($$2770$$,$$LETHBRIDGE PARK$$,#{state_id_nsw},-33.73024,150.822765), - ($$2770$$,$$MINCHINBURY$$,#{state_id_nsw},-33.73024,150.822765), - ($$2770$$,$$MOUNT DRUITT$$,#{state_id_nsw},-33.73024,150.822765), - ($$2770$$,$$MOUNT DRUITT VILLAGE$$,#{state_id_nsw},-33.73024,150.822765), - ($$2770$$,$$SHALVEY$$,#{state_id_nsw},-33.73024,150.822765), - ($$2770$$,$$TREGEAR$$,#{state_id_nsw},-33.73024,150.822765), - ($$2770$$,$$WHALAN$$,#{state_id_nsw},-33.73024,150.822765), - ($$2770$$,$$WILLMOT$$,#{state_id_nsw},-33.73024,150.822765), - ($$2773$$,$$GLENBROOK$$,#{state_id_nsw},-33.768024,150.621693), - ($$2773$$,$$LAPSTONE$$,#{state_id_nsw},-33.768024,150.621693), - ($$2774$$,$$BLAXLAND$$,#{state_id_nsw},-33.744263,150.610076), - ($$2774$$,$$BLAXLAND EAST$$,#{state_id_nsw},-33.744263,150.610076), - ($$2774$$,$$MOUNT RIVERVIEW$$,#{state_id_nsw},-33.744263,150.610076), - ($$2774$$,$$WARRIMOO$$,#{state_id_nsw},-33.744263,150.610076), - ($$2775$$,$$CENTRAL MACDONALD$$,#{state_id_nsw},-33.331658,150.975498), - ($$2775$$,$$FERNANCES$$,#{state_id_nsw},-33.331658,150.975498), - ($$2775$$,$$GUNDERMAN$$,#{state_id_nsw},-33.331658,150.975498), - ($$2775$$,$$HIGHER MACDONALD$$,#{state_id_nsw},-33.331658,150.975498), - ($$2775$$,$$LAUGHTONDALE$$,#{state_id_nsw},-33.331658,150.975498), - ($$2775$$,$$LEETS VALE$$,#{state_id_nsw},-33.331658,150.975498), - ($$2775$$,$$LOWER MACDONALD$$,#{state_id_nsw},-33.331658,150.975498), - ($$2775$$,$$MARLOW$$,#{state_id_nsw},-33.331658,150.975498), - ($$2775$$,$$MOGO CREEK$$,#{state_id_nsw},-33.331658,150.975498), - ($$2775$$,$$PERRYS CROSSING$$,#{state_id_nsw},-33.331658,150.975498), - ($$2775$$,$$SINGLETONS MILL$$,#{state_id_nsw},-33.331658,150.975498), - ($$2775$$,$$SPENCER$$,#{state_id_nsw},-33.331658,150.975498), - ($$2775$$,$$ST ALBANS$$,#{state_id_nsw},-33.331658,150.975498), - ($$2775$$,$$UPPER MACDONALD$$,#{state_id_nsw},-33.331658,150.975498), - ($$2775$$,$$WEBBS CREEK$$,#{state_id_nsw},-33.331658,150.975498), - ($$2775$$,$$WISEMANS FERRY$$,#{state_id_nsw},-33.331658,150.975498), - ($$2775$$,$$WRIGHTS CREEK$$,#{state_id_nsw},-33.331658,150.975498), - ($$2776$$,$$FAULCONBRIDGE$$,#{state_id_nsw},-33.696505,150.534998), - ($$2777$$,$$HAWKESBURY HEIGHTS$$,#{state_id_nsw},-33.665114,150.650802), - ($$2777$$,$$SPRINGWOOD$$,#{state_id_nsw},-33.665114,150.650802), - ($$2777$$,$$SUN VALLEY$$,#{state_id_nsw},-33.665114,150.650802), - ($$2777$$,$$VALLEY HEIGHTS$$,#{state_id_nsw},-33.665114,150.650802), - ($$2777$$,$$WINMALEE$$,#{state_id_nsw},-33.665114,150.650802), - ($$2777$$,$$YELLOW ROCK$$,#{state_id_nsw},-33.665114,150.650802), - ($$2778$$,$$LINDEN$$,#{state_id_nsw},-33.794605,150.04041), - ($$2778$$,$$WOODFORD$$,#{state_id_nsw},-33.794605,150.04041), - ($$2779$$,$$HAZELBROOK$$,#{state_id_nsw},-33.720992,150.451629), - ($$2780$$,$$KATOOMBA$$,#{state_id_nsw},-33.714043,150.311589), - ($$2780$$,$$KATOOMBA DC$$,#{state_id_nsw},-33.714043,150.311589), - ($$2780$$,$$LEURA$$,#{state_id_nsw},-33.714043,150.311589), - ($$2780$$,$$MEDLOW BATH$$,#{state_id_nsw},-33.714043,150.311589), - ($$2782$$,$$WENTWORTH FALLS$$,#{state_id_nsw},-33.709836,150.376454), - ($$2783$$,$$LAWSON$$,#{state_id_nsw},-33.718957,150.430094), - ($$2784$$,$$BULLABURRA$$,#{state_id_nsw},-33.722751,150.41364), - ($$2785$$,$$BLACKHEATH$$,#{state_id_nsw},-33.635556,150.28483), - ($$2785$$,$$MEGALONG$$,#{state_id_nsw},-33.635556,150.28483), - ($$2786$$,$$BELL$$,#{state_id_nsw},-33.513745,150.279391), - ($$2786$$,$$DARGAN$$,#{state_id_nsw},-33.513745,150.279391), - ($$2786$$,$$MOUNT IRVINE$$,#{state_id_nsw},-33.513745,150.279391), - ($$2786$$,$$MOUNT VICTORIA$$,#{state_id_nsw},-33.513745,150.279391), - ($$2786$$,$$MOUNT WILSON$$,#{state_id_nsw},-33.513745,150.279391), - ($$2787$$,$$BLACK SPRINGS$$,#{state_id_nsw},-33.840966,149.711027), - ($$2787$$,$$CHATHAM VALLEY$$,#{state_id_nsw},-33.840966,149.711027), - ($$2787$$,$$DUCKMALOI$$,#{state_id_nsw},-33.840966,149.711027), - ($$2787$$,$$EDITH$$,#{state_id_nsw},-33.840966,149.711027), - ($$2787$$,$$GINGKIN$$,#{state_id_nsw},-33.840966,149.711027), - ($$2787$$,$$GURNANG$$,#{state_id_nsw},-33.840966,149.711027), - ($$2787$$,$$HAZELGROVE$$,#{state_id_nsw},-33.840966,149.711027), - ($$2787$$,$$JAUNTER$$,#{state_id_nsw},-33.840966,149.711027), - ($$2787$$,$$KANANGRA$$,#{state_id_nsw},-33.840966,149.711027), - ($$2787$$,$$MAYFIELD$$,#{state_id_nsw},-33.840966,149.711027), - ($$2787$$,$$MOUNT OLIVE$$,#{state_id_nsw},-33.840966,149.711027), - ($$2787$$,$$MOUNT WERONG$$,#{state_id_nsw},-33.840966,149.711027), - ($$2787$$,$$MOZART$$,#{state_id_nsw},-33.840966,149.711027), - ($$2787$$,$$NORWAY$$,#{state_id_nsw},-33.840966,149.711027), - ($$2787$$,$$OBERON$$,#{state_id_nsw},-33.840966,149.711027), - ($$2787$$,$$PORTERS RETREAT$$,#{state_id_nsw},-33.840966,149.711027), - ($$2787$$,$$SHOOTERS HILL$$,#{state_id_nsw},-33.840966,149.711027), - ($$2787$$,$$TARANA$$,#{state_id_nsw},-33.840966,149.711027), - ($$2787$$,$$THE MEADOWS$$,#{state_id_nsw},-33.840966,149.711027), - ($$2790$$,$$BEN BULLEN$$,#{state_id_nsw},-33.219587,150.021733), - ($$2790$$,$$BLACKMANS FLAT$$,#{state_id_nsw},-33.219587,150.021733), - ($$2790$$,$$BOWENFELS$$,#{state_id_nsw},-33.219587,150.021733), - ($$2790$$,$$CLARENCE$$,#{state_id_nsw},-33.219587,150.021733), - ($$2790$$,$$COBAR PARK$$,#{state_id_nsw},-33.219587,150.021733), - ($$2790$$,$$CORNEY TOWN$$,#{state_id_nsw},-33.219587,150.021733), - ($$2790$$,$$CULLEN BULLEN$$,#{state_id_nsw},-33.219587,150.021733), - ($$2790$$,$$DOCTORS GAP$$,#{state_id_nsw},-33.219587,150.021733), - ($$2790$$,$$GANBENANG$$,#{state_id_nsw},-33.219587,150.021733), - ($$2790$$,$$HAMPTON$$,#{state_id_nsw},-33.219587,150.021733), - ($$2790$$,$$HARTLEY$$,#{state_id_nsw},-33.219587,150.021733), - ($$2790$$,$$HARTLEY VALE$$,#{state_id_nsw},-33.219587,150.021733), - ($$2790$$,$$HASSANS WALLS$$,#{state_id_nsw},-33.219587,150.021733), - ($$2790$$,$$HERMITAGE FLAT$$,#{state_id_nsw},-33.219587,150.021733), - ($$2790$$,$$JENOLAN$$,#{state_id_nsw},-33.219587,150.021733), - ($$2790$$,$$KANIMBLA$$,#{state_id_nsw},-33.219587,150.021733), - ($$2790$$,$$LIDSDALE$$,#{state_id_nsw},-33.219587,150.021733), - ($$2790$$,$$LITHGOW$$,#{state_id_nsw},-33.219587,150.021733), - ($$2790$$,$$LITHGOW DC$$,#{state_id_nsw},-33.219587,150.021733), - ($$2790$$,$$LITTLE HARTLEY$$,#{state_id_nsw},-33.219587,150.021733), - ($$2790$$,$$LITTLETON$$,#{state_id_nsw},-33.219587,150.021733), - ($$2790$$,$$LOWTHER$$,#{state_id_nsw},-33.219587,150.021733), - ($$2790$$,$$MARRANGAROO$$,#{state_id_nsw},-33.219587,150.021733), - ($$2790$$,$$MCKELLARS PARK$$,#{state_id_nsw},-33.219587,150.021733), - ($$2790$$,$$MORTS ESTATE$$,#{state_id_nsw},-33.219587,150.021733), - ($$2790$$,$$MOUNT LAMBIE$$,#{state_id_nsw},-33.219587,150.021733), - ($$2790$$,$$NEWNES$$,#{state_id_nsw},-33.219587,150.021733), - ($$2790$$,$$NEWNES PLATEAU$$,#{state_id_nsw},-33.219587,150.021733), - ($$2790$$,$$OAKY PARK$$,#{state_id_nsw},-33.219587,150.021733), - ($$2790$$,$$POTTERY ESTATE$$,#{state_id_nsw},-33.219587,150.021733), - ($$2790$$,$$RYDAL$$,#{state_id_nsw},-33.219587,150.021733), - ($$2790$$,$$SHEEDYS GULLY$$,#{state_id_nsw},-33.219587,150.021733), - ($$2790$$,$$SODWALLS$$,#{state_id_nsw},-33.219587,150.021733), - ($$2790$$,$$SOUTH BOWENFELS$$,#{state_id_nsw},-33.219587,150.021733), - ($$2790$$,$$SOUTH LITTLETON$$,#{state_id_nsw},-33.219587,150.021733), - ($$2790$$,$$SPRINGVALE$$,#{state_id_nsw},-33.219587,150.021733), - ($$2790$$,$$STATE MINE GULLY$$,#{state_id_nsw},-33.219587,150.021733), - ($$2790$$,$$VALE OF CLWYDD$$,#{state_id_nsw},-33.219587,150.021733), - ($$2790$$,$$WOLGAN VALLEY$$,#{state_id_nsw},-33.219587,150.021733), - ($$2790$$,$$WOLLANGAMBE$$,#{state_id_nsw},-33.219587,150.021733), - ($$2791$$,$$CARCOAR$$,#{state_id_nsw},-33.609622,149.140601), - ($$2791$$,$$ERROWANBANG$$,#{state_id_nsw},-33.609622,149.140601), - ($$2792$$,$$BURNT YARDS$$,#{state_id_nsw},-33.585625,149.031455), - ($$2792$$,$$MANDURAMA$$,#{state_id_nsw},-33.585625,149.031455), - ($$2793$$,$$DARBYS FALLS$$,#{state_id_nsw},-33.931096,148.859104), - ($$2793$$,$$ROSEBERG$$,#{state_id_nsw},-33.931096,148.859104), - ($$2793$$,$$WOODSTOCK$$,#{state_id_nsw},-33.931096,148.859104), - ($$2794$$,$$BUMBALDRY$$,#{state_id_nsw},-33.906339,148.456385), - ($$2794$$,$$COWRA$$,#{state_id_nsw},-33.906339,148.456385), - ($$2794$$,$$HOVELLS CREEK$$,#{state_id_nsw},-33.906339,148.456385), - ($$2794$$,$$MOUNT COLLINS$$,#{state_id_nsw},-33.906339,148.456385), - ($$2794$$,$$WATTAMONDARA$$,#{state_id_nsw},-33.906339,148.456385), - ($$2795$$,$$ABERCROMBIE$$,#{state_id_nsw},-33.911806,149.332781), - ($$2795$$,$$ABERCROMBIE RIVER$$,#{state_id_nsw},-33.911806,149.332781), - ($$2795$$,$$ARKELL$$,#{state_id_nsw},-33.911806,149.332781), - ($$2795$$,$$ARKSTONE$$,#{state_id_nsw},-33.911806,149.332781), - ($$2795$$,$$BALD RIDGE$$,#{state_id_nsw},-33.911806,149.332781), - ($$2795$$,$$BALLYROE$$,#{state_id_nsw},-33.911806,149.332781), - ($$2795$$,$$BATHAMPTON$$,#{state_id_nsw},-33.911806,149.332781), - ($$2795$$,$$BATHURST$$,#{state_id_nsw},-33.911806,149.332781), - ($$2795$$,$$BILLYWILLINGA$$,#{state_id_nsw},-33.911806,149.332781), - ($$2795$$,$$BREWONGLE$$,#{state_id_nsw},-33.911806,149.332781), - ($$2795$$,$$BRUINBUN$$,#{state_id_nsw},-33.911806,149.332781), - ($$2795$$,$$BURRAGA$$,#{state_id_nsw},-33.911806,149.332781), - ($$2795$$,$$CALOOLA$$,#{state_id_nsw},-33.911806,149.332781), - ($$2795$$,$$CHARLES STURT UNIVERSITY$$,#{state_id_nsw},-33.911806,149.332781), - ($$2795$$,$$CHARLTON$$,#{state_id_nsw},-33.911806,149.332781), - ($$2795$$,$$CLEAR CREEK$$,#{state_id_nsw},-33.911806,149.332781), - ($$2795$$,$$COLO$$,#{state_id_nsw},-33.911806,149.332781), - ($$2795$$,$$COPPERHANNIA$$,#{state_id_nsw},-33.911806,149.332781), - ($$2795$$,$$COW FLAT$$,#{state_id_nsw},-33.911806,149.332781), - ($$2795$$,$$CRUDINE$$,#{state_id_nsw},-33.911806,149.332781), - ($$2795$$,$$CURRAGH$$,#{state_id_nsw},-33.911806,149.332781), - ($$2795$$,$$DARK CORNER$$,#{state_id_nsw},-33.911806,149.332781), - ($$2795$$,$$DOG ROCKS$$,#{state_id_nsw},-33.911806,149.332781), - ($$2795$$,$$DUNKELD$$,#{state_id_nsw},-33.911806,149.332781), - ($$2795$$,$$DURAMANA$$,#{state_id_nsw},-33.911806,149.332781), - ($$2795$$,$$EGLINTON$$,#{state_id_nsw},-33.911806,149.332781), - ($$2795$$,$$ESSINGTON$$,#{state_id_nsw},-33.911806,149.332781), - ($$2795$$,$$EVANS PLAINS$$,#{state_id_nsw},-33.911806,149.332781), - ($$2795$$,$$FITZGERALDS VALLEY$$,#{state_id_nsw},-33.911806,149.332781), - ($$2795$$,$$FOREST GROVE$$,#{state_id_nsw},-33.911806,149.332781), - ($$2795$$,$$FOSTERS VALLEY$$,#{state_id_nsw},-33.911806,149.332781), - ($$2795$$,$$FREEMANTLE$$,#{state_id_nsw},-33.911806,149.332781), - ($$2795$$,$$GEMALLA$$,#{state_id_nsw},-33.911806,149.332781), - ($$2795$$,$$GEORGES PLAINS$$,#{state_id_nsw},-33.911806,149.332781), - ($$2795$$,$$GILMANDYKE$$,#{state_id_nsw},-33.911806,149.332781), - ($$2795$$,$$GLANMIRE$$,#{state_id_nsw},-33.911806,149.332781), - ($$2795$$,$$GORMANS HILL$$,#{state_id_nsw},-33.911806,149.332781), - ($$2795$$,$$GOWAN$$,#{state_id_nsw},-33.911806,149.332781), - ($$2795$$,$$HOBBYS YARDS$$,#{state_id_nsw},-33.911806,149.332781), - ($$2795$$,$$ISABELLA$$,#{state_id_nsw},-33.911806,149.332781), - ($$2795$$,$$JEREMY$$,#{state_id_nsw},-33.911806,149.332781), - ($$2795$$,$$JUDDS CREEK$$,#{state_id_nsw},-33.911806,149.332781), - ($$2795$$,$$KELSO$$,#{state_id_nsw},-33.911806,149.332781), - ($$2795$$,$$KILLONGBUTTA$$,#{state_id_nsw},-33.911806,149.332781), - ($$2795$$,$$KIRKCONNELL$$,#{state_id_nsw},-33.911806,149.332781), - ($$2795$$,$$LAFFING WATERS$$,#{state_id_nsw},-33.911806,149.332781), - ($$2795$$,$$LIMEKILNS$$,#{state_id_nsw},-33.911806,149.332781), - ($$2795$$,$$LLANARTH$$,#{state_id_nsw},-33.911806,149.332781), - ($$2795$$,$$LOCKSLEY$$,#{state_id_nsw},-33.911806,149.332781), - ($$2795$$,$$MEADOW FLAT$$,#{state_id_nsw},-33.911806,149.332781), - ($$2795$$,$$MILKERS FLAT$$,#{state_id_nsw},-33.911806,149.332781), - ($$2795$$,$$MILLAH MURRAH$$,#{state_id_nsw},-33.911806,149.332781), - ($$2795$$,$$MITCHELL$$,#{state_id_nsw},-33.911806,149.332781), - ($$2795$$,$$MOORILDA$$,#{state_id_nsw},-33.911806,149.332781), - ($$2795$$,$$MOUNT DAVID$$,#{state_id_nsw},-33.911806,149.332781), - ($$2795$$,$$MOUNT PANORAMA$$,#{state_id_nsw},-33.911806,149.332781), - ($$2795$$,$$MOUNT RANKIN$$,#{state_id_nsw},-33.911806,149.332781), - ($$2795$$,$$NAPOLEON REEF$$,#{state_id_nsw},-33.911806,149.332781), - ($$2795$$,$$NEWBRIDGE$$,#{state_id_nsw},-33.911806,149.332781), - ($$2795$$,$$O'CONNELL$$,#{state_id_nsw},-33.911806,149.332781), - ($$2795$$,$$ORTON PARK$$,#{state_id_nsw},-33.911806,149.332781), - ($$2795$$,$$PALING YARDS$$,#{state_id_nsw},-33.911806,149.332781), - ($$2795$$,$$PALMERS OAKY$$,#{state_id_nsw},-33.911806,149.332781), - ($$2795$$,$$PEEL$$,#{state_id_nsw},-33.911806,149.332781), - ($$2795$$,$$PERTHVILLE$$,#{state_id_nsw},-33.911806,149.332781), - ($$2795$$,$$RAGLAN$$,#{state_id_nsw},-33.911806,149.332781), - ($$2795$$,$$ROBIN HILL$$,#{state_id_nsw},-33.911806,149.332781), - ($$2795$$,$$ROCK FOREST$$,#{state_id_nsw},-33.911806,149.332781), - ($$2795$$,$$ROCKLEY$$,#{state_id_nsw},-33.911806,149.332781), - ($$2795$$,$$ROCKLEY MOUNT$$,#{state_id_nsw},-33.911806,149.332781), - ($$2795$$,$$SOFALA$$,#{state_id_nsw},-33.911806,149.332781), - ($$2795$$,$$SOUTH BATHURST$$,#{state_id_nsw},-33.911806,149.332781), - ($$2795$$,$$STEWARTS MOUNT$$,#{state_id_nsw},-33.911806,149.332781), - ($$2795$$,$$SUNNY CORNER$$,#{state_id_nsw},-33.911806,149.332781), - ($$2795$$,$$TAMBAROORA$$,#{state_id_nsw},-33.911806,149.332781), - ($$2795$$,$$TANNAS MOUNT$$,#{state_id_nsw},-33.911806,149.332781), - ($$2795$$,$$THE LAGOON$$,#{state_id_nsw},-33.911806,149.332781), - ($$2795$$,$$THE ROCKS$$,#{state_id_nsw},-33.911806,149.332781), - ($$2795$$,$$TRIANGLE FLAT$$,#{state_id_nsw},-33.911806,149.332781), - ($$2795$$,$$TRUNKEY CREEK$$,#{state_id_nsw},-33.911806,149.332781), - ($$2795$$,$$TURONDALE$$,#{state_id_nsw},-33.911806,149.332781), - ($$2795$$,$$TWENTY FORESTS$$,#{state_id_nsw},-33.911806,149.332781), - ($$2795$$,$$UPPER TURON$$,#{state_id_nsw},-33.911806,149.332781), - ($$2795$$,$$WALANG$$,#{state_id_nsw},-33.911806,149.332781), - ($$2795$$,$$WAMBOOL$$,#{state_id_nsw},-33.911806,149.332781), - ($$2795$$,$$WATTLE FLAT$$,#{state_id_nsw},-33.911806,149.332781), - ($$2795$$,$$WATTON$$,#{state_id_nsw},-33.911806,149.332781), - ($$2795$$,$$WEST BATHURST$$,#{state_id_nsw},-33.911806,149.332781), - ($$2795$$,$$WHITE ROCK$$,#{state_id_nsw},-33.911806,149.332781), - ($$2795$$,$$WIAGDON$$,#{state_id_nsw},-33.911806,149.332781), - ($$2795$$,$$WIMBLEDON$$,#{state_id_nsw},-33.911806,149.332781), - ($$2795$$,$$WINBURNDALE$$,#{state_id_nsw},-33.911806,149.332781), - ($$2795$$,$$WINDRADYNE$$,#{state_id_nsw},-33.911806,149.332781), - ($$2795$$,$$WISEMANS CREEK$$,#{state_id_nsw},-33.911806,149.332781), - ($$2795$$,$$YARRAS$$,#{state_id_nsw},-33.911806,149.332781), - ($$2795$$,$$YETHOLME$$,#{state_id_nsw},-33.911806,149.332781), - ($$2796$$,$$BATHURST MC$$,#{state_id_nsw},0.0,0.0), - ($$2797$$,$$GARLAND$$,#{state_id_nsw},-33.707873,149.025809), - ($$2797$$,$$LYNDHURST$$,#{state_id_nsw},-33.707873,149.025809), - ($$2798$$,$$BYNG$$,#{state_id_nsw},-33.343288,149.254464), - ($$2798$$,$$FOREST REEFS$$,#{state_id_nsw},-33.343288,149.254464), - ($$2798$$,$$GUYONG$$,#{state_id_nsw},-33.343288,149.254464), - ($$2798$$,$$MILLTHORPE$$,#{state_id_nsw},-33.343288,149.254464), - ($$2798$$,$$SPRING TERRACE$$,#{state_id_nsw},-33.343288,149.254464), - ($$2798$$,$$TALLWOOD$$,#{state_id_nsw},-33.343288,149.254464), - ($$2799$$,$$BARRY$$,#{state_id_nsw},-33.648293,149.269545), - ($$2799$$,$$BLAYNEY$$,#{state_id_nsw},-33.648293,149.269545), - ($$2799$$,$$BROWNS CREEK$$,#{state_id_nsw},-33.648293,149.269545), - ($$2799$$,$$FITZGERALDS MOUNT$$,#{state_id_nsw},-33.648293,149.269545), - ($$2799$$,$$KINGS PLAINS$$,#{state_id_nsw},-33.648293,149.269545), - ($$2799$$,$$NEVILLE$$,#{state_id_nsw},-33.648293,149.269545), - ($$2799$$,$$VITTORIA$$,#{state_id_nsw},-33.648293,149.269545), - ($$2800$$,$$BELGRAVIA$$,#{state_id_nsw},-33.123439,149.026661), - ($$2800$$,$$BORENORE$$,#{state_id_nsw},-33.123439,149.026661), - ($$2800$$,$$CADIA$$,#{state_id_nsw},-33.123439,149.026661), - ($$2800$$,$$CANOBOLAS$$,#{state_id_nsw},-33.123439,149.026661), - ($$2800$$,$$CARGO$$,#{state_id_nsw},-33.123439,149.026661), - ($$2800$$,$$CLERGATE$$,#{state_id_nsw},-33.123439,149.026661), - ($$2800$$,$$CLIFTON GROVE$$,#{state_id_nsw},-33.123439,149.026661), - ($$2800$$,$$EMU SWAMP$$,#{state_id_nsw},-33.123439,149.026661), - ($$2800$$,$$FOUR MILE CREEK$$,#{state_id_nsw},-33.123439,149.026661), - ($$2800$$,$$HUNTLEY$$,#{state_id_nsw},-33.123439,149.026661), - ($$2800$$,$$KANGAROOBIE$$,#{state_id_nsw},-33.123439,149.026661), - ($$2800$$,$$KERRS CREEK$$,#{state_id_nsw},-33.123439,149.026661), - ($$2800$$,$$LEWIS PONDS$$,#{state_id_nsw},-33.123439,149.026661), - ($$2800$$,$$LIDSTER$$,#{state_id_nsw},-33.123439,149.026661), - ($$2800$$,$$LOWER LEWIS PONDS$$,#{state_id_nsw},-33.123439,149.026661), - ($$2800$$,$$LUCKNOW$$,#{state_id_nsw},-33.123439,149.026661), - ($$2800$$,$$MARCH$$,#{state_id_nsw},-33.123439,149.026661), - ($$2800$$,$$MULLION CREEK$$,#{state_id_nsw},-33.123439,149.026661), - ($$2800$$,$$NANGAR$$,#{state_id_nsw},-33.123439,149.026661), - ($$2800$$,$$NASHDALE$$,#{state_id_nsw},-33.123439,149.026661), - ($$2800$$,$$OPHIR$$,#{state_id_nsw},-33.123439,149.026661), - ($$2800$$,$$ORANGE$$,#{state_id_nsw},-33.123439,149.026661), - ($$2800$$,$$ORANGE DC$$,#{state_id_nsw},-33.123439,149.026661), - ($$2800$$,$$ORANGE EAST$$,#{state_id_nsw},-33.123439,149.026661), - ($$2800$$,$$PANUARA$$,#{state_id_nsw},-33.123439,149.026661), - ($$2800$$,$$SHADFORTH$$,#{state_id_nsw},-33.123439,149.026661), - ($$2800$$,$$SPRING CREEK$$,#{state_id_nsw},-33.123439,149.026661), - ($$2800$$,$$SPRING HILL$$,#{state_id_nsw},-33.123439,149.026661), - ($$2800$$,$$SPRINGSIDE$$,#{state_id_nsw},-33.123439,149.026661), - ($$2800$$,$$SUMMER HILL CREEK$$,#{state_id_nsw},-33.123439,149.026661), - ($$2800$$,$$WALDEGRAVE$$,#{state_id_nsw},-33.123439,149.026661), - ($$2800$$,$$WINDERA$$,#{state_id_nsw},-33.123439,149.026661), - ($$2803$$,$$BENDICK MURRELL$$,#{state_id_nsw},-34.16282,148.449846), - ($$2803$$,$$CROWTHER$$,#{state_id_nsw},-34.16282,148.449846), - ($$2803$$,$$WIRRIMAH$$,#{state_id_nsw},-34.16282,148.449846), - ($$2804$$,$$BILLIMARI$$,#{state_id_nsw},-33.682514,148.615476), - ($$2804$$,$$CANOWINDRA$$,#{state_id_nsw},-33.682514,148.615476), - ($$2804$$,$$MOORBEL$$,#{state_id_nsw},-33.682514,148.615476), - ($$2804$$,$$NYRANG CREEK$$,#{state_id_nsw},-33.682514,148.615476), - ($$2805$$,$$GOOLOOGONG$$,#{state_id_nsw},-33.650848,148.41385), - ($$2806$$,$$EUGOWRA$$,#{state_id_nsw},-33.427107,148.37165), - ($$2807$$,$$KOORAWATHA$$,#{state_id_nsw},-34.039891,148.553887), - ($$2808$$,$$WYANGALA$$,#{state_id_nsw},-33.936693,149.046361), - ($$2809$$,$$GREENETHORPE$$,#{state_id_nsw},-34.041955,148.395001), - ($$2810$$,$$BIMBI$$,#{state_id_nsw},-33.985252,147.927491), - ($$2810$$,$$CARAGABAL$$,#{state_id_nsw},-33.985252,147.927491), - ($$2810$$,$$GLENELG$$,#{state_id_nsw},-33.985252,147.927491), - ($$2810$$,$$GRENFELL$$,#{state_id_nsw},-33.985252,147.927491), - ($$2810$$,$$PINEY RANGE$$,#{state_id_nsw},-33.985252,147.927491), - ($$2810$$,$$PINNACLE$$,#{state_id_nsw},-33.985252,147.927491), - ($$2810$$,$$PULLABOOKA$$,#{state_id_nsw},-33.985252,147.927491), - ($$2810$$,$$WARRADERRY$$,#{state_id_nsw},-33.985252,147.927491), - ($$2820$$,$$APSLEY$$,#{state_id_nsw},-32.598356,148.963925), - ($$2820$$,$$ARTHURVILLE$$,#{state_id_nsw},-32.598356,148.963925), - ($$2820$$,$$BAKERS SWAMP$$,#{state_id_nsw},-32.598356,148.963925), - ($$2820$$,$$BODANGORA$$,#{state_id_nsw},-32.598356,148.963925), - ($$2820$$,$$COMOBELLA$$,#{state_id_nsw},-32.598356,148.963925), - ($$2820$$,$$CURRA CREEK$$,#{state_id_nsw},-32.598356,148.963925), - ($$2820$$,$$DRIPSTONE$$,#{state_id_nsw},-32.598356,148.963925), - ($$2820$$,$$FARNHAM$$,#{state_id_nsw},-32.598356,148.963925), - ($$2820$$,$$GOLLAN$$,#{state_id_nsw},-32.598356,148.963925), - ($$2820$$,$$LAKE BURRENDONG$$,#{state_id_nsw},-32.598356,148.963925), - ($$2820$$,$$MARYVALE$$,#{state_id_nsw},-32.598356,148.963925), - ($$2820$$,$$MEDWAY$$,#{state_id_nsw},-32.598356,148.963925), - ($$2820$$,$$MONTEFIORES$$,#{state_id_nsw},-32.598356,148.963925), - ($$2820$$,$$MOOKERAWA$$,#{state_id_nsw},-32.598356,148.963925), - ($$2820$$,$$MOUNT AQUILA$$,#{state_id_nsw},-32.598356,148.963925), - ($$2820$$,$$MOUNT ARTHUR$$,#{state_id_nsw},-32.598356,148.963925), - ($$2820$$,$$MUMBIL$$,#{state_id_nsw},-32.598356,148.963925), - ($$2820$$,$$NANIMA$$,#{state_id_nsw},-32.598356,148.963925), - ($$2820$$,$$NEUREA$$,#{state_id_nsw},-32.598356,148.963925), - ($$2820$$,$$SPICERS CREEK$$,#{state_id_nsw},-32.598356,148.963925), - ($$2820$$,$$STUART TOWN$$,#{state_id_nsw},-32.598356,148.963925), - ($$2820$$,$$SUNTOP$$,#{state_id_nsw},-32.598356,148.963925), - ($$2820$$,$$WALMER$$,#{state_id_nsw},-32.598356,148.963925), - ($$2820$$,$$WELLINGTON$$,#{state_id_nsw},-32.598356,148.963925), - ($$2820$$,$$WUULUMAN$$,#{state_id_nsw},-32.598356,148.963925), - ($$2820$$,$$YARRAGAL$$,#{state_id_nsw},-32.598356,148.963925), - ($$2821$$,$$BURROWAY$$,#{state_id_nsw},-32.057228,148.263375), - ($$2821$$,$$NARROMINE$$,#{state_id_nsw},-32.057228,148.263375), - ($$2823$$,$$BUNDEMAR$$,#{state_id_nsw},-31.835994,148.180564), - ($$2823$$,$$CATHUNDRAL$$,#{state_id_nsw},-31.835994,148.180564), - ($$2823$$,$$DANDALOO$$,#{state_id_nsw},-31.835994,148.180564), - ($$2823$$,$$GIN GIN$$,#{state_id_nsw},-31.835994,148.180564), - ($$2823$$,$$TRANGIE$$,#{state_id_nsw},-31.835994,148.180564), - ($$2824$$,$$BEEMUNNEL$$,#{state_id_nsw},-31.673327,147.855054), - ($$2824$$,$$EENAWEENA$$,#{state_id_nsw},-31.673327,147.855054), - ($$2824$$,$$MARTHAGUY$$,#{state_id_nsw},-31.673327,147.855054), - ($$2824$$,$$MOUNT FOSTER$$,#{state_id_nsw},-31.673327,147.855054), - ($$2824$$,$$MOUNT HARRIS$$,#{state_id_nsw},-31.673327,147.855054), - ($$2824$$,$$MUMBLEBONE PLAIN$$,#{state_id_nsw},-31.673327,147.855054), - ($$2824$$,$$OXLEY$$,#{state_id_nsw},-31.673327,147.855054), - ($$2824$$,$$PIGEONBAH$$,#{state_id_nsw},-31.673327,147.855054), - ($$2824$$,$$RAVENSWOOD$$,#{state_id_nsw},-31.673327,147.855054), - ($$2824$$,$$RED HILL$$,#{state_id_nsw},-31.673327,147.855054), - ($$2824$$,$$SNAKES PLAIN$$,#{state_id_nsw},-31.673327,147.855054), - ($$2824$$,$$TENANDRA$$,#{state_id_nsw},-31.673327,147.855054), - ($$2824$$,$$WARREN$$,#{state_id_nsw},-31.673327,147.855054), - ($$2825$$,$$BABINDA$$,#{state_id_nsw},-31.940261,146.479353), - ($$2825$$,$$BOBADAH$$,#{state_id_nsw},-31.940261,146.479353), - ($$2825$$,$$BOGAN$$,#{state_id_nsw},-31.940261,146.479353), - ($$2825$$,$$BUDDABADAH$$,#{state_id_nsw},-31.940261,146.479353), - ($$2825$$,$$CANONBA$$,#{state_id_nsw},-31.940261,146.479353), - ($$2825$$,$$FIVE WAYS$$,#{state_id_nsw},-31.940261,146.479353), - ($$2825$$,$$HONEYBUGLE$$,#{state_id_nsw},-31.940261,146.479353), - ($$2825$$,$$MIANDETTA$$,#{state_id_nsw},-31.940261,146.479353), - ($$2825$$,$$MULLA$$,#{state_id_nsw},-31.940261,146.479353), - ($$2825$$,$$MULLENGUDGERY$$,#{state_id_nsw},-31.940261,146.479353), - ($$2825$$,$$MURRAWOMBIE$$,#{state_id_nsw},-31.940261,146.479353), - ($$2825$$,$$NYNGAN$$,#{state_id_nsw},-31.940261,146.479353), - ($$2825$$,$$PANGEE$$,#{state_id_nsw},-31.940261,146.479353), - ($$2827$$,$$BEARBONG$$,#{state_id_nsw},-31.66393,148.881171), - ($$2827$$,$$BIDDON$$,#{state_id_nsw},-31.66393,148.881171), - ($$2827$$,$$BREELONG$$,#{state_id_nsw},-31.66393,148.881171), - ($$2827$$,$$COLLIE$$,#{state_id_nsw},-31.66393,148.881171), - ($$2827$$,$$CURBAN$$,#{state_id_nsw},-31.66393,148.881171), - ($$2827$$,$$GILGANDRA$$,#{state_id_nsw},-31.66393,148.881171), - ($$2827$$,$$MERRIGAL$$,#{state_id_nsw},-31.66393,148.881171), - ($$2828$$,$$BLACK HOLLOW$$,#{state_id_nsw},-31.107835,148.862661), - ($$2828$$,$$BOURBAH$$,#{state_id_nsw},-31.107835,148.862661), - ($$2828$$,$$GULARGAMBONE$$,#{state_id_nsw},-31.107835,148.862661), - ($$2828$$,$$MOUNT TENANDRA$$,#{state_id_nsw},-31.107835,148.862661), - ($$2828$$,$$QUANDA$$,#{state_id_nsw},-31.107835,148.862661), - ($$2828$$,$$TONDERBURINE$$,#{state_id_nsw},-31.107835,148.862661), - ($$2828$$,$$WARRUMBUNGLE$$,#{state_id_nsw},-31.107835,148.862661), - ($$2829$$,$$BILLEROY$$,#{state_id_nsw},-31.124693,148.37341), - ($$2829$$,$$COMBARA$$,#{state_id_nsw},-31.124693,148.37341), - ($$2829$$,$$CONIMBIA$$,#{state_id_nsw},-31.124693,148.37341), - ($$2829$$,$$COONAMBLE$$,#{state_id_nsw},-31.124693,148.37341), - ($$2829$$,$$GILGOOMA$$,#{state_id_nsw},-31.124693,148.37341), - ($$2829$$,$$MAGOMETON$$,#{state_id_nsw},-31.124693,148.37341), - ($$2829$$,$$NEBEA$$,#{state_id_nsw},-31.124693,148.37341), - ($$2829$$,$$PINE GROVE$$,#{state_id_nsw},-31.124693,148.37341), - ($$2829$$,$$TERIDGERIE$$,#{state_id_nsw},-31.124693,148.37341), - ($$2829$$,$$URAWILKIE$$,#{state_id_nsw},-31.124693,148.37341), - ($$2829$$,$$WINGADEE$$,#{state_id_nsw},-31.124693,148.37341), - ($$2830$$,$$BALLIMORE$$,#{state_id_nsw},-32.195726,148.902065), - ($$2830$$,$$BARBIGAL$$,#{state_id_nsw},-32.195726,148.902065), - ($$2830$$,$$BENI$$,#{state_id_nsw},-32.195726,148.902065), - ($$2830$$,$$BOOTHENBA$$,#{state_id_nsw},-32.195726,148.902065), - ($$2830$$,$$BROCKLEHURST$$,#{state_id_nsw},-32.195726,148.902065), - ($$2830$$,$$BRUAH$$,#{state_id_nsw},-32.195726,148.902065), - ($$2830$$,$$BUNGLEGUMBIE$$,#{state_id_nsw},-32.195726,148.902065), - ($$2830$$,$$BURRABADINE$$,#{state_id_nsw},-32.195726,148.902065), - ($$2830$$,$$BUTLERS FALLS$$,#{state_id_nsw},-32.195726,148.902065), - ($$2830$$,$$COOLBAGGIE$$,#{state_id_nsw},-32.195726,148.902065), - ($$2830$$,$$CUMBOOGLE$$,#{state_id_nsw},-32.195726,148.902065), - ($$2830$$,$$DUBBO$$,#{state_id_nsw},-32.195726,148.902065), - ($$2830$$,$$DUBBO DC$$,#{state_id_nsw},-32.195726,148.902065), - ($$2830$$,$$DUBBO EAST$$,#{state_id_nsw},-32.195726,148.902065), - ($$2830$$,$$DUBBO GROVE$$,#{state_id_nsw},-32.195726,148.902065), - ($$2830$$,$$DUBBO WEST$$,#{state_id_nsw},-32.195726,148.902065), - ($$2830$$,$$ESCHOL$$,#{state_id_nsw},-32.195726,148.902065), - ($$2830$$,$$EULOMOGO$$,#{state_id_nsw},-32.195726,148.902065), - ($$2830$$,$$GLENGERRA$$,#{state_id_nsw},-32.195726,148.902065), - ($$2830$$,$$GOONOO FOREST$$,#{state_id_nsw},-32.195726,148.902065), - ($$2830$$,$$JONES CREEK$$,#{state_id_nsw},-32.195726,148.902065), - ($$2830$$,$$KICKABIL$$,#{state_id_nsw},-32.195726,148.902065), - ($$2830$$,$$MINORE$$,#{state_id_nsw},-32.195726,148.902065), - ($$2830$$,$$MOGRIGUY$$,#{state_id_nsw},-32.195726,148.902065), - ($$2830$$,$$MOUNTAIN CREEK$$,#{state_id_nsw},-32.195726,148.902065), - ($$2830$$,$$MURONBUNG$$,#{state_id_nsw},-32.195726,148.902065), - ($$2830$$,$$MURRUMBIDGERIE$$,#{state_id_nsw},-32.195726,148.902065), - ($$2830$$,$$RAWSONVILLE$$,#{state_id_nsw},-32.195726,148.902065), - ($$2830$$,$$TERRAMUNGAMINE$$,#{state_id_nsw},-32.195726,148.902065), - ($$2830$$,$$TOONGI$$,#{state_id_nsw},-32.195726,148.902065), - ($$2830$$,$$WAMBANGALANG$$,#{state_id_nsw},-32.195726,148.902065), - ($$2830$$,$$YARRABAR$$,#{state_id_nsw},-32.195726,148.902065), - ($$2831$$,$$ARMATREE$$,#{state_id_nsw},-31.453726,148.407695), - ($$2831$$,$$BALLADORAN$$,#{state_id_nsw},-31.453726,148.407695), - ($$2831$$,$$BRENDA$$,#{state_id_nsw},-31.453726,148.407695), - ($$2831$$,$$BULLAGREEN$$,#{state_id_nsw},-31.453726,148.407695), - ($$2831$$,$$BYROCK$$,#{state_id_nsw},-31.453726,148.407695), - ($$2831$$,$$CARINDA$$,#{state_id_nsw},-31.453726,148.407695), - ($$2831$$,$$COOLABAH$$,#{state_id_nsw},-31.453726,148.407695), - ($$2831$$,$$ELONG ELONG$$,#{state_id_nsw},-31.453726,148.407695), - ($$2831$$,$$EUMUNGERIE$$,#{state_id_nsw},-31.453726,148.407695), - ($$2831$$,$$GEURIE$$,#{state_id_nsw},-31.453726,148.407695), - ($$2831$$,$$GIRILAMBONE$$,#{state_id_nsw},-31.453726,148.407695), - ($$2831$$,$$GOODOOGA$$,#{state_id_nsw},-31.453726,148.407695), - ($$2831$$,$$GUNGALMAN$$,#{state_id_nsw},-31.453726,148.407695), - ($$2831$$,$$HERMIDALE$$,#{state_id_nsw},-31.453726,148.407695), - ($$2831$$,$$MACQUARIE MARSHES$$,#{state_id_nsw},-31.453726,148.407695), - ($$2831$$,$$MERRYGOEN$$,#{state_id_nsw},-31.453726,148.407695), - ($$2831$$,$$NEILREX$$,#{state_id_nsw},-31.453726,148.407695), - ($$2831$$,$$NEVERTIRE$$,#{state_id_nsw},-31.453726,148.407695), - ($$2831$$,$$NUBINGERIE$$,#{state_id_nsw},-31.453726,148.407695), - ($$2831$$,$$NYMAGEE$$,#{state_id_nsw},-31.453726,148.407695), - ($$2831$$,$$PINE CLUMP$$,#{state_id_nsw},-31.453726,148.407695), - ($$2831$$,$$PONTO$$,#{state_id_nsw},-31.453726,148.407695), - ($$2831$$,$$QUAMBONE$$,#{state_id_nsw},-31.453726,148.407695), - ($$2831$$,$$TERRABELLA$$,#{state_id_nsw},-31.453726,148.407695), - ($$2831$$,$$THE MARRA$$,#{state_id_nsw},-31.453726,148.407695), - ($$2831$$,$$TOOLOON$$,#{state_id_nsw},-31.453726,148.407695), - ($$2831$$,$$TOORAWEENAH$$,#{state_id_nsw},-31.453726,148.407695), - ($$2831$$,$$WESTELLA$$,#{state_id_nsw},-31.453726,148.407695), - ($$2831$$,$$WONGARBON$$,#{state_id_nsw},-31.453726,148.407695), - ($$2832$$,$$COME BY CHANCE$$,#{state_id_nsw},-29.113122,147.902562), - ($$2832$$,$$CRYON$$,#{state_id_nsw},-29.113122,147.902562), - ($$2832$$,$$CUMBORAH$$,#{state_id_nsw},-29.113122,147.902562), - ($$2832$$,$$WALGETT$$,#{state_id_nsw},-29.113122,147.902562), - ($$2833$$,$$COLLARENEBRI$$,#{state_id_nsw},-29.54581,148.576548), - ($$2834$$,$$ANGLEDOOL$$,#{state_id_nsw},-29.425724,147.979236), - ($$2834$$,$$LIGHTNING RIDGE$$,#{state_id_nsw},-29.425724,147.979236), - ($$2835$$,$$BULLA$$,#{state_id_nsw},-32.032911,144.440184), - ($$2835$$,$$CANBELEGO$$,#{state_id_nsw},-32.032911,144.440184), - ($$2835$$,$$COBAR$$,#{state_id_nsw},-32.032911,144.440184), - ($$2835$$,$$CUBBA$$,#{state_id_nsw},-32.032911,144.440184), - ($$2835$$,$$GILGUNNIA$$,#{state_id_nsw},-32.032911,144.440184), - ($$2835$$,$$IRYMPLE$$,#{state_id_nsw},-32.032911,144.440184), - ($$2835$$,$$KERRIGUNDI$$,#{state_id_nsw},-32.032911,144.440184), - ($$2835$$,$$KULWIN$$,#{state_id_nsw},-32.032911,144.440184), - ($$2835$$,$$LERIDA$$,#{state_id_nsw},-32.032911,144.440184), - ($$2835$$,$$NOONA$$,#{state_id_nsw},-32.032911,144.440184), - ($$2835$$,$$SANDY CREEK$$,#{state_id_nsw},-32.032911,144.440184), - ($$2835$$,$$TINDAREY$$,#{state_id_nsw},-32.032911,144.440184), - ($$2836$$,$$WHITE CLIFFS$$,#{state_id_nsw},-30.808105,142.879729), - ($$2836$$,$$WILCANNIA$$,#{state_id_nsw},-30.808105,142.879729), - ($$2839$$,$$BOGAN$$,#{state_id_nsw},-30.198141,146.5382), - ($$2839$$,$$BREWARRINA$$,#{state_id_nsw},-30.198141,146.5382), - ($$2839$$,$$COLLERINA$$,#{state_id_nsw},-30.198141,146.5382), - ($$2839$$,$$GONGOLGON$$,#{state_id_nsw},-30.198141,146.5382), - ($$2839$$,$$NARRAN LAKE$$,#{state_id_nsw},-30.198141,146.5382), - ($$2839$$,$$TALAWANTA$$,#{state_id_nsw},-30.198141,146.5382), - ($$2839$$,$$WEILMORINGLE$$,#{state_id_nsw},-30.198141,146.5382), - ($$2840$$,$$BOURKE$$,#{state_id_nsw},-29.18925,145.88159), - ($$2840$$,$$ENNGONIA$$,#{state_id_nsw},-29.18925,145.88159), - ($$2840$$,$$FORDS BRIDGE$$,#{state_id_nsw},-29.18925,145.88159), - ($$2840$$,$$GUMBALIE$$,#{state_id_nsw},-29.18925,145.88159), - ($$2840$$,$$GUNDERBOOKA$$,#{state_id_nsw},-29.18925,145.88159), - ($$2840$$,$$HUNGERFORD$$,#{state_id_nsw},-29.18925,145.88159), - ($$2840$$,$$LOUTH$$,#{state_id_nsw},-29.18925,145.88159), - ($$2840$$,$$TILPA$$,#{state_id_nsw},-29.18925,145.88159), - ($$2840$$,$$WANAARING$$,#{state_id_nsw},-29.18925,145.88159), - ($$2840$$,$$YANTABULLA$$,#{state_id_nsw},-29.18925,145.88159), - ($$2842$$,$$MENDOORAN$$,#{state_id_nsw},-31.822488,149.118008), - ($$2842$$,$$MOLLYAN$$,#{state_id_nsw},-31.822488,149.118008), - ($$2842$$,$$WATTLE SPRINGS$$,#{state_id_nsw},-31.822488,149.118008), - ($$2842$$,$$YARRAGRIN$$,#{state_id_nsw},-31.822488,149.118008), - ($$2843$$,$$COOLAH$$,#{state_id_nsw},-31.774418,149.611621), - ($$2844$$,$$BIRRIWA$$,#{state_id_nsw},-32.122232,149.465064), - ($$2844$$,$$DUNEDOO$$,#{state_id_nsw},-32.122232,149.465064), - ($$2844$$,$$LEADVILLE$$,#{state_id_nsw},-32.122232,149.465064), - ($$2845$$,$$WALLERAWANG$$,#{state_id_nsw},-33.410618,150.062597), - ($$2846$$,$$CAPERTEE$$,#{state_id_nsw},-33.148969,149.99001), - ($$2846$$,$$GLEN DAVIS$$,#{state_id_nsw},-33.148969,149.99001), - ($$2846$$,$$ROUND SWAMP$$,#{state_id_nsw},-33.148969,149.99001), - ($$2847$$,$$PORTLAND$$,#{state_id_nsw},-33.353124,149.98227), - ($$2848$$,$$BROGANS CREEK$$,#{state_id_nsw},-32.971718,149.959593), - ($$2848$$,$$CHARBON$$,#{state_id_nsw},-32.971718,149.959593), - ($$2848$$,$$CLANDULLA$$,#{state_id_nsw},-32.971718,149.959593), - ($$2848$$,$$KANDOS$$,#{state_id_nsw},-32.971718,149.959593), - ($$2849$$,$$BOGEE$$,#{state_id_nsw},-32.975826,150.104239), - ($$2849$$,$$BREAKFAST CREEK$$,#{state_id_nsw},-32.975826,150.104239), - ($$2849$$,$$BUDDEN$$,#{state_id_nsw},-32.975826,150.104239), - ($$2849$$,$$BYLONG$$,#{state_id_nsw},-32.975826,150.104239), - ($$2849$$,$$CAMBOON$$,#{state_id_nsw},-32.975826,150.104239), - ($$2849$$,$$CARWELL$$,#{state_id_nsw},-32.975826,150.104239), - ($$2849$$,$$COGGAN$$,#{state_id_nsw},-32.975826,150.104239), - ($$2849$$,$$COXS CREEK$$,#{state_id_nsw},-32.975826,150.104239), - ($$2849$$,$$COXS CROWN$$,#{state_id_nsw},-32.975826,150.104239), - ($$2849$$,$$DABEE$$,#{state_id_nsw},-32.975826,150.104239), - ($$2849$$,$$DUNGEREE$$,#{state_id_nsw},-32.975826,150.104239), - ($$2849$$,$$DUNVILLE LOOP$$,#{state_id_nsw},-32.975826,150.104239), - ($$2849$$,$$GINGHI$$,#{state_id_nsw},-32.975826,150.104239), - ($$2849$$,$$GLEN ALICE$$,#{state_id_nsw},-32.975826,150.104239), - ($$2849$$,$$GROWEE$$,#{state_id_nsw},-32.975826,150.104239), - ($$2849$$,$$KELGOOLA$$,#{state_id_nsw},-32.975826,150.104239), - ($$2849$$,$$LEE CREEK$$,#{state_id_nsw},-32.975826,150.104239), - ($$2849$$,$$MOUNT MARSDEN$$,#{state_id_nsw},-32.975826,150.104239), - ($$2849$$,$$MURRUMBO$$,#{state_id_nsw},-32.975826,150.104239), - ($$2849$$,$$NULLO MOUNTAIN$$,#{state_id_nsw},-32.975826,150.104239), - ($$2849$$,$$OLINDA$$,#{state_id_nsw},-32.975826,150.104239), - ($$2849$$,$$PINNACLE SWAMP$$,#{state_id_nsw},-32.975826,150.104239), - ($$2849$$,$$PYANGLE$$,#{state_id_nsw},-32.975826,150.104239), - ($$2849$$,$$REEDY CREEK$$,#{state_id_nsw},-32.975826,150.104239), - ($$2849$$,$$RYLSTONE$$,#{state_id_nsw},-32.975826,150.104239), - ($$2849$$,$$UPPER BYLONG$$,#{state_id_nsw},-32.975826,150.104239), - ($$2849$$,$$UPPER GROWEE$$,#{state_id_nsw},-32.975826,150.104239), - ($$2849$$,$$UPPER NILE$$,#{state_id_nsw},-32.975826,150.104239), - ($$2849$$,$$WIRRABA$$,#{state_id_nsw},-32.975826,150.104239), - ($$2850$$,$$AARONS PASS$$,#{state_id_nsw},-32.863277,149.803751), - ($$2850$$,$$APPLE TREE FLAT$$,#{state_id_nsw},-32.863277,149.803751), - ($$2850$$,$$AVISFORD$$,#{state_id_nsw},-32.863277,149.803751), - ($$2850$$,$$BARA$$,#{state_id_nsw},-32.863277,149.803751), - ($$2850$$,$$BARIGAN$$,#{state_id_nsw},-32.863277,149.803751), - ($$2850$$,$$BEN BUCKLEY$$,#{state_id_nsw},-32.863277,149.803751), - ($$2850$$,$$BOCOBLE$$,#{state_id_nsw},-32.863277,149.803751), - ($$2850$$,$$BOMBIRA$$,#{state_id_nsw},-32.863277,149.803751), - ($$2850$$,$$BOTOBOLAR$$,#{state_id_nsw},-32.863277,149.803751), - ($$2850$$,$$BUCKAROO$$,#{state_id_nsw},-32.863277,149.803751), - ($$2850$$,$$BUDGEE BUDGEE$$,#{state_id_nsw},-32.863277,149.803751), - ($$2850$$,$$BURRUNDULLA$$,#{state_id_nsw},-32.863277,149.803751), - ($$2850$$,$$CAERLEON$$,#{state_id_nsw},-32.863277,149.803751), - ($$2850$$,$$CANADIAN LEAD$$,#{state_id_nsw},-32.863277,149.803751), - ($$2850$$,$$CARCALGONG$$,#{state_id_nsw},-32.863277,149.803751), - ($$2850$$,$$COLLINGWOOD$$,#{state_id_nsw},-32.863277,149.803751), - ($$2850$$,$$COOKS GAP$$,#{state_id_nsw},-32.863277,149.803751), - ($$2850$$,$$COOYAL$$,#{state_id_nsw},-32.863277,149.803751), - ($$2850$$,$$CROSS ROADS$$,#{state_id_nsw},-32.863277,149.803751), - ($$2850$$,$$CUDGEGONG$$,#{state_id_nsw},-32.863277,149.803751), - ($$2850$$,$$CULLENBONE$$,#{state_id_nsw},-32.863277,149.803751), - ($$2850$$,$$CUMBO$$,#{state_id_nsw},-32.863277,149.803751), - ($$2850$$,$$ERUDGERE$$,#{state_id_nsw},-32.863277,149.803751), - ($$2850$$,$$EURUNDEREE$$,#{state_id_nsw},-32.863277,149.803751), - ($$2850$$,$$FROG ROCK$$,#{state_id_nsw},-32.863277,149.803751), - ($$2850$$,$$GALAMBINE$$,#{state_id_nsw},-32.863277,149.803751), - ($$2850$$,$$GLEN AYR$$,#{state_id_nsw},-32.863277,149.803751), - ($$2850$$,$$GRATTAI$$,#{state_id_nsw},-32.863277,149.803751), - ($$2850$$,$$GREEN GULLY$$,#{state_id_nsw},-32.863277,149.803751), - ($$2850$$,$$HARGRAVES$$,#{state_id_nsw},-32.863277,149.803751), - ($$2850$$,$$HAVILAH$$,#{state_id_nsw},-32.863277,149.803751), - ($$2850$$,$$HAYES GAP$$,#{state_id_nsw},-32.863277,149.803751), - ($$2850$$,$$HILL END$$,#{state_id_nsw},-32.863277,149.803751), - ($$2850$$,$$HOME RULE$$,#{state_id_nsw},-32.863277,149.803751), - ($$2850$$,$$ILFORD$$,#{state_id_nsw},-32.863277,149.803751), - ($$2850$$,$$KAINS FLAT$$,#{state_id_nsw},-32.863277,149.803751), - ($$2850$$,$$LINBURN$$,#{state_id_nsw},-32.863277,149.803751), - ($$2850$$,$$LUE$$,#{state_id_nsw},-32.863277,149.803751), - ($$2850$$,$$MAITLAND BAR$$,#{state_id_nsw},-32.863277,149.803751), - ($$2850$$,$$MENAH$$,#{state_id_nsw},-32.863277,149.803751), - ($$2850$$,$$MEROO$$,#{state_id_nsw},-32.863277,149.803751), - ($$2850$$,$$MILROY$$,#{state_id_nsw},-32.863277,149.803751), - ($$2850$$,$$MOGO$$,#{state_id_nsw},-32.863277,149.803751), - ($$2850$$,$$MONIVAE$$,#{state_id_nsw},-32.863277,149.803751), - ($$2850$$,$$MOOLARBEN$$,#{state_id_nsw},-32.863277,149.803751), - ($$2850$$,$$MOUNT FROME$$,#{state_id_nsw},-32.863277,149.803751), - ($$2850$$,$$MOUNT KNOWLES$$,#{state_id_nsw},-32.863277,149.803751), - ($$2850$$,$$MOUNT VINCENT$$,#{state_id_nsw},-32.863277,149.803751), - ($$2850$$,$$MUDGEE$$,#{state_id_nsw},-32.863277,149.803751), - ($$2850$$,$$MULLAMUDDY$$,#{state_id_nsw},-32.863277,149.803751), - ($$2850$$,$$MUNGHORN$$,#{state_id_nsw},-32.863277,149.803751), - ($$2850$$,$$PIAMBONG$$,#{state_id_nsw},-32.863277,149.803751), - ($$2850$$,$$PUTTA BUCCA$$,#{state_id_nsw},-32.863277,149.803751), - ($$2850$$,$$PYRAMUL$$,#{state_id_nsw},-32.863277,149.803751), - ($$2850$$,$$QUEENS PINCH$$,#{state_id_nsw},-32.863277,149.803751), - ($$2850$$,$$RAZORBACK$$,#{state_id_nsw},-32.863277,149.803751), - ($$2850$$,$$RIVERLEA$$,#{state_id_nsw},-32.863277,149.803751), - ($$2850$$,$$RUNNING STREAM$$,#{state_id_nsw},-32.863277,149.803751), - ($$2850$$,$$SALLYS FLAT$$,#{state_id_nsw},-32.863277,149.803751), - ($$2850$$,$$SPRING FLAT$$,#{state_id_nsw},-32.863277,149.803751), - ($$2850$$,$$ST FILLANS$$,#{state_id_nsw},-32.863277,149.803751), - ($$2850$$,$$STONY CREEK$$,#{state_id_nsw},-32.863277,149.803751), - ($$2850$$,$$TAMBAROORA$$,#{state_id_nsw},-32.863277,149.803751), - ($$2850$$,$$TICHULAR$$,#{state_id_nsw},-32.863277,149.803751), - ($$2850$$,$$TOTNES VALLEY$$,#{state_id_nsw},-32.863277,149.803751), - ($$2850$$,$$TRIAMBLE$$,#{state_id_nsw},-32.863277,149.803751), - ($$2850$$,$$TURILL$$,#{state_id_nsw},-32.863277,149.803751), - ($$2850$$,$$TWELVE MILE$$,#{state_id_nsw},-32.863277,149.803751), - ($$2850$$,$$ULAN$$,#{state_id_nsw},-32.863277,149.803751), - ($$2850$$,$$ULLAMALLA$$,#{state_id_nsw},-32.863277,149.803751), - ($$2850$$,$$WILBETREE$$,#{state_id_nsw},-32.863277,149.803751), - ($$2850$$,$$WILPINJONG$$,#{state_id_nsw},-32.863277,149.803751), - ($$2850$$,$$WINDEYER$$,#{state_id_nsw},-32.863277,149.803751), - ($$2850$$,$$WOLLAR$$,#{state_id_nsw},-32.863277,149.803751), - ($$2850$$,$$WORLDS END$$,#{state_id_nsw},-32.863277,149.803751), - ($$2850$$,$$YARRABIN$$,#{state_id_nsw},-32.863277,149.803751), - ($$2850$$,$$YARRAWONGA$$,#{state_id_nsw},-32.863277,149.803751), - ($$2852$$,$$BARNEYS REEF$$,#{state_id_nsw},-32.185066,149.529756), - ($$2852$$,$$BERYL$$,#{state_id_nsw},-32.185066,149.529756), - ($$2852$$,$$BIRAGANBIL$$,#{state_id_nsw},-32.185066,149.529756), - ($$2852$$,$$BUNGABA$$,#{state_id_nsw},-32.185066,149.529756), - ($$2852$$,$$COPE$$,#{state_id_nsw},-32.185066,149.529756), - ($$2852$$,$$CUMBANDRY$$,#{state_id_nsw},-32.185066,149.529756), - ($$2852$$,$$GOOLMA$$,#{state_id_nsw},-32.185066,149.529756), - ($$2852$$,$$GULGONG$$,#{state_id_nsw},-32.185066,149.529756), - ($$2852$$,$$MEBUL$$,#{state_id_nsw},-32.185066,149.529756), - ($$2852$$,$$MEROTHERIE$$,#{state_id_nsw},-32.185066,149.529756), - ($$2852$$,$$STUBBO$$,#{state_id_nsw},-32.185066,149.529756), - ($$2852$$,$$TALLAWANG$$,#{state_id_nsw},-32.185066,149.529756), - ($$2864$$,$$BOREE$$,#{state_id_nsw},-33.232729,148.841207), - ($$2864$$,$$BOWAN PARK$$,#{state_id_nsw},-33.232729,148.841207), - ($$2864$$,$$CUDAL$$,#{state_id_nsw},-33.232729,148.841207), - ($$2864$$,$$MURGA$$,#{state_id_nsw},-33.232729,148.841207), - ($$2864$$,$$TOOGONG$$,#{state_id_nsw},-33.232729,148.841207), - ($$2865$$,$$BOCOBRA$$,#{state_id_nsw},-33.100422,148.544553), - ($$2865$$,$$GUMBLE$$,#{state_id_nsw},-33.100422,148.544553), - ($$2865$$,$$MANILDRA$$,#{state_id_nsw},-33.100422,148.544553), - ($$2866$$,$$AMAROO$$,#{state_id_nsw},-33.184094,148.928934), - ($$2866$$,$$BOOMEY$$,#{state_id_nsw},-33.184094,148.928934), - ($$2866$$,$$CUNDUMBUL$$,#{state_id_nsw},-33.184094,148.928934), - ($$2866$$,$$EUCHAREENA$$,#{state_id_nsw},-33.184094,148.928934), - ($$2866$$,$$GARRA$$,#{state_id_nsw},-33.184094,148.928934), - ($$2866$$,$$LARRAS LEE$$,#{state_id_nsw},-33.184094,148.928934), - ($$2866$$,$$MOLONG$$,#{state_id_nsw},-33.184094,148.928934), - ($$2867$$,$$BALDRY$$,#{state_id_nsw},-32.865448,148.500123), - ($$2867$$,$$CUMNOCK$$,#{state_id_nsw},-32.865448,148.500123), - ($$2867$$,$$EURIMBLA$$,#{state_id_nsw},-32.865448,148.500123), - ($$2867$$,$$LOOMBAH$$,#{state_id_nsw},-32.865448,148.500123), - ($$2867$$,$$YULLUNDRY$$,#{state_id_nsw},-32.865448,148.500123), - ($$2868$$,$$BOURNEWOOD$$,#{state_id_nsw},-32.752421,148.753515), - ($$2868$$,$$LITTLE RIVER$$,#{state_id_nsw},-32.752421,148.753515), - ($$2868$$,$$NORTH YEOVAL$$,#{state_id_nsw},-32.752421,148.753515), - ($$2868$$,$$OBLEY$$,#{state_id_nsw},-32.752421,148.753515), - ($$2868$$,$$UPPER OBLEY$$,#{state_id_nsw},-32.752421,148.753515), - ($$2868$$,$$YEOVAL$$,#{state_id_nsw},-32.752421,148.753515), - ($$2869$$,$$PEAK HILL$$,#{state_id_nsw},-32.725328,148.185188), - ($$2869$$,$$TOMINGLEY$$,#{state_id_nsw},-32.725328,148.185188), - ($$2869$$,$$TREWILGA$$,#{state_id_nsw},-32.725328,148.185188), - ($$2870$$,$$ALECTOWN$$,#{state_id_nsw},-32.933075,148.257653), - ($$2870$$,$$BUMBERRY$$,#{state_id_nsw},-32.933075,148.257653), - ($$2870$$,$$COOKAMIDGERA$$,#{state_id_nsw},-32.933075,148.257653), - ($$2870$$,$$COOKS MYALLS$$,#{state_id_nsw},-32.933075,148.257653), - ($$2870$$,$$GOONUMBLA$$,#{state_id_nsw},-32.933075,148.257653), - ($$2870$$,$$MANDAGERY$$,#{state_id_nsw},-32.933075,148.257653), - ($$2870$$,$$PARKES$$,#{state_id_nsw},-32.933075,148.257653), - ($$2870$$,$$TICHBORNE$$,#{state_id_nsw},-32.933075,148.257653), - ($$2871$$,$$BANDON$$,#{state_id_nsw},-33.501308,148.305788), - ($$2871$$,$$BEDGEREBONG$$,#{state_id_nsw},-33.501308,148.305788), - ($$2871$$,$$BUNDABURRAH$$,#{state_id_nsw},-33.501308,148.305788), - ($$2871$$,$$CALARIE$$,#{state_id_nsw},-33.501308,148.305788), - ($$2871$$,$$CARRAWABBITY$$,#{state_id_nsw},-33.501308,148.305788), - ($$2871$$,$$CORINELLA$$,#{state_id_nsw},-33.501308,148.305788), - ($$2871$$,$$CUMBIJOWA$$,#{state_id_nsw},-33.501308,148.305788), - ($$2871$$,$$DAROOBALGIE$$,#{state_id_nsw},-33.501308,148.305788), - ($$2871$$,$$FAIRHOLME$$,#{state_id_nsw},-33.501308,148.305788), - ($$2871$$,$$FORBES$$,#{state_id_nsw},-33.501308,148.305788), - ($$2871$$,$$GAREMA$$,#{state_id_nsw},-33.501308,148.305788), - ($$2871$$,$$GRAWLIN$$,#{state_id_nsw},-33.501308,148.305788), - ($$2871$$,$$GUNNING GAP$$,#{state_id_nsw},-33.501308,148.305788), - ($$2871$$,$$JEMALONG$$,#{state_id_nsw},-33.501308,148.305788), - ($$2871$$,$$MULYANDRY$$,#{state_id_nsw},-33.501308,148.305788), - ($$2871$$,$$OOMA$$,#{state_id_nsw},-33.501308,148.305788), - ($$2871$$,$$WARROO$$,#{state_id_nsw},-33.501308,148.305788), - ($$2871$$,$$WEELONG$$,#{state_id_nsw},-33.501308,148.305788), - ($$2871$$,$$WIRRINYA$$,#{state_id_nsw},-33.501308,148.305788), - ($$2871$$,$$YARRAGONG$$,#{state_id_nsw},-33.501308,148.305788), - ($$2873$$,$$ALBERT$$,#{state_id_nsw},-32.41586,147.508111), - ($$2873$$,$$MIAMLEY$$,#{state_id_nsw},-32.41586,147.508111), - ($$2873$$,$$TOTTENHAM$$,#{state_id_nsw},-32.41586,147.508111), - ($$2874$$,$$TULLAMORE$$,#{state_id_nsw},-32.631463,147.564026), - ($$2875$$,$$BRUIE PLAINS$$,#{state_id_nsw},-32.780391,147.864053), - ($$2875$$,$$FIFIELD$$,#{state_id_nsw},-32.780391,147.864053), - ($$2875$$,$$OOTHA$$,#{state_id_nsw},-32.780391,147.864053), - ($$2875$$,$$TRUNDLE$$,#{state_id_nsw},-32.780391,147.864053), - ($$2875$$,$$YARRABANDAI$$,#{state_id_nsw},-32.780391,147.864053), - ($$2876$$,$$BOGAN GATE$$,#{state_id_nsw},-33.106229,147.802354), - ($$2876$$,$$GUNNINGBLAND$$,#{state_id_nsw},-33.106229,147.802354), - ($$2876$$,$$NELUNGALOO$$,#{state_id_nsw},-33.106229,147.802354), - ($$2877$$,$$BOONA MOUNT$$,#{state_id_nsw},-33.089095,147.15218), - ($$2877$$,$$CONDOBOLIN$$,#{state_id_nsw},-33.089095,147.15218), - ($$2877$$,$$DERRIWONG$$,#{state_id_nsw},-33.089095,147.15218), - ($$2877$$,$$EREMERANG$$,#{state_id_nsw},-33.089095,147.15218), - ($$2877$$,$$EUABALONG$$,#{state_id_nsw},-33.089095,147.15218), - ($$2877$$,$$EUABALONG WEST$$,#{state_id_nsw},-33.089095,147.15218), - ($$2877$$,$$KIACATOO$$,#{state_id_nsw},-33.089095,147.15218), - ($$2877$$,$$MOUNT HOPE$$,#{state_id_nsw},-33.089095,147.15218), - ($$2877$$,$$MULGUTHRIE$$,#{state_id_nsw},-33.089095,147.15218), - ($$2878$$,$$IVANHOE$$,#{state_id_nsw},-32.827693,143.859144), - ($$2878$$,$$MOSSGIEL$$,#{state_id_nsw},-32.827693,143.859144), - ($$2879$$,$$MENINDEE$$,#{state_id_nsw},-32.263499,142.4066), - ($$2879$$,$$SUNSET STRIP$$,#{state_id_nsw},-32.263499,142.4066), - ($$2880$$,$$BROKEN HILL$$,#{state_id_nsw},-31.959193,141.466614), - ($$2880$$,$$BROKEN HILL NORTH$$,#{state_id_nsw},-31.959193,141.466614), - ($$2880$$,$$BROKEN HILL WEST$$,#{state_id_nsw},-31.959193,141.466614), - ($$2880$$,$$BROUGHAMS GATE$$,#{state_id_nsw},-31.959193,141.466614), - ($$2880$$,$$FOWLERS GAP$$,#{state_id_nsw},-31.959193,141.466614), - ($$2880$$,$$LITTLE TOPAR$$,#{state_id_nsw},-31.959193,141.466614), - ($$2880$$,$$MILPARINKA$$,#{state_id_nsw},-31.959193,141.466614), - ($$2880$$,$$MUTAWINTJI$$,#{state_id_nsw},-31.959193,141.466614), - ($$2880$$,$$PACKSADDLE$$,#{state_id_nsw},-31.959193,141.466614), - ($$2880$$,$$SILVERTON$$,#{state_id_nsw},-31.959193,141.466614), - ($$2880$$,$$SOUTH BROKEN HILL$$,#{state_id_nsw},-31.959193,141.466614), - ($$2880$$,$$TIBOOBURRA$$,#{state_id_nsw},-31.959193,141.466614), - ($$2890$$,$$AUSTRALIAN DEFENCE FORCES$$,#{state_id_nsw},0.0,0.0), - ($$2891$$,$$SYDNEY GATEWAY FACILITY$$,#{state_id_nsw},0.0,0.0), - ($$2898$$,$$LORD HOWE ISLAND$$,#{state_id_nsw},-31.55247,159.081217), - ($$2899$$,$$NORFOLK ISLAND$$,#{state_id_nsw},-36.084231,146.928783), - ($$2900$$,$$GREENWAY$$,#{state_id_act},-35.417991,149.069414), - ($$2900$$,$$TUGGERANONG$$,#{state_id_act},-35.417991,149.069414), - ($$2901$$,$$TUGGERANONG DC$$,#{state_id_act},0.0,0.0), - ($$2902$$,$$KAMBAH$$,#{state_id_act},-35.378876,149.045895), - ($$2902$$,$$KAMBAH VILLAGE$$,#{state_id_act},-35.378876,149.045895), - ($$2903$$,$$ERINDALE CENTRE$$,#{state_id_act},-35.403016,149.097207), - ($$2903$$,$$OXLEY$$,#{state_id_act},-35.403016,149.097207), - ($$2903$$,$$WANNIASSA$$,#{state_id_act},-35.403016,149.097207), - ($$2904$$,$$FADDEN$$,#{state_id_act},-35.400996,149.115023), - ($$2904$$,$$GOWRIE$$,#{state_id_act},-35.400996,149.115023), - ($$2904$$,$$MACARTHUR$$,#{state_id_act},-35.400996,149.115023), - ($$2904$$,$$MONASH$$,#{state_id_act},-35.400996,149.115023), - ($$2905$$,$$BONYTHON$$,#{state_id_act},-35.429018,149.081746), - ($$2905$$,$$CALWELL$$,#{state_id_act},-35.429018,149.081746), - ($$2905$$,$$CHISHOLM$$,#{state_id_act},-35.429018,149.081746), - ($$2905$$,$$GILMORE$$,#{state_id_act},-35.429018,149.081746), - ($$2905$$,$$ISABELLA PLAINS$$,#{state_id_act},-35.429018,149.081746), - ($$2905$$,$$RICHARDSON$$,#{state_id_act},-35.429018,149.081746), - ($$2905$$,$$THEODORE$$,#{state_id_act},-35.429018,149.081746), - ($$2906$$,$$BANKS$$,#{state_id_act},-35.522639,149.08098), - ($$2906$$,$$CONDER$$,#{state_id_act},-35.522639,149.08098), - ($$2906$$,$$GORDON$$,#{state_id_act},-35.522639,149.08098), - ($$2911$$,$$CRACE$$,#{state_id_act},-35.218473,149.124813), - ($$2911$$,$$MITCHELL$$,#{state_id_act},-35.218473,149.124813), - ($$2912$$,$$GUNGAHLIN$$,#{state_id_act},-35.522639,149.08098), - ($$2913$$,$$CASEY$$,#{state_id_act},-35.201802,149.132349), - ($$2913$$,$$FRANKLIN$$,#{state_id_act},-35.201802,149.132349), - ($$2913$$,$$GINNINDERRA VILLAGE$$,#{state_id_act},-35.201802,149.132349), - ($$2913$$,$$KINLYSIDE$$,#{state_id_act},-35.201802,149.132349), - ($$2913$$,$$NGUNNAWAL$$,#{state_id_act},-35.201802,149.132349), - ($$2913$$,$$NICHOLLS$$,#{state_id_act},-35.201802,149.132349), - ($$2913$$,$$PALMERSTON$$,#{state_id_act},-35.201802,149.132349), - ($$2913$$,$$TAYLOR$$,#{state_id_act},-35.201802,149.132349), - ($$2914$$,$$AMAROO$$,#{state_id_act},-35.170334,149.125877), - ($$2914$$,$$BONNER$$,#{state_id_act},-35.170334,149.125877), - ($$2914$$,$$FORDE$$,#{state_id_act},-35.170334,149.125877), - ($$2914$$,$$HARRISON$$,#{state_id_act},-35.170334,149.125877), - ($$2914$$,$$MONCRIEFF$$,#{state_id_act},-35.170334,149.125877), - ($$3000$$,$$MELBOURNE$$,#{state_id_vic},-37.814563,144.970267), - ($$3001$$,$$MELBOURNE$$,#{state_id_vic},-38.365017,144.76592), - ($$3002$$,$$EAST MELBOURNE$$,#{state_id_vic},-37.81664,144.987811), - ($$3003$$,$$WEST MELBOURNE$$,#{state_id_vic},-37.806255,144.941123), - ($$3004$$,$$MELBOURNE$$,#{state_id_vic},-37.837324,144.976335), - ($$3004$$,$$ST KILDA ROAD CENTRAL$$,#{state_id_vic},-37.837324,144.976335), - ($$3005$$,$$WORLD TRADE CENTRE$$,#{state_id_vic},-37.822262,144.954856), - ($$3006$$,$$SOUTH WHARF$$,#{state_id_vic},-37.823258,144.965926), - ($$3006$$,$$SOUTHBANK$$,#{state_id_vic},-37.823258,144.965926), - ($$3008$$,$$DOCKLANDS$$,#{state_id_vic},-37.814719,144.948039), - ($$3010$$,$$UNIVERSITY OF MELBOURNE$$,#{state_id_vic},-37.796152,144.961351), - ($$3011$$,$$FOOTSCRAY$$,#{state_id_vic},-37.79977,144.899587), - ($$3011$$,$$SEDDON$$,#{state_id_vic},-37.79977,144.899587), - ($$3011$$,$$SEDDON WEST$$,#{state_id_vic},-37.79977,144.899587), - ($$3012$$,$$BROOKLYN$$,#{state_id_vic},-37.814624,144.847108), - ($$3012$$,$$KINGSVILLE$$,#{state_id_vic},-37.814624,144.847108), - ($$3012$$,$$KINGSVILLE WEST$$,#{state_id_vic},-37.814624,144.847108), - ($$3012$$,$$MAIDSTONE$$,#{state_id_vic},-37.814624,144.847108), - ($$3012$$,$$TOTTENHAM$$,#{state_id_vic},-37.814624,144.847108), - ($$3012$$,$$WEST FOOTSCRAY$$,#{state_id_vic},-37.814624,144.847108), - ($$3013$$,$$YARRAVILLE$$,#{state_id_vic},-37.816178,144.889774), - ($$3013$$,$$YARRAVILLE WEST$$,#{state_id_vic},-37.816178,144.889774), - ($$3015$$,$$NEWPORT$$,#{state_id_vic},-37.842477,144.883145), - ($$3015$$,$$SOUTH KINGSVILLE$$,#{state_id_vic},-37.842477,144.883145), - ($$3015$$,$$SPOTSWOOD$$,#{state_id_vic},-37.842477,144.883145), - ($$3016$$,$$WILLIAMSTOWN$$,#{state_id_vic},-37.856902,144.897698), - ($$3016$$,$$WILLIAMSTOWN NORTH$$,#{state_id_vic},-37.856902,144.897698), - ($$3018$$,$$ALTONA$$,#{state_id_vic},-37.869275,144.830286), - ($$3018$$,$$SEAHOLME$$,#{state_id_vic},-37.869275,144.830286), - ($$3019$$,$$BRAYBROOK$$,#{state_id_vic},-37.779309,144.855359), - ($$3019$$,$$BRAYBROOK NORTH$$,#{state_id_vic},-37.779309,144.855359), - ($$3019$$,$$ROBINSON$$,#{state_id_vic},-37.779309,144.855359), - ($$3020$$,$$ALBION$$,#{state_id_vic},-37.775954,144.819395), - ($$3020$$,$$GLENGALA$$,#{state_id_vic},-37.775954,144.819395), - ($$3020$$,$$SUNSHINE$$,#{state_id_vic},-37.775954,144.819395), - ($$3020$$,$$SUNSHINE NORTH$$,#{state_id_vic},-37.775954,144.819395), - ($$3020$$,$$SUNSHINE WEST$$,#{state_id_vic},-37.775954,144.819395), - ($$3021$$,$$ALBANVALE$$,#{state_id_vic},-37.745934,144.770027), - ($$3021$$,$$KEALBA$$,#{state_id_vic},-37.745934,144.770027), - ($$3021$$,$$KINGS PARK$$,#{state_id_vic},-37.745934,144.770027), - ($$3021$$,$$ST ALBANS$$,#{state_id_vic},-37.745934,144.770027), - ($$3022$$,$$ARDEER$$,#{state_id_vic},-37.78292,144.801018), - ($$3022$$,$$DEER PARK EAST$$,#{state_id_vic},-37.78292,144.801018), - ($$3023$$,$$BURNSIDE$$,#{state_id_vic},-37.753381,144.752618), - ($$3023$$,$$BURNSIDE HEIGHTS$$,#{state_id_vic},-37.753381,144.752618), - ($$3023$$,$$CAIRNLEA$$,#{state_id_vic},-37.753381,144.752618), - ($$3023$$,$$CAROLINE SPRINGS$$,#{state_id_vic},-37.753381,144.752618), - ($$3023$$,$$DEER PARK$$,#{state_id_vic},-37.753381,144.752618), - ($$3023$$,$$DEER PARK NORTH$$,#{state_id_vic},-37.753381,144.752618), - ($$3023$$,$$RAVENHALL$$,#{state_id_vic},-37.753381,144.752618), - ($$3024$$,$$MAMBOURIN$$,#{state_id_vic},-37.899873,144.556552), - ($$3024$$,$$MOUNT COTTRELL$$,#{state_id_vic},-37.899873,144.556552), - ($$3024$$,$$WYNDHAM VALE$$,#{state_id_vic},-37.899873,144.556552), - ($$3025$$,$$ALTONA EAST$$,#{state_id_vic},-37.835657,144.85995), - ($$3025$$,$$ALTONA GATE$$,#{state_id_vic},-37.835657,144.85995), - ($$3025$$,$$ALTONA NORTH$$,#{state_id_vic},-37.835657,144.85995), - ($$3026$$,$$LAVERTON NORTH$$,#{state_id_vic},-37.841653,144.795959), - ($$3027$$,$$WILLIAMS LANDING$$,#{state_id_vic},-37.859941,144.760517), - ($$3028$$,$$ALTONA MEADOWS$$,#{state_id_vic},-37.871632,144.778084), - ($$3028$$,$$LAVERTON$$,#{state_id_vic},-37.871632,144.778084), - ($$3028$$,$$SEABROOK$$,#{state_id_vic},-37.871632,144.778084), - ($$3029$$,$$HOPPERS CROSSING$$,#{state_id_vic},-37.882636,144.700297), - ($$3029$$,$$TARNEIT$$,#{state_id_vic},-37.882636,144.700297), - ($$3029$$,$$TRUGANINA$$,#{state_id_vic},-37.882636,144.700297), - ($$3030$$,$$COCOROC$$,#{state_id_vic},-37.969994,144.582987), - ($$3030$$,$$DERRIMUT$$,#{state_id_vic},-37.969994,144.582987), - ($$3030$$,$$POINT COOK$$,#{state_id_vic},-37.969994,144.582987), - ($$3030$$,$$QUANDONG$$,#{state_id_vic},-37.969994,144.582987), - ($$3030$$,$$WERRIBEE$$,#{state_id_vic},-37.969994,144.582987), - ($$3030$$,$$WERRIBEE SOUTH$$,#{state_id_vic},-37.969994,144.582987), - ($$3031$$,$$FLEMINGTON$$,#{state_id_vic},-37.788375,144.931472), - ($$3031$$,$$KENSINGTON$$,#{state_id_vic},-37.788375,144.931472), - ($$3032$$,$$ASCOT VALE$$,#{state_id_vic},-37.77583,144.923377), - ($$3032$$,$$HIGHPOINT CITY$$,#{state_id_vic},-37.77583,144.923377), - ($$3032$$,$$MARIBYRNONG$$,#{state_id_vic},-37.77583,144.923377), - ($$3032$$,$$TRAVANCORE$$,#{state_id_vic},-37.77583,144.923377), - ($$3033$$,$$KEILOR EAST$$,#{state_id_vic},-37.736264,144.796336), - ($$3034$$,$$AVONDALE HEIGHTS$$,#{state_id_vic},-37.742284,144.81515), - ($$3036$$,$$KEILOR$$,#{state_id_vic},-37.718965,144.834166), - ($$3036$$,$$KEILOR NORTH$$,#{state_id_vic},-37.718965,144.834166), - ($$3037$$,$$CALDER PARK$$,#{state_id_vic},-37.715579,144.780474), - ($$3037$$,$$DELAHEY$$,#{state_id_vic},-37.715579,144.780474), - ($$3037$$,$$HILLSIDE$$,#{state_id_vic},-37.715579,144.780474), - ($$3037$$,$$SYDENHAM$$,#{state_id_vic},-37.715579,144.780474), - ($$3037$$,$$TAYLORS HILL$$,#{state_id_vic},-37.715579,144.780474), - ($$3038$$,$$KEILOR DOWNS$$,#{state_id_vic},-37.725726,144.811346), - ($$3038$$,$$KEILOR LODGE$$,#{state_id_vic},-37.725726,144.811346), - ($$3038$$,$$TAYLORS LAKES$$,#{state_id_vic},-37.725726,144.811346), - ($$3038$$,$$WATERGARDENS$$,#{state_id_vic},-37.725726,144.811346), - ($$3039$$,$$MOONEE PONDS$$,#{state_id_vic},-37.765707,144.919163), - ($$3040$$,$$ABERFELDIE$$,#{state_id_vic},-37.75669,144.896259), - ($$3040$$,$$ESSENDON$$,#{state_id_vic},-37.75669,144.896259), - ($$3040$$,$$ESSENDON WEST$$,#{state_id_vic},-37.75669,144.896259), - ($$3041$$,$$ESSENDON FIELDS$$,#{state_id_vic},-37.744374,144.909853), - ($$3041$$,$$ESSENDON NORTH$$,#{state_id_vic},-37.744374,144.909853), - ($$3041$$,$$STRATHMORE$$,#{state_id_vic},-37.744374,144.909853), - ($$3041$$,$$STRATHMORE HEIGHTS$$,#{state_id_vic},-37.744374,144.909853), - ($$3042$$,$$AIRPORT WEST$$,#{state_id_vic},-37.711698,144.887037), - ($$3042$$,$$KEILOR PARK$$,#{state_id_vic},-37.711698,144.887037), - ($$3042$$,$$NIDDRIE$$,#{state_id_vic},-37.711698,144.887037), - ($$3043$$,$$GLADSTONE PARK$$,#{state_id_vic},-37.68861,144.883628), - ($$3043$$,$$GOWANBRAE$$,#{state_id_vic},-37.68861,144.883628), - ($$3043$$,$$TULLAMARINE$$,#{state_id_vic},-37.68861,144.883628), - ($$3044$$,$$PASCOE VALE$$,#{state_id_vic},-37.727568,144.939122), - ($$3044$$,$$PASCOE VALE SOUTH$$,#{state_id_vic},-37.727568,144.939122), - ($$3045$$,$$MELBOURNE AIRPORT$$,#{state_id_vic},-37.668873,144.833931), - ($$3046$$,$$GLENROY$$,#{state_id_vic},-37.704581,144.915758), - ($$3046$$,$$HADFIELD$$,#{state_id_vic},-37.704581,144.915758), - ($$3046$$,$$OAK PARK$$,#{state_id_vic},-37.704581,144.915758), - ($$3047$$,$$BROADMEADOWS$$,#{state_id_vic},-37.680792,144.921009), - ($$3047$$,$$DALLAS$$,#{state_id_vic},-37.680792,144.921009), - ($$3047$$,$$JACANA$$,#{state_id_vic},-37.680792,144.921009), - ($$3048$$,$$COOLAROO$$,#{state_id_vic},-37.651811,144.930466), - ($$3048$$,$$MEADOW HEIGHTS$$,#{state_id_vic},-37.651811,144.930466), - ($$3049$$,$$ATTWOOD$$,#{state_id_vic},-37.667515,144.88529), - ($$3049$$,$$WESTMEADOWS$$,#{state_id_vic},-37.667515,144.88529), - ($$3050$$,$$ROYAL MELBOURNE HOSPITAL$$,#{state_id_vic},-37.798631,144.955627), - ($$3051$$,$$HOTHAM HILL$$,#{state_id_vic},-37.905996,145.056254), - ($$3051$$,$$NORTH MELBOURNE$$,#{state_id_vic},-37.905996,145.056254), - ($$3052$$,$$MELBOURNE UNIVERSITY$$,#{state_id_vic},-37.796152,144.961351), - ($$3052$$,$$PARKVILLE$$,#{state_id_vic},-37.796152,144.961351), - ($$3053$$,$$CARLTON$$,#{state_id_vic},-37.784337,144.969747), - ($$3053$$,$$CARLTON SOUTH$$,#{state_id_vic},-37.784337,144.969747), - ($$3054$$,$$CARLTON NORTH$$,#{state_id_vic},-37.784337,144.969747), - ($$3054$$,$$PRINCES HILL$$,#{state_id_vic},-37.784337,144.969747), - ($$3055$$,$$BRUNSWICK SOUTH$$,#{state_id_vic},-37.772049,144.944635), - ($$3055$$,$$BRUNSWICK WEST$$,#{state_id_vic},-37.772049,144.944635), - ($$3055$$,$$MOONEE VALE$$,#{state_id_vic},-37.772049,144.944635), - ($$3055$$,$$MORELAND WEST$$,#{state_id_vic},-37.772049,144.944635), - ($$3056$$,$$BRUNSWICK$$,#{state_id_vic},-37.764829,144.943778), - ($$3056$$,$$BRUNSWICK LOWER$$,#{state_id_vic},-37.764829,144.943778), - ($$3056$$,$$BRUNSWICK NORTH$$,#{state_id_vic},-37.764829,144.943778), - ($$3057$$,$$BRUNSWICK EAST$$,#{state_id_vic},-37.76491,144.979567), - ($$3057$$,$$LYGON STREET NORTH$$,#{state_id_vic},-37.76491,144.979567), - ($$3057$$,$$SUMNER$$,#{state_id_vic},-37.76491,144.979567), - ($$3058$$,$$BATMAN$$,#{state_id_vic},-37.733524,144.962837), - ($$3058$$,$$COBURG$$,#{state_id_vic},-37.733524,144.962837), - ($$3058$$,$$COBURG NORTH$$,#{state_id_vic},-37.733524,144.962837), - ($$3058$$,$$MERLYNSTON$$,#{state_id_vic},-37.733524,144.962837), - ($$3058$$,$$MORELAND$$,#{state_id_vic},-37.733524,144.962837), - ($$3059$$,$$GREENVALE$$,#{state_id_vic},-37.642984,144.88872), - ($$3060$$,$$FAWKNER$$,#{state_id_vic},-37.759823,144.89571), - ($$3060$$,$$FAWKNER EAST$$,#{state_id_vic},-37.759823,144.89571), - ($$3060$$,$$FAWKNER NORTH$$,#{state_id_vic},-37.759823,144.89571), - ($$3061$$,$$CAMPBELLFIELD$$,#{state_id_vic},-37.643746,144.951369), - ($$3062$$,$$SOMERTON$$,#{state_id_vic},-37.642563,144.944259), - ($$3063$$,$$OAKLANDS JUNCTION$$,#{state_id_vic},-37.629826,144.839244), - ($$3063$$,$$YUROKE$$,#{state_id_vic},-37.629826,144.839244), - ($$3064$$,$$CRAIGIEBURN$$,#{state_id_vic},-37.598975,144.941287), - ($$3064$$,$$DONNYBROOK$$,#{state_id_vic},-37.598975,144.941287), - ($$3064$$,$$KALKALLO$$,#{state_id_vic},-37.598975,144.941287), - ($$3064$$,$$MICKLEHAM$$,#{state_id_vic},-37.598975,144.941287), - ($$3064$$,$$ROXBURGH PARK$$,#{state_id_vic},-37.598975,144.941287), - ($$3065$$,$$FITZROY$$,#{state_id_vic},-37.800917,144.979165), - ($$3066$$,$$COLLINGWOOD$$,#{state_id_vic},-37.800366,144.984149), - ($$3066$$,$$COLLINGWOOD NORTH$$,#{state_id_vic},-37.800366,144.984149), - ($$3067$$,$$ABBOTSFORD$$,#{state_id_vic},-37.801781,144.998752), - ($$3068$$,$$CLIFTON HILL$$,#{state_id_vic},-37.788118,144.992067), - ($$3068$$,$$FITZROY NORTH$$,#{state_id_vic},-37.788118,144.992067), - ($$3070$$,$$NORTHCOTE$$,#{state_id_vic},-37.769857,144.995276), - ($$3070$$,$$NORTHCOTE SOUTH$$,#{state_id_vic},-37.769857,144.995276), - ($$3071$$,$$THORNBURY$$,#{state_id_vic},-37.75504,144.998589), - ($$3072$$,$$GILBERTON$$,#{state_id_vic},-37.742047,145.027716), - ($$3072$$,$$NORTHLAND CENTRE$$,#{state_id_vic},-37.742047,145.027716), - ($$3072$$,$$PRESTON$$,#{state_id_vic},-37.742047,145.027716), - ($$3072$$,$$PRESTON LOWER$$,#{state_id_vic},-37.742047,145.027716), - ($$3072$$,$$PRESTON SOUTH$$,#{state_id_vic},-37.742047,145.027716), - ($$3072$$,$$PRESTON WEST$$,#{state_id_vic},-37.742047,145.027716), - ($$3072$$,$$REGENT WEST$$,#{state_id_vic},-37.742047,145.027716), - ($$3073$$,$$KEON PARK$$,#{state_id_vic},-37.694672,145.011907), - ($$3073$$,$$RESERVOIR$$,#{state_id_vic},-37.694672,145.011907), - ($$3073$$,$$RESERVOIR EAST$$,#{state_id_vic},-37.694672,145.011907), - ($$3073$$,$$RESERVOIR NORTH$$,#{state_id_vic},-37.694672,145.011907), - ($$3073$$,$$RESERVOIR SOUTH$$,#{state_id_vic},-37.694672,145.011907), - ($$3074$$,$$THOMASTOWN$$,#{state_id_vic},-37.680338,145.014287), - ($$3075$$,$$LALOR$$,#{state_id_vic},-37.665857,145.017194), - ($$3075$$,$$LALOR PLAZA$$,#{state_id_vic},-37.665857,145.017194), - ($$3076$$,$$EPPING$$,#{state_id_vic},-37.638363,145.009493), - ($$3076$$,$$EPPING DC$$,#{state_id_vic},-37.638363,145.009493), - ($$3078$$,$$ALPHINGTON$$,#{state_id_vic},-37.780767,145.03116), - ($$3078$$,$$FAIRFIELD$$,#{state_id_vic},-37.780767,145.03116), - ($$3079$$,$$IVANHOE$$,#{state_id_vic},-37.76964,145.041425), - ($$3079$$,$$IVANHOE EAST$$,#{state_id_vic},-37.76964,145.041425), - ($$3079$$,$$IVANHOE NORTH$$,#{state_id_vic},-37.76964,145.041425), - ($$3081$$,$$BELLFIELD$$,#{state_id_vic},-37.751819,145.045449), - ($$3081$$,$$HEIDELBERG HEIGHTS$$,#{state_id_vic},-37.751819,145.045449), - ($$3081$$,$$HEIDELBERG RGH$$,#{state_id_vic},-37.751819,145.045449), - ($$3081$$,$$HEIDELBERG WEST$$,#{state_id_vic},-37.751819,145.045449), - ($$3082$$,$$MILL PARK$$,#{state_id_vic},-37.667957,145.060693), - ($$3083$$,$$BUNDOORA$$,#{state_id_vic},-37.70132,145.071967), - ($$3083$$,$$KINGSBURY$$,#{state_id_vic},-37.70132,145.071967), - ($$3083$$,$$LA TROBE UNIVERSITY$$,#{state_id_vic},-37.70132,145.071967), - ($$3084$$,$$BANYULE$$,#{state_id_vic},-37.744219,145.08793), - ($$3084$$,$$EAGLEMONT$$,#{state_id_vic},-37.744219,145.08793), - ($$3084$$,$$HEIDELBERG$$,#{state_id_vic},-37.744219,145.08793), - ($$3084$$,$$ROSANNA$$,#{state_id_vic},-37.744219,145.08793), - ($$3084$$,$$VIEWBANK$$,#{state_id_vic},-37.744219,145.08793), - ($$3085$$,$$MACLEOD$$,#{state_id_vic},-37.726038,145.068457), - ($$3085$$,$$MACLEOD WEST$$,#{state_id_vic},-37.726038,145.068457), - ($$3085$$,$$YALLAMBIE$$,#{state_id_vic},-37.726038,145.068457), - ($$3086$$,$$LA TROBE UNIVERSITY$$,#{state_id_vic},-37.721328,145.047012), - ($$3087$$,$$WATSONIA$$,#{state_id_vic},-37.712531,145.082098), - ($$3087$$,$$WATSONIA NORTH$$,#{state_id_vic},-37.712531,145.082098), - ($$3088$$,$$BRIAR HILL$$,#{state_id_vic},-37.709483,145.120135), - ($$3088$$,$$GREENSBOROUGH$$,#{state_id_vic},-37.709483,145.120135), - ($$3088$$,$$ST HELENA$$,#{state_id_vic},-37.709483,145.120135), - ($$3089$$,$$DIAMOND CREEK$$,#{state_id_vic},-37.642629,145.217595), - ($$3090$$,$$PLENTY$$,#{state_id_vic},-37.671565,145.124024), - ($$3091$$,$$YARRAMBAT$$,#{state_id_vic},-37.639769,145.132663), - ($$3093$$,$$LOWER PLENTY$$,#{state_id_vic},-37.730758,145.088118), - ($$3094$$,$$MONTMORENCY$$,#{state_id_vic},-37.715294,145.121583), - ($$3095$$,$$ELTHAM$$,#{state_id_vic},-37.71383,145.148537), - ($$3095$$,$$ELTHAM NORTH$$,#{state_id_vic},-37.71383,145.148537), - ($$3095$$,$$RESEARCH$$,#{state_id_vic},-37.71383,145.148537), - ($$3096$$,$$WATTLE GLEN$$,#{state_id_vic},-37.670101,145.190928), - ($$3097$$,$$BEND OF ISLANDS$$,#{state_id_vic},-37.697232,145.284134), - ($$3097$$,$$KANGAROO GROUND$$,#{state_id_vic},-37.697232,145.284134), - ($$3097$$,$$WATSONS CREEK$$,#{state_id_vic},-37.697232,145.284134), - ($$3099$$,$$ARTHURS CREEK$$,#{state_id_vic},-37.587697,145.218156), - ($$3099$$,$$COTTLES BRIDGE$$,#{state_id_vic},-37.587697,145.218156), - ($$3099$$,$$HURSTBRIDGE$$,#{state_id_vic},-37.587697,145.218156), - ($$3099$$,$$NUTFIELD$$,#{state_id_vic},-37.587697,145.218156), - ($$3099$$,$$STRATHEWEN$$,#{state_id_vic},-37.587697,145.218156), - ($$3101$$,$$COTHAM$$,#{state_id_vic},-37.808497,145.044922), - ($$3101$$,$$KEW$$,#{state_id_vic},-37.808497,145.044922), - ($$3102$$,$$KEW EAST$$,#{state_id_vic},-37.796246,145.049017), - ($$3103$$,$$BALWYN$$,#{state_id_vic},-37.809701,145.082303), - ($$3103$$,$$BALWYN EAST$$,#{state_id_vic},-37.809701,145.082303), - ($$3104$$,$$BALWYN NORTH$$,#{state_id_vic},-37.792835,145.071727), - ($$3104$$,$$GREYTHORN$$,#{state_id_vic},-37.792835,145.071727), - ($$3105$$,$$BULLEEN$$,#{state_id_vic},-37.768554,145.079543), - ($$3105$$,$$BULLEEN SOUTH$$,#{state_id_vic},-37.768554,145.079543), - ($$3106$$,$$TEMPLESTOWE$$,#{state_id_vic},-37.768882,145.117873), - ($$3107$$,$$TEMPLESTOWE LOWER$$,#{state_id_vic},-37.7563,145.102997), - ($$3108$$,$$DONCASTER$$,#{state_id_vic},-37.783031,145.122517), - ($$3109$$,$$DONCASTER EAST$$,#{state_id_vic},-37.811994,145.19474), - ($$3109$$,$$DONCASTER HEIGHTS$$,#{state_id_vic},-37.811994,145.19474), - ($$3109$$,$$THE PINES$$,#{state_id_vic},-37.811994,145.19474), - ($$3109$$,$$TUNSTALL SQUARE PO$$,#{state_id_vic},-37.811994,145.19474), - ($$3110$$,$$NUNAWADING BC$$,#{state_id_vic},0.0,0.0), - ($$3111$$,$$DONVALE$$,#{state_id_vic},-38.183899,144.468019), - ($$3113$$,$$NORTH WARRANDYTE$$,#{state_id_vic},-37.731758,145.221282), - ($$3113$$,$$WARRANDYTE$$,#{state_id_vic},-37.731758,145.221282), - ($$3114$$,$$PARK ORCHARDS$$,#{state_id_vic},-37.778442,145.214586), - ($$3115$$,$$WONGA PARK$$,#{state_id_vic},-37.738666,145.270483), - ($$3116$$,$$CHIRNSIDE PARK$$,#{state_id_vic},-37.750325,145.326463), - ($$3121$$,$$BURNLEY$$,#{state_id_vic},-37.826869,145.007098), - ($$3121$$,$$BURNLEY NORTH$$,#{state_id_vic},-37.826869,145.007098), - ($$3121$$,$$CREMORNE$$,#{state_id_vic},-37.826869,145.007098), - ($$3121$$,$$RICHMOND$$,#{state_id_vic},-37.826869,145.007098), - ($$3121$$,$$RICHMOND EAST$$,#{state_id_vic},-37.826869,145.007098), - ($$3121$$,$$RICHMOND NORTH$$,#{state_id_vic},-37.826869,145.007098), - ($$3121$$,$$RICHMOND SOUTH$$,#{state_id_vic},-37.826869,145.007098), - ($$3122$$,$$AUBURN SOUTH$$,#{state_id_vic},-37.842105,145.045951), - ($$3122$$,$$GLENFERRIE SOUTH$$,#{state_id_vic},-37.842105,145.045951), - ($$3122$$,$$HAWTHORN$$,#{state_id_vic},-37.842105,145.045951), - ($$3122$$,$$HAWTHORN NORTH$$,#{state_id_vic},-37.842105,145.045951), - ($$3122$$,$$HAWTHORN WEST$$,#{state_id_vic},-37.842105,145.045951), - ($$3123$$,$$AUBURN$$,#{state_id_vic},-37.832121,145.044832), - ($$3123$$,$$HAWTHORN EAST$$,#{state_id_vic},-37.832121,145.044832), - ($$3124$$,$$CAMBERWELL$$,#{state_id_vic},-37.824818,145.057957), - ($$3124$$,$$CAMBERWELL NORTH$$,#{state_id_vic},-37.824818,145.057957), - ($$3124$$,$$CAMBERWELL SOUTH$$,#{state_id_vic},-37.824818,145.057957), - ($$3124$$,$$CAMBERWELL WEST$$,#{state_id_vic},-37.824818,145.057957), - ($$3124$$,$$HARTWELL$$,#{state_id_vic},-37.824818,145.057957), - ($$3124$$,$$MIDDLE CAMBERWELL$$,#{state_id_vic},-37.824818,145.057957), - ($$3125$$,$$BENNETTSWOOD$$,#{state_id_vic},-37.844825,145.115681), - ($$3125$$,$$BURWOOD$$,#{state_id_vic},-37.844825,145.115681), - ($$3125$$,$$SURREY HILLS SOUTH$$,#{state_id_vic},-37.844825,145.115681), - ($$3126$$,$$CAMBERWELL EAST$$,#{state_id_vic},-37.840195,145.094524), - ($$3126$$,$$CANTERBURY$$,#{state_id_vic},-37.840195,145.094524), - ($$3127$$,$$MONT ALBERT$$,#{state_id_vic},-37.821232,145.104996), - ($$3127$$,$$SURREY HILLS$$,#{state_id_vic},-37.821232,145.104996), - ($$3127$$,$$SURREY HILLS NORTH$$,#{state_id_vic},-37.821232,145.104996), - ($$3128$$,$$BOX HILL$$,#{state_id_vic},-37.817455,145.119314), - ($$3128$$,$$BOX HILL CENTRAL$$,#{state_id_vic},-37.817455,145.119314), - ($$3128$$,$$BOX HILL SOUTH$$,#{state_id_vic},-37.817455,145.119314), - ($$3128$$,$$HOUSTON$$,#{state_id_vic},-37.817455,145.119314), - ($$3128$$,$$WATTLE PARK$$,#{state_id_vic},-37.817455,145.119314), - ($$3129$$,$$BOX HILL NORTH$$,#{state_id_vic},-37.801761,145.126869), - ($$3129$$,$$KERRIMUIR$$,#{state_id_vic},-37.801761,145.126869), - ($$3129$$,$$MONT ALBERT NORTH$$,#{state_id_vic},-37.801761,145.126869), - ($$3130$$,$$BLACKBURN$$,#{state_id_vic},-37.819374,145.153852), - ($$3130$$,$$BLACKBURN NORTH$$,#{state_id_vic},-37.819374,145.153852), - ($$3130$$,$$BLACKBURN SOUTH$$,#{state_id_vic},-37.819374,145.153852), - ($$3130$$,$$LABURNUM$$,#{state_id_vic},-37.819374,145.153852), - ($$3131$$,$$BRENTFORD SQUARE$$,#{state_id_vic},-37.837379,145.183948), - ($$3131$$,$$FOREST HILL$$,#{state_id_vic},-37.837379,145.183948), - ($$3131$$,$$NUNAWADING$$,#{state_id_vic},-37.837379,145.183948), - ($$3132$$,$$MITCHAM$$,#{state_id_vic},-37.816878,145.193712), - ($$3132$$,$$MITCHAM NORTH$$,#{state_id_vic},-37.816878,145.193712), - ($$3132$$,$$RANGEVIEW$$,#{state_id_vic},-37.816878,145.193712), - ($$3133$$,$$VERMONT$$,#{state_id_vic},-37.836235,145.194651), - ($$3133$$,$$VERMONT SOUTH$$,#{state_id_vic},-37.836235,145.194651), - ($$3134$$,$$HEATHWOOD$$,#{state_id_vic},-37.81402,145.227362), - ($$3134$$,$$RINGWOOD$$,#{state_id_vic},-37.81402,145.227362), - ($$3134$$,$$RINGWOOD NORTH$$,#{state_id_vic},-37.81402,145.227362), - ($$3134$$,$$WARRANDYTE SOUTH$$,#{state_id_vic},-37.81402,145.227362), - ($$3134$$,$$WARRANWOOD$$,#{state_id_vic},-37.81402,145.227362), - ($$3135$$,$$BEDFORD ROAD$$,#{state_id_vic},-37.820951,145.246663), - ($$3135$$,$$HEATHMONT$$,#{state_id_vic},-37.820951,145.246663), - ($$3135$$,$$RINGWOOD EAST$$,#{state_id_vic},-37.820951,145.246663), - ($$3136$$,$$CROYDON$$,#{state_id_vic},-37.798729,145.280685), - ($$3136$$,$$CROYDON HILLS$$,#{state_id_vic},-37.798729,145.280685), - ($$3136$$,$$CROYDON NORTH$$,#{state_id_vic},-37.798729,145.280685), - ($$3136$$,$$CROYDON SOUTH$$,#{state_id_vic},-37.798729,145.280685), - ($$3137$$,$$KILSYTH$$,#{state_id_vic},-37.802304,145.312198), - ($$3137$$,$$KILSYTH SOUTH$$,#{state_id_vic},-37.802304,145.312198), - ($$3138$$,$$MOOROOLBARK$$,#{state_id_vic},-37.774337,145.329954), - ($$3139$$,$$BEENAK$$,#{state_id_vic},-37.795679,145.538569), - ($$3139$$,$$DON VALLEY$$,#{state_id_vic},-37.795679,145.538569), - ($$3139$$,$$HODDLES CREEK$$,#{state_id_vic},-37.795679,145.538569), - ($$3139$$,$$LAUNCHING PLACE$$,#{state_id_vic},-37.795679,145.538569), - ($$3139$$,$$SEVILLE$$,#{state_id_vic},-37.795679,145.538569), - ($$3139$$,$$SEVILLE EAST$$,#{state_id_vic},-37.795679,145.538569), - ($$3139$$,$$WANDIN EAST$$,#{state_id_vic},-37.795679,145.538569), - ($$3139$$,$$WANDIN NORTH$$,#{state_id_vic},-37.795679,145.538569), - ($$3139$$,$$WOORI YALLOCK$$,#{state_id_vic},-37.795679,145.538569), - ($$3139$$,$$YELLINGBO$$,#{state_id_vic},-37.795679,145.538569), - ($$3140$$,$$LILYDALE$$,#{state_id_vic},-37.755519,145.347707), - ($$3141$$,$$CHAPEL STREET NORTH$$,#{state_id_vic},-36.990185,144.063338), - ($$3141$$,$$DOMAIN ROAD PO$$,#{state_id_vic},-36.990185,144.063338), - ($$3141$$,$$SOUTH YARRA$$,#{state_id_vic},-36.990185,144.063338), - ($$3142$$,$$HAWKSBURN$$,#{state_id_vic},-37.842378,145.001779), - ($$3142$$,$$TOORAK$$,#{state_id_vic},-37.842378,145.001779), - ($$3143$$,$$ARMADALE$$,#{state_id_vic},-37.85934,145.018505), - ($$3143$$,$$ARMADALE NORTH$$,#{state_id_vic},-37.85934,145.018505), - ($$3144$$,$$KOOYONG$$,#{state_id_vic},-37.840702,145.032101), - ($$3144$$,$$MALVERN$$,#{state_id_vic},-37.840702,145.032101), - ($$3144$$,$$MALVERN NORTH$$,#{state_id_vic},-37.840702,145.032101), - ($$3145$$,$$CAULFIELD EAST$$,#{state_id_vic},-37.875412,145.041976), - ($$3145$$,$$CENTRAL PARK$$,#{state_id_vic},-37.875412,145.041976), - ($$3145$$,$$DARLING$$,#{state_id_vic},-37.875412,145.041976), - ($$3145$$,$$DARLING SOUTH$$,#{state_id_vic},-37.875412,145.041976), - ($$3145$$,$$MALVERN EAST$$,#{state_id_vic},-37.875412,145.041976), - ($$3145$$,$$WATTLETREE ROAD PO$$,#{state_id_vic},-37.875412,145.041976), - ($$3146$$,$$GLEN IRIS$$,#{state_id_vic},-37.854687,145.067215), - ($$3147$$,$$ASHBURTON$$,#{state_id_vic},-37.863393,145.07942), - ($$3147$$,$$ASHWOOD$$,#{state_id_vic},-37.863393,145.07942), - ($$3148$$,$$CHADSTONE$$,#{state_id_vic},-37.886372,145.082527), - ($$3148$$,$$CHADSTONE CENTRE$$,#{state_id_vic},-37.886372,145.082527), - ($$3148$$,$$HOLMESGLEN$$,#{state_id_vic},-37.886372,145.082527), - ($$3148$$,$$JORDANVILLE$$,#{state_id_vic},-37.886372,145.082527), - ($$3149$$,$$MOUNT WAVERLEY$$,#{state_id_vic},-37.875273,145.128398), - ($$3149$$,$$PINEWOOD$$,#{state_id_vic},-37.875273,145.128398), - ($$3149$$,$$SYNDAL$$,#{state_id_vic},-37.875273,145.128398), - ($$3150$$,$$BRANDON PARK$$,#{state_id_vic},-37.877631,145.166222), - ($$3150$$,$$GLEN WAVERLEY$$,#{state_id_vic},-37.877631,145.166222), - ($$3150$$,$$WHEELERS HILL$$,#{state_id_vic},-37.877631,145.166222), - ($$3151$$,$$BURWOOD EAST$$,#{state_id_vic},-37.858352,145.138553), - ($$3151$$,$$BURWOOD HEIGHTS$$,#{state_id_vic},-37.858352,145.138553), - ($$3152$$,$$KNOX CITY CENTRE$$,#{state_id_vic},-37.869224,145.241382), - ($$3152$$,$$STUDFIELD$$,#{state_id_vic},-37.869224,145.241382), - ($$3152$$,$$WANTIRNA$$,#{state_id_vic},-37.869224,145.241382), - ($$3152$$,$$WANTIRNA SOUTH$$,#{state_id_vic},-37.869224,145.241382), - ($$3153$$,$$BAYSWATER$$,#{state_id_vic},-37.84126,145.266725), - ($$3153$$,$$BAYSWATER NORTH$$,#{state_id_vic},-37.84126,145.266725), - ($$3154$$,$$THE BASIN$$,#{state_id_vic},-37.851467,145.307133), - ($$3155$$,$$BORONIA$$,#{state_id_vic},-37.861504,145.275762), - ($$3156$$,$$FERNTREE GULLY$$,#{state_id_vic},-37.883019,145.295404), - ($$3156$$,$$LYSTERFIELD$$,#{state_id_vic},-37.883019,145.295404), - ($$3156$$,$$LYSTERFIELD SOUTH$$,#{state_id_vic},-37.883019,145.295404), - ($$3156$$,$$MOUNTAIN GATE$$,#{state_id_vic},-37.883019,145.295404), - ($$3156$$,$$UPPER FERNTREE GULLY$$,#{state_id_vic},-37.883019,145.295404), - ($$3158$$,$$UPWEY$$,#{state_id_vic},-37.903672,145.33131), - ($$3159$$,$$MENZIES CREEK$$,#{state_id_vic},-37.921323,145.403546), - ($$3159$$,$$SELBY$$,#{state_id_vic},-37.921323,145.403546), - ($$3160$$,$$BELGRAVE$$,#{state_id_vic},-37.908422,145.355075), - ($$3160$$,$$BELGRAVE HEIGHTS$$,#{state_id_vic},-37.908422,145.355075), - ($$3160$$,$$BELGRAVE SOUTH$$,#{state_id_vic},-37.908422,145.355075), - ($$3160$$,$$TECOMA$$,#{state_id_vic},-37.908422,145.355075), - ($$3161$$,$$CAULFIELD JUNCTION$$,#{state_id_vic},-38.033451,145.309748), - ($$3161$$,$$CAULFIELD NORTH$$,#{state_id_vic},-38.033451,145.309748), - ($$3162$$,$$CAULFIELD$$,#{state_id_vic},-37.880479,145.026806), - ($$3162$$,$$CAULFIELD SOUTH$$,#{state_id_vic},-37.880479,145.026806), - ($$3162$$,$$HOPETOUN GARDENS$$,#{state_id_vic},-37.880479,145.026806), - ($$3163$$,$$BOORAN ROAD PO$$,#{state_id_vic},-37.889336,145.058121), - ($$3163$$,$$CARNEGIE$$,#{state_id_vic},-37.889336,145.058121), - ($$3163$$,$$GLEN HUNTLY$$,#{state_id_vic},-37.889336,145.058121), - ($$3163$$,$$MURRUMBEENA$$,#{state_id_vic},-37.889336,145.058121), - ($$3164$$,$$DANDENONG SOUTH$$,#{state_id_vic},-38.02243,145.23738), - ($$3165$$,$$BENTLEIGH EAST$$,#{state_id_vic},-37.927402,145.059412), - ($$3165$$,$$COATESVILLE$$,#{state_id_vic},-37.927402,145.059412), - ($$3166$$,$$HUGHESDALE$$,#{state_id_vic},-37.895763,145.076545), - ($$3166$$,$$HUNTINGDALE$$,#{state_id_vic},-37.895763,145.076545), - ($$3166$$,$$OAKLEIGH$$,#{state_id_vic},-37.895763,145.076545), - ($$3166$$,$$OAKLEIGH EAST$$,#{state_id_vic},-37.895763,145.076545), - ($$3167$$,$$OAKLEIGH SOUTH$$,#{state_id_vic},-37.926986,145.0964), - ($$3168$$,$$CLAYTON$$,#{state_id_vic},-37.925488,145.119662), - ($$3168$$,$$NOTTING HILL$$,#{state_id_vic},-37.925488,145.119662), - ($$3169$$,$$CLARINDA$$,#{state_id_vic},-37.941228,145.10244), - ($$3169$$,$$CLAYTON SOUTH$$,#{state_id_vic},-37.941228,145.10244), - ($$3170$$,$$MULGRAVE$$,#{state_id_vic},-37.869611,145.102866), - ($$3170$$,$$WAVERLEY GARDENS$$,#{state_id_vic},-37.869611,145.102866), - ($$3171$$,$$SANDOWN VILLAGE$$,#{state_id_vic},-37.950802,145.15696), - ($$3171$$,$$SPRINGVALE$$,#{state_id_vic},-37.950802,145.15696), - ($$3172$$,$$DINGLEY VILLAGE$$,#{state_id_vic},-37.973323,145.119941), - ($$3172$$,$$SPRINGVALE SOUTH$$,#{state_id_vic},-37.973323,145.119941), - ($$3173$$,$$KEYSBOROUGH$$,#{state_id_vic},-37.989707,145.149037), - ($$3174$$,$$NOBLE PARK$$,#{state_id_vic},-37.967254,145.176167), - ($$3174$$,$$NOBLE PARK EAST$$,#{state_id_vic},-37.967254,145.176167), - ($$3174$$,$$NOBLE PARK NORTH$$,#{state_id_vic},-37.967254,145.176167), - ($$3175$$,$$BANGHOLME$$,#{state_id_vic},-38.033107,145.179482), - ($$3175$$,$$DANDENONG$$,#{state_id_vic},-38.033107,145.179482), - ($$3175$$,$$DANDENONG EAST$$,#{state_id_vic},-38.033107,145.179482), - ($$3175$$,$$DANDENONG NORTH$$,#{state_id_vic},-38.033107,145.179482), - ($$3175$$,$$DANDENONG PLAZA$$,#{state_id_vic},-38.033107,145.179482), - ($$3175$$,$$DANDENONG SOUTH$$,#{state_id_vic},-38.033107,145.179482), - ($$3175$$,$$DUNEARN$$,#{state_id_vic},-38.033107,145.179482), - ($$3176$$,$$SCORESBY BC$$,#{state_id_vic},0.0,0.0), - ($$3177$$,$$DOVETON$$,#{state_id_vic},-37.995245,145.240156), - ($$3177$$,$$EUMEMMERRING$$,#{state_id_vic},-37.995245,145.240156), - ($$3178$$,$$ROWVILLE$$,#{state_id_vic},-37.928005,145.235811), - ($$3179$$,$$SCORESBY$$,#{state_id_vic},-37.864883,145.26427), - ($$3180$$,$$KNOXFIELD$$,#{state_id_vic},-37.888895,145.248383), - ($$3181$$,$$PRAHRAN$$,#{state_id_vic},-37.849577,144.993714), - ($$3181$$,$$PRAHRAN EAST$$,#{state_id_vic},-37.849577,144.993714), - ($$3181$$,$$WINDSOR$$,#{state_id_vic},-37.849577,144.993714), - ($$3182$$,$$ST KILDA$$,#{state_id_vic},-37.867573,144.978814), - ($$3182$$,$$ST KILDA SOUTH$$,#{state_id_vic},-37.867573,144.978814), - ($$3182$$,$$ST KILDA WEST$$,#{state_id_vic},-37.867573,144.978814), - ($$3183$$,$$BALACLAVA$$,#{state_id_vic},-37.869023,144.995478), - ($$3183$$,$$ST KILDA EAST$$,#{state_id_vic},-37.869023,144.995478), - ($$3184$$,$$BRIGHTON ROAD$$,#{state_id_vic},-37.882825,144.996354), - ($$3184$$,$$ELWOOD$$,#{state_id_vic},-37.882825,144.996354), - ($$3185$$,$$ELSTERNWICK$$,#{state_id_vic},-37.884724,145.004153), - ($$3185$$,$$GARDENVALE$$,#{state_id_vic},-37.884724,145.004153), - ($$3185$$,$$RIPPONLEA$$,#{state_id_vic},-37.884724,145.004153), - ($$3186$$,$$BRIGHTON$$,#{state_id_vic},-37.913149,144.991682), - ($$3186$$,$$BRIGHTON NORTH$$,#{state_id_vic},-37.913149,144.991682), - ($$3186$$,$$DENDY$$,#{state_id_vic},-37.913149,144.991682), - ($$3186$$,$$WERE STREET PO$$,#{state_id_vic},-37.913149,144.991682), - ($$3187$$,$$BRIGHTON EAST$$,#{state_id_vic},-37.904879,145.002603), - ($$3187$$,$$NORTH ROAD$$,#{state_id_vic},-37.904879,145.002603), - ($$3188$$,$$HAMPTON$$,#{state_id_vic},-37.933603,145.034175), - ($$3188$$,$$HAMPTON EAST$$,#{state_id_vic},-37.933603,145.034175), - ($$3188$$,$$HAMPTON NORTH$$,#{state_id_vic},-37.933603,145.034175), - ($$3189$$,$$MOORABBIN$$,#{state_id_vic},-37.934352,145.036735), - ($$3189$$,$$MOORABBIN EAST$$,#{state_id_vic},-37.934352,145.036735), - ($$3189$$,$$WISHART$$,#{state_id_vic},-37.934352,145.036735), - ($$3190$$,$$HIGHETT$$,#{state_id_vic},-37.947848,145.034294), - ($$3191$$,$$SANDRINGHAM$$,#{state_id_vic},-37.952493,145.012316), - ($$3192$$,$$CHELTENHAM$$,#{state_id_vic},-37.96451,145.055873), - ($$3192$$,$$CHELTENHAM EAST$$,#{state_id_vic},-37.96451,145.055873), - ($$3192$$,$$CHELTENHAM NORTH$$,#{state_id_vic},-37.96451,145.055873), - ($$3192$$,$$SOUTHLAND CENTRE$$,#{state_id_vic},-37.96451,145.055873), - ($$3193$$,$$BEAUMARIS$$,#{state_id_vic},-37.986285,145.032876), - ($$3193$$,$$BLACK ROCK$$,#{state_id_vic},-37.986285,145.032876), - ($$3193$$,$$BLACK ROCK NORTH$$,#{state_id_vic},-37.986285,145.032876), - ($$3193$$,$$CROMER$$,#{state_id_vic},-37.986285,145.032876), - ($$3194$$,$$MENTONE$$,#{state_id_vic},-37.982859,145.0649), - ($$3194$$,$$MENTONE EAST$$,#{state_id_vic},-37.982859,145.0649), - ($$3194$$,$$MOORABBIN AIRPORT$$,#{state_id_vic},-37.982859,145.0649), - ($$3195$$,$$ASPENDALE$$,#{state_id_vic},-38.026479,145.101908), - ($$3195$$,$$ASPENDALE GARDENS$$,#{state_id_vic},-38.026479,145.101908), - ($$3195$$,$$BRAESIDE$$,#{state_id_vic},-38.026479,145.101908), - ($$3195$$,$$MORDIALLOC$$,#{state_id_vic},-38.026479,145.101908), - ($$3195$$,$$MORDIALLOC NORTH$$,#{state_id_vic},-38.026479,145.101908), - ($$3195$$,$$PARKDALE$$,#{state_id_vic},-38.026479,145.101908), - ($$3195$$,$$WATERWAYS$$,#{state_id_vic},-38.026479,145.101908), - ($$3196$$,$$BONBEACH$$,#{state_id_vic},-38.062472,145.119404), - ($$3196$$,$$CHELSEA$$,#{state_id_vic},-38.062472,145.119404), - ($$3196$$,$$CHELSEA HEIGHTS$$,#{state_id_vic},-38.062472,145.119404), - ($$3196$$,$$EDITHVALE$$,#{state_id_vic},-38.062472,145.119404), - ($$3197$$,$$CARRUM$$,#{state_id_vic},-38.076364,145.123205), - ($$3197$$,$$PATTERSON LAKES$$,#{state_id_vic},-38.076364,145.123205), - ($$3198$$,$$BELVEDERE PARK$$,#{state_id_vic},-38.10667,145.158496), - ($$3198$$,$$SEAFORD$$,#{state_id_vic},-38.10667,145.158496), - ($$3199$$,$$FRANKSTON$$,#{state_id_vic},-38.145001,145.122477), - ($$3199$$,$$FRANKSTON EAST$$,#{state_id_vic},-38.145001,145.122477), - ($$3199$$,$$FRANKSTON HEIGHTS$$,#{state_id_vic},-38.145001,145.122477), - ($$3199$$,$$FRANKSTON SOUTH$$,#{state_id_vic},-38.145001,145.122477), - ($$3199$$,$$KARINGAL$$,#{state_id_vic},-38.145001,145.122477), - ($$3199$$,$$KARINGAL CENTRE$$,#{state_id_vic},-38.145001,145.122477), - ($$3200$$,$$FRANKSTON NORTH$$,#{state_id_vic},-38.165002,145.188595), - ($$3200$$,$$PINES FOREST$$,#{state_id_vic},-38.165002,145.188595), - ($$3201$$,$$CARRUM DOWNS$$,#{state_id_vic},-38.090783,145.191719), - ($$3202$$,$$HEATHERTON$$,#{state_id_vic},-37.969954,145.214164), - ($$3204$$,$$BENTLEIGH$$,#{state_id_vic},-37.918057,145.035444), - ($$3204$$,$$MCKINNON$$,#{state_id_vic},-37.918057,145.035444), - ($$3204$$,$$ORMOND$$,#{state_id_vic},-37.918057,145.035444), - ($$3204$$,$$PATTERSON$$,#{state_id_vic},-37.918057,145.035444), - ($$3205$$,$$SOUTH MELBOURNE$$,#{state_id_vic},-37.93291,145.033718), - ($$3205$$,$$SOUTH MELBOURNE DC$$,#{state_id_vic},-37.93291,145.033718), - ($$3206$$,$$ALBERT PARK$$,#{state_id_vic},-37.840705,144.95571), - ($$3206$$,$$MIDDLE PARK$$,#{state_id_vic},-37.840705,144.95571), - ($$3207$$,$$GARDEN CITY$$,#{state_id_vic},-37.829244,144.956207), - ($$3207$$,$$PORT MELBOURNE$$,#{state_id_vic},-37.829244,144.956207), - ($$3211$$,$$LITTLE RIVER$$,#{state_id_vic},-37.971627,144.526585), - ($$3212$$,$$AVALON$$,#{state_id_vic},-38.022416,144.407891), - ($$3212$$,$$LARA$$,#{state_id_vic},-38.022416,144.407891), - ($$3212$$,$$POINT WILSON$$,#{state_id_vic},-38.022416,144.407891), - ($$3214$$,$$CORIO$$,#{state_id_vic},-38.074162,144.358659), - ($$3214$$,$$NORLANE$$,#{state_id_vic},-38.074162,144.358659), - ($$3214$$,$$NORTH SHORE$$,#{state_id_vic},-38.074162,144.358659), - ($$3215$$,$$BELL PARK$$,#{state_id_vic},-38.113738,144.330983), - ($$3215$$,$$BELL POST HILL$$,#{state_id_vic},-38.113738,144.330983), - ($$3215$$,$$DRUMCONDRA$$,#{state_id_vic},-38.113738,144.330983), - ($$3215$$,$$GEELONG NORTH$$,#{state_id_vic},-38.113738,144.330983), - ($$3215$$,$$HAMLYN HEIGHTS$$,#{state_id_vic},-38.113738,144.330983), - ($$3215$$,$$NORTH GEELONG$$,#{state_id_vic},-38.113738,144.330983), - ($$3215$$,$$RIPPLESIDE$$,#{state_id_vic},-38.113738,144.330983), - ($$3216$$,$$BELMONT$$,#{state_id_vic},-38.175587,144.342666), - ($$3216$$,$$FRESHWATER CREEK$$,#{state_id_vic},-38.175587,144.342666), - ($$3216$$,$$GROVEDALE$$,#{state_id_vic},-38.175587,144.342666), - ($$3216$$,$$GROVEDALE EAST$$,#{state_id_vic},-38.175587,144.342666), - ($$3216$$,$$HIGHTON$$,#{state_id_vic},-38.175587,144.342666), - ($$3216$$,$$MARSHALL$$,#{state_id_vic},-38.175587,144.342666), - ($$3216$$,$$MOUNT DUNEED$$,#{state_id_vic},-38.175587,144.342666), - ($$3216$$,$$WANDANA HEIGHTS$$,#{state_id_vic},-38.175587,144.342666), - ($$3216$$,$$WAURN PONDS$$,#{state_id_vic},-38.175587,144.342666), - ($$3217$$,$$DEAKIN UNIVERSITY$$,#{state_id_vic},-38.198946,144.297736), - ($$3218$$,$$GEELONG WEST$$,#{state_id_vic},-38.142148,144.348006), - ($$3218$$,$$HERNE HILL$$,#{state_id_vic},-38.142148,144.348006), - ($$3218$$,$$MANIFOLD HEIGHTS$$,#{state_id_vic},-38.142148,144.348006), - ($$3219$$,$$BREAKWATER$$,#{state_id_vic},-38.180291,144.382352), - ($$3219$$,$$EAST GEELONG$$,#{state_id_vic},-38.180291,144.382352), - ($$3219$$,$$NEWCOMB$$,#{state_id_vic},-38.180291,144.382352), - ($$3219$$,$$ST ALBANS PARK$$,#{state_id_vic},-38.180291,144.382352), - ($$3219$$,$$THOMSON$$,#{state_id_vic},-38.180291,144.382352), - ($$3219$$,$$WHITTINGTON$$,#{state_id_vic},-38.180291,144.382352), - ($$3220$$,$$BAREENA$$,#{state_id_vic},-38.181164,145.109544), - ($$3220$$,$$GEELONG$$,#{state_id_vic},-38.181164,145.109544), - ($$3220$$,$$NEWTOWN$$,#{state_id_vic},-38.181164,145.109544), - ($$3220$$,$$SOUTH GEELONG$$,#{state_id_vic},-38.181164,145.109544), - ($$3221$$,$$ANAKIE$$,#{state_id_vic},-37.896843,144.253862), - ($$3221$$,$$BARRABOOL$$,#{state_id_vic},-37.896843,144.253862), - ($$3221$$,$$BATESFORD$$,#{state_id_vic},-37.896843,144.253862), - ($$3221$$,$$BELLARINE$$,#{state_id_vic},-37.896843,144.253862), - ($$3221$$,$$CERES$$,#{state_id_vic},-37.896843,144.253862), - ($$3221$$,$$FYANSFORD$$,#{state_id_vic},-37.896843,144.253862), - ($$3221$$,$$GEELONG MC$$,#{state_id_vic},-37.896843,144.253862), - ($$3221$$,$$GNARWARRE$$,#{state_id_vic},-37.896843,144.253862), - ($$3221$$,$$GREY RIVER$$,#{state_id_vic},-37.896843,144.253862), - ($$3221$$,$$KENNETT RIVER$$,#{state_id_vic},-37.896843,144.253862), - ($$3221$$,$$LOVELY BANKS$$,#{state_id_vic},-37.896843,144.253862), - ($$3221$$,$$MOOLAP$$,#{state_id_vic},-37.896843,144.253862), - ($$3221$$,$$MOORABOOL$$,#{state_id_vic},-37.896843,144.253862), - ($$3221$$,$$MURGHEBOLUC$$,#{state_id_vic},-37.896843,144.253862), - ($$3221$$,$$SEPARATION CREEK$$,#{state_id_vic},-37.896843,144.253862), - ($$3221$$,$$STAUGHTON VALE$$,#{state_id_vic},-37.896843,144.253862), - ($$3221$$,$$STONEHAVEN$$,#{state_id_vic},-37.896843,144.253862), - ($$3221$$,$$SUGARLOAF$$,#{state_id_vic},-37.896843,144.253862), - ($$3221$$,$$WALLINGTON$$,#{state_id_vic},-37.896843,144.253862), - ($$3221$$,$$WONGARRA$$,#{state_id_vic},-37.896843,144.253862), - ($$3221$$,$$WYE RIVER$$,#{state_id_vic},-37.896843,144.253862), - ($$3222$$,$$CLIFTON SPRINGS$$,#{state_id_vic},-38.15748,144.561541), - ($$3222$$,$$CURLEWIS$$,#{state_id_vic},-38.15748,144.561541), - ($$3222$$,$$DRYSDALE$$,#{state_id_vic},-38.15748,144.561541), - ($$3222$$,$$MANNERIM$$,#{state_id_vic},-38.15748,144.561541), - ($$3222$$,$$MARCUS HILL$$,#{state_id_vic},-38.15748,144.561541), - ($$3223$$,$$INDENTED HEAD$$,#{state_id_vic},-38.139121,144.711239), - ($$3223$$,$$PORTARLINGTON$$,#{state_id_vic},-38.139121,144.711239), - ($$3223$$,$$ST LEONARDS$$,#{state_id_vic},-38.139121,144.711239), - ($$3224$$,$$LEOPOLD$$,#{state_id_vic},-38.183967,144.459914), - ($$3225$$,$$POINT LONSDALE$$,#{state_id_vic},-38.286113,144.614489), - ($$3225$$,$$QUEENSCLIFF$$,#{state_id_vic},-38.286113,144.614489), - ($$3225$$,$$SWAN BAY$$,#{state_id_vic},-38.286113,144.614489), - ($$3225$$,$$SWAN ISLAND$$,#{state_id_vic},-38.286113,144.614489), - ($$3226$$,$$OCEAN GROVE$$,#{state_id_vic},-38.270293,144.540778), - ($$3227$$,$$BARWON HEADS$$,#{state_id_vic},-38.281078,144.49179), - ($$3227$$,$$BREAMLEA$$,#{state_id_vic},-38.281078,144.49179), - ($$3227$$,$$CONNEWARRE$$,#{state_id_vic},-38.281078,144.49179), - ($$3228$$,$$BELLBRAE$$,#{state_id_vic},-38.329558,144.262559), - ($$3228$$,$$BELLS BEACH$$,#{state_id_vic},-38.329558,144.262559), - ($$3228$$,$$JAN JUC$$,#{state_id_vic},-38.329558,144.262559), - ($$3228$$,$$TORQUAY$$,#{state_id_vic},-38.329558,144.262559), - ($$3230$$,$$ANGLESEA$$,#{state_id_vic},-38.405129,144.189268), - ($$3231$$,$$AIREYS INLET$$,#{state_id_vic},-38.459435,144.106892), - ($$3231$$,$$BIG HILL$$,#{state_id_vic},-38.459435,144.106892), - ($$3231$$,$$EASTERN VIEW$$,#{state_id_vic},-38.459435,144.106892), - ($$3231$$,$$FAIRHAVEN$$,#{state_id_vic},-38.459435,144.106892), - ($$3231$$,$$MOGGS CREEK$$,#{state_id_vic},-38.459435,144.106892), - ($$3232$$,$$LORNE$$,#{state_id_vic},-38.518801,143.99579), - ($$3233$$,$$APOLLO BAY$$,#{state_id_vic},-38.748434,143.670432), - ($$3233$$,$$CAPE OTWAY$$,#{state_id_vic},-38.748434,143.670432), - ($$3233$$,$$MARENGO$$,#{state_id_vic},-38.748434,143.670432), - ($$3233$$,$$PETTICOAT CREEK$$,#{state_id_vic},-38.748434,143.670432), - ($$3233$$,$$SKENES CREEK$$,#{state_id_vic},-38.748434,143.670432), - ($$3233$$,$$SKENES CREEK NORTH$$,#{state_id_vic},-38.748434,143.670432), - ($$3235$$,$$BENWERRIN$$,#{state_id_vic},-38.472753,143.93252), - ($$3235$$,$$BOONAH$$,#{state_id_vic},-38.472753,143.93252), - ($$3235$$,$$DEANS MARSH$$,#{state_id_vic},-38.472753,143.93252), - ($$3235$$,$$PENNYROYAL$$,#{state_id_vic},-38.472753,143.93252), - ($$3236$$,$$FORREST$$,#{state_id_vic},-38.51882,143.71457), - ($$3236$$,$$MOUNT SABINE$$,#{state_id_vic},-38.51882,143.71457), - ($$3237$$,$$AIRE VALLEY$$,#{state_id_vic},-38.693169,143.564483), - ($$3237$$,$$BEECH FOREST$$,#{state_id_vic},-38.693169,143.564483), - ($$3237$$,$$FERGUSON$$,#{state_id_vic},-38.693169,143.564483), - ($$3237$$,$$GELLIBRAND LOWER$$,#{state_id_vic},-38.693169,143.564483), - ($$3237$$,$$WATTLE HILL$$,#{state_id_vic},-38.693169,143.564483), - ($$3237$$,$$WEEAPROINAH$$,#{state_id_vic},-38.693169,143.564483), - ($$3237$$,$$WYELANGTA$$,#{state_id_vic},-38.693169,143.564483), - ($$3237$$,$$YUULONG$$,#{state_id_vic},-38.693169,143.564483), - ($$3238$$,$$GLENAIRE$$,#{state_id_vic},-38.781787,143.429978), - ($$3238$$,$$HORDERN VALE$$,#{state_id_vic},-38.781787,143.429978), - ($$3238$$,$$JOHANNA$$,#{state_id_vic},-38.781787,143.429978), - ($$3238$$,$$LAVERS HILL$$,#{state_id_vic},-38.781787,143.429978), - ($$3239$$,$$CARLISLE RIVER$$,#{state_id_vic},-38.556481,143.398828), - ($$3239$$,$$CHAPPLE VALE$$,#{state_id_vic},-38.556481,143.398828), - ($$3239$$,$$GELLIBRAND$$,#{state_id_vic},-38.556481,143.398828), - ($$3239$$,$$KENNEDYS CREEK$$,#{state_id_vic},-38.556481,143.398828), - ($$3240$$,$$BUCKLEY$$,#{state_id_vic},-38.216777,144.076977), - ($$3240$$,$$GHERANG$$,#{state_id_vic},-38.216777,144.076977), - ($$3240$$,$$MODEWARRE$$,#{state_id_vic},-38.216777,144.076977), - ($$3240$$,$$MORIAC$$,#{state_id_vic},-38.216777,144.076977), - ($$3240$$,$$MOUNT MORIAC$$,#{state_id_vic},-38.216777,144.076977), - ($$3240$$,$$PARAPARAP$$,#{state_id_vic},-38.216777,144.076977), - ($$3241$$,$$BAMBRA$$,#{state_id_vic},-38.366594,143.946392), - ($$3241$$,$$OMBERSLEY$$,#{state_id_vic},-38.366594,143.946392), - ($$3241$$,$$WENSLEYDALE$$,#{state_id_vic},-38.366594,143.946392), - ($$3241$$,$$WINCHELSEA$$,#{state_id_vic},-38.366594,143.946392), - ($$3241$$,$$WINCHELSEA SOUTH$$,#{state_id_vic},-38.366594,143.946392), - ($$3241$$,$$WURDIBOLUC$$,#{state_id_vic},-38.366594,143.946392), - ($$3242$$,$$BIRREGURRA$$,#{state_id_vic},-38.314855,143.789367), - ($$3243$$,$$BARWON DOWNS$$,#{state_id_vic},-38.468578,143.758408), - ($$3243$$,$$MURROON$$,#{state_id_vic},-38.468578,143.758408), - ($$3243$$,$$WARNCOORT$$,#{state_id_vic},-38.468578,143.758408), - ($$3243$$,$$WHOOREL$$,#{state_id_vic},-38.468578,143.758408), - ($$3249$$,$$ALVIE$$,#{state_id_vic},-38.244321,143.481901), - ($$3249$$,$$BALINTORE$$,#{state_id_vic},-38.244321,143.481901), - ($$3249$$,$$BARONGAROOK$$,#{state_id_vic},-38.244321,143.481901), - ($$3249$$,$$BARONGAROOK WEST$$,#{state_id_vic},-38.244321,143.481901), - ($$3249$$,$$BARRAMUNGA$$,#{state_id_vic},-38.244321,143.481901), - ($$3249$$,$$CORAGULAC$$,#{state_id_vic},-38.244321,143.481901), - ($$3249$$,$$CORUNNUN$$,#{state_id_vic},-38.244321,143.481901), - ($$3249$$,$$DREEITE$$,#{state_id_vic},-38.244321,143.481901), - ($$3249$$,$$DREEITE SOUTH$$,#{state_id_vic},-38.244321,143.481901), - ($$3249$$,$$GERANGAMETE$$,#{state_id_vic},-38.244321,143.481901), - ($$3249$$,$$IRREWARRA$$,#{state_id_vic},-38.244321,143.481901), - ($$3249$$,$$IRREWILLIPE$$,#{state_id_vic},-38.244321,143.481901), - ($$3249$$,$$IRREWILLIPE EAST$$,#{state_id_vic},-38.244321,143.481901), - ($$3249$$,$$KAWARREN$$,#{state_id_vic},-38.244321,143.481901), - ($$3249$$,$$LARPENT$$,#{state_id_vic},-38.244321,143.481901), - ($$3249$$,$$NALANGIL$$,#{state_id_vic},-38.244321,143.481901), - ($$3249$$,$$ONDIT$$,#{state_id_vic},-38.244321,143.481901), - ($$3249$$,$$PIRRON YALLOCK$$,#{state_id_vic},-38.244321,143.481901), - ($$3249$$,$$POMBORNEIT EAST$$,#{state_id_vic},-38.244321,143.481901), - ($$3249$$,$$SWAN MARSH$$,#{state_id_vic},-38.244321,143.481901), - ($$3249$$,$$TANYBRYN$$,#{state_id_vic},-38.244321,143.481901), - ($$3249$$,$$WARRION$$,#{state_id_vic},-38.244321,143.481901), - ($$3249$$,$$WOOL WOOL$$,#{state_id_vic},-38.244321,143.481901), - ($$3249$$,$$YEO$$,#{state_id_vic},-38.244321,143.481901), - ($$3249$$,$$YEODENE$$,#{state_id_vic},-38.244321,143.481901), - ($$3250$$,$$COLAC$$,#{state_id_vic},-38.339298,143.58166), - ($$3250$$,$$COLAC EAST$$,#{state_id_vic},-38.339298,143.58166), - ($$3250$$,$$COLAC WEST$$,#{state_id_vic},-38.339298,143.58166), - ($$3250$$,$$ELLIMINYT$$,#{state_id_vic},-38.339298,143.58166), - ($$3251$$,$$BEEAC$$,#{state_id_vic},-38.193925,143.640459), - ($$3251$$,$$CUNDARE$$,#{state_id_vic},-38.193925,143.640459), - ($$3251$$,$$CUNDARE NORTH$$,#{state_id_vic},-38.193925,143.640459), - ($$3251$$,$$EURACK$$,#{state_id_vic},-38.193925,143.640459), - ($$3251$$,$$WEERING$$,#{state_id_vic},-38.193925,143.640459), - ($$3254$$,$$COROROOKE$$,#{state_id_vic},-38.296263,143.522505), - ($$3260$$,$$BOOKAAR$$,#{state_id_vic},-38.134947,143.102803), - ($$3260$$,$$BOSTOCKS CREEK$$,#{state_id_vic},-38.134947,143.102803), - ($$3260$$,$$BUNGADOR$$,#{state_id_vic},-38.134947,143.102803), - ($$3260$$,$$CAMPERDOWN$$,#{state_id_vic},-38.134947,143.102803), - ($$3260$$,$$CARPENDEIT$$,#{state_id_vic},-38.134947,143.102803), - ($$3260$$,$$CHOCOLYN$$,#{state_id_vic},-38.134947,143.102803), - ($$3260$$,$$GNOTUK$$,#{state_id_vic},-38.134947,143.102803), - ($$3260$$,$$KARIAH$$,#{state_id_vic},-38.134947,143.102803), - ($$3260$$,$$KOALLAH$$,#{state_id_vic},-38.134947,143.102803), - ($$3260$$,$$LESLIE MANOR$$,#{state_id_vic},-38.134947,143.102803), - ($$3260$$,$$POMBORNEIT$$,#{state_id_vic},-38.134947,143.102803), - ($$3260$$,$$POMBORNEIT NORTH$$,#{state_id_vic},-38.134947,143.102803), - ($$3260$$,$$SKIBO$$,#{state_id_vic},-38.134947,143.102803), - ($$3260$$,$$SOUTH PURRUMBETE$$,#{state_id_vic},-38.134947,143.102803), - ($$3260$$,$$STONYFORD$$,#{state_id_vic},-38.134947,143.102803), - ($$3260$$,$$TANDAROOK$$,#{state_id_vic},-38.134947,143.102803), - ($$3260$$,$$TESBURY$$,#{state_id_vic},-38.134947,143.102803), - ($$3260$$,$$WEERITE$$,#{state_id_vic},-38.134947,143.102803), - ($$3264$$,$$TERANG$$,#{state_id_vic},-38.236207,142.911483), - ($$3265$$,$$BOORCAN$$,#{state_id_vic},-38.21456,143.013298), - ($$3265$$,$$CUDGEE$$,#{state_id_vic},-38.21456,143.013298), - ($$3265$$,$$DIXIE$$,#{state_id_vic},-38.21456,143.013298), - ($$3265$$,$$ECKLIN SOUTH$$,#{state_id_vic},-38.21456,143.013298), - ($$3265$$,$$ELLERSLIE$$,#{state_id_vic},-38.21456,143.013298), - ($$3265$$,$$FRAMLINGHAM$$,#{state_id_vic},-38.21456,143.013298), - ($$3265$$,$$FRAMLINGHAM EAST$$,#{state_id_vic},-38.21456,143.013298), - ($$3265$$,$$GARVOC$$,#{state_id_vic},-38.21456,143.013298), - ($$3265$$,$$GLENORMISTON NORTH$$,#{state_id_vic},-38.21456,143.013298), - ($$3265$$,$$GLENORMISTON SOUTH$$,#{state_id_vic},-38.21456,143.013298), - ($$3265$$,$$KOLORA$$,#{state_id_vic},-38.21456,143.013298), - ($$3265$$,$$LAANG$$,#{state_id_vic},-38.21456,143.013298), - ($$3265$$,$$NOORAT$$,#{state_id_vic},-38.21456,143.013298), - ($$3265$$,$$NOORAT EAST$$,#{state_id_vic},-38.21456,143.013298), - ($$3265$$,$$PANMURE$$,#{state_id_vic},-38.21456,143.013298), - ($$3265$$,$$TAROON$$,#{state_id_vic},-38.21456,143.013298), - ($$3265$$,$$THE SISTERS$$,#{state_id_vic},-38.21456,143.013298), - ($$3266$$,$$BULLAHARRE$$,#{state_id_vic},-38.343488,143.141647), - ($$3266$$,$$COBDEN$$,#{state_id_vic},-38.343488,143.141647), - ($$3266$$,$$COBRICO$$,#{state_id_vic},-38.343488,143.141647), - ($$3266$$,$$ELINGAMITE$$,#{state_id_vic},-38.343488,143.141647), - ($$3266$$,$$ELINGAMITE NORTH$$,#{state_id_vic},-38.343488,143.141647), - ($$3266$$,$$GLENFYNE$$,#{state_id_vic},-38.343488,143.141647), - ($$3266$$,$$JANCOURT$$,#{state_id_vic},-38.343488,143.141647), - ($$3266$$,$$JANCOURT EAST$$,#{state_id_vic},-38.343488,143.141647), - ($$3266$$,$$NAROGHID$$,#{state_id_vic},-38.343488,143.141647), - ($$3266$$,$$SIMPSON$$,#{state_id_vic},-38.343488,143.141647), - ($$3267$$,$$SCOTTS CREEK$$,#{state_id_vic},-38.448909,143.10623), - ($$3268$$,$$AYRFORD$$,#{state_id_vic},-38.415663,142.85945), - ($$3268$$,$$BRUCKNELL$$,#{state_id_vic},-38.415663,142.85945), - ($$3268$$,$$COORIEMUNGLE$$,#{state_id_vic},-38.415663,142.85945), - ($$3268$$,$$COWLEYS CREEK$$,#{state_id_vic},-38.415663,142.85945), - ($$3268$$,$$CURDIES RIVER$$,#{state_id_vic},-38.415663,142.85945), - ($$3268$$,$$CURDIEVALE$$,#{state_id_vic},-38.415663,142.85945), - ($$3268$$,$$HEYTESBURY LOWER$$,#{state_id_vic},-38.415663,142.85945), - ($$3268$$,$$NEWFIELD$$,#{state_id_vic},-38.415663,142.85945), - ($$3268$$,$$NIRRANDA$$,#{state_id_vic},-38.415663,142.85945), - ($$3268$$,$$NIRRANDA EAST$$,#{state_id_vic},-38.415663,142.85945), - ($$3268$$,$$NIRRANDA SOUTH$$,#{state_id_vic},-38.415663,142.85945), - ($$3268$$,$$NULLAWARRE$$,#{state_id_vic},-38.415663,142.85945), - ($$3268$$,$$NULLAWARRE NORTH$$,#{state_id_vic},-38.415663,142.85945), - ($$3268$$,$$PAARATTE$$,#{state_id_vic},-38.415663,142.85945), - ($$3268$$,$$THE COVE$$,#{state_id_vic},-38.415663,142.85945), - ($$3268$$,$$TIMBOON$$,#{state_id_vic},-38.415663,142.85945), - ($$3268$$,$$TIMBOON WEST$$,#{state_id_vic},-38.415663,142.85945), - ($$3269$$,$$PORT CAMPBELL$$,#{state_id_vic},-38.619083,142.996136), - ($$3269$$,$$PRINCETOWN$$,#{state_id_vic},-38.619083,142.996136), - ($$3269$$,$$WAARRE$$,#{state_id_vic},-38.619083,142.996136), - ($$3270$$,$$PETERBOROUGH$$,#{state_id_vic},-38.579938,142.857049), - ($$3271$$,$$DARLINGTON$$,#{state_id_vic},-37.998527,143.051384), - ($$3271$$,$$DUNDONNELL$$,#{state_id_vic},-37.998527,143.051384), - ($$3271$$,$$PURA PURA$$,#{state_id_vic},-37.998527,143.051384), - ($$3272$$,$$MORTLAKE$$,#{state_id_vic},-38.081236,142.807986), - ($$3272$$,$$WOORNDOO$$,#{state_id_vic},-38.081236,142.807986), - ($$3273$$,$$HEXHAM$$,#{state_id_vic},-38.057308,142.607374), - ($$3274$$,$$CARAMUT$$,#{state_id_vic},-37.912068,142.523202), - ($$3275$$,$$MAILORS FLAT$$,#{state_id_vic},-38.301794,142.457611), - ($$3276$$,$$MINJAH$$,#{state_id_vic},-38.035006,142.441935), - ($$3276$$,$$WOOLSTHORPE$$,#{state_id_vic},-38.035006,142.441935), - ($$3277$$,$$ALLANSFORD$$,#{state_id_vic},-38.386474,142.590674), - ($$3277$$,$$MEPUNGA$$,#{state_id_vic},-38.386474,142.590674), - ($$3277$$,$$MEPUNGA EAST$$,#{state_id_vic},-38.386474,142.590674), - ($$3277$$,$$MEPUNGA WEST$$,#{state_id_vic},-38.386474,142.590674), - ($$3277$$,$$NARINGAL$$,#{state_id_vic},-38.386474,142.590674), - ($$3277$$,$$NARINGAL EAST$$,#{state_id_vic},-38.386474,142.590674), - ($$3278$$,$$PURNIM$$,#{state_id_vic},-38.278013,142.624719), - ($$3278$$,$$PURNIM WEST$$,#{state_id_vic},-38.278013,142.624719), - ($$3279$$,$$BALLANGEICH$$,#{state_id_vic},-38.214555,142.657483), - ($$3279$$,$$WANGOOM$$,#{state_id_vic},-38.214555,142.657483), - ($$3280$$,$$DENNINGTON$$,#{state_id_vic},-38.35811,142.441982), - ($$3280$$,$$WARRNAMBOOL$$,#{state_id_vic},-38.35811,142.441982), - ($$3280$$,$$WARRNAMBOOL EAST$$,#{state_id_vic},-38.35811,142.441982), - ($$3280$$,$$WARRNAMBOOL WEST$$,#{state_id_vic},-38.35811,142.441982), - ($$3281$$,$$BUSHFIELD$$,#{state_id_vic},-38.325329,142.505522), - ($$3281$$,$$GRASSMERE$$,#{state_id_vic},-38.325329,142.505522), - ($$3281$$,$$WINSLOW$$,#{state_id_vic},-38.325329,142.505522), - ($$3281$$,$$WOODFORD$$,#{state_id_vic},-38.325329,142.505522), - ($$3282$$,$$ILLOWA$$,#{state_id_vic},-38.32991,142.411507), - ($$3282$$,$$KOROIT$$,#{state_id_vic},-38.32991,142.411507), - ($$3283$$,$$CROSSLEY$$,#{state_id_vic},-38.313434,142.328761), - ($$3283$$,$$KILLARNEY$$,#{state_id_vic},-38.313434,142.328761), - ($$3283$$,$$KIRKSTALL$$,#{state_id_vic},-38.313434,142.328761), - ($$3283$$,$$SOUTHERN CROSS$$,#{state_id_vic},-38.313434,142.328761), - ($$3283$$,$$TARRONE$$,#{state_id_vic},-38.313434,142.328761), - ($$3283$$,$$TOWER HILL$$,#{state_id_vic},-38.313434,142.328761), - ($$3283$$,$$WARRONG$$,#{state_id_vic},-38.313434,142.328761), - ($$3283$$,$$WILLATOOK$$,#{state_id_vic},-38.313434,142.328761), - ($$3283$$,$$YANGERY$$,#{state_id_vic},-38.313434,142.328761), - ($$3283$$,$$YARPTURK$$,#{state_id_vic},-38.313434,142.328761), - ($$3284$$,$$ORFORD$$,#{state_id_vic},-38.214987,142.153594), - ($$3284$$,$$PORT FAIRY$$,#{state_id_vic},-38.214987,142.153594), - ($$3285$$,$$CODRINGTON$$,#{state_id_vic},-38.272587,141.973181), - ($$3285$$,$$NARRAWONG$$,#{state_id_vic},-38.272587,141.973181), - ($$3285$$,$$ROSEBROOK$$,#{state_id_vic},-38.272587,141.973181), - ($$3285$$,$$ST HELENS$$,#{state_id_vic},-38.272587,141.973181), - ($$3285$$,$$TOOLONG$$,#{state_id_vic},-38.272587,141.973181), - ($$3285$$,$$TYRENDARRA$$,#{state_id_vic},-38.272587,141.973181), - ($$3285$$,$$TYRENDARRA EAST$$,#{state_id_vic},-38.272587,141.973181), - ($$3285$$,$$YAMBUK$$,#{state_id_vic},-38.272587,141.973181), - ($$3286$$,$$CONDAH SWAMP$$,#{state_id_vic},-37.972228,141.833705), - ($$3286$$,$$KNEBSWORTH$$,#{state_id_vic},-37.972228,141.833705), - ($$3286$$,$$MACARTHUR$$,#{state_id_vic},-37.972228,141.833705), - ($$3286$$,$$WARRABKOOK$$,#{state_id_vic},-37.972228,141.833705), - ($$3287$$,$$HAWKESDALE$$,#{state_id_vic},-38.106999,142.322188), - ($$3287$$,$$MINHAMITE$$,#{state_id_vic},-38.106999,142.322188), - ($$3289$$,$$GAZETTE$$,#{state_id_vic},-37.897958,142.174884), - ($$3289$$,$$GERRIGERRUP$$,#{state_id_vic},-37.897958,142.174884), - ($$3289$$,$$PENSHURST$$,#{state_id_vic},-37.897958,142.174884), - ($$3289$$,$$PURDEET$$,#{state_id_vic},-37.897958,142.174884), - ($$3289$$,$$TABOR$$,#{state_id_vic},-37.897958,142.174884), - ($$3292$$,$$NELSON$$,#{state_id_vic},-38.04659,141.006938), - ($$3293$$,$$GLENTHOMPSON$$,#{state_id_vic},-37.636489,142.545707), - ($$3293$$,$$NAREEB$$,#{state_id_vic},-37.636489,142.545707), - ($$3293$$,$$NARRAPUMELAP SOUTH$$,#{state_id_vic},-37.636489,142.545707), - ($$3294$$,$$DUNKELD$$,#{state_id_vic},-37.649713,142.344662), - ($$3294$$,$$KARABEAL$$,#{state_id_vic},-37.649713,142.344662), - ($$3294$$,$$MIRRANATWA$$,#{state_id_vic},-37.649713,142.344662), - ($$3294$$,$$MOUTAJUP$$,#{state_id_vic},-37.649713,142.344662), - ($$3294$$,$$VICTORIA POINT$$,#{state_id_vic},-37.649713,142.344662), - ($$3294$$,$$VICTORIA VALLEY$$,#{state_id_vic},-37.649713,142.344662), - ($$3294$$,$$WOODHOUSE$$,#{state_id_vic},-37.649713,142.344662), - ($$3300$$,$$BYADUK NORTH$$,#{state_id_vic},-37.880712,141.960066), - ($$3300$$,$$HAMILTON$$,#{state_id_vic},-37.880712,141.960066), - ($$3301$$,$$BOCHARA$$,#{state_id_vic},-37.704033,141.927084), - ($$3301$$,$$BROADWATER$$,#{state_id_vic},-37.704033,141.927084), - ($$3301$$,$$BUCKLEY SWAMP$$,#{state_id_vic},-37.704033,141.927084), - ($$3301$$,$$BYADUK$$,#{state_id_vic},-37.704033,141.927084), - ($$3301$$,$$CROXTON EAST$$,#{state_id_vic},-37.704033,141.927084), - ($$3301$$,$$HENSLEY PARK$$,#{state_id_vic},-37.704033,141.927084), - ($$3301$$,$$MORGIANA$$,#{state_id_vic},-37.704033,141.927084), - ($$3301$$,$$MOUNT NAPIER$$,#{state_id_vic},-37.704033,141.927084), - ($$3301$$,$$STRATHKELLAR$$,#{state_id_vic},-37.704033,141.927084), - ($$3301$$,$$TAHARA$$,#{state_id_vic},-37.704033,141.927084), - ($$3301$$,$$TARRINGTON$$,#{state_id_vic},-37.704033,141.927084), - ($$3301$$,$$WANNON$$,#{state_id_vic},-37.704033,141.927084), - ($$3301$$,$$WARRAYURE$$,#{state_id_vic},-37.704033,141.927084), - ($$3301$$,$$YATCHAW$$,#{state_id_vic},-37.704033,141.927084), - ($$3301$$,$$YULECART$$,#{state_id_vic},-37.704033,141.927084), - ($$3302$$,$$BRANXHOLME$$,#{state_id_vic},-37.856656,141.794565), - ($$3302$$,$$GRASSDALE$$,#{state_id_vic},-37.856656,141.794565), - ($$3303$$,$$BREAKAWAY CREEK$$,#{state_id_vic},-38.03119,141.814595), - ($$3303$$,$$CONDAH$$,#{state_id_vic},-38.03119,141.814595), - ($$3303$$,$$HOTSPUR$$,#{state_id_vic},-38.03119,141.814595), - ($$3303$$,$$LAKE CONDAH$$,#{state_id_vic},-38.03119,141.814595), - ($$3303$$,$$WALLACEDALE$$,#{state_id_vic},-38.03119,141.814595), - ($$3304$$,$$BESSIEBELLE$$,#{state_id_vic},-38.146567,141.966132), - ($$3304$$,$$DARTMOOR$$,#{state_id_vic},-38.146567,141.966132), - ($$3304$$,$$DRIK DRIK$$,#{state_id_vic},-38.146567,141.966132), - ($$3304$$,$$DRUMBORG$$,#{state_id_vic},-38.146567,141.966132), - ($$3304$$,$$GREENWALD$$,#{state_id_vic},-38.146567,141.966132), - ($$3304$$,$$HEYWOOD$$,#{state_id_vic},-38.146567,141.966132), - ($$3304$$,$$HOMERTON$$,#{state_id_vic},-38.146567,141.966132), - ($$3304$$,$$LYONS$$,#{state_id_vic},-38.146567,141.966132), - ($$3304$$,$$MILLTOWN$$,#{state_id_vic},-38.146567,141.966132), - ($$3304$$,$$MUMBANNAR$$,#{state_id_vic},-38.146567,141.966132), - ($$3304$$,$$MYAMYN$$,#{state_id_vic},-38.146567,141.966132), - ($$3304$$,$$WINNAP$$,#{state_id_vic},-38.146567,141.966132), - ($$3305$$,$$ALLESTREE$$,#{state_id_vic},-38.274855,141.646078), - ($$3305$$,$$BOLWARRA$$,#{state_id_vic},-38.274855,141.646078), - ($$3305$$,$$CAPE BRIDGEWATER$$,#{state_id_vic},-38.274855,141.646078), - ($$3305$$,$$CASHMORE$$,#{state_id_vic},-38.274855,141.646078), - ($$3305$$,$$DUTTON WAY$$,#{state_id_vic},-38.274855,141.646078), - ($$3305$$,$$GORAE$$,#{state_id_vic},-38.274855,141.646078), - ($$3305$$,$$GORAE WEST$$,#{state_id_vic},-38.274855,141.646078), - ($$3305$$,$$HEATHMERE$$,#{state_id_vic},-38.274855,141.646078), - ($$3305$$,$$MOUNT RICHMOND$$,#{state_id_vic},-38.274855,141.646078), - ($$3305$$,$$PORTLAND$$,#{state_id_vic},-38.274855,141.646078), - ($$3305$$,$$PORTLAND NORTH$$,#{state_id_vic},-38.274855,141.646078), - ($$3305$$,$$PORTLAND WEST$$,#{state_id_vic},-38.274855,141.646078), - ($$3309$$,$$DIGBY$$,#{state_id_vic},-37.794364,141.500674), - ($$3310$$,$$MERINO$$,#{state_id_vic},-37.720455,141.548293), - ($$3310$$,$$TAHARA WEST$$,#{state_id_vic},-37.720455,141.548293), - ($$3311$$,$$CASTERTON$$,#{state_id_vic},-37.584211,141.405944), - ($$3311$$,$$CORNDALE$$,#{state_id_vic},-37.584211,141.405944), - ($$3312$$,$$BAHGALLAH$$,#{state_id_vic},-37.6391,141.365747), - ($$3312$$,$$BRIMBOAL$$,#{state_id_vic},-37.6391,141.365747), - ($$3312$$,$$CARAPOOK$$,#{state_id_vic},-37.6391,141.365747), - ($$3312$$,$$CHETWYND$$,#{state_id_vic},-37.6391,141.365747), - ($$3312$$,$$DERGHOLM$$,#{state_id_vic},-37.6391,141.365747), - ($$3312$$,$$DORODONG$$,#{state_id_vic},-37.6391,141.365747), - ($$3312$$,$$DUNROBIN$$,#{state_id_vic},-37.6391,141.365747), - ($$3312$$,$$HENTY$$,#{state_id_vic},-37.6391,141.365747), - ($$3312$$,$$KILLARA$$,#{state_id_vic},-37.6391,141.365747), - ($$3312$$,$$LAKE MUNDI$$,#{state_id_vic},-37.6391,141.365747), - ($$3312$$,$$LINDSAY$$,#{state_id_vic},-37.6391,141.365747), - ($$3312$$,$$NANGEELA$$,#{state_id_vic},-37.6391,141.365747), - ($$3312$$,$$POOLAIJELO$$,#{state_id_vic},-37.6391,141.365747), - ($$3312$$,$$POWERS CREEK$$,#{state_id_vic},-37.6391,141.365747), - ($$3312$$,$$SANDFORD$$,#{state_id_vic},-37.6391,141.365747), - ($$3312$$,$$STRATHDOWNIE$$,#{state_id_vic},-37.6391,141.365747), - ($$3312$$,$$WANDO BRIDGE$$,#{state_id_vic},-37.6391,141.365747), - ($$3312$$,$$WANDO VALE$$,#{state_id_vic},-37.6391,141.365747), - ($$3312$$,$$WARROCK$$,#{state_id_vic},-37.6391,141.365747), - ($$3314$$,$$BULART$$,#{state_id_vic},-37.586718,141.935665), - ($$3314$$,$$CAVENDISH$$,#{state_id_vic},-37.586718,141.935665), - ($$3314$$,$$GLENISLA$$,#{state_id_vic},-37.586718,141.935665), - ($$3314$$,$$GRAMPIANS$$,#{state_id_vic},-37.586718,141.935665), - ($$3314$$,$$MOORALLA$$,#{state_id_vic},-37.586718,141.935665), - ($$3315$$,$$BRIT BRIT$$,#{state_id_vic},-37.441278,141.758617), - ($$3315$$,$$CLOVER FLAT$$,#{state_id_vic},-37.441278,141.758617), - ($$3315$$,$$COLERAINE$$,#{state_id_vic},-37.441278,141.758617), - ($$3315$$,$$COOJAR$$,#{state_id_vic},-37.441278,141.758617), - ($$3315$$,$$CULLA$$,#{state_id_vic},-37.441278,141.758617), - ($$3315$$,$$GRINGEGALGONA$$,#{state_id_vic},-37.441278,141.758617), - ($$3315$$,$$GRITJURK$$,#{state_id_vic},-37.441278,141.758617), - ($$3315$$,$$HILGAY$$,#{state_id_vic},-37.441278,141.758617), - ($$3315$$,$$KONONGWOOTONG$$,#{state_id_vic},-37.441278,141.758617), - ($$3315$$,$$MELVILLE FOREST$$,#{state_id_vic},-37.441278,141.758617), - ($$3315$$,$$MUNTHAM$$,#{state_id_vic},-37.441278,141.758617), - ($$3315$$,$$NAREEN$$,#{state_id_vic},-37.441278,141.758617), - ($$3315$$,$$PASCHENDALE$$,#{state_id_vic},-37.441278,141.758617), - ($$3315$$,$$TAHARA BRIDGE$$,#{state_id_vic},-37.441278,141.758617), - ($$3315$$,$$TARRAYOUKYAN$$,#{state_id_vic},-37.441278,141.758617), - ($$3315$$,$$TARRENLEA$$,#{state_id_vic},-37.441278,141.758617), - ($$3315$$,$$WOOTONG VALE$$,#{state_id_vic},-37.441278,141.758617), - ($$3317$$,$$HARROW$$,#{state_id_vic},-37.119835,141.600473), - ($$3318$$,$$CHARAM$$,#{state_id_vic},-36.986424,141.510896), - ($$3318$$,$$CONNEWIRRICOO$$,#{state_id_vic},-36.986424,141.510896), - ($$3318$$,$$EDENHOPE$$,#{state_id_vic},-36.986424,141.510896), - ($$3318$$,$$KADNOOK$$,#{state_id_vic},-36.986424,141.510896), - ($$3318$$,$$LANGKOOP$$,#{state_id_vic},-36.986424,141.510896), - ($$3318$$,$$PATYAH$$,#{state_id_vic},-36.986424,141.510896), - ($$3318$$,$$ULLSWATER$$,#{state_id_vic},-36.986424,141.510896), - ($$3319$$,$$APSLEY$$,#{state_id_vic},-36.967557,141.080748), - ($$3319$$,$$BENAYEO$$,#{state_id_vic},-36.967557,141.080748), - ($$3319$$,$$BRINGALBERT$$,#{state_id_vic},-36.967557,141.080748), - ($$3321$$,$$HESSE$$,#{state_id_vic},-38.111995,143.855664), - ($$3321$$,$$INVERLEIGH$$,#{state_id_vic},-38.111995,143.855664), - ($$3321$$,$$WINGEEL$$,#{state_id_vic},-38.111995,143.855664), - ($$3322$$,$$CRESSY$$,#{state_id_vic},-38.028752,143.643643), - ($$3323$$,$$BERRYBANK$$,#{state_id_vic},-37.991587,143.486027), - ($$3323$$,$$DUVERNEY$$,#{state_id_vic},-37.991587,143.486027), - ($$3323$$,$$FOXHOW$$,#{state_id_vic},-37.991587,143.486027), - ($$3324$$,$$LISMORE$$,#{state_id_vic},-37.953552,143.34368), - ($$3324$$,$$MINGAY$$,#{state_id_vic},-37.953552,143.34368), - ($$3324$$,$$MOUNT BUTE$$,#{state_id_vic},-37.953552,143.34368), - ($$3325$$,$$DERRINALLUM$$,#{state_id_vic},-37.948485,143.219987), - ($$3325$$,$$LARRALEA$$,#{state_id_vic},-37.948485,143.219987), - ($$3325$$,$$VITE VITE$$,#{state_id_vic},-37.948485,143.219987), - ($$3325$$,$$VITE VITE NORTH$$,#{state_id_vic},-37.948485,143.219987), - ($$3328$$,$$TEESDALE$$,#{state_id_vic},-38.039758,144.048285), - ($$3329$$,$$BARUNAH PARK$$,#{state_id_vic},-38.02107,143.856658), - ($$3329$$,$$BARUNAH PLAINS$$,#{state_id_vic},-38.02107,143.856658), - ($$3329$$,$$SHELFORD$$,#{state_id_vic},-38.02107,143.856658), - ($$3330$$,$$ROKEWOOD$$,#{state_id_vic},-37.844281,143.677351), - ($$3331$$,$$BANNOCKBURN$$,#{state_id_vic},-38.046528,144.171067), - ($$3331$$,$$GHERINGHAP$$,#{state_id_vic},-38.046528,144.171067), - ($$3331$$,$$MAUDE$$,#{state_id_vic},-38.046528,144.171067), - ($$3331$$,$$RUSSELLS BRIDGE$$,#{state_id_vic},-38.046528,144.171067), - ($$3331$$,$$SHE OAKS$$,#{state_id_vic},-38.046528,144.171067), - ($$3331$$,$$STEIGLITZ$$,#{state_id_vic},-38.046528,144.171067), - ($$3331$$,$$SUTHERLANDS CREEK$$,#{state_id_vic},-38.046528,144.171067), - ($$3332$$,$$LETHBRIDGE$$,#{state_id_vic},-37.962809,144.139178), - ($$3333$$,$$BAMGANIE$$,#{state_id_vic},-37.924962,144.025575), - ($$3333$$,$$MEREDITH$$,#{state_id_vic},-37.924962,144.025575), - ($$3334$$,$$BUNGAL$$,#{state_id_vic},-37.710436,144.094545), - ($$3334$$,$$CARGERIE$$,#{state_id_vic},-37.710436,144.094545), - ($$3334$$,$$ELAINE$$,#{state_id_vic},-37.710436,144.094545), - ($$3334$$,$$MORRISONS$$,#{state_id_vic},-37.710436,144.094545), - ($$3334$$,$$MOUNT DORAN$$,#{state_id_vic},-37.710436,144.094545), - ($$3335$$,$$PLUMPTON$$,#{state_id_vic},-37.68584,144.685449), - ($$3335$$,$$ROCKBANK$$,#{state_id_vic},-37.68584,144.685449), - ($$3337$$,$$KURUNJANG$$,#{state_id_vic},-37.675127,144.58855), - ($$3337$$,$$MELTON$$,#{state_id_vic},-37.675127,144.58855), - ($$3337$$,$$MELTON WEST$$,#{state_id_vic},-37.675127,144.58855), - ($$3337$$,$$TOOLERN VALE$$,#{state_id_vic},-37.675127,144.58855), - ($$3338$$,$$BROOKFIELD$$,#{state_id_vic},-37.701172,144.540792), - ($$3338$$,$$EXFORD$$,#{state_id_vic},-37.701172,144.540792), - ($$3338$$,$$EYNESBURY$$,#{state_id_vic},-37.701172,144.540792), - ($$3338$$,$$MELTON SOUTH$$,#{state_id_vic},-37.701172,144.540792), - ($$3340$$,$$BACCHUS MARSH$$,#{state_id_vic},-37.675855,144.437663), - ($$3340$$,$$BALLIANG$$,#{state_id_vic},-37.675855,144.437663), - ($$3340$$,$$BALLIANG EAST$$,#{state_id_vic},-37.675855,144.437663), - ($$3340$$,$$COIMADAI$$,#{state_id_vic},-37.675855,144.437663), - ($$3340$$,$$DARLEY$$,#{state_id_vic},-37.675855,144.437663), - ($$3340$$,$$GLENMORE$$,#{state_id_vic},-37.675855,144.437663), - ($$3340$$,$$HOPETOUN PARK$$,#{state_id_vic},-37.675855,144.437663), - ($$3340$$,$$LONG FOREST$$,#{state_id_vic},-37.675855,144.437663), - ($$3340$$,$$MADDINGLEY$$,#{state_id_vic},-37.675855,144.437663), - ($$3340$$,$$MERRIMU$$,#{state_id_vic},-37.675855,144.437663), - ($$3340$$,$$PARWAN$$,#{state_id_vic},-37.675855,144.437663), - ($$3340$$,$$ROWSLEY$$,#{state_id_vic},-37.675855,144.437663), - ($$3341$$,$$DALES CREEK$$,#{state_id_vic},-37.519237,144.309955), - ($$3341$$,$$GREENDALE$$,#{state_id_vic},-37.519237,144.309955), - ($$3341$$,$$KOROBEIT$$,#{state_id_vic},-37.519237,144.309955), - ($$3341$$,$$MYRNIONG$$,#{state_id_vic},-37.519237,144.309955), - ($$3341$$,$$PENTLAND HILLS$$,#{state_id_vic},-37.519237,144.309955), - ($$3342$$,$$BALLAN$$,#{state_id_vic},-37.60016,144.225458), - ($$3342$$,$$BEREMBOKE$$,#{state_id_vic},-37.60016,144.225458), - ($$3342$$,$$BLAKEVILLE$$,#{state_id_vic},-37.60016,144.225458), - ($$3342$$,$$BUNDING$$,#{state_id_vic},-37.60016,144.225458), - ($$3342$$,$$COLBROOK$$,#{state_id_vic},-37.60016,144.225458), - ($$3342$$,$$DURDIDWARRAH$$,#{state_id_vic},-37.60016,144.225458), - ($$3342$$,$$FISKVILLE$$,#{state_id_vic},-37.60016,144.225458), - ($$3342$$,$$INGLISTON$$,#{state_id_vic},-37.60016,144.225458), - ($$3342$$,$$MOUNT WALLACE$$,#{state_id_vic},-37.60016,144.225458), - ($$3345$$,$$GORDON$$,#{state_id_vic},-37.594546,144.099862), - ($$3350$$,$$ALFREDTON$$,#{state_id_vic},-37.556958,143.817225), - ($$3350$$,$$BAKERY HILL$$,#{state_id_vic},-37.556958,143.817225), - ($$3350$$,$$BALLARAT$$,#{state_id_vic},-37.556958,143.817225), - ($$3350$$,$$BALLARAT CENTRAL$$,#{state_id_vic},-37.556958,143.817225), - ($$3350$$,$$BALLARAT EAST$$,#{state_id_vic},-37.556958,143.817225), - ($$3350$$,$$BALLARAT NORTH$$,#{state_id_vic},-37.556958,143.817225), - ($$3350$$,$$BALLARAT WEST$$,#{state_id_vic},-37.556958,143.817225), - ($$3350$$,$$BLACK HILL$$,#{state_id_vic},-37.556958,143.817225), - ($$3350$$,$$BROWN HILL$$,#{state_id_vic},-37.556958,143.817225), - ($$3350$$,$$CANADIAN$$,#{state_id_vic},-37.556958,143.817225), - ($$3350$$,$$EUREKA$$,#{state_id_vic},-37.556958,143.817225), - ($$3350$$,$$GOLDEN POINT$$,#{state_id_vic},-37.556958,143.817225), - ($$3350$$,$$INVERMAY PARK$$,#{state_id_vic},-37.556958,143.817225), - ($$3350$$,$$LAKE WENDOUREE$$,#{state_id_vic},-37.556958,143.817225), - ($$3350$$,$$MOUNT CLEAR$$,#{state_id_vic},-37.556958,143.817225), - ($$3350$$,$$MOUNT HELEN$$,#{state_id_vic},-37.556958,143.817225), - ($$3350$$,$$MOUNT PLEASANT$$,#{state_id_vic},-37.556958,143.817225), - ($$3350$$,$$NERRINA$$,#{state_id_vic},-37.556958,143.817225), - ($$3350$$,$$NEWINGTON$$,#{state_id_vic},-37.556958,143.817225), - ($$3350$$,$$REDAN$$,#{state_id_vic},-37.556958,143.817225), - ($$3350$$,$$SOLDIERS HILL$$,#{state_id_vic},-37.556958,143.817225), - ($$3351$$,$$BERRINGA$$,#{state_id_vic},-37.770766,143.683679), - ($$3351$$,$$BO PEEP$$,#{state_id_vic},-37.770766,143.683679), - ($$3351$$,$$CAPE CLEAR$$,#{state_id_vic},-37.770766,143.683679), - ($$3351$$,$$CARNGHAM$$,#{state_id_vic},-37.770766,143.683679), - ($$3351$$,$$CHEPSTOWE$$,#{state_id_vic},-37.770766,143.683679), - ($$3351$$,$$HADDON$$,#{state_id_vic},-37.770766,143.683679), - ($$3351$$,$$HILLCREST$$,#{state_id_vic},-37.770766,143.683679), - ($$3351$$,$$ILLABAROOK$$,#{state_id_vic},-37.770766,143.683679), - ($$3351$$,$$LAKE BOLAC$$,#{state_id_vic},-37.770766,143.683679), - ($$3351$$,$$MININERA$$,#{state_id_vic},-37.770766,143.683679), - ($$3351$$,$$MOUNT EMU$$,#{state_id_vic},-37.770766,143.683679), - ($$3351$$,$$NERRIN NERRIN$$,#{state_id_vic},-37.770766,143.683679), - ($$3351$$,$$NEWTOWN$$,#{state_id_vic},-37.770766,143.683679), - ($$3351$$,$$NINTINGBOOL$$,#{state_id_vic},-37.770766,143.683679), - ($$3351$$,$$PIGGOREET$$,#{state_id_vic},-37.770766,143.683679), - ($$3351$$,$$PITFIELD$$,#{state_id_vic},-37.770766,143.683679), - ($$3351$$,$$ROKEWOOD JUNCTION$$,#{state_id_vic},-37.770766,143.683679), - ($$3351$$,$$ROSS CREEK$$,#{state_id_vic},-37.770766,143.683679), - ($$3351$$,$$SCARSDALE$$,#{state_id_vic},-37.770766,143.683679), - ($$3351$$,$$SMYTHES CREEK$$,#{state_id_vic},-37.770766,143.683679), - ($$3351$$,$$SMYTHESDALE$$,#{state_id_vic},-37.770766,143.683679), - ($$3351$$,$$SNAKE VALLEY$$,#{state_id_vic},-37.770766,143.683679), - ($$3351$$,$$SPRINGDALLAH$$,#{state_id_vic},-37.770766,143.683679), - ($$3351$$,$$STAFFORDSHIRE REEF$$,#{state_id_vic},-37.770766,143.683679), - ($$3351$$,$$STREATHAM$$,#{state_id_vic},-37.770766,143.683679), - ($$3351$$,$$WALLINDUC$$,#{state_id_vic},-37.770766,143.683679), - ($$3351$$,$$WESTMERE$$,#{state_id_vic},-37.770766,143.683679), - ($$3352$$,$$ADDINGTON$$,#{state_id_vic},-37.384464,143.672506), - ($$3352$$,$$BALLARAT ROADSIDE DELIVERY$$,#{state_id_vic},-37.384464,143.672506), - ($$3352$$,$$BLOWHARD$$,#{state_id_vic},-37.384464,143.672506), - ($$3352$$,$$BOLWARRAH$$,#{state_id_vic},-37.384464,143.672506), - ($$3352$$,$$BONSHAW$$,#{state_id_vic},-37.384464,143.672506), - ($$3352$$,$$BREWSTER$$,#{state_id_vic},-37.384464,143.672506), - ($$3352$$,$$BULLAROOK$$,#{state_id_vic},-37.384464,143.672506), - ($$3352$$,$$BUNGAREE$$,#{state_id_vic},-37.384464,143.672506), - ($$3352$$,$$BUNKERS HILL$$,#{state_id_vic},-37.384464,143.672506), - ($$3352$$,$$BURRUMBEET$$,#{state_id_vic},-37.384464,143.672506), - ($$3352$$,$$CAMBRIAN HILL$$,#{state_id_vic},-37.384464,143.672506), - ($$3352$$,$$CARDIGAN$$,#{state_id_vic},-37.384464,143.672506), - ($$3352$$,$$CARDIGAN VILLAGE$$,#{state_id_vic},-37.384464,143.672506), - ($$3352$$,$$CHAPEL FLAT$$,#{state_id_vic},-37.384464,143.672506), - ($$3352$$,$$CLARENDON$$,#{state_id_vic},-37.384464,143.672506), - ($$3352$$,$$CLARETOWN$$,#{state_id_vic},-37.384464,143.672506), - ($$3352$$,$$CLARKES HILL$$,#{state_id_vic},-37.384464,143.672506), - ($$3352$$,$$CORINDHAP$$,#{state_id_vic},-37.384464,143.672506), - ($$3352$$,$$DEREEL$$,#{state_id_vic},-37.384464,143.672506), - ($$3352$$,$$DUNNSTOWN$$,#{state_id_vic},-37.384464,143.672506), - ($$3352$$,$$DURHAM LEAD$$,#{state_id_vic},-37.384464,143.672506), - ($$3352$$,$$ENFIELD$$,#{state_id_vic},-37.384464,143.672506), - ($$3352$$,$$ERCILDOUNE$$,#{state_id_vic},-37.384464,143.672506), - ($$3352$$,$$GARIBALDI$$,#{state_id_vic},-37.384464,143.672506), - ($$3352$$,$$GLEN PARK$$,#{state_id_vic},-37.384464,143.672506), - ($$3352$$,$$GLENBRAE$$,#{state_id_vic},-37.384464,143.672506), - ($$3352$$,$$GONG GONG$$,#{state_id_vic},-37.384464,143.672506), - ($$3352$$,$$GRENVILLE$$,#{state_id_vic},-37.384464,143.672506), - ($$3352$$,$$INVERMAY$$,#{state_id_vic},-37.384464,143.672506), - ($$3352$$,$$LAL LAL$$,#{state_id_vic},-37.384464,143.672506), - ($$3352$$,$$LAMPLOUGH$$,#{state_id_vic},-37.384464,143.672506), - ($$3352$$,$$LANGI KAL KAL$$,#{state_id_vic},-37.384464,143.672506), - ($$3352$$,$$LEARMONTH$$,#{state_id_vic},-37.384464,143.672506), - ($$3352$$,$$LEIGH CREEK$$,#{state_id_vic},-37.384464,143.672506), - ($$3352$$,$$LEXTON$$,#{state_id_vic},-37.384464,143.672506), - ($$3352$$,$$MAGPIE$$,#{state_id_vic},-37.384464,143.672506), - ($$3352$$,$$MILLBROOK$$,#{state_id_vic},-37.384464,143.672506), - ($$3352$$,$$MINERS REST$$,#{state_id_vic},-37.384464,143.672506), - ($$3352$$,$$MITCHELL PARK$$,#{state_id_vic},-37.384464,143.672506), - ($$3352$$,$$MOLLONGGHIP$$,#{state_id_vic},-37.384464,143.672506), - ($$3352$$,$$MOUNT BOLTON$$,#{state_id_vic},-37.384464,143.672506), - ($$3352$$,$$MOUNT EGERTON$$,#{state_id_vic},-37.384464,143.672506), - ($$3352$$,$$MOUNT MERCER$$,#{state_id_vic},-37.384464,143.672506), - ($$3352$$,$$MOUNT ROWAN$$,#{state_id_vic},-37.384464,143.672506), - ($$3352$$,$$NAPOLEONS$$,#{state_id_vic},-37.384464,143.672506), - ($$3352$$,$$NAVIGATORS$$,#{state_id_vic},-37.384464,143.672506), - ($$3352$$,$$POOTILLA$$,#{state_id_vic},-37.384464,143.672506), - ($$3352$$,$$SCOTCHMANS LEAD$$,#{state_id_vic},-37.384464,143.672506), - ($$3352$$,$$SCOTSBURN$$,#{state_id_vic},-37.384464,143.672506), - ($$3352$$,$$SPRINGBANK$$,#{state_id_vic},-37.384464,143.672506), - ($$3352$$,$$SULKY$$,#{state_id_vic},-37.384464,143.672506), - ($$3352$$,$$WALLACE$$,#{state_id_vic},-37.384464,143.672506), - ($$3352$$,$$WARRENHEIP$$,#{state_id_vic},-37.384464,143.672506), - ($$3352$$,$$WATTLE FLAT$$,#{state_id_vic},-37.384464,143.672506), - ($$3352$$,$$WAUBRA$$,#{state_id_vic},-37.384464,143.672506), - ($$3352$$,$$WEATHERBOARD$$,#{state_id_vic},-37.384464,143.672506), - ($$3352$$,$$WERNETH$$,#{state_id_vic},-37.384464,143.672506), - ($$3352$$,$$WINDERMERE$$,#{state_id_vic},-37.384464,143.672506), - ($$3352$$,$$YENDON$$,#{state_id_vic},-37.384464,143.672506), - ($$3353$$,$$BALLARAT$$,#{state_id_vic},-37.7778,144.835743), - ($$3354$$,$$BAKERY HILL$$,#{state_id_vic},-37.560917,143.867158), - ($$3354$$,$$BALLARAT MC$$,#{state_id_vic},-37.560917,143.867158), - ($$3355$$,$$LAKE GARDENS$$,#{state_id_vic},-37.545723,143.820973), - ($$3355$$,$$MITCHELL PARK$$,#{state_id_vic},-37.545723,143.820973), - ($$3355$$,$$WENDOUREE$$,#{state_id_vic},-37.545723,143.820973), - ($$3355$$,$$WENDOUREE VILLAGE$$,#{state_id_vic},-37.545723,143.820973), - ($$3356$$,$$DELACOMBE$$,#{state_id_vic},-37.591298,143.828273), - ($$3356$$,$$SEBASTOPOL$$,#{state_id_vic},-37.591298,143.828273), - ($$3357$$,$$BUNINYONG$$,#{state_id_vic},-37.648547,143.918563), - ($$3357$$,$$SCOTSMANS LEAD$$,#{state_id_vic},-37.648547,143.918563), - ($$3360$$,$$HAPPY VALLEY$$,#{state_id_vic},-37.686374,143.562977), - ($$3360$$,$$LINTON$$,#{state_id_vic},-37.686374,143.562977), - ($$3360$$,$$MANNIBADAR$$,#{state_id_vic},-37.686374,143.562977), - ($$3360$$,$$PITTONG$$,#{state_id_vic},-37.686374,143.562977), - ($$3360$$,$$WILLOWVALE$$,#{state_id_vic},-37.686374,143.562977), - ($$3361$$,$$BRADVALE$$,#{state_id_vic},-37.779772,143.407202), - ($$3361$$,$$CARRANBALLAC$$,#{state_id_vic},-37.779772,143.407202), - ($$3361$$,$$SKIPTON$$,#{state_id_vic},-37.779772,143.407202), - ($$3363$$,$$CRESWICK$$,#{state_id_vic},-37.424838,143.894483), - ($$3363$$,$$CRESWICK NORTH$$,#{state_id_vic},-37.424838,143.894483), - ($$3363$$,$$DEAN$$,#{state_id_vic},-37.424838,143.894483), - ($$3363$$,$$GLENDARUEL$$,#{state_id_vic},-37.424838,143.894483), - ($$3363$$,$$LANGDONS HILL$$,#{state_id_vic},-37.424838,143.894483), - ($$3363$$,$$MOUNT BECKWORTH$$,#{state_id_vic},-37.424838,143.894483), - ($$3363$$,$$TOURELLO$$,#{state_id_vic},-37.424838,143.894483), - ($$3364$$,$$ALLENDALE$$,#{state_id_vic},-37.368711,143.938548), - ($$3364$$,$$ASCOT$$,#{state_id_vic},-37.368711,143.938548), - ($$3364$$,$$BALD HILLS$$,#{state_id_vic},-37.368711,143.938548), - ($$3364$$,$$BARKSTEAD$$,#{state_id_vic},-37.368711,143.938548), - ($$3364$$,$$BLAMPIED$$,#{state_id_vic},-37.368711,143.938548), - ($$3364$$,$$BROOMFIELD$$,#{state_id_vic},-37.368711,143.938548), - ($$3364$$,$$CABBAGE TREE$$,#{state_id_vic},-37.368711,143.938548), - ($$3364$$,$$CAMPBELLTOWN$$,#{state_id_vic},-37.368711,143.938548), - ($$3364$$,$$COGHILLS CREEK$$,#{state_id_vic},-37.368711,143.938548), - ($$3364$$,$$GLENDONALD$$,#{state_id_vic},-37.368711,143.938548), - ($$3364$$,$$KINGSTON$$,#{state_id_vic},-37.368711,143.938548), - ($$3364$$,$$KOOROOCHEANG$$,#{state_id_vic},-37.368711,143.938548), - ($$3364$$,$$LAWRENCE$$,#{state_id_vic},-37.368711,143.938548), - ($$3364$$,$$MOUNT PROSPECT$$,#{state_id_vic},-37.368711,143.938548), - ($$3364$$,$$NEWLYN$$,#{state_id_vic},-37.368711,143.938548), - ($$3364$$,$$NEWLYN NORTH$$,#{state_id_vic},-37.368711,143.938548), - ($$3364$$,$$ROCKLYN$$,#{state_id_vic},-37.368711,143.938548), - ($$3364$$,$$SMEATON$$,#{state_id_vic},-37.368711,143.938548), - ($$3364$$,$$SMOKEYTOWN$$,#{state_id_vic},-37.368711,143.938548), - ($$3364$$,$$SPRINGMOUNT$$,#{state_id_vic},-37.368711,143.938548), - ($$3364$$,$$STRATHLEA$$,#{state_id_vic},-37.368711,143.938548), - ($$3364$$,$$WERONA$$,#{state_id_vic},-37.368711,143.938548), - ($$3370$$,$$CLUNES$$,#{state_id_vic},-37.293942,143.787341), - ($$3370$$,$$GLENGOWER$$,#{state_id_vic},-37.293942,143.787341), - ($$3370$$,$$MOUNT CAMERON$$,#{state_id_vic},-37.293942,143.787341), - ($$3370$$,$$ULLINA$$,#{state_id_vic},-37.293942,143.787341), - ($$3371$$,$$AMHERST$$,#{state_id_vic},-37.146466,143.670166), - ($$3371$$,$$BURNBANK$$,#{state_id_vic},-37.146466,143.670166), - ($$3371$$,$$CARALULUP$$,#{state_id_vic},-37.146466,143.670166), - ($$3371$$,$$DUNACH$$,#{state_id_vic},-37.146466,143.670166), - ($$3371$$,$$EVANSFORD$$,#{state_id_vic},-37.146466,143.670166), - ($$3371$$,$$LILLICUR$$,#{state_id_vic},-37.146466,143.670166), - ($$3371$$,$$MOUNT GLASGOW$$,#{state_id_vic},-37.146466,143.670166), - ($$3371$$,$$RED LION$$,#{state_id_vic},-37.146466,143.670166), - ($$3371$$,$$STONY CREEK$$,#{state_id_vic},-37.146466,143.670166), - ($$3371$$,$$TALBOT$$,#{state_id_vic},-37.146466,143.670166), - ($$3373$$,$$BEAUFORT$$,#{state_id_vic},-37.429812,143.383709), - ($$3373$$,$$CHUTE$$,#{state_id_vic},-37.429812,143.383709), - ($$3373$$,$$CROSS ROADS$$,#{state_id_vic},-37.429812,143.383709), - ($$3373$$,$$LAKE GOLDSMITH$$,#{state_id_vic},-37.429812,143.383709), - ($$3373$$,$$LAKE WONGAN$$,#{state_id_vic},-37.429812,143.383709), - ($$3373$$,$$MAIN LEAD$$,#{state_id_vic},-37.429812,143.383709), - ($$3373$$,$$MENA PARK$$,#{state_id_vic},-37.429812,143.383709), - ($$3373$$,$$NERRING$$,#{state_id_vic},-37.429812,143.383709), - ($$3373$$,$$RAGLAN$$,#{state_id_vic},-37.429812,143.383709), - ($$3373$$,$$STOCKYARD HILL$$,#{state_id_vic},-37.429812,143.383709), - ($$3373$$,$$STONELEIGH$$,#{state_id_vic},-37.429812,143.383709), - ($$3373$$,$$TRAWALLA$$,#{state_id_vic},-37.429812,143.383709), - ($$3373$$,$$WATERLOO$$,#{state_id_vic},-37.429812,143.383709), - ($$3374$$,$$GREAT WESTERN$$,#{state_id_vic},0.0,0.0), - ($$3375$$,$$BALLYROGAN$$,#{state_id_vic},-37.427465,143.132372), - ($$3375$$,$$BAYINDEEN$$,#{state_id_vic},-37.427465,143.132372), - ($$3375$$,$$BUANGOR$$,#{state_id_vic},-37.427465,143.132372), - ($$3375$$,$$MIDDLE CREEK$$,#{state_id_vic},-37.427465,143.132372), - ($$3377$$,$$ARARAT$$,#{state_id_vic},-37.284301,142.928227), - ($$3377$$,$$ARMSTRONG$$,#{state_id_vic},-37.284301,142.928227), - ($$3377$$,$$BULGANA$$,#{state_id_vic},-37.284301,142.928227), - ($$3377$$,$$CATHCART$$,#{state_id_vic},-37.284301,142.928227), - ($$3377$$,$$CROWLANDS$$,#{state_id_vic},-37.284301,142.928227), - ($$3377$$,$$DENICULL CREEK$$,#{state_id_vic},-37.284301,142.928227), - ($$3377$$,$$DOBIE$$,#{state_id_vic},-37.284301,142.928227), - ($$3377$$,$$DUNNEWORTHY$$,#{state_id_vic},-37.284301,142.928227), - ($$3377$$,$$EVERSLEY$$,#{state_id_vic},-37.284301,142.928227), - ($$3377$$,$$LANGI LOGAN$$,#{state_id_vic},-37.284301,142.928227), - ($$3377$$,$$MAROONA$$,#{state_id_vic},-37.284301,142.928227), - ($$3377$$,$$MOUNT COLE$$,#{state_id_vic},-37.284301,142.928227), - ($$3377$$,$$MOUNT COLE CREEK$$,#{state_id_vic},-37.284301,142.928227), - ($$3377$$,$$MOYSTON$$,#{state_id_vic},-37.284301,142.928227), - ($$3377$$,$$NORVAL$$,#{state_id_vic},-37.284301,142.928227), - ($$3377$$,$$RHYMNEY$$,#{state_id_vic},-37.284301,142.928227), - ($$3377$$,$$ROCKY POINT$$,#{state_id_vic},-37.284301,142.928227), - ($$3377$$,$$ROSSBRIDGE$$,#{state_id_vic},-37.284301,142.928227), - ($$3377$$,$$SHAYS FLAT$$,#{state_id_vic},-37.284301,142.928227), - ($$3377$$,$$WARRAK$$,#{state_id_vic},-37.284301,142.928227), - ($$3378$$,$$TATYOON$$,#{state_id_vic},-37.529051,142.944769), - ($$3378$$,$$YALLA-Y-POORA$$,#{state_id_vic},-37.529051,142.944769), - ($$3379$$,$$BORNES HILL$$,#{state_id_vic},-37.534037,142.521439), - ($$3379$$,$$CHATSWORTH$$,#{state_id_vic},-37.534037,142.521439), - ($$3379$$,$$MAFEKING$$,#{state_id_vic},-37.534037,142.521439), - ($$3379$$,$$STAVELY$$,#{state_id_vic},-37.534037,142.521439), - ($$3379$$,$$WICKLIFFE$$,#{state_id_vic},-37.534037,142.521439), - ($$3379$$,$$WILLAURA$$,#{state_id_vic},-37.534037,142.521439), - ($$3379$$,$$WILLAURA NORTH$$,#{state_id_vic},-37.534037,142.521439), - ($$3380$$,$$STAWELL$$,#{state_id_vic},-37.157329,142.699342), - ($$3380$$,$$STAWELL WEST$$,#{state_id_vic},-37.157329,142.699342), - ($$3381$$,$$BELLELLEN$$,#{state_id_vic},-36.941793,143.211642), - ($$3381$$,$$BELLFIELD$$,#{state_id_vic},-36.941793,143.211642), - ($$3381$$,$$BLACK RANGE$$,#{state_id_vic},-36.941793,143.211642), - ($$3381$$,$$FYANS CREEK$$,#{state_id_vic},-36.941793,143.211642), - ($$3381$$,$$HALLS GAP$$,#{state_id_vic},-36.941793,143.211642), - ($$3381$$,$$ILLAWARRA$$,#{state_id_vic},-36.941793,143.211642), - ($$3381$$,$$LAKE FYANS$$,#{state_id_vic},-36.941793,143.211642), - ($$3381$$,$$LAKE LONSDALE$$,#{state_id_vic},-36.941793,143.211642), - ($$3381$$,$$MOKEPILLY$$,#{state_id_vic},-36.941793,143.211642), - ($$3381$$,$$MOUNT DRYDEN$$,#{state_id_vic},-36.941793,143.211642), - ($$3381$$,$$POMONAL$$,#{state_id_vic},-36.941793,143.211642), - ($$3384$$,$$BARKLY$$,#{state_id_vic},-36.979212,143.198999), - ($$3384$$,$$CONCONGELLA$$,#{state_id_vic},-36.979212,143.198999), - ($$3384$$,$$FRENCHMANS$$,#{state_id_vic},-36.979212,143.198999), - ($$3384$$,$$JOEL JOEL$$,#{state_id_vic},-36.979212,143.198999), - ($$3384$$,$$JOEL SOUTH$$,#{state_id_vic},-36.979212,143.198999), - ($$3384$$,$$LANDSBOROUGH$$,#{state_id_vic},-36.979212,143.198999), - ($$3384$$,$$LANDSBOROUGH WEST$$,#{state_id_vic},-36.979212,143.198999), - ($$3384$$,$$NAVARRE$$,#{state_id_vic},-36.979212,143.198999), - ($$3384$$,$$TULKARA$$,#{state_id_vic},-36.979212,143.198999), - ($$3384$$,$$WATTLE CREEK$$,#{state_id_vic},-36.979212,143.198999), - ($$3385$$,$$DADSWELLS BRIDGE$$,#{state_id_vic},-36.916062,142.511313), - ($$3385$$,$$DEEP LEAD$$,#{state_id_vic},-36.916062,142.511313), - ($$3385$$,$$GLENORCHY$$,#{state_id_vic},-36.916062,142.511313), - ($$3385$$,$$LEDCOURT$$,#{state_id_vic},-36.916062,142.511313), - ($$3385$$,$$LUBECK$$,#{state_id_vic},-36.916062,142.511313), - ($$3385$$,$$RIACHELLA$$,#{state_id_vic},-36.916062,142.511313), - ($$3385$$,$$ROSES GAP$$,#{state_id_vic},-36.916062,142.511313), - ($$3385$$,$$WAL WAL$$,#{state_id_vic},-36.916062,142.511313), - ($$3387$$,$$BOLANGUM$$,#{state_id_vic},-36.671662,142.869768), - ($$3387$$,$$CALLAWADDA$$,#{state_id_vic},-36.671662,142.869768), - ($$3387$$,$$CAMPBELLS BRIDGE$$,#{state_id_vic},-36.671662,142.869768), - ($$3387$$,$$GERMANIA$$,#{state_id_vic},-36.671662,142.869768), - ($$3387$$,$$GREENS CREEK$$,#{state_id_vic},-36.671662,142.869768), - ($$3387$$,$$KANYA$$,#{state_id_vic},-36.671662,142.869768), - ($$3387$$,$$MARNOO$$,#{state_id_vic},-36.671662,142.869768), - ($$3387$$,$$MARNOO WEST$$,#{state_id_vic},-36.671662,142.869768), - ($$3387$$,$$MORRL MORRL$$,#{state_id_vic},-36.671662,142.869768), - ($$3387$$,$$WALLALOO$$,#{state_id_vic},-36.671662,142.869768), - ($$3387$$,$$WALLALOO EAST$$,#{state_id_vic},-36.671662,142.869768), - ($$3388$$,$$BANYENA$$,#{state_id_vic},-36.572229,142.816541), - ($$3388$$,$$RUPANYUP$$,#{state_id_vic},-36.572229,142.816541), - ($$3390$$,$$KEWELL$$,#{state_id_vic},-36.449414,142.422325), - ($$3390$$,$$MURTOA$$,#{state_id_vic},-36.449414,142.422325), - ($$3391$$,$$BRIM$$,#{state_id_vic},-36.069357,142.519496), - ($$3392$$,$$BOOLITE$$,#{state_id_vic},-36.460553,142.586163), - ($$3392$$,$$MINYIP$$,#{state_id_vic},-36.460553,142.586163), - ($$3392$$,$$SHEEP HILLS$$,#{state_id_vic},-36.460553,142.586163), - ($$3393$$,$$AUBREY$$,#{state_id_vic},-36.331465,142.363833), - ($$3393$$,$$BANGERANG$$,#{state_id_vic},-36.331465,142.363833), - ($$3393$$,$$CANNUM$$,#{state_id_vic},-36.331465,142.363833), - ($$3393$$,$$CRYMELON$$,#{state_id_vic},-36.331465,142.363833), - ($$3393$$,$$KELLALAC$$,#{state_id_vic},-36.331465,142.363833), - ($$3393$$,$$LAH$$,#{state_id_vic},-36.331465,142.363833), - ($$3393$$,$$WARRACKNABEAL$$,#{state_id_vic},-36.331465,142.363833), - ($$3393$$,$$WILKUR$$,#{state_id_vic},-36.331465,142.363833), - ($$3393$$,$$WILLENABRINA$$,#{state_id_vic},-36.331465,142.363833), - ($$3395$$,$$BEULAH$$,#{state_id_vic},-35.93804,142.418961), - ($$3395$$,$$KENMARE$$,#{state_id_vic},-35.93804,142.418961), - ($$3395$$,$$REEDY DAM$$,#{state_id_vic},-35.93804,142.418961), - ($$3395$$,$$ROSEBERY$$,#{state_id_vic},-35.93804,142.418961), - ($$3396$$,$$HOPETOUN$$,#{state_id_vic},-35.727235,142.364734), - ($$3400$$,$$HORSHAM$$,#{state_id_vic},-36.711714,142.204899), - ($$3400$$,$$HORSHAM WEST$$,#{state_id_vic},-36.711714,142.204899), - ($$3401$$,$$BLACKHEATH$$,#{state_id_vic},-36.455387,142.30917), - ($$3401$$,$$BRIMPAEN$$,#{state_id_vic},-36.455387,142.30917), - ($$3401$$,$$BUNGALALLY$$,#{state_id_vic},-36.455387,142.30917), - ($$3401$$,$$CHERRYPOOL$$,#{state_id_vic},-36.455387,142.30917), - ($$3401$$,$$DOOEN$$,#{state_id_vic},-36.455387,142.30917), - ($$3401$$,$$DRUNG$$,#{state_id_vic},-36.455387,142.30917), - ($$3401$$,$$GYMBOWEN$$,#{state_id_vic},-36.455387,142.30917), - ($$3401$$,$$HAVEN$$,#{state_id_vic},-36.455387,142.30917), - ($$3401$$,$$JUNG$$,#{state_id_vic},-36.455387,142.30917), - ($$3401$$,$$KALKEE$$,#{state_id_vic},-36.455387,142.30917), - ($$3401$$,$$KANAGULK$$,#{state_id_vic},-36.455387,142.30917), - ($$3401$$,$$KARNAK$$,#{state_id_vic},-36.455387,142.30917), - ($$3401$$,$$LAHARUM$$,#{state_id_vic},-36.455387,142.30917), - ($$3401$$,$$LONGERENONG$$,#{state_id_vic},-36.455387,142.30917), - ($$3401$$,$$LOWER NORTON$$,#{state_id_vic},-36.455387,142.30917), - ($$3401$$,$$MCKENZIE CREEK$$,#{state_id_vic},-36.455387,142.30917), - ($$3401$$,$$MOCKINYA$$,#{state_id_vic},-36.455387,142.30917), - ($$3401$$,$$MURRA WARRA$$,#{state_id_vic},-36.455387,142.30917), - ($$3401$$,$$NURCOUNG$$,#{state_id_vic},-36.455387,142.30917), - ($$3401$$,$$NURRABIEL$$,#{state_id_vic},-36.455387,142.30917), - ($$3401$$,$$PIMPINIO$$,#{state_id_vic},-36.455387,142.30917), - ($$3401$$,$$QUANTONG$$,#{state_id_vic},-36.455387,142.30917), - ($$3401$$,$$RIVERSIDE$$,#{state_id_vic},-36.455387,142.30917), - ($$3401$$,$$ROCKLANDS$$,#{state_id_vic},-36.455387,142.30917), - ($$3401$$,$$ST HELENS PLAINS$$,#{state_id_vic},-36.455387,142.30917), - ($$3401$$,$$TELANGATUK EAST$$,#{state_id_vic},-36.455387,142.30917), - ($$3401$$,$$TOOLONDO$$,#{state_id_vic},-36.455387,142.30917), - ($$3401$$,$$VECTIS$$,#{state_id_vic},-36.455387,142.30917), - ($$3401$$,$$WAIL$$,#{state_id_vic},-36.455387,142.30917), - ($$3401$$,$$WALLUP$$,#{state_id_vic},-36.455387,142.30917), - ($$3401$$,$$WARTOOK$$,#{state_id_vic},-36.455387,142.30917), - ($$3401$$,$$WONWONDAH$$,#{state_id_vic},-36.455387,142.30917), - ($$3401$$,$$ZUMSTEINS$$,#{state_id_vic},-36.455387,142.30917), - ($$3402$$,$$HORSHAM$$,#{state_id_vic},-37.164183,142.666302), - ($$3407$$,$$BALMORAL$$,#{state_id_vic},-37.248109,141.841762), - ($$3407$$,$$ENGLEFIELD$$,#{state_id_vic},-37.248109,141.841762), - ($$3407$$,$$GATUM$$,#{state_id_vic},-37.248109,141.841762), - ($$3407$$,$$PIGEON PONDS$$,#{state_id_vic},-37.248109,141.841762), - ($$3407$$,$$VASEY$$,#{state_id_vic},-37.248109,141.841762), - ($$3409$$,$$ARAPILES$$,#{state_id_vic},-36.779163,141.762526), - ($$3409$$,$$CLEAR LAKE$$,#{state_id_vic},-36.779163,141.762526), - ($$3409$$,$$DOUGLAS$$,#{state_id_vic},-36.779163,141.762526), - ($$3409$$,$$DUCHEMBEGARRA$$,#{state_id_vic},-36.779163,141.762526), - ($$3409$$,$$GRASS FLAT$$,#{state_id_vic},-36.779163,141.762526), - ($$3409$$,$$JILPANGER$$,#{state_id_vic},-36.779163,141.762526), - ($$3409$$,$$MIGA LAKE$$,#{state_id_vic},-36.779163,141.762526), - ($$3409$$,$$MITRE$$,#{state_id_vic},-36.779163,141.762526), - ($$3409$$,$$NATIMUK$$,#{state_id_vic},-36.779163,141.762526), - ($$3409$$,$$NORADJUHA$$,#{state_id_vic},-36.779163,141.762526), - ($$3409$$,$$TOOAN$$,#{state_id_vic},-36.779163,141.762526), - ($$3409$$,$$WOMBELANO$$,#{state_id_vic},-36.779163,141.762526), - ($$3412$$,$$GOROKE$$,#{state_id_vic},-36.747329,141.472623), - ($$3413$$,$$MINIMAY$$,#{state_id_vic},-36.714185,141.180738), - ($$3413$$,$$NEUARPURR$$,#{state_id_vic},-36.714185,141.180738), - ($$3413$$,$$OZENKADNOOK$$,#{state_id_vic},-36.714185,141.180738), - ($$3413$$,$$PERONNE$$,#{state_id_vic},-36.714185,141.180738), - ($$3414$$,$$ANTWERP$$,#{state_id_vic},-36.298418,142.024557), - ($$3414$$,$$DIMBOOLA$$,#{state_id_vic},-36.298418,142.024557), - ($$3414$$,$$TARRANYURK$$,#{state_id_vic},-36.298418,142.024557), - ($$3415$$,$$MIRAM$$,#{state_id_vic},-36.378089,141.357823), - ($$3418$$,$$BROUGHTON$$,#{state_id_vic},-36.165178,141.334582), - ($$3418$$,$$GERANG GERUNG$$,#{state_id_vic},-36.165178,141.334582), - ($$3418$$,$$GLENLEE$$,#{state_id_vic},-36.165178,141.334582), - ($$3418$$,$$KIATA$$,#{state_id_vic},-36.165178,141.334582), - ($$3418$$,$$LAWLOIT$$,#{state_id_vic},-36.165178,141.334582), - ($$3418$$,$$LITTLE DESERT$$,#{state_id_vic},-36.165178,141.334582), - ($$3418$$,$$LORQUON$$,#{state_id_vic},-36.165178,141.334582), - ($$3418$$,$$NETHERBY$$,#{state_id_vic},-36.165178,141.334582), - ($$3418$$,$$NHILL$$,#{state_id_vic},-36.165178,141.334582), - ($$3418$$,$$YANAC$$,#{state_id_vic},-36.165178,141.334582), - ($$3419$$,$$KANIVA$$,#{state_id_vic},-36.377563,141.244901), - ($$3420$$,$$LILLIMUR$$,#{state_id_vic},-36.361245,141.117251), - ($$3420$$,$$SERVICETON$$,#{state_id_vic},-36.361245,141.117251), - ($$3420$$,$$TELOPEA DOWNS$$,#{state_id_vic},-36.361245,141.117251), - ($$3423$$,$$JEPARIT$$,#{state_id_vic},-36.14057,141.987191), - ($$3424$$,$$ALBACUTYA$$,#{state_id_vic},-35.691918,141.974601), - ($$3424$$,$$RAINBOW$$,#{state_id_vic},-35.691918,141.974601), - ($$3424$$,$$YAAPEET$$,#{state_id_vic},-35.691918,141.974601), - ($$3427$$,$$DIGGERS REST$$,#{state_id_vic},-37.627752,144.719981), - ($$3428$$,$$BULLA$$,#{state_id_vic},-37.637155,144.804139), - ($$3429$$,$$SUNBURY$$,#{state_id_vic},-37.576859,144.731425), - ($$3429$$,$$WILDWOOD$$,#{state_id_vic},-37.576859,144.731425), - ($$3430$$,$$CLARKEFIELD$$,#{state_id_vic},-37.483711,144.745723), - ($$3431$$,$$RIDDELLS CREEK$$,#{state_id_vic},-37.463999,144.665048), - ($$3432$$,$$BOLINDA$$,#{state_id_vic},-37.433632,144.77977), - ($$3433$$,$$MONEGEETTA$$,#{state_id_vic},0.0,0.0), - ($$3434$$,$$CHEROKEE$$,#{state_id_vic},-37.389195,144.6379), - ($$3434$$,$$KERRIE$$,#{state_id_vic},-37.389195,144.6379), - ($$3434$$,$$ROMSEY$$,#{state_id_vic},-37.389195,144.6379), - ($$3434$$,$$SPRINGFIELD$$,#{state_id_vic},-37.389195,144.6379), - ($$3435$$,$$BENLOCH$$,#{state_id_vic},-37.189508,144.692899), - ($$3435$$,$$GOLDIE$$,#{state_id_vic},-37.189508,144.692899), - ($$3435$$,$$LANCEFIELD$$,#{state_id_vic},-37.189508,144.692899), - ($$3435$$,$$NULLA VALE$$,#{state_id_vic},-37.189508,144.692899), - ($$3437$$,$$BULLENGAROOK$$,#{state_id_vic},-37.523331,144.477431), - ($$3437$$,$$GISBORNE$$,#{state_id_vic},-37.523331,144.477431), - ($$3437$$,$$GISBORNE SOUTH$$,#{state_id_vic},-37.523331,144.477431), - ($$3438$$,$$NEW GISBORNE$$,#{state_id_vic},-37.459119,144.599206), - ($$3440$$,$$MACEDON$$,#{state_id_vic},-37.399584,144.588405), - ($$3441$$,$$MOUNT MACEDON$$,#{state_id_vic},-37.396044,144.590721), - ($$3442$$,$$ASHBOURNE$$,#{state_id_vic},-37.385948,144.446407), - ($$3442$$,$$CADELLO$$,#{state_id_vic},-37.385948,144.446407), - ($$3442$$,$$CARLSRUHE$$,#{state_id_vic},-37.385948,144.446407), - ($$3442$$,$$COBAW$$,#{state_id_vic},-37.385948,144.446407), - ($$3442$$,$$HESKET$$,#{state_id_vic},-37.385948,144.446407), - ($$3442$$,$$NEWHAM$$,#{state_id_vic},-37.385948,144.446407), - ($$3442$$,$$ROCHFORD$$,#{state_id_vic},-37.385948,144.446407), - ($$3442$$,$$WOODEND$$,#{state_id_vic},-37.385948,144.446407), - ($$3442$$,$$WOODEND NORTH$$,#{state_id_vic},-37.385948,144.446407), - ($$3444$$,$$BARFOLD$$,#{state_id_vic},-37.091978,144.50611), - ($$3444$$,$$BAYNTON$$,#{state_id_vic},-37.091978,144.50611), - ($$3444$$,$$BAYNTON EAST$$,#{state_id_vic},-37.091978,144.50611), - ($$3444$$,$$EDGECOMBE$$,#{state_id_vic},-37.091978,144.50611), - ($$3444$$,$$GLENHOPE$$,#{state_id_vic},-37.091978,144.50611), - ($$3444$$,$$GREENHILL$$,#{state_id_vic},-37.091978,144.50611), - ($$3444$$,$$KYNETON$$,#{state_id_vic},-37.091978,144.50611), - ($$3444$$,$$KYNETON SOUTH$$,#{state_id_vic},-37.091978,144.50611), - ($$3444$$,$$LANGLEY$$,#{state_id_vic},-37.091978,144.50611), - ($$3444$$,$$LAURISTON$$,#{state_id_vic},-37.091978,144.50611), - ($$3444$$,$$LYAL$$,#{state_id_vic},-37.091978,144.50611), - ($$3444$$,$$METCALFE EAST$$,#{state_id_vic},-37.091978,144.50611), - ($$3444$$,$$MIA MIA$$,#{state_id_vic},-37.091978,144.50611), - ($$3444$$,$$MYRTLE CREEK$$,#{state_id_vic},-37.091978,144.50611), - ($$3444$$,$$PASTORIA$$,#{state_id_vic},-37.091978,144.50611), - ($$3444$$,$$PASTORIA EAST$$,#{state_id_vic},-37.091978,144.50611), - ($$3444$$,$$PIPERS CREEK$$,#{state_id_vic},-37.091978,144.50611), - ($$3444$$,$$REDESDALE$$,#{state_id_vic},-37.091978,144.50611), - ($$3444$$,$$SIDONIA$$,#{state_id_vic},-37.091978,144.50611), - ($$3444$$,$$SPRING HILL$$,#{state_id_vic},-37.091978,144.50611), - ($$3444$$,$$TYLDEN$$,#{state_id_vic},-37.091978,144.50611), - ($$3444$$,$$TYLDEN SOUTH$$,#{state_id_vic},-37.091978,144.50611), - ($$3446$$,$$DRUMMOND NORTH$$,#{state_id_vic},-37.199978,144.29344), - ($$3446$$,$$MALMSBURY$$,#{state_id_vic},-37.199978,144.29344), - ($$3447$$,$$TARADALE$$,#{state_id_vic},-37.133438,144.356519), - ($$3448$$,$$ELPHINSTONE$$,#{state_id_vic},-37.105,144.337783), - ($$3448$$,$$METCALFE$$,#{state_id_vic},-37.105,144.337783), - ($$3448$$,$$SUTTON GRANGE$$,#{state_id_vic},-37.105,144.337783), - ($$3450$$,$$CASTLEMAINE$$,#{state_id_vic},-37.063869,144.217101), - ($$3450$$,$$MOONLIGHT FLAT$$,#{state_id_vic},-37.063869,144.217101), - ($$3451$$,$$BARKERS CREEK$$,#{state_id_vic},-37.0292,144.239946), - ($$3451$$,$$CAMPBELLS CREEK$$,#{state_id_vic},-37.0292,144.239946), - ($$3451$$,$$CHEWTON$$,#{state_id_vic},-37.0292,144.239946), - ($$3451$$,$$CHEWTON BUSHLANDS$$,#{state_id_vic},-37.0292,144.239946), - ($$3451$$,$$FARADAY$$,#{state_id_vic},-37.0292,144.239946), - ($$3451$$,$$FRYERSTOWN$$,#{state_id_vic},-37.0292,144.239946), - ($$3451$$,$$GLENLUCE$$,#{state_id_vic},-37.0292,144.239946), - ($$3451$$,$$GOLDEN POINT$$,#{state_id_vic},-37.0292,144.239946), - ($$3451$$,$$GOWER$$,#{state_id_vic},-37.0292,144.239946), - ($$3451$$,$$GUILDFORD$$,#{state_id_vic},-37.0292,144.239946), - ($$3451$$,$$IRISHTOWN$$,#{state_id_vic},-37.0292,144.239946), - ($$3451$$,$$MCKENZIE HILL$$,#{state_id_vic},-37.0292,144.239946), - ($$3451$$,$$MUCKLEFORD$$,#{state_id_vic},-37.0292,144.239946), - ($$3451$$,$$TARILTA$$,#{state_id_vic},-37.0292,144.239946), - ($$3451$$,$$VAUGHAN$$,#{state_id_vic},-37.0292,144.239946), - ($$3451$$,$$YAPEEN$$,#{state_id_vic},-37.0292,144.239946), - ($$3453$$,$$HARCOURT$$,#{state_id_vic},-36.998965,144.262588), - ($$3453$$,$$HARCOURT NORTH$$,#{state_id_vic},-36.998965,144.262588), - ($$3453$$,$$RAVENSWOOD$$,#{state_id_vic},-36.998965,144.262588), - ($$3453$$,$$RAVENSWOOD SOUTH$$,#{state_id_vic},-36.998965,144.262588), - ($$3458$$,$$BARRYS REEF$$,#{state_id_vic},-37.451989,144.293564), - ($$3458$$,$$BLACKWOOD$$,#{state_id_vic},-37.451989,144.293564), - ($$3458$$,$$FERN HILL$$,#{state_id_vic},-37.451989,144.293564), - ($$3458$$,$$LERDERDERG$$,#{state_id_vic},-37.451989,144.293564), - ($$3458$$,$$LITTLE HAMPTON$$,#{state_id_vic},-37.451989,144.293564), - ($$3458$$,$$NEWBURY$$,#{state_id_vic},-37.451989,144.293564), - ($$3458$$,$$NORTH BLACKWOOD$$,#{state_id_vic},-37.451989,144.293564), - ($$3458$$,$$TRENTHAM$$,#{state_id_vic},-37.451989,144.293564), - ($$3458$$,$$TRENTHAM EAST$$,#{state_id_vic},-37.451989,144.293564), - ($$3460$$,$$BASALT$$,#{state_id_vic},-37.308233,144.094647), - ($$3460$$,$$DAYLESFORD$$,#{state_id_vic},-37.308233,144.094647), - ($$3461$$,$$BULLARTO$$,#{state_id_vic},-37.394334,144.221001), - ($$3461$$,$$BULLARTO SOUTH$$,#{state_id_vic},-37.394334,144.221001), - ($$3461$$,$$CLYDESDALE$$,#{state_id_vic},-37.394334,144.221001), - ($$3461$$,$$COOMOORA$$,#{state_id_vic},-37.394334,144.221001), - ($$3461$$,$$DENVER$$,#{state_id_vic},-37.394334,144.221001), - ($$3461$$,$$DRUMMOND$$,#{state_id_vic},-37.394334,144.221001), - ($$3461$$,$$DRY DIGGINGS$$,#{state_id_vic},-37.394334,144.221001), - ($$3461$$,$$EGANSTOWN$$,#{state_id_vic},-37.394334,144.221001), - ($$3461$$,$$ELEVATED PLAINS$$,#{state_id_vic},-37.394334,144.221001), - ($$3461$$,$$FRANKLINFORD$$,#{state_id_vic},-37.394334,144.221001), - ($$3461$$,$$GLENLYON$$,#{state_id_vic},-37.394334,144.221001), - ($$3461$$,$$HEPBURN$$,#{state_id_vic},-37.394334,144.221001), - ($$3461$$,$$HEPBURN SPRINGS$$,#{state_id_vic},-37.394334,144.221001), - ($$3461$$,$$KORWEINGUBOORA$$,#{state_id_vic},-37.394334,144.221001), - ($$3461$$,$$LEONARDS HILL$$,#{state_id_vic},-37.394334,144.221001), - ($$3461$$,$$LYONVILLE$$,#{state_id_vic},-37.394334,144.221001), - ($$3461$$,$$MOUNT FRANKLIN$$,#{state_id_vic},-37.394334,144.221001), - ($$3461$$,$$MUSK$$,#{state_id_vic},-37.394334,144.221001), - ($$3461$$,$$MUSK VALE$$,#{state_id_vic},-37.394334,144.221001), - ($$3461$$,$$PORCUPINE RIDGE$$,#{state_id_vic},-37.394334,144.221001), - ($$3461$$,$$SAILORS FALLS$$,#{state_id_vic},-37.394334,144.221001), - ($$3461$$,$$SAILORS HILL$$,#{state_id_vic},-37.394334,144.221001), - ($$3461$$,$$SHEPHERDS FLAT$$,#{state_id_vic},-37.394334,144.221001), - ($$3461$$,$$SPARGO CREEK$$,#{state_id_vic},-37.394334,144.221001), - ($$3461$$,$$STRANGWAYS$$,#{state_id_vic},-37.394334,144.221001), - ($$3461$$,$$WHEATSHEAF$$,#{state_id_vic},-37.394334,144.221001), - ($$3461$$,$$YANDOIT$$,#{state_id_vic},-37.394334,144.221001), - ($$3461$$,$$YANDOIT HILLS$$,#{state_id_vic},-37.394334,144.221001), - ($$3462$$,$$GREEN GULLY$$,#{state_id_vic},-37.107829,144.095795), - ($$3462$$,$$JOYCES CREEK$$,#{state_id_vic},-37.107829,144.095795), - ($$3462$$,$$MUCKLEFORD SOUTH$$,#{state_id_vic},-37.107829,144.095795), - ($$3462$$,$$NEWSTEAD$$,#{state_id_vic},-37.107829,144.095795), - ($$3462$$,$$SANDON$$,#{state_id_vic},-37.107829,144.095795), - ($$3462$$,$$WELSHMANS REEF$$,#{state_id_vic},-37.107829,144.095795), - ($$3463$$,$$BARINGHUP$$,#{state_id_vic},-36.984271,143.951487), - ($$3463$$,$$BARINGHUP WEST$$,#{state_id_vic},-36.984271,143.951487), - ($$3463$$,$$BRADFORD$$,#{state_id_vic},-36.984271,143.951487), - ($$3463$$,$$EASTVILLE$$,#{state_id_vic},-36.984271,143.951487), - ($$3463$$,$$LAANECOORIE$$,#{state_id_vic},-36.984271,143.951487), - ($$3463$$,$$MALDON$$,#{state_id_vic},-36.984271,143.951487), - ($$3463$$,$$NEEREMAN$$,#{state_id_vic},-36.984271,143.951487), - ($$3463$$,$$NUGGETTY$$,#{state_id_vic},-36.984271,143.951487), - ($$3463$$,$$SHELBOURNE$$,#{state_id_vic},-36.984271,143.951487), - ($$3463$$,$$TARRENGOWER$$,#{state_id_vic},-36.984271,143.951487), - ($$3463$$,$$WALMER$$,#{state_id_vic},-36.984271,143.951487), - ($$3463$$,$$WOODSTOCK WEST$$,#{state_id_vic},-36.984271,143.951487), - ($$3464$$,$$CARISBROOK$$,#{state_id_vic},-37.007853,143.796275), - ($$3465$$,$$ADELAIDE LEAD$$,#{state_id_vic},-37.074671,143.677358), - ($$3465$$,$$ALMA$$,#{state_id_vic},-37.074671,143.677358), - ($$3465$$,$$BOWENVALE$$,#{state_id_vic},-37.074671,143.677358), - ($$3465$$,$$BUNG BONG$$,#{state_id_vic},-37.074671,143.677358), - ($$3465$$,$$COTSWOLD$$,#{state_id_vic},-37.074671,143.677358), - ($$3465$$,$$CRAIGIE$$,#{state_id_vic},-37.074671,143.677358), - ($$3465$$,$$DAISY HILL$$,#{state_id_vic},-37.074671,143.677358), - ($$3465$$,$$FLAGSTAFF$$,#{state_id_vic},-37.074671,143.677358), - ($$3465$$,$$GOLDEN POINT$$,#{state_id_vic},-37.074671,143.677358), - ($$3465$$,$$HAVELOCK$$,#{state_id_vic},-37.074671,143.677358), - ($$3465$$,$$HOMEBUSH$$,#{state_id_vic},-37.074671,143.677358), - ($$3465$$,$$MAJORCA$$,#{state_id_vic},-37.074671,143.677358), - ($$3465$$,$$MARYBOROUGH$$,#{state_id_vic},-37.074671,143.677358), - ($$3465$$,$$MOOLORT$$,#{state_id_vic},-37.074671,143.677358), - ($$3465$$,$$MOONLIGHT FLAT$$,#{state_id_vic},-37.074671,143.677358), - ($$3465$$,$$NATTE YALLOCK$$,#{state_id_vic},-37.074671,143.677358), - ($$3465$$,$$RATHSCAR$$,#{state_id_vic},-37.074671,143.677358), - ($$3465$$,$$RATHSCAR WEST$$,#{state_id_vic},-37.074671,143.677358), - ($$3465$$,$$SIMSON$$,#{state_id_vic},-37.074671,143.677358), - ($$3465$$,$$TIMOR$$,#{state_id_vic},-37.074671,143.677358), - ($$3465$$,$$TIMOR WEST$$,#{state_id_vic},-37.074671,143.677358), - ($$3465$$,$$WAREEK$$,#{state_id_vic},-37.074671,143.677358), - ($$3467$$,$$AVOCA$$,#{state_id_vic},-37.088539,143.473798), - ($$3468$$,$$AMPHITHEATRE$$,#{state_id_vic},-37.182344,143.399915), - ($$3468$$,$$MOUNT LONARCH$$,#{state_id_vic},-37.182344,143.399915), - ($$3469$$,$$ELMHURST$$,#{state_id_vic},-37.179436,143.249097), - ($$3469$$,$$GLENLOFTY$$,#{state_id_vic},-37.179436,143.249097), - ($$3469$$,$$GLENLOGIE$$,#{state_id_vic},-37.179436,143.249097), - ($$3469$$,$$GLENPATRICK$$,#{state_id_vic},-37.179436,143.249097), - ($$3469$$,$$NOWHERE CREEK$$,#{state_id_vic},-37.179436,143.249097), - ($$3472$$,$$BET BET$$,#{state_id_vic},-36.924333,143.756154), - ($$3472$$,$$BETLEY$$,#{state_id_vic},-36.924333,143.756154), - ($$3472$$,$$BROMLEY$$,#{state_id_vic},-36.924333,143.756154), - ($$3472$$,$$DUNLUCE$$,#{state_id_vic},-36.924333,143.756154), - ($$3472$$,$$DUNOLLY$$,#{state_id_vic},-36.924333,143.756154), - ($$3472$$,$$EDDINGTON$$,#{state_id_vic},-36.924333,143.756154), - ($$3472$$,$$GOLDSBOROUGH$$,#{state_id_vic},-36.924333,143.756154), - ($$3472$$,$$INKERMAN$$,#{state_id_vic},-36.924333,143.756154), - ($$3472$$,$$MCINTYRE$$,#{state_id_vic},-36.924333,143.756154), - ($$3472$$,$$MOLIAGUL$$,#{state_id_vic},-36.924333,143.756154), - ($$3472$$,$$MOUNT HOOGHLY$$,#{state_id_vic},-36.924333,143.756154), - ($$3475$$,$$ARCHDALE$$,#{state_id_vic},-36.830903,143.502295), - ($$3475$$,$$ARCHDALE JUNCTION$$,#{state_id_vic},-36.830903,143.502295), - ($$3475$$,$$BEALIBA$$,#{state_id_vic},-36.830903,143.502295), - ($$3475$$,$$BURKES FLAT$$,#{state_id_vic},-36.830903,143.502295), - ($$3475$$,$$COCHRANES CREEK$$,#{state_id_vic},-36.830903,143.502295), - ($$3475$$,$$EMU$$,#{state_id_vic},-36.830903,143.502295), - ($$3475$$,$$LOGAN$$,#{state_id_vic},-36.830903,143.502295), - ($$3477$$,$$AVON PLAINS$$,#{state_id_vic},0.0,0.0), - ($$3477$$,$$BEAZLEYS BRIDGE$$,#{state_id_vic},0.0,0.0), - ($$3477$$,$$CARAPOOEE$$,#{state_id_vic},0.0,0.0), - ($$3477$$,$$CARAPOOEE WEST$$,#{state_id_vic},0.0,0.0), - ($$3477$$,$$COONOOER BRIDGE$$,#{state_id_vic},0.0,0.0), - ($$3477$$,$$COONOOER WEST$$,#{state_id_vic},0.0,0.0), - ($$3477$$,$$DALYENONG$$,#{state_id_vic},0.0,0.0), - ($$3477$$,$$GOOROC$$,#{state_id_vic},0.0,0.0), - ($$3477$$,$$GOWAR EAST$$,#{state_id_vic},0.0,0.0), - ($$3477$$,$$GRAYS BRIDGE$$,#{state_id_vic},0.0,0.0), - ($$3477$$,$$GRE GRE$$,#{state_id_vic},0.0,0.0), - ($$3477$$,$$GRE GRE NORTH$$,#{state_id_vic},0.0,0.0), - ($$3477$$,$$GRE GRE SOUTH$$,#{state_id_vic},0.0,0.0), - ($$3477$$,$$KOOREH$$,#{state_id_vic},0.0,0.0), - ($$3477$$,$$MARNOO EAST$$,#{state_id_vic},0.0,0.0), - ($$3477$$,$$MOOLERR$$,#{state_id_vic},0.0,0.0), - ($$3477$$,$$MOYREISK$$,#{state_id_vic},0.0,0.0), - ($$3477$$,$$PARADISE$$,#{state_id_vic},0.0,0.0), - ($$3477$$,$$REDBANK$$,#{state_id_vic},0.0,0.0), - ($$3477$$,$$ROSTRON$$,#{state_id_vic},0.0,0.0), - ($$3477$$,$$SLATY CREEK$$,#{state_id_vic},0.0,0.0), - ($$3477$$,$$ST ARNAUD EAST$$,#{state_id_vic},0.0,0.0), - ($$3477$$,$$ST ARNAUD NORTH$$,#{state_id_vic},0.0,0.0), - ($$3477$$,$$STUART MILL$$,#{state_id_vic},0.0,0.0), - ($$3477$$,$$SUTHERLAND$$,#{state_id_vic},0.0,0.0), - ($$3477$$,$$SWANWATER$$,#{state_id_vic},0.0,0.0), - ($$3477$$,$$TOTTINGTON$$,#{state_id_vic},0.0,0.0), - ($$3477$$,$$TRAYNORS LAGOON$$,#{state_id_vic},0.0,0.0), - ($$3477$$,$$WINJALLOK$$,#{state_id_vic},0.0,0.0), - ($$3477$$,$$YORK PLAINS$$,#{state_id_vic},0.0,0.0), - ($$3478$$,$$DOOBOOBETIC$$,#{state_id_vic},-36.544552,142.921227), - ($$3478$$,$$MEDLYN$$,#{state_id_vic},-36.544552,142.921227), - ($$3478$$,$$MOONAMBEL$$,#{state_id_vic},-36.544552,142.921227), - ($$3478$$,$$PERCYDALE$$,#{state_id_vic},-36.544552,142.921227), - ($$3478$$,$$ST ARNAUD$$,#{state_id_vic},-36.544552,142.921227), - ($$3478$$,$$TANWOOD$$,#{state_id_vic},-36.544552,142.921227), - ($$3478$$,$$WARRENMANG$$,#{state_id_vic},-36.544552,142.921227), - ($$3478$$,$$YAWONG HILLS$$,#{state_id_vic},-36.544552,142.921227), - ($$3480$$,$$AREEGRA$$,#{state_id_vic},-36.236565,142.687307), - ($$3480$$,$$CARRON$$,#{state_id_vic},-36.236565,142.687307), - ($$3480$$,$$COPE COPE$$,#{state_id_vic},-36.236565,142.687307), - ($$3480$$,$$CORACK$$,#{state_id_vic},-36.236565,142.687307), - ($$3480$$,$$CORACK EAST$$,#{state_id_vic},-36.236565,142.687307), - ($$3480$$,$$DONALD$$,#{state_id_vic},-36.236565,142.687307), - ($$3480$$,$$GIL GIL$$,#{state_id_vic},-36.236565,142.687307), - ($$3480$$,$$JEFFCOTT$$,#{state_id_vic},-36.236565,142.687307), - ($$3480$$,$$JEFFCOTT NORTH$$,#{state_id_vic},-36.236565,142.687307), - ($$3480$$,$$LAEN$$,#{state_id_vic},-36.236565,142.687307), - ($$3480$$,$$LAEN EAST$$,#{state_id_vic},-36.236565,142.687307), - ($$3480$$,$$LAEN NORTH$$,#{state_id_vic},-36.236565,142.687307), - ($$3480$$,$$LAKE BULOKE$$,#{state_id_vic},-36.236565,142.687307), - ($$3480$$,$$LAWLER$$,#{state_id_vic},-36.236565,142.687307), - ($$3480$$,$$LITCHFIELD$$,#{state_id_vic},-36.236565,142.687307), - ($$3480$$,$$RICH AVON$$,#{state_id_vic},-36.236565,142.687307), - ($$3480$$,$$RICH AVON EAST$$,#{state_id_vic},-36.236565,142.687307), - ($$3480$$,$$RICH AVON WEST$$,#{state_id_vic},-36.236565,142.687307), - ($$3480$$,$$SWANWATER WEST$$,#{state_id_vic},-36.236565,142.687307), - ($$3482$$,$$MASSEY$$,#{state_id_vic},-36.226051,142.859931), - ($$3482$$,$$MORTON PLAINS$$,#{state_id_vic},-36.226051,142.859931), - ($$3482$$,$$WARMUR$$,#{state_id_vic},-36.226051,142.859931), - ($$3482$$,$$WATCHEM$$,#{state_id_vic},-36.226051,142.859931), - ($$3482$$,$$WATCHEM WEST$$,#{state_id_vic},-36.226051,142.859931), - ($$3483$$,$$BALLAPUR$$,#{state_id_vic},-35.979172,142.747934), - ($$3483$$,$$BIRCHIP$$,#{state_id_vic},-35.979172,142.747934), - ($$3483$$,$$BIRCHIP WEST$$,#{state_id_vic},-35.979172,142.747934), - ($$3483$$,$$CURYO$$,#{state_id_vic},-35.979172,142.747934), - ($$3483$$,$$JIL JIL$$,#{state_id_vic},-35.979172,142.747934), - ($$3483$$,$$KARYRIE$$,#{state_id_vic},-35.979172,142.747934), - ($$3483$$,$$KINNABULLA$$,#{state_id_vic},-35.979172,142.747934), - ($$3483$$,$$MARLBED$$,#{state_id_vic},-35.979172,142.747934), - ($$3483$$,$$NARRAPORT$$,#{state_id_vic},-35.979172,142.747934), - ($$3483$$,$$WHIRILY$$,#{state_id_vic},-35.979172,142.747934), - ($$3485$$,$$BANYAN$$,#{state_id_vic},-35.635088,142.766116), - ($$3485$$,$$WATCHUPGA$$,#{state_id_vic},-35.635088,142.766116), - ($$3485$$,$$WILLANGIE$$,#{state_id_vic},-35.635088,142.766116), - ($$3485$$,$$WOOMELANG$$,#{state_id_vic},-35.635088,142.766116), - ($$3487$$,$$LASCELLES$$,#{state_id_vic},-35.606765,142.578891), - ($$3488$$,$$SPEED$$,#{state_id_vic},-35.400818,142.44027), - ($$3488$$,$$TURRIFF$$,#{state_id_vic},-35.400818,142.44027), - ($$3488$$,$$TURRIFF EAST$$,#{state_id_vic},-35.400818,142.44027), - ($$3489$$,$$TEMPY$$,#{state_id_vic},-35.344581,142.476178), - ($$3490$$,$$BIG DESERT$$,#{state_id_vic},-35.199697,141.600652), - ($$3490$$,$$BOINKA$$,#{state_id_vic},-35.199697,141.600652), - ($$3490$$,$$KULWIN$$,#{state_id_vic},-35.199697,141.600652), - ($$3490$$,$$MITTYACK$$,#{state_id_vic},-35.199697,141.600652), - ($$3490$$,$$MURRAY-SUNSET$$,#{state_id_vic},-35.199697,141.600652), - ($$3490$$,$$OUYEN$$,#{state_id_vic},-35.199697,141.600652), - ($$3490$$,$$TORRITA$$,#{state_id_vic},-35.199697,141.600652), - ($$3490$$,$$TUTYE$$,#{state_id_vic},-35.199697,141.600652), - ($$3491$$,$$PATCHEWOLLOCK$$,#{state_id_vic},-35.382896,142.1895), - ($$3494$$,$$CARWARP$$,#{state_id_vic},-34.457991,142.230811), - ($$3494$$,$$COLIGNAN$$,#{state_id_vic},-34.457991,142.230811), - ($$3494$$,$$IRAAK$$,#{state_id_vic},-34.457991,142.230811), - ($$3494$$,$$NANGILOC$$,#{state_id_vic},-34.457991,142.230811), - ($$3496$$,$$CARDROSS$$,#{state_id_vic},-34.292017,142.145463), - ($$3496$$,$$CULLULLERAINE$$,#{state_id_vic},-34.292017,142.145463), - ($$3496$$,$$LINDSAY POINT$$,#{state_id_vic},-34.292017,142.145463), - ($$3496$$,$$MERINGUR$$,#{state_id_vic},-34.292017,142.145463), - ($$3496$$,$$MERRINEE$$,#{state_id_vic},-34.292017,142.145463), - ($$3496$$,$$NEDS CORNER$$,#{state_id_vic},-34.292017,142.145463), - ($$3496$$,$$RED CLIFFS$$,#{state_id_vic},-34.292017,142.145463), - ($$3496$$,$$SUNNYCLIFFS$$,#{state_id_vic},-34.292017,142.145463), - ($$3496$$,$$WERRIMULL$$,#{state_id_vic},-34.292017,142.145463), - ($$3498$$,$$IRYMPLE$$,#{state_id_vic},-34.234604,142.181724), - ($$3500$$,$$MILDURA$$,#{state_id_vic},-34.181714,142.163072), - ($$3500$$,$$MILDURA WEST$$,#{state_id_vic},-34.181714,142.163072), - ($$3500$$,$$PARINGI$$,#{state_id_nsw},-34.181714,142.163072), - ($$3501$$,$$HATTAH$$,#{state_id_vic},-34.850899,142.327634), - ($$3501$$,$$KOORLONG$$,#{state_id_vic},-34.850899,142.327634), - ($$3501$$,$$MILDURA CENTRE PLAZA$$,#{state_id_vic},-34.850899,142.327634), - ($$3501$$,$$MILDURA SOUTH$$,#{state_id_vic},-34.850899,142.327634), - ($$3501$$,$$NICHOLS POINT$$,#{state_id_vic},-34.850899,142.327634), - ($$3502$$,$$MILDURA$$,#{state_id_vic},-37.972887,145.25835), - ($$3505$$,$$BIRDWOODTON$$,#{state_id_vic},-34.19768,142.057629), - ($$3505$$,$$CABARITA$$,#{state_id_vic},-34.19768,142.057629), - ($$3505$$,$$MERBEIN$$,#{state_id_vic},-34.19768,142.057629), - ($$3505$$,$$MERBEIN SOUTH$$,#{state_id_vic},-34.19768,142.057629), - ($$3505$$,$$MERBEIN WEST$$,#{state_id_vic},-34.19768,142.057629), - ($$3505$$,$$WARGAN$$,#{state_id_vic},-34.19768,142.057629), - ($$3505$$,$$YELTA$$,#{state_id_vic},-34.19768,142.057629), - ($$3506$$,$$COWANGIE$$,#{state_id_vic},-35.27579,141.399006), - ($$3507$$,$$WALPEUP$$,#{state_id_vic},-35.191803,142.02364), - ($$3509$$,$$LINGA$$,#{state_id_vic},-35.173454,141.692549), - ($$3509$$,$$UNDERBOOL$$,#{state_id_vic},-35.173454,141.692549), - ($$3512$$,$$CARINA$$,#{state_id_vic},-35.218149,141.090483), - ($$3512$$,$$MURRAYVILLE$$,#{state_id_vic},-35.218149,141.090483), - ($$3512$$,$$PANITYA$$,#{state_id_vic},-35.218149,141.090483), - ($$3515$$,$$MARONG$$,#{state_id_vic},-36.736592,144.132572), - ($$3515$$,$$SHELBOURNE$$,#{state_id_vic},-36.736592,144.132572), - ($$3515$$,$$WILSONS HILL$$,#{state_id_vic},-36.736592,144.132572), - ($$3516$$,$$BRIDGEWATER$$,#{state_id_vic},-36.593021,143.924643), - ($$3516$$,$$BRIDGEWATER NORTH$$,#{state_id_vic},-36.593021,143.924643), - ($$3516$$,$$BRIDGEWATER ON LODDON$$,#{state_id_vic},-36.593021,143.924643), - ($$3516$$,$$DERBY$$,#{state_id_vic},-36.593021,143.924643), - ($$3516$$,$$LEICHARDT$$,#{state_id_vic},-36.593021,143.924643), - ($$3516$$,$$YARRABERB$$,#{state_id_vic},-36.593021,143.924643), - ($$3517$$,$$BEARS LAGOON$$,#{state_id_vic},-36.298881,143.915229), - ($$3517$$,$$BRENANAH$$,#{state_id_vic},-36.298881,143.915229), - ($$3517$$,$$GLENALBYN$$,#{state_id_vic},-36.298881,143.915229), - ($$3517$$,$$INGLEWOOD$$,#{state_id_vic},-36.298881,143.915229), - ($$3517$$,$$JARKLIN$$,#{state_id_vic},-36.298881,143.915229), - ($$3517$$,$$KINGOWER$$,#{state_id_vic},-36.298881,143.915229), - ($$3517$$,$$KURTING$$,#{state_id_vic},-36.298881,143.915229), - ($$3517$$,$$POWLETT PLAINS$$,#{state_id_vic},-36.298881,143.915229), - ($$3517$$,$$RHEOLA$$,#{state_id_vic},-36.298881,143.915229), - ($$3517$$,$$SALISBURY WEST$$,#{state_id_vic},-36.298881,143.915229), - ($$3517$$,$$SERPENTINE$$,#{state_id_vic},-36.298881,143.915229), - ($$3518$$,$$BERRIMAL$$,#{state_id_vic},-36.501479,143.456459), - ($$3518$$,$$BORUNG$$,#{state_id_vic},-36.501479,143.456459), - ($$3518$$,$$FENTONS CREEK$$,#{state_id_vic},-36.501479,143.456459), - ($$3518$$,$$FERNIHURST$$,#{state_id_vic},-36.501479,143.456459), - ($$3518$$,$$FIERY FLAT$$,#{state_id_vic},-36.501479,143.456459), - ($$3518$$,$$KURRACA$$,#{state_id_vic},-36.501479,143.456459), - ($$3518$$,$$KURRACA WEST$$,#{state_id_vic},-36.501479,143.456459), - ($$3518$$,$$MYSIA$$,#{state_id_vic},-36.501479,143.456459), - ($$3518$$,$$NINE MILE$$,#{state_id_vic},-36.501479,143.456459), - ($$3518$$,$$RICHMOND PLAINS$$,#{state_id_vic},-36.501479,143.456459), - ($$3518$$,$$SKINNERS FLAT$$,#{state_id_vic},-36.501479,143.456459), - ($$3518$$,$$WEDDERBURN$$,#{state_id_vic},-36.501479,143.456459), - ($$3518$$,$$WEDDERBURN JUNCTION$$,#{state_id_vic},-36.501479,143.456459), - ($$3518$$,$$WEHLA$$,#{state_id_vic},-36.501479,143.456459), - ($$3518$$,$$WOOLSHED FLAT$$,#{state_id_vic},-36.501479,143.456459), - ($$3518$$,$$WOOSANG$$,#{state_id_vic},-36.501479,143.456459), - ($$3520$$,$$KINYPANIAL$$,#{state_id_vic},-36.331469,143.83557), - ($$3520$$,$$KORONG VALE$$,#{state_id_vic},-36.331469,143.83557), - ($$3521$$,$$PYALONG$$,#{state_id_vic},-37.120609,144.882105), - ($$3522$$,$$GLENHOPE EAST$$,#{state_id_vic},-37.126178,144.750364), - ($$3522$$,$$TOOBORAC$$,#{state_id_vic},-37.126178,144.750364), - ($$3523$$,$$ARGYLE$$,#{state_id_vic},-36.944132,144.732986), - ($$3523$$,$$COSTERFIELD$$,#{state_id_vic},-36.944132,144.732986), - ($$3523$$,$$DERRINAL$$,#{state_id_vic},-36.944132,144.732986), - ($$3523$$,$$HEATHCOTE$$,#{state_id_vic},-36.944132,144.732986), - ($$3523$$,$$HEATHCOTE SOUTH$$,#{state_id_vic},-36.944132,144.732986), - ($$3523$$,$$KNOWSLEY$$,#{state_id_vic},-36.944132,144.732986), - ($$3523$$,$$LADYS PASS$$,#{state_id_vic},-36.944132,144.732986), - ($$3523$$,$$MOORMBOOL WEST$$,#{state_id_vic},-36.944132,144.732986), - ($$3523$$,$$MOUNT CAMEL$$,#{state_id_vic},-36.944132,144.732986), - ($$3523$$,$$REDCASTLE$$,#{state_id_vic},-36.944132,144.732986), - ($$3525$$,$$BARRAKEE$$,#{state_id_vic},-36.268867,143.426085), - ($$3525$$,$$BUCKRABANYULE$$,#{state_id_vic},-36.268867,143.426085), - ($$3525$$,$$CHARLTON$$,#{state_id_vic},-36.268867,143.426085), - ($$3525$$,$$CHIRRIP$$,#{state_id_vic},-36.268867,143.426085), - ($$3525$$,$$GRANITE FLAT$$,#{state_id_vic},-36.268867,143.426085), - ($$3525$$,$$LAKE MARMAL$$,#{state_id_vic},-36.268867,143.426085), - ($$3525$$,$$NAREEWILLOCK$$,#{state_id_vic},-36.268867,143.426085), - ($$3525$$,$$TERRAPPEE$$,#{state_id_vic},-36.268867,143.426085), - ($$3525$$,$$WOOROONOOK$$,#{state_id_vic},-36.268867,143.426085), - ($$3525$$,$$WYCHITELLA$$,#{state_id_vic},-36.268867,143.426085), - ($$3525$$,$$WYCHITELLA NORTH$$,#{state_id_vic},-36.268867,143.426085), - ($$3525$$,$$YEUNGROON$$,#{state_id_vic},-36.268867,143.426085), - ($$3525$$,$$YEUNGROON EAST$$,#{state_id_vic},-36.268867,143.426085), - ($$3527$$,$$BUNGULUKE$$,#{state_id_vic},-36.055958,143.38661), - ($$3527$$,$$DUMOSA$$,#{state_id_vic},-36.055958,143.38661), - ($$3527$$,$$GLENLOTH$$,#{state_id_vic},-36.055958,143.38661), - ($$3527$$,$$GLENLOTH EAST$$,#{state_id_vic},-36.055958,143.38661), - ($$3527$$,$$JERUK$$,#{state_id_vic},-36.055958,143.38661), - ($$3527$$,$$NINYEUNOOK$$,#{state_id_vic},-36.055958,143.38661), - ($$3527$$,$$TEDDYWADDY$$,#{state_id_vic},-36.055958,143.38661), - ($$3527$$,$$TEDDYWADDY WEST$$,#{state_id_vic},-36.055958,143.38661), - ($$3527$$,$$THALIA$$,#{state_id_vic},-36.055958,143.38661), - ($$3527$$,$$TOWANINNY$$,#{state_id_vic},-36.055958,143.38661), - ($$3527$$,$$TOWANINNY SOUTH$$,#{state_id_vic},-36.055958,143.38661), - ($$3527$$,$$WYCHEPROOF$$,#{state_id_vic},-36.055958,143.38661), - ($$3527$$,$$WYCHEPROOF SOUTH$$,#{state_id_vic},-36.055958,143.38661), - ($$3529$$,$$KALPIENUNG$$,#{state_id_vic},-35.780006,143.259314), - ($$3529$$,$$NULLAWIL$$,#{state_id_vic},-35.780006,143.259314), - ($$3530$$,$$CULGOA$$,#{state_id_vic},-35.718468,143.107379), - ($$3530$$,$$SUTTON$$,#{state_id_vic},-35.718468,143.107379), - ($$3530$$,$$WANGIE$$,#{state_id_vic},-35.718468,143.107379), - ($$3530$$,$$WARNE$$,#{state_id_vic},-35.718468,143.107379), - ($$3531$$,$$BERRIWILLOCK$$,#{state_id_vic},-35.635336,142.991804), - ($$3531$$,$$BOIGBEAT$$,#{state_id_vic},-35.635336,142.991804), - ($$3533$$,$$BIMBOURIE$$,#{state_id_vic},-35.360723,142.786824), - ($$3533$$,$$LAKE TYRRELL$$,#{state_id_vic},-35.360723,142.786824), - ($$3533$$,$$MYALL$$,#{state_id_vic},-35.360723,142.786824), - ($$3533$$,$$NANDALY$$,#{state_id_vic},-35.360723,142.786824), - ($$3533$$,$$NINDA$$,#{state_id_vic},-35.360723,142.786824), - ($$3533$$,$$NYARRIN$$,#{state_id_vic},-35.360723,142.786824), - ($$3533$$,$$PIER MILAN$$,#{state_id_vic},-35.360723,142.786824), - ($$3533$$,$$SEA LAKE$$,#{state_id_vic},-35.360723,142.786824), - ($$3533$$,$$STRATEN$$,#{state_id_vic},-35.360723,142.786824), - ($$3533$$,$$TYENNA$$,#{state_id_vic},-35.360723,142.786824), - ($$3533$$,$$TYRRELL$$,#{state_id_vic},-35.360723,142.786824), - ($$3533$$,$$TYRRELL DOWNS$$,#{state_id_vic},-35.360723,142.786824), - ($$3537$$,$$BARRAPORT$$,#{state_id_vic},-36.011488,143.671965), - ($$3537$$,$$BARRAPORT WEST$$,#{state_id_vic},-36.011488,143.671965), - ($$3537$$,$$BOORT$$,#{state_id_vic},-36.011488,143.671965), - ($$3537$$,$$CANARY ISLAND$$,#{state_id_vic},-36.011488,143.671965), - ($$3537$$,$$CATUMNAL$$,#{state_id_vic},-36.011488,143.671965), - ($$3537$$,$$GREDGWIN$$,#{state_id_vic},-36.011488,143.671965), - ($$3537$$,$$LEAGHUR$$,#{state_id_vic},-36.011488,143.671965), - ($$3537$$,$$MINMINDIE$$,#{state_id_vic},-36.011488,143.671965), - ($$3537$$,$$YANDO$$,#{state_id_vic},-36.011488,143.671965), - ($$3540$$,$$CANNIE$$,#{state_id_vic},-35.75855,143.445376), - ($$3540$$,$$OAKVALE$$,#{state_id_vic},-35.75855,143.445376), - ($$3540$$,$$QUAMBATOOK$$,#{state_id_vic},-35.75855,143.445376), - ($$3542$$,$$COKUM$$,#{state_id_vic},-35.758096,143.284751), - ($$3542$$,$$LALBERT$$,#{state_id_vic},-35.758096,143.284751), - ($$3542$$,$$TITTYBONG$$,#{state_id_vic},-35.758096,143.284751), - ($$3544$$,$$CHINANGIN$$,#{state_id_vic},-35.513341,143.197025), - ($$3544$$,$$GOWANFORD$$,#{state_id_vic},-35.513341,143.197025), - ($$3544$$,$$MURNUNGIN$$,#{state_id_vic},-35.513341,143.197025), - ($$3544$$,$$SPRINGFIELD$$,#{state_id_vic},-35.513341,143.197025), - ($$3544$$,$$ULTIMA$$,#{state_id_vic},-35.513341,143.197025), - ($$3544$$,$$ULTIMA EAST$$,#{state_id_vic},-35.513341,143.197025), - ($$3544$$,$$WAITCHIE$$,#{state_id_vic},-35.513341,143.197025), - ($$3546$$,$$BOLTON$$,#{state_id_vic},-34.96464,142.884361), - ($$3546$$,$$CHINKAPOOK$$,#{state_id_vic},-34.96464,142.884361), - ($$3546$$,$$COCAMBA$$,#{state_id_vic},-34.96464,142.884361), - ($$3546$$,$$GERAHMIN$$,#{state_id_vic},-34.96464,142.884361), - ($$3546$$,$$MANANGATANG$$,#{state_id_vic},-34.96464,142.884361), - ($$3546$$,$$TUROAR$$,#{state_id_vic},-34.96464,142.884361), - ($$3546$$,$$WINNAMBOOL$$,#{state_id_vic},-34.96464,142.884361), - ($$3549$$,$$ANNUELLO$$,#{state_id_vic},-34.847699,142.830117), - ($$3549$$,$$BANNERTON$$,#{state_id_vic},-34.847699,142.830117), - ($$3549$$,$$HAPPY VALLEY$$,#{state_id_vic},-34.847699,142.830117), - ($$3549$$,$$LIPAROO$$,#{state_id_vic},-34.847699,142.830117), - ($$3549$$,$$ROBINVALE$$,#{state_id_vic},-34.847699,142.830117), - ($$3549$$,$$ROBINVALE IRRIGATION DISTRICT SECTION B$$,#{state_id_vic},-34.847699,142.830117), - ($$3549$$,$$ROBINVALE IRRIGATION DISTRICT SECTION C$$,#{state_id_vic},-34.847699,142.830117), - ($$3549$$,$$ROBINVALE IRRIGATION DISTRICT SECTION D$$,#{state_id_vic},-34.847699,142.830117), - ($$3549$$,$$ROBINVALE IRRIGATION DISTRICT SECTION E$$,#{state_id_vic},-34.847699,142.830117), - ($$3549$$,$$TOL TOL$$,#{state_id_vic},-34.847699,142.830117), - ($$3549$$,$$WANDOWN$$,#{state_id_vic},-34.847699,142.830117), - ($$3549$$,$$WEMEN$$,#{state_id_vic},-34.847699,142.830117), - ($$3550$$,$$BENDIGO$$,#{state_id_vic},-36.758492,144.280075), - ($$3550$$,$$BENDIGO SOUTH$$,#{state_id_vic},-36.758492,144.280075), - ($$3550$$,$$EAST BENDIGO$$,#{state_id_vic},-36.758492,144.280075), - ($$3550$$,$$FLORA HILL$$,#{state_id_vic},-36.758492,144.280075), - ($$3550$$,$$IRONBARK$$,#{state_id_vic},-36.758492,144.280075), - ($$3550$$,$$KENNINGTON$$,#{state_id_vic},-36.758492,144.280075), - ($$3550$$,$$LONG GULLY$$,#{state_id_vic},-36.758492,144.280075), - ($$3550$$,$$NORTH BENDIGO$$,#{state_id_vic},-36.758492,144.280075), - ($$3550$$,$$QUARRY HILL$$,#{state_id_vic},-36.758492,144.280075), - ($$3550$$,$$SANDHURST EAST$$,#{state_id_vic},-36.758492,144.280075), - ($$3550$$,$$SPRING GULLY$$,#{state_id_vic},-36.758492,144.280075), - ($$3550$$,$$STRATHDALE$$,#{state_id_vic},-36.758492,144.280075), - ($$3550$$,$$WEST BENDIGO$$,#{state_id_vic},-36.758492,144.280075), - ($$3550$$,$$WHITE HILLS$$,#{state_id_vic},-36.758492,144.280075), - ($$3551$$,$$ARNOLD$$,#{state_id_vic},-36.690822,143.868371), - ($$3551$$,$$ARNOLD WEST$$,#{state_id_vic},-36.690822,143.868371), - ($$3551$$,$$ASCOT$$,#{state_id_vic},-36.690822,143.868371), - ($$3551$$,$$AXE CREEK$$,#{state_id_vic},-36.690822,143.868371), - ($$3551$$,$$AXEDALE$$,#{state_id_vic},-36.690822,143.868371), - ($$3551$$,$$BAGSHOT$$,#{state_id_vic},-36.690822,143.868371), - ($$3551$$,$$BAGSHOT NORTH$$,#{state_id_vic},-36.690822,143.868371), - ($$3551$$,$$BENDIGO FORWARD$$,#{state_id_vic},-36.690822,143.868371), - ($$3551$$,$$CORNELLA$$,#{state_id_vic},-36.690822,143.868371), - ($$3551$$,$$EMU CREEK$$,#{state_id_vic},-36.690822,143.868371), - ($$3551$$,$$EPPALOCK$$,#{state_id_vic},-36.690822,143.868371), - ($$3551$$,$$EPSOM$$,#{state_id_vic},-36.690822,143.868371), - ($$3551$$,$$HUNTLY$$,#{state_id_vic},-36.690822,143.868371), - ($$3551$$,$$HUNTLY NORTH$$,#{state_id_vic},-36.690822,143.868371), - ($$3551$$,$$JUNORTOUN$$,#{state_id_vic},-36.690822,143.868371), - ($$3551$$,$$KIMBOLTON$$,#{state_id_vic},-36.690822,143.868371), - ($$3551$$,$$LAKE EPPALOCK$$,#{state_id_vic},-36.690822,143.868371), - ($$3551$$,$$LLANELLY$$,#{state_id_vic},-36.690822,143.868371), - ($$3551$$,$$LOCKWOOD$$,#{state_id_vic},-36.690822,143.868371), - ($$3551$$,$$LOCKWOOD SOUTH$$,#{state_id_vic},-36.690822,143.868371), - ($$3551$$,$$LONGLEA$$,#{state_id_vic},-36.690822,143.868371), - ($$3551$$,$$MAIDEN GULLY$$,#{state_id_vic},-36.690822,143.868371), - ($$3551$$,$$MANDURANG$$,#{state_id_vic},-36.690822,143.868371), - ($$3551$$,$$MANDURANG SOUTH$$,#{state_id_vic},-36.690822,143.868371), - ($$3551$$,$$MINTO$$,#{state_id_vic},-36.690822,143.868371), - ($$3551$$,$$MURPHYS CREEK$$,#{state_id_vic},-36.690822,143.868371), - ($$3551$$,$$MYOLA$$,#{state_id_vic},-36.690822,143.868371), - ($$3551$$,$$NEWBRIDGE$$,#{state_id_vic},-36.690822,143.868371), - ($$3551$$,$$PAINSWICK$$,#{state_id_vic},-36.690822,143.868371), - ($$3551$$,$$SEDGWICK$$,#{state_id_vic},-36.690822,143.868371), - ($$3551$$,$$STRATHFIELDSAYE$$,#{state_id_vic},-36.690822,143.868371), - ($$3551$$,$$TARNAGULLA$$,#{state_id_vic},-36.690822,143.868371), - ($$3551$$,$$TOOLLEEN$$,#{state_id_vic},-36.690822,143.868371), - ($$3551$$,$$WAANYARRA$$,#{state_id_vic},-36.690822,143.868371), - ($$3551$$,$$WELLSFORD$$,#{state_id_vic},-36.690822,143.868371), - ($$3551$$,$$WOODSTOCK ON LODDON$$,#{state_id_vic},-36.690822,143.868371), - ($$3552$$,$$BENDIGO$$,#{state_id_vic},-37.825288,145.011924), - ($$3554$$,$$BENDIGO DC$$,#{state_id_vic},0.0,0.0), - ($$3555$$,$$BIG HILL$$,#{state_id_vic},-36.834685,144.230004), - ($$3555$$,$$GOLDEN GULLY$$,#{state_id_vic},-36.834685,144.230004), - ($$3555$$,$$GOLDEN SQUARE$$,#{state_id_vic},-36.834685,144.230004), - ($$3555$$,$$KANGAROO FLAT$$,#{state_id_vic},-36.834685,144.230004), - ($$3555$$,$$LANSELL PLAZA$$,#{state_id_vic},-36.834685,144.230004), - ($$3556$$,$$CALIFORNIA GULLY$$,#{state_id_vic},-36.732656,144.257173), - ($$3556$$,$$CAMPBELLS FOREST$$,#{state_id_vic},-36.732656,144.257173), - ($$3556$$,$$EAGLEHAWK$$,#{state_id_vic},-36.732656,144.257173), - ($$3556$$,$$EAGLEHAWK NORTH$$,#{state_id_vic},-36.732656,144.257173), - ($$3556$$,$$JACKASS FLAT$$,#{state_id_vic},-36.732656,144.257173), - ($$3556$$,$$MYERS FLAT$$,#{state_id_vic},-36.732656,144.257173), - ($$3556$$,$$SAILORS GULLY$$,#{state_id_vic},-36.732656,144.257173), - ($$3556$$,$$SEBASTIAN$$,#{state_id_vic},-36.732656,144.257173), - ($$3556$$,$$WHIPSTICK$$,#{state_id_vic},-36.732656,144.257173), - ($$3556$$,$$WOODVALE$$,#{state_id_vic},-36.732656,144.257173), - ($$3557$$,$$BARNADOWN$$,#{state_id_vic},-36.653535,144.506417), - ($$3557$$,$$FOSTERVILLE$$,#{state_id_vic},-36.653535,144.506417), - ($$3557$$,$$GOORNONG$$,#{state_id_vic},-36.653535,144.506417), - ($$3557$$,$$MUSKERRY$$,#{state_id_vic},-36.653535,144.506417), - ($$3558$$,$$BURNEWANG$$,#{state_id_vic},-36.485796,144.713905), - ($$3558$$,$$COROP WEST$$,#{state_id_vic},-36.485796,144.713905), - ($$3558$$,$$ELMORE$$,#{state_id_vic},-36.485796,144.713905), - ($$3558$$,$$HUNTER$$,#{state_id_vic},-36.485796,144.713905), - ($$3558$$,$$RUNNYMEDE$$,#{state_id_vic},-36.485796,144.713905), - ($$3559$$,$$AVONMORE$$,#{state_id_vic},-36.529882,144.604459), - ($$3559$$,$$BURRAMBOOT$$,#{state_id_vic},-36.529882,144.604459), - ($$3559$$,$$COLBINABBIN$$,#{state_id_vic},-36.529882,144.604459), - ($$3559$$,$$COROP$$,#{state_id_vic},-36.529882,144.604459), - ($$3559$$,$$GOBARUP$$,#{state_id_vic},-36.529882,144.604459), - ($$3561$$,$$BALLENDELLA$$,#{state_id_vic},-36.298882,144.673529), - ($$3561$$,$$BAMAWM$$,#{state_id_vic},-36.298882,144.673529), - ($$3561$$,$$BAMAWM EXTENSION$$,#{state_id_vic},-36.298882,144.673529), - ($$3561$$,$$BONN$$,#{state_id_vic},-36.298882,144.673529), - ($$3561$$,$$DIGGORA$$,#{state_id_vic},-36.298882,144.673529), - ($$3561$$,$$FAIRY DELL$$,#{state_id_vic},-36.298882,144.673529), - ($$3561$$,$$NANNEELLA$$,#{state_id_vic},-36.298882,144.673529), - ($$3561$$,$$ROCHESTER$$,#{state_id_vic},-36.298882,144.673529), - ($$3561$$,$$TIMMERING$$,#{state_id_vic},-36.298882,144.673529), - ($$3562$$,$$TORRUMBARRY$$,#{state_id_vic},-35.975398,144.459855), - ($$3563$$,$$LOCKINGTON$$,#{state_id_vic},-36.271227,144.481629), - ($$3564$$,$$ECHUCA$$,#{state_id_vic},-36.131154,144.728548), - ($$3564$$,$$ECHUCA SOUTH$$,#{state_id_vic},-36.131154,144.728548), - ($$3564$$,$$ECHUCA VILLAGE$$,#{state_id_vic},-36.131154,144.728548), - ($$3564$$,$$ECHUCA WEST$$,#{state_id_vic},-36.131154,144.728548), - ($$3564$$,$$KANYAPELLA$$,#{state_id_vic},-36.131154,144.728548), - ($$3564$$,$$PATHO$$,#{state_id_vic},-36.131154,144.728548), - ($$3564$$,$$ROSLYNMEAD$$,#{state_id_vic},-36.131154,144.728548), - ($$3564$$,$$WHARPARILLA$$,#{state_id_vic},-36.131154,144.728548), - ($$3565$$,$$KOTTA$$,#{state_id_vic},-36.192354,144.526275), - ($$3566$$,$$GUNBOWER$$,#{state_id_vic},-35.957441,144.366659), - ($$3567$$,$$HORFIELD$$,#{state_id_vic},-35.886223,144.242273), - ($$3567$$,$$LEITCHVILLE$$,#{state_id_vic},-35.886223,144.242273), - ($$3568$$,$$BURKES BRIDGE$$,#{state_id_vic},-35.836345,144.336635), - ($$3568$$,$$COHUNA$$,#{state_id_vic},-35.836345,144.336635), - ($$3568$$,$$CULLEN$$,#{state_id_vic},-35.836345,144.336635), - ($$3568$$,$$DALTONS BRIDGE$$,#{state_id_vic},-35.836345,144.336635), - ($$3568$$,$$GANNAWARRA$$,#{state_id_vic},-35.836345,144.336635), - ($$3568$$,$$KEELY$$,#{state_id_vic},-35.836345,144.336635), - ($$3568$$,$$MACORNA NORTH$$,#{state_id_vic},-35.836345,144.336635), - ($$3568$$,$$MCMILLANS$$,#{state_id_vic},-35.836345,144.336635), - ($$3568$$,$$MEAD$$,#{state_id_vic},-35.836345,144.336635), - ($$3568$$,$$MINCHA WEST$$,#{state_id_vic},-35.836345,144.336635), - ($$3568$$,$$WEE WEE RUP$$,#{state_id_vic},-35.836345,144.336635), - ($$3570$$,$$AUCHMORE$$,#{state_id_vic},-36.474005,144.1055), - ($$3570$$,$$DRUMMARTIN$$,#{state_id_vic},-36.474005,144.1055), - ($$3570$$,$$KAMAROOKA$$,#{state_id_vic},-36.474005,144.1055), - ($$3570$$,$$NEILBOROUGH$$,#{state_id_vic},-36.474005,144.1055), - ($$3570$$,$$RAYWOOD$$,#{state_id_vic},-36.474005,144.1055), - ($$3571$$,$$DINGEE$$,#{state_id_vic},-36.370912,144.231949), - ($$3571$$,$$KAMAROOKA NORTH$$,#{state_id_vic},-36.370912,144.231949), - ($$3571$$,$$POMPAPIEL$$,#{state_id_vic},-36.370912,144.231949), - ($$3571$$,$$TANDARRA$$,#{state_id_vic},-36.370912,144.231949), - ($$3572$$,$$MILLOO$$,#{state_id_vic},-36.356189,144.381891), - ($$3572$$,$$PIAVELLA$$,#{state_id_vic},-36.356189,144.381891), - ($$3572$$,$$PRAIRIE$$,#{state_id_vic},-36.356189,144.381891), - ($$3572$$,$$TENNYSON$$,#{state_id_vic},-36.356189,144.381891), - ($$3573$$,$$CALIVIL$$,#{state_id_vic},-36.298763,144.085645), - ($$3573$$,$$MITIAMO$$,#{state_id_vic},-36.298763,144.085645), - ($$3573$$,$$PINE GROVE$$,#{state_id_vic},-36.298763,144.085645), - ($$3573$$,$$TERRICK TERRICK EAST$$,#{state_id_vic},-36.298763,144.085645), - ($$3575$$,$$GLADFIELD$$,#{state_id_vic},-36.042544,143.990513), - ($$3575$$,$$JUNGABURRA$$,#{state_id_vic},-36.042544,143.990513), - ($$3575$$,$$LODDON VALE$$,#{state_id_vic},-36.042544,143.990513), - ($$3575$$,$$MINCHA$$,#{state_id_vic},-36.042544,143.990513), - ($$3575$$,$$MOLOGA$$,#{state_id_vic},-36.042544,143.990513), - ($$3575$$,$$PYRAMID HILL$$,#{state_id_vic},-36.042544,143.990513), - ($$3575$$,$$SYLVATERRE$$,#{state_id_vic},-36.042544,143.990513), - ($$3575$$,$$TERRICK TERRICK$$,#{state_id_vic},-36.042544,143.990513), - ($$3575$$,$$YARRAWALLA$$,#{state_id_vic},-36.042544,143.990513), - ($$3576$$,$$DURHAM OX$$,#{state_id_vic},-36.198759,143.956055), - ($$3579$$,$$APPIN$$,#{state_id_vic},-35.878433,143.872782), - ($$3579$$,$$APPIN SOUTH$$,#{state_id_vic},-35.878433,143.872782), - ($$3579$$,$$BAEL BAEL$$,#{state_id_vic},-35.878433,143.872782), - ($$3579$$,$$BEAUCHAMP$$,#{state_id_vic},-35.878433,143.872782), - ($$3579$$,$$BENJEROOP$$,#{state_id_vic},-35.878433,143.872782), - ($$3579$$,$$BUDGERUM EAST$$,#{state_id_vic},-35.878433,143.872782), - ($$3579$$,$$CAPELS CROSSING$$,#{state_id_vic},-35.878433,143.872782), - ($$3579$$,$$DINGWALL$$,#{state_id_vic},-35.878433,143.872782), - ($$3579$$,$$FAIRLEY$$,#{state_id_vic},-35.878433,143.872782), - ($$3579$$,$$GONN CROSSING$$,#{state_id_vic},-35.878433,143.872782), - ($$3579$$,$$KERANG$$,#{state_id_vic},-35.878433,143.872782), - ($$3579$$,$$KERANG EAST$$,#{state_id_vic},-35.878433,143.872782), - ($$3579$$,$$KOROOP$$,#{state_id_vic},-35.878433,143.872782), - ($$3579$$,$$LAKE MERAN$$,#{state_id_vic},-35.878433,143.872782), - ($$3579$$,$$MACORNA$$,#{state_id_vic},-35.878433,143.872782), - ($$3579$$,$$MEERING WEST$$,#{state_id_vic},-35.878433,143.872782), - ($$3579$$,$$MILNES BRIDGE$$,#{state_id_vic},-35.878433,143.872782), - ($$3579$$,$$MURRABIT$$,#{state_id_vic},-35.878433,143.872782), - ($$3579$$,$$MURRABIT WEST$$,#{state_id_vic},-35.878433,143.872782), - ($$3579$$,$$MYALL$$,#{state_id_vic},-35.878433,143.872782), - ($$3579$$,$$MYSTIC PARK$$,#{state_id_vic},-35.878433,143.872782), - ($$3579$$,$$NORMANVILLE$$,#{state_id_vic},-35.878433,143.872782), - ($$3579$$,$$PINE VIEW$$,#{state_id_vic},-35.878433,143.872782), - ($$3579$$,$$REEDY LAKE$$,#{state_id_vic},-35.878433,143.872782), - ($$3579$$,$$SANDHILL LAKE$$,#{state_id_vic},-35.878433,143.872782), - ($$3579$$,$$TEAL POINT$$,#{state_id_vic},-35.878433,143.872782), - ($$3579$$,$$TRAGOWEL$$,#{state_id_vic},-35.878433,143.872782), - ($$3579$$,$$WANDELLA$$,#{state_id_vic},-35.878433,143.872782), - ($$3579$$,$$WESTBY$$,#{state_id_vic},-35.878433,143.872782), - ($$3580$$,$$KOONDROOK$$,#{state_id_vic},-35.64272,144.108584), - ($$3581$$,$$LAKE CHARM$$,#{state_id_vic},-35.620733,143.804041), - ($$3583$$,$$TRESCO$$,#{state_id_vic},-35.506577,143.638434), - ($$3584$$,$$LAKE BOGA$$,#{state_id_vic},-35.461804,143.634454), - ($$3584$$,$$TRESCO WEST$$,#{state_id_vic},-35.461804,143.634454), - ($$3585$$,$$CASTLE DONNINGTON$$,#{state_id_vic},-35.418038,143.594275), - ($$3585$$,$$CHILLINGOLLAH$$,#{state_id_vic},-35.418038,143.594275), - ($$3585$$,$$FISH POINT$$,#{state_id_vic},-35.418038,143.594275), - ($$3585$$,$$GOSCHEN$$,#{state_id_vic},-35.418038,143.594275), - ($$3585$$,$$KUNAT$$,#{state_id_vic},-35.418038,143.594275), - ($$3585$$,$$MEATIAN$$,#{state_id_vic},-35.418038,143.594275), - ($$3585$$,$$MURRAY DOWNS$$,#{state_id_nsw},-35.418038,143.594275), - ($$3585$$,$$NOWIE$$,#{state_id_vic},-35.418038,143.594275), - ($$3585$$,$$NYRRABY$$,#{state_id_vic},-35.418038,143.594275), - ($$3585$$,$$PIRA$$,#{state_id_vic},-35.418038,143.594275), - ($$3585$$,$$POLISBET$$,#{state_id_vic},-35.418038,143.594275), - ($$3585$$,$$SPEEWA$$,#{state_id_vic},-35.418038,143.594275), - ($$3585$$,$$SWAN HILL$$,#{state_id_vic},-35.418038,143.594275), - ($$3585$$,$$SWAN HILL PIONEER$$,#{state_id_vic},-35.418038,143.594275), - ($$3585$$,$$SWAN HILL WEST$$,#{state_id_vic},-35.418038,143.594275), - ($$3585$$,$$WINLATON$$,#{state_id_vic},-35.418038,143.594275), - ($$3586$$,$$BULGA$$,#{state_id_vic},-35.304516,143.359804), - ($$3586$$,$$MALLAN$$,#{state_id_nsw},-35.304516,143.359804), - ($$3586$$,$$MURRAWEE$$,#{state_id_vic},-35.304516,143.359804), - ($$3586$$,$$MURRAYDALE$$,#{state_id_vic},-35.304516,143.359804), - ($$3586$$,$$PENTAL ISLAND$$,#{state_id_vic},-35.304516,143.359804), - ($$3586$$,$$TYNTYNDER$$,#{state_id_vic},-35.304516,143.359804), - ($$3586$$,$$TYNTYNDER SOUTH$$,#{state_id_vic},-35.304516,143.359804), - ($$3588$$,$$WOORINEN SOUTH$$,#{state_id_vic},-35.270527,143.454012), - ($$3589$$,$$WOORINEN$$,#{state_id_vic},-35.264373,143.471892), - ($$3589$$,$$WOORINEN NORTH$$,#{state_id_vic},-35.264373,143.471892), - ($$3590$$,$$BEVERFORD$$,#{state_id_vic},-35.235882,143.480953), - ($$3591$$,$$VINIFERA$$,#{state_id_vic},-35.217734,143.405908), - ($$3594$$,$$NYAH$$,#{state_id_vic},-35.152584,143.362249), - ($$3595$$,$$NYAH WEST$$,#{state_id_vic},-35.187893,143.348862), - ($$3596$$,$$MIRALIE$$,#{state_id_vic},-35.123806,143.327682), - ($$3596$$,$$TOWAN$$,#{state_id_vic},-35.123806,143.327682), - ($$3596$$,$$WOOD WOOD$$,#{state_id_vic},-35.123806,143.327682), - ($$3597$$,$$KENLEY$$,#{state_id_vic},-34.856397,143.341123), - ($$3597$$,$$KOOLOONONG$$,#{state_id_vic},-34.856397,143.341123), - ($$3597$$,$$LAKE POWELL$$,#{state_id_vic},-34.856397,143.341123), - ($$3597$$,$$NARRUNG$$,#{state_id_vic},-34.856397,143.341123), - ($$3597$$,$$NATYA$$,#{state_id_vic},-34.856397,143.341123), - ($$3597$$,$$PIANGIL$$,#{state_id_vic},-34.856397,143.341123), - ($$3599$$,$$BOUNDARY BEND$$,#{state_id_vic},-34.742098,143.144838), - ($$3607$$,$$TABILK$$,#{state_id_vic},-36.846437,145.199887), - ($$3608$$,$$BAILIESTON$$,#{state_id_vic},-36.699872,145.034359), - ($$3608$$,$$GOULBURN WEIR$$,#{state_id_vic},-36.699872,145.034359), - ($$3608$$,$$GRAYTOWN$$,#{state_id_vic},-36.699872,145.034359), - ($$3608$$,$$KIRWANS BRIDGE$$,#{state_id_vic},-36.699872,145.034359), - ($$3608$$,$$MITCHELLSTOWN$$,#{state_id_vic},-36.699872,145.034359), - ($$3608$$,$$NAGAMBIE$$,#{state_id_vic},-36.699872,145.034359), - ($$3608$$,$$WAHRING$$,#{state_id_vic},-36.699872,145.034359), - ($$3608$$,$$WIRRATE$$,#{state_id_vic},-36.699872,145.034359), - ($$3610$$,$$DHURRINGILE$$,#{state_id_vic},-36.542846,145.277533), - ($$3610$$,$$MOORILIM$$,#{state_id_vic},-36.542846,145.277533), - ($$3610$$,$$MURCHISON$$,#{state_id_vic},-36.542846,145.277533), - ($$3610$$,$$MURCHISON EAST$$,#{state_id_vic},-36.542846,145.277533), - ($$3610$$,$$MURCHISON NORTH$$,#{state_id_vic},-36.542846,145.277533), - ($$3612$$,$$MOORA$$,#{state_id_vic},-36.591412,144.948216), - ($$3612$$,$$RUSHWORTH$$,#{state_id_vic},-36.591412,144.948216), - ($$3612$$,$$WANALTA$$,#{state_id_vic},-36.591412,144.948216), - ($$3612$$,$$WARANGA SHORES$$,#{state_id_vic},-36.591412,144.948216), - ($$3612$$,$$WHROO$$,#{state_id_vic},-36.591412,144.948216), - ($$3614$$,$$TOOLAMBA$$,#{state_id_vic},-36.484229,145.259357), - ($$3614$$,$$TOOLAMBA WEST$$,#{state_id_vic},-36.484229,145.259357), - ($$3616$$,$$COOMA$$,#{state_id_vic},-36.421146,145.068244), - ($$3616$$,$$GILLIESTON$$,#{state_id_vic},-36.421146,145.068244), - ($$3616$$,$$GIRGARRE EAST$$,#{state_id_vic},-36.421146,145.068244), - ($$3616$$,$$HARSTON$$,#{state_id_vic},-36.421146,145.068244), - ($$3616$$,$$MOOROOPNA NORTH WEST$$,#{state_id_vic},-36.421146,145.068244), - ($$3616$$,$$TATURA$$,#{state_id_vic},-36.421146,145.068244), - ($$3616$$,$$TATURA EAST$$,#{state_id_vic},-36.421146,145.068244), - ($$3616$$,$$WARANGA$$,#{state_id_vic},-36.421146,145.068244), - ($$3617$$,$$BYRNESIDE$$,#{state_id_vic},-36.416975,145.149914), - ($$3618$$,$$MERRIGUM$$,#{state_id_vic},-36.372412,145.170237), - ($$3619$$,$$KYABRAM$$,#{state_id_vic},-37.659566,144.936295), - ($$3620$$,$$KYABRAM$$,#{state_id_vic},-36.308063,145.049552), - ($$3620$$,$$KYABRAM SOUTH$$,#{state_id_vic},-36.308063,145.049552), - ($$3620$$,$$LANCASTER$$,#{state_id_vic},-36.308063,145.049552), - ($$3620$$,$$ST GERMAINS$$,#{state_id_vic},-36.308063,145.049552), - ($$3620$$,$$TARIPTA$$,#{state_id_vic},-36.308063,145.049552), - ($$3620$$,$$WYUNA$$,#{state_id_vic},-36.308063,145.049552), - ($$3620$$,$$WYUNA EAST$$,#{state_id_vic},-36.308063,145.049552), - ($$3621$$,$$KYVALLEY$$,#{state_id_vic},-36.269418,144.850248), - ($$3621$$,$$TONGALA$$,#{state_id_vic},-36.269418,144.850248), - ($$3621$$,$$YAMBUNA$$,#{state_id_vic},-36.269418,144.850248), - ($$3622$$,$$KOYUGA$$,#{state_id_vic},-36.161595,144.765651), - ($$3622$$,$$STRATHALLAN$$,#{state_id_vic},-36.161595,144.765651), - ($$3623$$,$$CARAG CARAG$$,#{state_id_vic},-36.462334,144.904277), - ($$3623$$,$$STANHOPE$$,#{state_id_vic},-36.462334,144.904277), - ($$3623$$,$$STANHOPE SOUTH$$,#{state_id_vic},-36.462334,144.904277), - ($$3624$$,$$GIRGARRE$$,#{state_id_vic},-36.443573,145.068304), - ($$3629$$,$$ARDMONA$$,#{state_id_vic},-36.378536,145.295366), - ($$3629$$,$$COOMBOONA$$,#{state_id_vic},-36.378536,145.295366), - ($$3629$$,$$MOOROOPNA$$,#{state_id_vic},-36.378536,145.295366), - ($$3629$$,$$MOOROOPNA NORTH$$,#{state_id_vic},-36.378536,145.295366), - ($$3629$$,$$UNDERA$$,#{state_id_vic},-36.378536,145.295366), - ($$3630$$,$$BRANDITT$$,#{state_id_vic},-36.360193,145.401927), - ($$3630$$,$$CANIAMBO$$,#{state_id_vic},-36.360193,145.401927), - ($$3630$$,$$COLLIVER$$,#{state_id_vic},-36.360193,145.401927), - ($$3630$$,$$DUNKIRK$$,#{state_id_vic},-36.360193,145.401927), - ($$3630$$,$$SHEPPARTON$$,#{state_id_vic},-36.360193,145.401927), - ($$3630$$,$$SHEPPARTON SOUTH$$,#{state_id_vic},-36.360193,145.401927), - ($$3631$$,$$ARCADIA$$,#{state_id_vic},-36.541604,145.362556), - ($$3631$$,$$ARCADIA SOUTH$$,#{state_id_vic},-36.541604,145.362556), - ($$3631$$,$$COSGROVE$$,#{state_id_vic},-36.541604,145.362556), - ($$3631$$,$$COSGROVE SOUTH$$,#{state_id_vic},-36.541604,145.362556), - ($$3631$$,$$GRAHAMVALE$$,#{state_id_vic},-36.541604,145.362556), - ($$3631$$,$$KARRAMOMUS$$,#{state_id_vic},-36.541604,145.362556), - ($$3631$$,$$KIALLA$$,#{state_id_vic},-36.541604,145.362556), - ($$3631$$,$$KIALLA EAST$$,#{state_id_vic},-36.541604,145.362556), - ($$3631$$,$$KIALLA WEST$$,#{state_id_vic},-36.541604,145.362556), - ($$3631$$,$$LEMNOS$$,#{state_id_vic},-36.541604,145.362556), - ($$3631$$,$$ORRVALE$$,#{state_id_vic},-36.541604,145.362556), - ($$3631$$,$$PINE LODGE$$,#{state_id_vic},-36.541604,145.362556), - ($$3631$$,$$SHEPPARTON EAST$$,#{state_id_vic},-36.541604,145.362556), - ($$3631$$,$$SHEPPARTON NORTH$$,#{state_id_vic},-36.541604,145.362556), - ($$3632$$,$$SHEPPARTON$$,#{state_id_vic},-36.544462,145.603242), - ($$3633$$,$$CONGUPNA$$,#{state_id_vic},-36.299252,145.481126), - ($$3634$$,$$BUNBARTHA$$,#{state_id_vic},-36.220101,145.344007), - ($$3634$$,$$KATANDRA$$,#{state_id_vic},-36.220101,145.344007), - ($$3634$$,$$KATANDRA WEST$$,#{state_id_vic},-36.220101,145.344007), - ($$3634$$,$$MARIONVALE$$,#{state_id_vic},-36.220101,145.344007), - ($$3634$$,$$MARUNGI$$,#{state_id_vic},-36.220101,145.344007), - ($$3634$$,$$TALLYGAROOPNA$$,#{state_id_vic},-36.220101,145.344007), - ($$3634$$,$$ZEERUST$$,#{state_id_vic},-36.220101,145.344007), - ($$3635$$,$$KAARIMBA$$,#{state_id_vic},-36.16323,145.301771), - ($$3635$$,$$MUNDOONA$$,#{state_id_vic},-36.16323,145.301771), - ($$3635$$,$$WUNGHNU$$,#{state_id_vic},-36.16323,145.301771), - ($$3636$$,$$DRUMANURE$$,#{state_id_vic},-36.135713,145.504192), - ($$3636$$,$$INVERGORDON$$,#{state_id_vic},-36.135713,145.504192), - ($$3636$$,$$NARING$$,#{state_id_vic},-36.135713,145.504192), - ($$3636$$,$$NUMURKAH$$,#{state_id_vic},-36.135713,145.504192), - ($$3637$$,$$WAAIA$$,#{state_id_vic},-36.05372,145.33176), - ($$3637$$,$$YALCA$$,#{state_id_vic},-36.05372,145.33176), - ($$3638$$,$$KOTUPNA$$,#{state_id_vic},-36.145467,145.165703), - ($$3638$$,$$NATHALIA$$,#{state_id_vic},-36.145467,145.165703), - ($$3638$$,$$YIELIMA$$,#{state_id_vic},-36.145467,145.165703), - ($$3639$$,$$BARMAH$$,#{state_id_vic},-36.023458,144.973324), - ($$3639$$,$$LOWER MOIRA$$,#{state_id_vic},-36.023458,144.973324), - ($$3639$$,$$PICOLA$$,#{state_id_vic},-36.023458,144.973324), - ($$3639$$,$$PICOLA WEST$$,#{state_id_vic},-36.023458,144.973324), - ($$3640$$,$$KATUNGA$$,#{state_id_vic},-35.973776,145.460399), - ($$3641$$,$$BEARII$$,#{state_id_vic},-35.918029,145.33172), - ($$3641$$,$$MYWEE$$,#{state_id_vic},-35.918029,145.33172), - ($$3641$$,$$STRATHMERTON$$,#{state_id_vic},-35.918029,145.33172), - ($$3641$$,$$ULUPNA$$,#{state_id_vic},-35.918029,145.33172), - ($$3643$$,$$COBRAM$$,#{state_id_vic},-35.955363,145.631874), - ($$3644$$,$$BAROOGA$$,#{state_id_nsw},-35.913566,145.688503), - ($$3644$$,$$COBRAM$$,#{state_id_vic},-35.913566,145.688503), - ($$3644$$,$$COBRAM EAST$$,#{state_id_vic},-35.913566,145.688503), - ($$3644$$,$$KOONOOMOO$$,#{state_id_vic},-35.913566,145.688503), - ($$3644$$,$$LALALTY$$,#{state_id_nsw},-35.913566,145.688503), - ($$3644$$,$$MUCKATAH$$,#{state_id_vic},-35.913566,145.688503), - ($$3644$$,$$YARROWEYAH$$,#{state_id_vic},-35.913566,145.688503), - ($$3646$$,$$DOOKIE$$,#{state_id_vic},-36.327503,145.686223), - ($$3646$$,$$MOUNT MAJOR$$,#{state_id_vic},-36.327503,145.686223), - ($$3646$$,$$NALINGA$$,#{state_id_vic},-36.327503,145.686223), - ($$3646$$,$$WAGGARANDALL$$,#{state_id_vic},-36.327503,145.686223), - ($$3646$$,$$YABBA NORTH$$,#{state_id_vic},-36.327503,145.686223), - ($$3646$$,$$YABBA SOUTH$$,#{state_id_vic},-36.327503,145.686223), - ($$3646$$,$$YOUANMITE$$,#{state_id_vic},-36.327503,145.686223), - ($$3647$$,$$DOOKIE COLLEGE$$,#{state_id_vic},-36.395102,145.703035), - ($$3649$$,$$KATAMATITE$$,#{state_id_vic},-36.078387,145.688645), - ($$3649$$,$$KATAMATITE EAST$$,#{state_id_vic},-36.078387,145.688645), - ($$3658$$,$$BROADFORD$$,#{state_id_vic},-37.203001,145.050171), - ($$3658$$,$$CLONBINANE$$,#{state_id_vic},-37.203001,145.050171), - ($$3658$$,$$FLOWERDALE$$,#{state_id_vic},-37.203001,145.050171), - ($$3658$$,$$HAZELDENE$$,#{state_id_vic},-37.203001,145.050171), - ($$3658$$,$$REEDY CREEK$$,#{state_id_vic},-37.203001,145.050171), - ($$3658$$,$$STRATH CREEK$$,#{state_id_vic},-37.203001,145.050171), - ($$3658$$,$$SUGARLOAF CREEK$$,#{state_id_vic},-37.203001,145.050171), - ($$3658$$,$$SUNDAY CREEK$$,#{state_id_vic},-37.203001,145.050171), - ($$3658$$,$$TYAAK$$,#{state_id_vic},-37.203001,145.050171), - ($$3658$$,$$WATERFORD PARK$$,#{state_id_vic},-37.203001,145.050171), - ($$3659$$,$$TALLAROOK$$,#{state_id_vic},-37.128108,145.024365), - ($$3660$$,$$CAVEAT$$,#{state_id_vic},-37.091133,145.495713), - ($$3660$$,$$DROPMORE$$,#{state_id_vic},-37.091133,145.495713), - ($$3660$$,$$HIGHLANDS$$,#{state_id_vic},-37.091133,145.495713), - ($$3660$$,$$HILLDENE$$,#{state_id_vic},-37.091133,145.495713), - ($$3660$$,$$KERRISDALE$$,#{state_id_vic},-37.091133,145.495713), - ($$3660$$,$$NORTHWOOD$$,#{state_id_vic},-37.091133,145.495713), - ($$3660$$,$$SEYMOUR$$,#{state_id_vic},-37.091133,145.495713), - ($$3660$$,$$SEYMOUR SOUTH$$,#{state_id_vic},-37.091133,145.495713), - ($$3660$$,$$TRAWOOL$$,#{state_id_vic},-37.091133,145.495713), - ($$3660$$,$$WHITEHEADS CREEK$$,#{state_id_vic},-37.091133,145.495713), - ($$3661$$,$$SEYMOUR$$,#{state_id_vic},-38.195993,146.535346), - ($$3662$$,$$PUCKAPUNYAL$$,#{state_id_vic},0.0,0.0), - ($$3662$$,$$PUCKAPUNYAL MILPO$$,#{state_id_vic},0.0,0.0), - ($$3663$$,$$MANGALORE$$,#{state_id_vic},-36.941616,145.161897), - ($$3664$$,$$AVENEL$$,#{state_id_vic},-36.892793,145.23059), - ($$3664$$,$$UPTON HILL$$,#{state_id_vic},-36.892793,145.23059), - ($$3665$$,$$LOCKSLEY$$,#{state_id_vic},-36.81576,145.35517), - ($$3665$$,$$LONGWOOD$$,#{state_id_vic},-36.81576,145.35517), - ($$3666$$,$$BALMATTUM$$,#{state_id_vic},-36.705961,145.640263), - ($$3666$$,$$CREIGHTONS CREEK$$,#{state_id_vic},-36.705961,145.640263), - ($$3666$$,$$EUROA$$,#{state_id_vic},-36.705961,145.640263), - ($$3666$$,$$GOORAM$$,#{state_id_vic},-36.705961,145.640263), - ($$3666$$,$$KELVIN VIEW$$,#{state_id_vic},-36.705961,145.640263), - ($$3666$$,$$KITHBROOK$$,#{state_id_vic},-36.705961,145.640263), - ($$3666$$,$$LONGWOOD EAST$$,#{state_id_vic},-36.705961,145.640263), - ($$3666$$,$$MIEPOLL$$,#{state_id_vic},-36.705961,145.640263), - ($$3666$$,$$MOGLONEMBY$$,#{state_id_vic},-36.705961,145.640263), - ($$3666$$,$$MOLKA$$,#{state_id_vic},-36.705961,145.640263), - ($$3666$$,$$PRANJIP$$,#{state_id_vic},-36.705961,145.640263), - ($$3666$$,$$RIGGS CREEK$$,#{state_id_vic},-36.705961,145.640263), - ($$3666$$,$$RUFFY$$,#{state_id_vic},-36.705961,145.640263), - ($$3666$$,$$SHEANS CREEK$$,#{state_id_vic},-36.705961,145.640263), - ($$3666$$,$$STRATHBOGIE$$,#{state_id_vic},-36.705961,145.640263), - ($$3666$$,$$TARCOMBE$$,#{state_id_vic},-36.705961,145.640263), - ($$3669$$,$$BOHO$$,#{state_id_vic},-36.696659,145.771354), - ($$3669$$,$$BOHO SOUTH$$,#{state_id_vic},-36.696659,145.771354), - ($$3669$$,$$CREEK JUNCTION$$,#{state_id_vic},-36.696659,145.771354), - ($$3669$$,$$EARLSTON$$,#{state_id_vic},-36.696659,145.771354), - ($$3669$$,$$GOWANGARDIE$$,#{state_id_vic},-36.696659,145.771354), - ($$3669$$,$$KOONDA$$,#{state_id_vic},-36.696659,145.771354), - ($$3669$$,$$MARRAWEENEY$$,#{state_id_vic},-36.696659,145.771354), - ($$3669$$,$$TAMLEUGH$$,#{state_id_vic},-36.696659,145.771354), - ($$3669$$,$$TAMLEUGH NORTH$$,#{state_id_vic},-36.696659,145.771354), - ($$3669$$,$$UPOTIPOTPON$$,#{state_id_vic},-36.696659,145.771354), - ($$3669$$,$$VIOLET TOWN$$,#{state_id_vic},-36.696659,145.771354), - ($$3670$$,$$BADDAGINNIE$$,#{state_id_vic},-36.592633,145.861036), - ($$3670$$,$$TARNOOK$$,#{state_id_vic},-36.592633,145.861036), - ($$3670$$,$$WARRENBAYNE$$,#{state_id_vic},-36.592633,145.861036), - ($$3671$$,$$BENALLA$$,#{state_id_vic},-36.385144,145.420691), - ($$3672$$,$$BENALLA$$,#{state_id_vic},-36.546895,145.98565), - ($$3672$$,$$BENALLA WEST$$,#{state_id_vic},-36.546895,145.98565), - ($$3673$$,$$BROKEN CREEK$$,#{state_id_vic},-36.434779,145.890351), - ($$3673$$,$$GOOMALIBEE$$,#{state_id_vic},-36.434779,145.890351), - ($$3673$$,$$LIMA$$,#{state_id_vic},-36.434779,145.890351), - ($$3673$$,$$LIMA EAST$$,#{state_id_vic},-36.434779,145.890351), - ($$3673$$,$$LIMA SOUTH$$,#{state_id_vic},-36.434779,145.890351), - ($$3673$$,$$LURG$$,#{state_id_vic},-36.434779,145.890351), - ($$3673$$,$$MOLYULLAH$$,#{state_id_vic},-36.434779,145.890351), - ($$3673$$,$$MOORNGAG$$,#{state_id_vic},-36.434779,145.890351), - ($$3673$$,$$SAMARIA$$,#{state_id_vic},-36.434779,145.890351), - ($$3673$$,$$SWANPOOL$$,#{state_id_vic},-36.434779,145.890351), - ($$3673$$,$$TATONG$$,#{state_id_vic},-36.434779,145.890351), - ($$3673$$,$$UPPER LURG$$,#{state_id_vic},-36.434779,145.890351), - ($$3673$$,$$UPPER RYANS CREEK$$,#{state_id_vic},-36.434779,145.890351), - ($$3673$$,$$WINTON$$,#{state_id_vic},-36.434779,145.890351), - ($$3673$$,$$WINTON NORTH$$,#{state_id_vic},-36.434779,145.890351), - ($$3675$$,$$BOWEYA$$,#{state_id_vic},-36.269789,146.131319), - ($$3675$$,$$BOWEYA NORTH$$,#{state_id_vic},-36.269789,146.131319), - ($$3675$$,$$GLENROWAN$$,#{state_id_vic},-36.269789,146.131319), - ($$3675$$,$$GLENROWAN WEST$$,#{state_id_vic},-36.269789,146.131319), - ($$3675$$,$$GRETA$$,#{state_id_vic},-36.269789,146.131319), - ($$3675$$,$$GRETA SOUTH$$,#{state_id_vic},-36.269789,146.131319), - ($$3675$$,$$GRETA WEST$$,#{state_id_vic},-36.269789,146.131319), - ($$3675$$,$$HANSONVILLE$$,#{state_id_vic},-36.269789,146.131319), - ($$3675$$,$$MOUNT BRUNO$$,#{state_id_vic},-36.269789,146.131319), - ($$3675$$,$$TAMINICK$$,#{state_id_vic},-36.269789,146.131319), - ($$3676$$,$$WANGARATTA$$,#{state_id_vic},-36.341245,146.338612), - ($$3677$$,$$WANGARATTA$$,#{state_id_vic},-36.353243,146.297244), - ($$3677$$,$$WANGARATTA WEST$$,#{state_id_vic},-36.353243,146.297244), - ($$3677$$,$$YARRUNGA$$,#{state_id_vic},-36.353243,146.297244), - ($$3678$$,$$BOBINAWARRAH$$,#{state_id_vic},-36.521737,146.498737), - ($$3678$$,$$BOORHAMAN$$,#{state_id_vic},-36.521737,146.498737), - ($$3678$$,$$BOORHAMAN EAST$$,#{state_id_vic},-36.521737,146.498737), - ($$3678$$,$$BOWSER$$,#{state_id_vic},-36.521737,146.498737), - ($$3678$$,$$BYAWATHA$$,#{state_id_vic},-36.521737,146.498737), - ($$3678$$,$$CARBOOR$$,#{state_id_vic},-36.521737,146.498737), - ($$3678$$,$$CHESHUNT$$,#{state_id_vic},-36.521737,146.498737), - ($$3678$$,$$CHESHUNT SOUTH$$,#{state_id_vic},-36.521737,146.498737), - ($$3678$$,$$DOCKER$$,#{state_id_vic},-36.521737,146.498737), - ($$3678$$,$$DOCKERS PLAINS$$,#{state_id_vic},-36.521737,146.498737), - ($$3678$$,$$EAST WANGARATTA$$,#{state_id_vic},-36.521737,146.498737), - ($$3678$$,$$EDI$$,#{state_id_vic},-36.521737,146.498737), - ($$3678$$,$$EDI UPPER$$,#{state_id_vic},-36.521737,146.498737), - ($$3678$$,$$EVERTON$$,#{state_id_vic},-36.521737,146.498737), - ($$3678$$,$$EVERTON UPPER$$,#{state_id_vic},-36.521737,146.498737), - ($$3678$$,$$KILLAWARRA$$,#{state_id_vic},-36.521737,146.498737), - ($$3678$$,$$KING VALLEY$$,#{state_id_vic},-36.521737,146.498737), - ($$3678$$,$$LACEBY$$,#{state_id_vic},-36.521737,146.498737), - ($$3678$$,$$LONDRIGAN$$,#{state_id_vic},-36.521737,146.498737), - ($$3678$$,$$MARKWOOD$$,#{state_id_vic},-36.521737,146.498737), - ($$3678$$,$$MEADOW CREEK$$,#{state_id_vic},-36.521737,146.498737), - ($$3678$$,$$MILAWA$$,#{state_id_vic},-36.521737,146.498737), - ($$3678$$,$$NORTH WANGARATTA$$,#{state_id_vic},-36.521737,146.498737), - ($$3678$$,$$OXLEY$$,#{state_id_vic},-36.521737,146.498737), - ($$3678$$,$$OXLEY FLATS$$,#{state_id_vic},-36.521737,146.498737), - ($$3678$$,$$PEECHELBA$$,#{state_id_vic},-36.521737,146.498737), - ($$3678$$,$$PEECHELBA EAST$$,#{state_id_vic},-36.521737,146.498737), - ($$3678$$,$$ROSE RIVER$$,#{state_id_vic},-36.521737,146.498737), - ($$3678$$,$$TARRAWINGEE$$,#{state_id_vic},-36.521737,146.498737), - ($$3678$$,$$WABONGA$$,#{state_id_vic},-36.521737,146.498737), - ($$3678$$,$$WALDARA$$,#{state_id_vic},-36.521737,146.498737), - ($$3678$$,$$WANGANDARY$$,#{state_id_vic},-36.521737,146.498737), - ($$3678$$,$$WANGARATTA FORWARD$$,#{state_id_vic},-36.521737,146.498737), - ($$3678$$,$$WANGARATTA SOUTH$$,#{state_id_vic},-36.521737,146.498737), - ($$3678$$,$$WHITLANDS$$,#{state_id_vic},-36.521737,146.498737), - ($$3682$$,$$BORALMA$$,#{state_id_vic},-36.243951,146.411677), - ($$3682$$,$$LILLIPUT$$,#{state_id_vic},-36.243951,146.411677), - ($$3682$$,$$NORONG$$,#{state_id_vic},-36.243951,146.411677), - ($$3682$$,$$SPRINGHURST$$,#{state_id_vic},-36.243951,146.411677), - ($$3683$$,$$CHILTERN$$,#{state_id_vic},-36.148307,146.610311), - ($$3683$$,$$CHILTERN VALLEY$$,#{state_id_vic},-36.148307,146.610311), - ($$3683$$,$$CORNISHTOWN$$,#{state_id_vic},-36.148307,146.610311), - ($$3685$$,$$BOORHAMAN NORTH$$,#{state_id_vic},-36.098847,146.222359), - ($$3685$$,$$BRIMIN$$,#{state_id_vic},-36.098847,146.222359), - ($$3685$$,$$BROWNS PLAINS$$,#{state_id_vic},-36.098847,146.222359), - ($$3685$$,$$CARLYLE$$,#{state_id_vic},-36.098847,146.222359), - ($$3685$$,$$GOORAMADDA$$,#{state_id_vic},-36.098847,146.222359), - ($$3685$$,$$GREAT SOUTHERN$$,#{state_id_vic},-36.098847,146.222359), - ($$3685$$,$$LAKE MOODEMERE$$,#{state_id_vic},-36.098847,146.222359), - ($$3685$$,$$PRENTICE NORTH$$,#{state_id_vic},-36.098847,146.222359), - ($$3685$$,$$RUTHERGLEN$$,#{state_id_vic},-36.098847,146.222359), - ($$3687$$,$$WAHGUNYAH$$,#{state_id_vic},-36.011553,146.40193), - ($$3688$$,$$BARNAWARTHA$$,#{state_id_vic},-36.105488,146.673164), - ($$3688$$,$$INDIGO VALLEY$$,#{state_id_vic},-36.105488,146.673164), - ($$3689$$,$$WODONGA$$,#{state_id_vic},-37.67576,144.989793), - ($$3690$$,$$WEST WODONGA$$,#{state_id_vic},-36.099768,146.821149), - ($$3690$$,$$WODONGA$$,#{state_id_vic},-36.099768,146.821149), - ($$3690$$,$$WODONGA PLAZA$$,#{state_id_vic},-36.099768,146.821149), - ($$3691$$,$$ALLANS FLAT$$,#{state_id_vic},-36.276114,146.909823), - ($$3691$$,$$BANDIANA$$,#{state_id_vic},-36.276114,146.909823), - ($$3691$$,$$BARANDUDA$$,#{state_id_vic},-36.276114,146.909823), - ($$3691$$,$$BARNAWARTHA NORTH$$,#{state_id_vic},-36.276114,146.909823), - ($$3691$$,$$BELLBRIDGE$$,#{state_id_vic},-36.276114,146.909823), - ($$3691$$,$$BERRINGAMA$$,#{state_id_vic},-36.276114,146.909823), - ($$3691$$,$$BETHANGA$$,#{state_id_vic},-36.276114,146.909823), - ($$3691$$,$$BONEGILLA$$,#{state_id_vic},-36.276114,146.909823), - ($$3691$$,$$BUNGIL$$,#{state_id_vic},-36.276114,146.909823), - ($$3691$$,$$CASTLE CREEK$$,#{state_id_vic},-36.276114,146.909823), - ($$3691$$,$$CORAL BANK$$,#{state_id_vic},-36.276114,146.909823), - ($$3691$$,$$DEDERANG$$,#{state_id_vic},-36.276114,146.909823), - ($$3691$$,$$EBDEN$$,#{state_id_vic},-36.276114,146.909823), - ($$3691$$,$$GATEWAY ISLAND$$,#{state_id_vic},-36.276114,146.909823), - ($$3691$$,$$GLEN CREEK$$,#{state_id_vic},-36.276114,146.909823), - ($$3691$$,$$GUNDOWRING$$,#{state_id_vic},-36.276114,146.909823), - ($$3691$$,$$HUON CREEK$$,#{state_id_vic},-36.276114,146.909823), - ($$3691$$,$$KANCOONA$$,#{state_id_vic},-36.276114,146.909823), - ($$3691$$,$$KERGUNYAH$$,#{state_id_vic},-36.276114,146.909823), - ($$3691$$,$$KERGUNYAH SOUTH$$,#{state_id_vic},-36.276114,146.909823), - ($$3691$$,$$KIEWA$$,#{state_id_vic},-36.276114,146.909823), - ($$3691$$,$$KILLARA$$,#{state_id_vic},-36.276114,146.909823), - ($$3691$$,$$LAKE HUME VILLAGE$$,#{state_id_nsw},-36.276114,146.909823), - ($$3691$$,$$LENEVA$$,#{state_id_vic},-36.276114,146.909823), - ($$3691$$,$$LUCYVALE$$,#{state_id_vic},-36.276114,146.909823), - ($$3691$$,$$MONGANS BRIDGE$$,#{state_id_vic},-36.276114,146.909823), - ($$3691$$,$$OSBORNES FLAT$$,#{state_id_vic},-36.276114,146.909823), - ($$3691$$,$$RUNNING CREEK$$,#{state_id_vic},-36.276114,146.909823), - ($$3691$$,$$STAGHORN FLAT$$,#{state_id_vic},-36.276114,146.909823), - ($$3691$$,$$TALGARNO$$,#{state_id_vic},-36.276114,146.909823), - ($$3691$$,$$TANGAMBALANGA$$,#{state_id_vic},-36.276114,146.909823), - ($$3691$$,$$THOLOGOLONG$$,#{state_id_vic},-36.276114,146.909823), - ($$3691$$,$$UPPER GUNDOWRING$$,#{state_id_vic},-36.276114,146.909823), - ($$3691$$,$$WODONGA FORWARD$$,#{state_id_vic},-36.276114,146.909823), - ($$3694$$,$$BANDIANA MILPO$$,#{state_id_vic},-36.141317,146.916596), - ($$3695$$,$$CHARLEROI$$,#{state_id_vic},-36.30384,147.139316), - ($$3695$$,$$HUON$$,#{state_id_vic},-36.30384,147.139316), - ($$3695$$,$$SANDY CREEK$$,#{state_id_vic},-36.30384,147.139316), - ($$3697$$,$$TAWONGA$$,#{state_id_vic},-36.66901,147.205089), - ($$3698$$,$$TAWONGA SOUTH$$,#{state_id_vic},0.0,0.0), - ($$3699$$,$$BOGONG$$,#{state_id_vic},-36.80517,147.224996), - ($$3699$$,$$FALLS CREEK$$,#{state_id_vic},-36.80517,147.224996), - ($$3699$$,$$MOUNT BEAUTY$$,#{state_id_vic},-36.80517,147.224996), - ($$3699$$,$$NELSE$$,#{state_id_vic},-36.80517,147.224996), - ($$3700$$,$$BULLIOH$$,#{state_id_vic},-36.175761,147.338065), - ($$3700$$,$$GEORGES CREEK$$,#{state_id_vic},-36.175761,147.338065), - ($$3700$$,$$JARVIS CREEK$$,#{state_id_vic},-36.175761,147.338065), - ($$3700$$,$$TALLANGATTA$$,#{state_id_vic},-36.175761,147.338065), - ($$3700$$,$$TALLANGATTA EAST$$,#{state_id_vic},-36.175761,147.338065), - ($$3701$$,$$DARTMOUTH$$,#{state_id_vic},-36.533461,147.497235), - ($$3701$$,$$ESKDALE$$,#{state_id_vic},-36.533461,147.497235), - ($$3701$$,$$GRANYA$$,#{state_id_vic},-36.533461,147.497235), - ($$3701$$,$$MITTA MITTA$$,#{state_id_vic},-36.533461,147.497235), - ($$3701$$,$$OLD TALLANGATTA$$,#{state_id_vic},-36.533461,147.497235), - ($$3701$$,$$SHELLEY$$,#{state_id_vic},-36.533461,147.497235), - ($$3701$$,$$TALLANDOON$$,#{state_id_vic},-36.533461,147.497235), - ($$3701$$,$$TALLANGATTA SOUTH$$,#{state_id_vic},-36.533461,147.497235), - ($$3701$$,$$TALLANGATTA VALLEY$$,#{state_id_vic},-36.533461,147.497235), - ($$3704$$,$$KOETONG$$,#{state_id_vic},-36.104644,147.538225), - ($$3705$$,$$CUDGEWA$$,#{state_id_vic},-36.190684,147.771573), - ($$3707$$,$$BIGGARA$$,#{state_id_vic},-36.279153,148.025563), - ($$3707$$,$$BRINGENBRONG$$,#{state_id_nsw},-36.279153,148.025563), - ($$3707$$,$$COLAC COLAC$$,#{state_id_vic},-36.279153,148.025563), - ($$3707$$,$$CORRYONG$$,#{state_id_vic},-36.279153,148.025563), - ($$3707$$,$$NARIEL VALLEY$$,#{state_id_vic},-36.279153,148.025563), - ($$3707$$,$$THOWGLA VALLEY$$,#{state_id_vic},-36.279153,148.025563), - ($$3707$$,$$TOM GROGGIN$$,#{state_id_vic},-36.279153,148.025563), - ($$3707$$,$$TOWONG$$,#{state_id_vic},-36.279153,148.025563), - ($$3707$$,$$TOWONG UPPER$$,#{state_id_vic},-36.279153,148.025563), - ($$3708$$,$$TINTALDRA$$,#{state_id_vic},-36.048986,147.930952), - ($$3709$$,$$BURROWYE$$,#{state_id_vic},-36.048847,147.561427), - ($$3709$$,$$GUYS FOREST$$,#{state_id_vic},-36.048847,147.561427), - ($$3709$$,$$MOUNT ALFRED$$,#{state_id_vic},-36.048847,147.561427), - ($$3709$$,$$PINE MOUNTAIN$$,#{state_id_vic},-36.048847,147.561427), - ($$3709$$,$$WALWA$$,#{state_id_vic},-36.048847,147.561427), - ($$3711$$,$$BUXTON$$,#{state_id_vic},-37.420458,145.712999), - ($$3712$$,$$RUBICON$$,#{state_id_vic},-37.326297,145.862023), - ($$3712$$,$$THORNTON$$,#{state_id_vic},-37.326297,145.862023), - ($$3713$$,$$EILDON$$,#{state_id_vic},-37.234547,145.910525), - ($$3713$$,$$LAKE EILDON$$,#{state_id_vic},-37.234547,145.910525), - ($$3713$$,$$TAYLOR BAY$$,#{state_id_vic},-37.234547,145.910525), - ($$3714$$,$$ACHERON$$,#{state_id_vic},-37.252433,145.702015), - ($$3714$$,$$ALEXANDRA$$,#{state_id_vic},-37.252433,145.702015), - ($$3714$$,$$CATHKIN$$,#{state_id_vic},-37.252433,145.702015), - ($$3714$$,$$CRYSTAL CREEK$$,#{state_id_vic},-37.252433,145.702015), - ($$3714$$,$$DEVILS RIVER$$,#{state_id_vic},-37.252433,145.702015), - ($$3714$$,$$FAWCETT$$,#{state_id_vic},-37.252433,145.702015), - ($$3714$$,$$KORIELLA$$,#{state_id_vic},-37.252433,145.702015), - ($$3714$$,$$MAINTONGOON$$,#{state_id_vic},-37.252433,145.702015), - ($$3714$$,$$TAGGERTY$$,#{state_id_vic},-37.252433,145.702015), - ($$3714$$,$$WHANREGARWEN$$,#{state_id_vic},-37.252433,145.702015), - ($$3715$$,$$ANCONA$$,#{state_id_vic},-36.972266,145.790539), - ($$3715$$,$$MERTON$$,#{state_id_vic},-36.972266,145.790539), - ($$3715$$,$$WOODFIELD$$,#{state_id_vic},-36.972266,145.790539), - ($$3717$$,$$FLOWERDALE$$,#{state_id_vic},-37.344477,145.289109), - ($$3717$$,$$GHIN GHIN$$,#{state_id_vic},-37.344477,145.289109), - ($$3717$$,$$GLENBURN$$,#{state_id_vic},-37.344477,145.289109), - ($$3717$$,$$HOMEWOOD$$,#{state_id_vic},-37.344477,145.289109), - ($$3717$$,$$KILLINGWORTH$$,#{state_id_vic},-37.344477,145.289109), - ($$3717$$,$$LIMESTONE$$,#{state_id_vic},-37.344477,145.289109), - ($$3717$$,$$MURRINDINDI$$,#{state_id_vic},-37.344477,145.289109), - ($$3717$$,$$YEA$$,#{state_id_vic},-37.344477,145.289109), - ($$3718$$,$$MOLESWORTH$$,#{state_id_vic},-37.127985,145.525714), - ($$3719$$,$$GOBUR$$,#{state_id_vic},-37.014402,145.617965), - ($$3719$$,$$KANUMBRA$$,#{state_id_vic},-37.014402,145.617965), - ($$3719$$,$$TERIP TERIP$$,#{state_id_vic},-37.014402,145.617965), - ($$3719$$,$$YARCK$$,#{state_id_vic},-37.014402,145.617965), - ($$3720$$,$$BONNIE DOON$$,#{state_id_vic},-37.023006,145.86227), - ($$3722$$,$$BARWITE$$,#{state_id_vic},-37.009455,146.21744), - ($$3722$$,$$MANSFIELD$$,#{state_id_vic},-37.009455,146.21744), - ($$3722$$,$$MIRIMBAH$$,#{state_id_vic},-37.009455,146.21744), - ($$3723$$,$$ARCHERTON$$,#{state_id_vic},-36.894465,146.256507), - ($$3723$$,$$BARJARG$$,#{state_id_vic},-36.894465,146.256507), - ($$3723$$,$$BOOROLITE$$,#{state_id_vic},-36.894465,146.256507), - ($$3723$$,$$BRIDGE CREEK$$,#{state_id_vic},-36.894465,146.256507), - ($$3723$$,$$DELATITE$$,#{state_id_vic},-36.894465,146.256507), - ($$3723$$,$$ENOCHS POINT$$,#{state_id_vic},-36.894465,146.256507), - ($$3723$$,$$GAFFNEYS CREEK$$,#{state_id_vic},-36.894465,146.256507), - ($$3723$$,$$GOUGHS BAY$$,#{state_id_vic},-36.894465,146.256507), - ($$3723$$,$$HOWES CREEK$$,#{state_id_vic},-36.894465,146.256507), - ($$3723$$,$$HOWQUA$$,#{state_id_vic},-36.894465,146.256507), - ($$3723$$,$$HOWQUA HILLS$$,#{state_id_vic},-36.894465,146.256507), - ($$3723$$,$$HOWQUA INLET$$,#{state_id_vic},-36.894465,146.256507), - ($$3723$$,$$JAMIESON$$,#{state_id_vic},-36.894465,146.256507), - ($$3723$$,$$KEVINGTON$$,#{state_id_vic},-36.894465,146.256507), - ($$3723$$,$$KNOCKWOOD$$,#{state_id_vic},-36.894465,146.256507), - ($$3723$$,$$MACS COVE$$,#{state_id_vic},-36.894465,146.256507), - ($$3723$$,$$MAINDAMPLE$$,#{state_id_vic},-36.894465,146.256507), - ($$3723$$,$$MATLOCK$$,#{state_id_vic},-36.894465,146.256507), - ($$3723$$,$$MERRIJIG$$,#{state_id_vic},-36.894465,146.256507), - ($$3723$$,$$MOUNT BULLER$$,#{state_id_vic},-36.894465,146.256507), - ($$3723$$,$$MOUNTAIN BAY$$,#{state_id_vic},-36.894465,146.256507), - ($$3723$$,$$PIRIES$$,#{state_id_vic},-36.894465,146.256507), - ($$3723$$,$$SAWMILL SETTLEMENT$$,#{state_id_vic},-36.894465,146.256507), - ($$3723$$,$$TOLMIE$$,#{state_id_vic},-36.894465,146.256507), - ($$3723$$,$$WOODS POINT$$,#{state_id_vic},-36.894465,146.256507), - ($$3724$$,$$MANSFIELD$$,#{state_id_vic},-37.053108,146.076832), - ($$3725$$,$$BOXWOOD$$,#{state_id_vic},-36.323886,145.79839), - ($$3725$$,$$CHESNEY VALE$$,#{state_id_vic},-36.323886,145.79839), - ($$3725$$,$$GOORAMBAT$$,#{state_id_vic},-36.323886,145.79839), - ($$3725$$,$$MAJOR PLAINS$$,#{state_id_vic},-36.323886,145.79839), - ($$3725$$,$$STEWARTON$$,#{state_id_vic},-36.323886,145.79839), - ($$3726$$,$$BUNGEET$$,#{state_id_vic},-36.28166,146.057876), - ($$3726$$,$$BUNGEET WEST$$,#{state_id_vic},-36.28166,146.057876), - ($$3726$$,$$DEVENISH$$,#{state_id_vic},-36.28166,146.057876), - ($$3726$$,$$THOONA$$,#{state_id_vic},-36.28166,146.057876), - ($$3727$$,$$ALMONDS$$,#{state_id_vic},-36.232281,146.056216), - ($$3727$$,$$LAKE ROWAN$$,#{state_id_vic},-36.232281,146.056216), - ($$3727$$,$$PELLUEBLA$$,#{state_id_vic},-36.232281,146.056216), - ($$3727$$,$$ST JAMES$$,#{state_id_vic},-36.232281,146.056216), - ($$3727$$,$$YUNDOOL$$,#{state_id_vic},-36.232281,146.056216), - ($$3728$$,$$BOOMAHNOOMOONAH$$,#{state_id_vic},-36.099254,146.084897), - ($$3728$$,$$TUNGAMAH$$,#{state_id_vic},-36.099254,146.084897), - ($$3728$$,$$WILBY$$,#{state_id_vic},-36.099254,146.084897), - ($$3728$$,$$YOUARANG$$,#{state_id_vic},-36.099254,146.084897), - ($$3730$$,$$BATHUMI$$,#{state_id_vic},-36.026715,146.084897), - ($$3730$$,$$BOOSEY$$,#{state_id_vic},-36.026715,146.084897), - ($$3730$$,$$BUNDALONG$$,#{state_id_vic},-36.026715,146.084897), - ($$3730$$,$$BUNDALONG SOUTH$$,#{state_id_vic},-36.026715,146.084897), - ($$3730$$,$$BURRAMINE$$,#{state_id_vic},-36.026715,146.084897), - ($$3730$$,$$BURRAMINE SOUTH$$,#{state_id_vic},-36.026715,146.084897), - ($$3730$$,$$ESMOND$$,#{state_id_vic},-36.026715,146.084897), - ($$3730$$,$$TELFORD$$,#{state_id_vic},-36.026715,146.084897), - ($$3730$$,$$YARRAWONGA$$,#{state_id_vic},-36.026715,146.084897), - ($$3730$$,$$YARRAWONGA SOUTH$$,#{state_id_vic},-36.026715,146.084897), - ($$3732$$,$$MOYHU$$,#{state_id_vic},-36.578223,146.378463), - ($$3732$$,$$MYRRHEE$$,#{state_id_vic},-36.578223,146.378463), - ($$3733$$,$$WHITFIELD$$,#{state_id_vic},-36.749752,146.413426), - ($$3735$$,$$BOWMANS FOREST$$,#{state_id_vic},-36.520364,146.577229), - ($$3735$$,$$WHOROULY$$,#{state_id_vic},-36.520364,146.577229), - ($$3735$$,$$WHOROULY EAST$$,#{state_id_vic},-36.520364,146.577229), - ($$3735$$,$$WHOROULY SOUTH$$,#{state_id_vic},-36.520364,146.577229), - ($$3736$$,$$MYRTLEFORD$$,#{state_id_vic},-36.455136,146.479489), - ($$3737$$,$$ABBEYARD$$,#{state_id_vic},-36.976415,146.782515), - ($$3737$$,$$BARWIDGEE$$,#{state_id_vic},-36.976415,146.782515), - ($$3737$$,$$BUFFALO RIVER$$,#{state_id_vic},-36.976415,146.782515), - ($$3737$$,$$DANDONGADALE$$,#{state_id_vic},-36.976415,146.782515), - ($$3737$$,$$GAPSTED$$,#{state_id_vic},-36.976415,146.782515), - ($$3737$$,$$HAVILAH$$,#{state_id_vic},-36.976415,146.782515), - ($$3737$$,$$MERRIANG$$,#{state_id_vic},-36.976415,146.782515), - ($$3737$$,$$MERRIANG SOUTH$$,#{state_id_vic},-36.976415,146.782515), - ($$3737$$,$$MUDGEGONGA$$,#{state_id_vic},-36.976415,146.782515), - ($$3737$$,$$MYRTLEFORD$$,#{state_id_vic},-36.976415,146.782515), - ($$3737$$,$$NUG NUG$$,#{state_id_vic},-36.976415,146.782515), - ($$3737$$,$$ROSEWHITE$$,#{state_id_vic},-36.976415,146.782515), - ($$3737$$,$$SELWYN$$,#{state_id_vic},-36.976415,146.782515), - ($$3737$$,$$WONNANGATTA$$,#{state_id_vic},-36.976415,146.782515), - ($$3738$$,$$OVENS$$,#{state_id_vic},-36.606337,146.792102), - ($$3739$$,$$EUROBIN$$,#{state_id_vic},-36.703255,146.84579), - ($$3740$$,$$BUCKLAND$$,#{state_id_vic},-36.815383,146.852777), - ($$3740$$,$$MOUNT BUFFALO$$,#{state_id_vic},-36.815383,146.852777), - ($$3740$$,$$POREPUNKAH$$,#{state_id_vic},-36.815383,146.852777), - ($$3741$$,$$BRIGHT$$,#{state_id_vic},-36.727237,146.960491), - ($$3741$$,$$FREEBURGH$$,#{state_id_vic},-36.727237,146.960491), - ($$3741$$,$$GERMANTOWN$$,#{state_id_vic},-36.727237,146.960491), - ($$3741$$,$$HARRIETVILLE$$,#{state_id_vic},-36.727237,146.960491), - ($$3741$$,$$HOTHAM HEIGHTS$$,#{state_id_vic},-36.727237,146.960491), - ($$3741$$,$$MOUNT HOTHAM$$,#{state_id_vic},-36.727237,146.960491), - ($$3741$$,$$SMOKO$$,#{state_id_vic},-36.727237,146.960491), - ($$3744$$,$$WANDILIGONG$$,#{state_id_vic},-36.753565,146.982157), - ($$3746$$,$$ELDORADO$$,#{state_id_vic},-36.30376,146.584447), - ($$3747$$,$$BEECHWORTH$$,#{state_id_vic},-36.360341,146.687966), - ($$3747$$,$$MURMUNGEE$$,#{state_id_vic},-36.360341,146.687966), - ($$3747$$,$$STANLEY$$,#{state_id_vic},-36.360341,146.687966), - ($$3747$$,$$WOOLSHED$$,#{state_id_vic},-36.360341,146.687966), - ($$3747$$,$$WOORAGEE$$,#{state_id_vic},-36.360341,146.687966), - ($$3749$$,$$BRUARONG$$,#{state_id_vic},-36.414545,146.862235), - ($$3749$$,$$YACKANDANDAH$$,#{state_id_vic},-36.414545,146.862235), - ($$3750$$,$$WOLLERT$$,#{state_id_vic},-38.380955,144.811911), - ($$3751$$,$$WOODSTOCK$$,#{state_id_vic},-37.692001,144.885657), - ($$3752$$,$$MORANG SOUTH$$,#{state_id_vic},-37.653074,145.095043), - ($$3752$$,$$SOUTH MORANG$$,#{state_id_vic},-37.653074,145.095043), - ($$3753$$,$$BEVERIDGE$$,#{state_id_vic},-37.468668,144.996239), - ($$3754$$,$$DOREEN$$,#{state_id_vic},-37.617671,145.154899), - ($$3754$$,$$MERNDA$$,#{state_id_vic},-37.617671,145.154899), - ($$3755$$,$$YAN YEAN$$,#{state_id_vic},-37.572602,145.144089), - ($$3756$$,$$CHINTIN$$,#{state_id_vic},-37.402359,144.80284), - ($$3756$$,$$DARRAWEIT GUIM$$,#{state_id_vic},-37.402359,144.80284), - ($$3756$$,$$UPPER PLENTY$$,#{state_id_vic},-37.402359,144.80284), - ($$3756$$,$$WALLAN$$,#{state_id_vic},-37.402359,144.80284), - ($$3757$$,$$EDEN PARK$$,#{state_id_vic},-37.450676,145.106435), - ($$3757$$,$$HUMEVALE$$,#{state_id_vic},-37.450676,145.106435), - ($$3757$$,$$KINGLAKE CENTRAL$$,#{state_id_vic},-37.450676,145.106435), - ($$3757$$,$$KINGLAKE WEST$$,#{state_id_vic},-37.450676,145.106435), - ($$3757$$,$$PHEASANT CREEK$$,#{state_id_vic},-37.450676,145.106435), - ($$3757$$,$$WHITTLESEA$$,#{state_id_vic},-37.450676,145.106435), - ($$3758$$,$$HEATHCOTE JUNCTION$$,#{state_id_vic},-37.383,145.029955), - ($$3758$$,$$WANDONG$$,#{state_id_vic},-37.383,145.029955), - ($$3759$$,$$PANTON HILL$$,#{state_id_vic},-37.641394,145.240086), - ($$3760$$,$$SMITHS GULLY$$,#{state_id_vic},-37.620131,145.287916), - ($$3761$$,$$ST ANDREWS$$,#{state_id_vic},0.0,0.0), - ($$3762$$,$$BYLANDS$$,#{state_id_vic},0.0,0.0), - ($$3763$$,$$KINGLAKE$$,#{state_id_vic},-37.481664,145.277344), - ($$3764$$,$$FORBES$$,#{state_id_vic},-37.31432,144.889007), - ($$3764$$,$$GLENAROUA$$,#{state_id_vic},-37.31432,144.889007), - ($$3764$$,$$HIGH CAMP$$,#{state_id_vic},-37.31432,144.889007), - ($$3764$$,$$KILMORE$$,#{state_id_vic},-37.31432,144.889007), - ($$3764$$,$$KILMORE EAST$$,#{state_id_vic},-37.31432,144.889007), - ($$3764$$,$$MORANDING$$,#{state_id_vic},-37.31432,144.889007), - ($$3764$$,$$TANTARABOO$$,#{state_id_vic},-37.31432,144.889007), - ($$3764$$,$$WILLOWMAVIN$$,#{state_id_vic},-37.31432,144.889007), - ($$3765$$,$$MONTROSE$$,#{state_id_vic},-37.805976,145.339514), - ($$3766$$,$$KALORAMA$$,#{state_id_vic},-37.816839,145.367371), - ($$3767$$,$$MOUNT DANDENONG$$,#{state_id_vic},-37.835062,145.357831), - ($$3770$$,$$COLDSTREAM$$,#{state_id_vic},-37.724701,145.378596), - ($$3770$$,$$GRUYERE$$,#{state_id_vic},-37.724701,145.378596), - ($$3770$$,$$YERING$$,#{state_id_vic},-37.724701,145.378596), - ($$3775$$,$$CHRISTMAS HILLS$$,#{state_id_vic},-37.650855,145.317827), - ($$3775$$,$$DIXONS CREEK$$,#{state_id_vic},-37.650855,145.317827), - ($$3775$$,$$STEELS CREEK$$,#{state_id_vic},-37.650855,145.317827), - ($$3775$$,$$TARRAWARRA$$,#{state_id_vic},-37.650855,145.317827), - ($$3775$$,$$YARRA GLEN$$,#{state_id_vic},-37.650855,145.317827), - ($$3777$$,$$BADGER CREEK$$,#{state_id_vic},-37.686067,145.541351), - ($$3777$$,$$CASTELLA$$,#{state_id_vic},-37.686067,145.541351), - ($$3777$$,$$CHUM CREEK$$,#{state_id_vic},-37.686067,145.541351), - ($$3777$$,$$HEALESVILLE$$,#{state_id_vic},-37.686067,145.541351), - ($$3777$$,$$HEALESVILLE MAIN STREET$$,#{state_id_vic},-37.686067,145.541351), - ($$3777$$,$$HEALESVILLE POST SHOP$$,#{state_id_vic},-37.686067,145.541351), - ($$3777$$,$$MOUNT TOOLEBEWONG$$,#{state_id_vic},-37.686067,145.541351), - ($$3777$$,$$TOOLANGI$$,#{state_id_vic},-37.686067,145.541351), - ($$3778$$,$$FERNSHAW$$,#{state_id_vic},-37.632252,145.699176), - ($$3778$$,$$NARBETHONG$$,#{state_id_vic},-37.632252,145.699176), - ($$3779$$,$$CAMBARVILLE$$,#{state_id_vic},-37.560312,145.885335), - ($$3779$$,$$MARYSVILLE$$,#{state_id_vic},-37.560312,145.885335), - ($$3781$$,$$COCKATOO$$,#{state_id_vic},-37.93626,145.49137), - ($$3781$$,$$MOUNT BURNETT$$,#{state_id_vic},-37.93626,145.49137), - ($$3781$$,$$NANGANA$$,#{state_id_vic},-37.93626,145.49137), - ($$3782$$,$$AVONSLEIGH$$,#{state_id_vic},-37.920702,145.467246), - ($$3782$$,$$CLEMATIS$$,#{state_id_vic},-37.920702,145.467246), - ($$3782$$,$$EMERALD$$,#{state_id_vic},-37.920702,145.467246), - ($$3782$$,$$MACCLESFIELD$$,#{state_id_vic},-37.920702,145.467246), - ($$3783$$,$$GEMBROOK$$,#{state_id_vic},-37.952497,145.550457), - ($$3785$$,$$TREMONT$$,#{state_id_vic},-38.27059,144.491163), - ($$3786$$,$$FERNY CREEK$$,#{state_id_vic},-37.881284,145.342274), - ($$3787$$,$$SASSAFRAS$$,#{state_id_vic},-37.876027,145.36695), - ($$3787$$,$$SASSAFRAS GULLY$$,#{state_id_vic},-37.876027,145.36695), - ($$3788$$,$$OLINDA$$,#{state_id_vic},-37.85984,145.363835), - ($$3789$$,$$SHERBROOKE$$,#{state_id_vic},-37.883737,145.364031), - ($$3791$$,$$KALLISTA$$,#{state_id_vic},-37.900585,145.410171), - ($$3792$$,$$THE PATCH$$,#{state_id_vic},-37.887353,145.392574), - ($$3793$$,$$MONBULK$$,#{state_id_vic},-37.867678,145.408664), - ($$3795$$,$$SILVAN$$,#{state_id_vic},-37.844151,145.413447), - ($$3796$$,$$MOUNT EVELYN$$,#{state_id_vic},-37.792515,145.375605), - ($$3797$$,$$GILDEROY$$,#{state_id_vic},-37.855038,145.711284), - ($$3797$$,$$GLADYSDALE$$,#{state_id_vic},-37.855038,145.711284), - ($$3797$$,$$POWELLTOWN$$,#{state_id_vic},-37.855038,145.711284), - ($$3797$$,$$THREE BRIDGES$$,#{state_id_vic},-37.855038,145.711284), - ($$3797$$,$$YARRA JUNCTION$$,#{state_id_vic},-37.855038,145.711284), - ($$3799$$,$$BIG PATS CREEK$$,#{state_id_vic},-37.763917,145.746221), - ($$3799$$,$$EAST WARBURTON$$,#{state_id_vic},-37.763917,145.746221), - ($$3799$$,$$MCMAHONS CREEK$$,#{state_id_vic},-37.763917,145.746221), - ($$3799$$,$$MILLGROVE$$,#{state_id_vic},-37.763917,145.746221), - ($$3799$$,$$REEFTON$$,#{state_id_vic},-37.763917,145.746221), - ($$3799$$,$$WARBURTON$$,#{state_id_vic},-37.763917,145.746221), - ($$3799$$,$$WESBURN$$,#{state_id_vic},-37.763917,145.746221), - ($$3800$$,$$MONASH UNIVERSITY$$,#{state_id_vic},-37.910545,145.134935), - ($$3802$$,$$ENDEAVOUR HILLS$$,#{state_id_vic},-37.975281,145.254481), - ($$3803$$,$$HALLAM$$,#{state_id_vic},-38.004302,145.269261), - ($$3804$$,$$NARRE WARREN EAST$$,#{state_id_vic},-37.96602,145.363213), - ($$3804$$,$$NARRE WARREN NORTH$$,#{state_id_vic},-37.96602,145.363213), - ($$3805$$,$$FOUNTAIN GATE$$,#{state_id_vic},-38.021278,145.299771), - ($$3805$$,$$NARRE WARREN$$,#{state_id_vic},-38.021278,145.299771), - ($$3805$$,$$NARRE WARREN SOUTH$$,#{state_id_vic},-38.021278,145.299771), - ($$3806$$,$$BERWICK$$,#{state_id_vic},-38.030467,145.344621), - ($$3806$$,$$HARKAWAY$$,#{state_id_vic},-38.030467,145.344621), - ($$3807$$,$$BEACONSFIELD$$,#{state_id_vic},-38.045159,145.36746), - ($$3807$$,$$GUYS HILL$$,#{state_id_vic},-38.045159,145.36746), - ($$3808$$,$$BEACONSFIELD UPPER$$,#{state_id_vic},-38.004643,145.409755), - ($$3808$$,$$DEWHURST$$,#{state_id_vic},-38.004643,145.409755), - ($$3809$$,$$OFFICER$$,#{state_id_vic},-38.063056,145.40958), - ($$3809$$,$$OFFICER SOUTH$$,#{state_id_vic},-38.063056,145.40958), - ($$3810$$,$$PAKENHAM$$,#{state_id_vic},-38.077355,145.493402), - ($$3810$$,$$PAKENHAM SOUTH$$,#{state_id_vic},-38.077355,145.493402), - ($$3810$$,$$PAKENHAM UPPER$$,#{state_id_vic},-38.077355,145.493402), - ($$3810$$,$$RYTHDALE$$,#{state_id_vic},-38.077355,145.493402), - ($$3812$$,$$MARYKNOLL$$,#{state_id_vic},-38.035781,145.609548), - ($$3812$$,$$NAR NAR GOON$$,#{state_id_vic},-38.035781,145.609548), - ($$3812$$,$$NAR NAR GOON NORTH$$,#{state_id_vic},-38.035781,145.609548), - ($$3813$$,$$TYNONG$$,#{state_id_vic},-38.085068,145.625456), - ($$3813$$,$$TYNONG NORTH$$,#{state_id_vic},-38.085068,145.625456), - ($$3814$$,$$CORA LYNN$$,#{state_id_vic},-38.145284,145.60591), - ($$3814$$,$$GARFIELD$$,#{state_id_vic},-38.145284,145.60591), - ($$3814$$,$$GARFIELD NORTH$$,#{state_id_vic},-38.145284,145.60591), - ($$3814$$,$$VERVALE$$,#{state_id_vic},-38.145284,145.60591), - ($$3815$$,$$BUNYIP$$,#{state_id_vic},-38.09708,145.715492), - ($$3815$$,$$BUNYIP NORTH$$,#{state_id_vic},-38.09708,145.715492), - ($$3815$$,$$IONA$$,#{state_id_vic},-38.09708,145.715492), - ($$3815$$,$$TONIMBUK$$,#{state_id_vic},-38.09708,145.715492), - ($$3816$$,$$LABERTOUCHE$$,#{state_id_vic},-38.049843,145.82875), - ($$3816$$,$$LONGWARRY$$,#{state_id_vic},-38.049843,145.82875), - ($$3816$$,$$LONGWARRY NORTH$$,#{state_id_vic},-38.049843,145.82875), - ($$3816$$,$$MODELLA$$,#{state_id_vic},-38.049843,145.82875), - ($$3818$$,$$ATHLONE$$,#{state_id_vic},-38.239904,145.779817), - ($$3818$$,$$DROUIN$$,#{state_id_vic},-38.239904,145.779817), - ($$3818$$,$$DROUIN EAST$$,#{state_id_vic},-38.239904,145.779817), - ($$3818$$,$$DROUIN SOUTH$$,#{state_id_vic},-38.239904,145.779817), - ($$3818$$,$$DROUIN WEST$$,#{state_id_vic},-38.239904,145.779817), - ($$3818$$,$$HALLORA$$,#{state_id_vic},-38.239904,145.779817), - ($$3818$$,$$JINDIVICK$$,#{state_id_vic},-38.239904,145.779817), - ($$3818$$,$$RIPPLEBROOK$$,#{state_id_vic},-38.239904,145.779817), - ($$3820$$,$$BONA VISTA$$,#{state_id_vic},-38.206715,145.964873), - ($$3820$$,$$LILLICO$$,#{state_id_vic},-38.206715,145.964873), - ($$3820$$,$$WARRAGUL$$,#{state_id_vic},-38.206715,145.964873), - ($$3821$$,$$BRANDY CREEK$$,#{state_id_vic},-38.099914,145.923506), - ($$3821$$,$$BRAVINGTON$$,#{state_id_vic},-38.099914,145.923506), - ($$3821$$,$$BULN BULN$$,#{state_id_vic},-38.099914,145.923506), - ($$3821$$,$$BULN BULN EAST$$,#{state_id_vic},-38.099914,145.923506), - ($$3821$$,$$CROSSOVER$$,#{state_id_vic},-38.099914,145.923506), - ($$3821$$,$$ELLINBANK$$,#{state_id_vic},-38.099914,145.923506), - ($$3821$$,$$FERNDALE$$,#{state_id_vic},-38.099914,145.923506), - ($$3821$$,$$LARDNER$$,#{state_id_vic},-38.099914,145.923506), - ($$3821$$,$$NILMA$$,#{state_id_vic},-38.099914,145.923506), - ($$3821$$,$$NILMA NORTH$$,#{state_id_vic},-38.099914,145.923506), - ($$3821$$,$$ROKEBY$$,#{state_id_vic},-38.099914,145.923506), - ($$3821$$,$$SEAVIEW$$,#{state_id_vic},-38.099914,145.923506), - ($$3821$$,$$SHADY CREEK$$,#{state_id_vic},-38.099914,145.923506), - ($$3821$$,$$TETOORA ROAD$$,#{state_id_vic},-38.099914,145.923506), - ($$3821$$,$$TORWOOD$$,#{state_id_vic},-38.099914,145.923506), - ($$3821$$,$$WARRAGUL SOUTH$$,#{state_id_vic},-38.099914,145.923506), - ($$3821$$,$$WARRAGUL WEST$$,#{state_id_vic},-38.099914,145.923506), - ($$3822$$,$$CLOVERLEA$$,#{state_id_vic},-38.244952,145.999245), - ($$3822$$,$$DARNUM$$,#{state_id_vic},-38.244952,145.999245), - ($$3822$$,$$GAINSBOROUGH$$,#{state_id_vic},-38.244952,145.999245), - ($$3823$$,$$ALLAMBEE$$,#{state_id_vic},-38.263213,146.034059), - ($$3823$$,$$YARRAGON$$,#{state_id_vic},-38.263213,146.034059), - ($$3823$$,$$YARRAGON SOUTH$$,#{state_id_vic},-38.263213,146.034059), - ($$3824$$,$$CHILDERS$$,#{state_id_vic},-38.298917,146.114577), - ($$3824$$,$$NARRACAN$$,#{state_id_vic},-38.298917,146.114577), - ($$3824$$,$$THORPDALE SOUTH$$,#{state_id_vic},-38.298917,146.114577), - ($$3824$$,$$TRAFALGAR$$,#{state_id_vic},-38.298917,146.114577), - ($$3824$$,$$TRAFALGAR EAST$$,#{state_id_vic},-38.298917,146.114577), - ($$3824$$,$$TRAFALGAR SOUTH$$,#{state_id_vic},-38.298917,146.114577), - ($$3825$$,$$ABERFELDY$$,#{state_id_vic},-37.696566,146.364064), - ($$3825$$,$$AMOR$$,#{state_id_vic},-37.696566,146.364064), - ($$3825$$,$$BOOLA$$,#{state_id_vic},-37.696566,146.364064), - ($$3825$$,$$CARINGAL$$,#{state_id_vic},-37.696566,146.364064), - ($$3825$$,$$COALVILLE$$,#{state_id_vic},-37.696566,146.364064), - ($$3825$$,$$COOPERS CREEK$$,#{state_id_vic},-37.696566,146.364064), - ($$3825$$,$$ERICA$$,#{state_id_vic},-37.696566,146.364064), - ($$3825$$,$$FUMINA$$,#{state_id_vic},-37.696566,146.364064), - ($$3825$$,$$FUMINA SOUTH$$,#{state_id_vic},-37.696566,146.364064), - ($$3825$$,$$HERNES OAK$$,#{state_id_vic},-37.696566,146.364064), - ($$3825$$,$$HILL END$$,#{state_id_vic},-37.696566,146.364064), - ($$3825$$,$$JACOB CREEK$$,#{state_id_vic},-37.696566,146.364064), - ($$3825$$,$$JERICHO$$,#{state_id_vic},-37.696566,146.364064), - ($$3825$$,$$MOE$$,#{state_id_vic},-37.696566,146.364064), - ($$3825$$,$$MOE SOUTH$$,#{state_id_vic},-37.696566,146.364064), - ($$3825$$,$$MOONDARRA$$,#{state_id_vic},-37.696566,146.364064), - ($$3825$$,$$NEWBOROUGH$$,#{state_id_vic},-37.696566,146.364064), - ($$3825$$,$$NEWBOROUGH EAST$$,#{state_id_vic},-37.696566,146.364064), - ($$3825$$,$$RAWSON$$,#{state_id_vic},-37.696566,146.364064), - ($$3825$$,$$TANJIL$$,#{state_id_vic},-37.696566,146.364064), - ($$3825$$,$$TANJIL SOUTH$$,#{state_id_vic},-37.696566,146.364064), - ($$3825$$,$$THALLOO$$,#{state_id_vic},-37.696566,146.364064), - ($$3825$$,$$THOMSON$$,#{state_id_vic},-37.696566,146.364064), - ($$3825$$,$$TOOMBON$$,#{state_id_vic},-37.696566,146.364064), - ($$3825$$,$$WALHALLA$$,#{state_id_vic},-37.696566,146.364064), - ($$3825$$,$$WALHALLA EAST$$,#{state_id_vic},-37.696566,146.364064), - ($$3825$$,$$WESTBURY$$,#{state_id_vic},-37.696566,146.364064), - ($$3825$$,$$WILLOW GROVE$$,#{state_id_vic},-37.696566,146.364064), - ($$3825$$,$$YALLOURN$$,#{state_id_vic},-37.696566,146.364064), - ($$3825$$,$$YALLOURN NORTH$$,#{state_id_vic},-37.696566,146.364064), - ($$3831$$,$$NEERIM$$,#{state_id_vic},-37.962769,145.993328), - ($$3831$$,$$NEERIM EAST$$,#{state_id_vic},-37.962769,145.993328), - ($$3831$$,$$NEERIM SOUTH$$,#{state_id_vic},-37.962769,145.993328), - ($$3832$$,$$NAYOOK$$,#{state_id_vic},-37.9133,145.9339), - ($$3832$$,$$NEERIM JUNCTION$$,#{state_id_vic},-37.9133,145.9339), - ($$3832$$,$$NEERIM NORTH$$,#{state_id_vic},-37.9133,145.9339), - ($$3833$$,$$ADA$$,#{state_id_vic},-37.84087,145.849688), - ($$3833$$,$$BAW BAW$$,#{state_id_vic},-37.84087,145.849688), - ($$3833$$,$$BAW BAW VILLAGE$$,#{state_id_vic},-37.84087,145.849688), - ($$3833$$,$$GENTLE ANNIE$$,#{state_id_vic},-37.84087,145.849688), - ($$3833$$,$$ICY CREEK$$,#{state_id_vic},-37.84087,145.849688), - ($$3833$$,$$LOCH VALLEY$$,#{state_id_vic},-37.84087,145.849688), - ($$3833$$,$$NOOJEE$$,#{state_id_vic},-37.84087,145.849688), - ($$3833$$,$$PIEDMONT$$,#{state_id_vic},-37.84087,145.849688), - ($$3833$$,$$TANJIL BREN$$,#{state_id_vic},-37.84087,145.849688), - ($$3833$$,$$TOORONGO$$,#{state_id_vic},-37.84087,145.849688), - ($$3833$$,$$VESPER$$,#{state_id_vic},-37.84087,145.849688), - ($$3835$$,$$THORPDALE$$,#{state_id_vic},-38.331875,146.118691), - ($$3840$$,$$DRIFFIELD$$,#{state_id_vic},-38.278708,146.321254), - ($$3840$$,$$HAZELWOOD$$,#{state_id_vic},-38.278708,146.321254), - ($$3840$$,$$HAZELWOOD NORTH$$,#{state_id_vic},-38.278708,146.321254), - ($$3840$$,$$HAZELWOOD SOUTH$$,#{state_id_vic},-38.278708,146.321254), - ($$3840$$,$$JEERALANG$$,#{state_id_vic},-38.278708,146.321254), - ($$3840$$,$$JEERALANG JUNCTION$$,#{state_id_vic},-38.278708,146.321254), - ($$3840$$,$$MARYVALE$$,#{state_id_vic},-38.278708,146.321254), - ($$3840$$,$$MID VALLEY$$,#{state_id_vic},-38.278708,146.321254), - ($$3840$$,$$MORWELL$$,#{state_id_vic},-38.278708,146.321254), - ($$3840$$,$$MORWELL EAST$$,#{state_id_vic},-38.278708,146.321254), - ($$3840$$,$$MORWELL UPPER$$,#{state_id_vic},-38.278708,146.321254), - ($$3841$$,$$GIPPSLAND MC$$,#{state_id_vic},0.0,0.0), - ($$3842$$,$$CHURCHILL$$,#{state_id_vic},-38.321315,146.417986), - ($$3844$$,$$BLACKWARRY$$,#{state_id_vic},-38.406405,146.637984), - ($$3844$$,$$CALLIGNEE$$,#{state_id_vic},-38.406405,146.637984), - ($$3844$$,$$CALLIGNEE NORTH$$,#{state_id_vic},-38.406405,146.637984), - ($$3844$$,$$CALLIGNEE SOUTH$$,#{state_id_vic},-38.406405,146.637984), - ($$3844$$,$$CARRAJUNG$$,#{state_id_vic},-38.406405,146.637984), - ($$3844$$,$$CARRAJUNG LOWER$$,#{state_id_vic},-38.406405,146.637984), - ($$3844$$,$$CARRAJUNG SOUTH$$,#{state_id_vic},-38.406405,146.637984), - ($$3844$$,$$FLYNN$$,#{state_id_vic},-38.406405,146.637984), - ($$3844$$,$$FLYNNS CREEK$$,#{state_id_vic},-38.406405,146.637984), - ($$3844$$,$$KOORNALLA$$,#{state_id_vic},-38.406405,146.637984), - ($$3844$$,$$LOY YANG$$,#{state_id_vic},-38.406405,146.637984), - ($$3844$$,$$MOUNT TASSIE$$,#{state_id_vic},-38.406405,146.637984), - ($$3844$$,$$TRARALGON$$,#{state_id_vic},-38.406405,146.637984), - ($$3844$$,$$TRARALGON EAST$$,#{state_id_vic},-38.406405,146.637984), - ($$3844$$,$$TRARALGON SOUTH$$,#{state_id_vic},-38.406405,146.637984), - ($$3844$$,$$TYERS$$,#{state_id_vic},-38.406405,146.637984), - ($$3847$$,$$HIAMDALE$$,#{state_id_vic},-38.26363,146.753319), - ($$3847$$,$$NAMBROK$$,#{state_id_vic},-38.26363,146.753319), - ($$3847$$,$$ROSEDALE$$,#{state_id_vic},-38.26363,146.753319), - ($$3847$$,$$WILLUNG$$,#{state_id_vic},-38.26363,146.753319), - ($$3847$$,$$WILLUNG SOUTH$$,#{state_id_vic},-38.26363,146.753319), - ($$3850$$,$$SALE$$,#{state_id_vic},-38.415897,147.083536), - ($$3850$$,$$SALE NORTH$$,#{state_id_vic},-38.415897,147.083536), - ($$3850$$,$$WURRUK$$,#{state_id_vic},-38.415897,147.083536), - ($$3851$$,$$AIRLY$$,#{state_id_vic},-38.035548,147.084398), - ($$3851$$,$$BUNDALAGUAH$$,#{state_id_vic},-38.035548,147.084398), - ($$3851$$,$$CLYDEBANK$$,#{state_id_vic},-38.035548,147.084398), - ($$3851$$,$$COBAINS$$,#{state_id_vic},-38.035548,147.084398), - ($$3851$$,$$DARRIMAN$$,#{state_id_vic},-38.035548,147.084398), - ($$3851$$,$$DUTSON$$,#{state_id_vic},-38.035548,147.084398), - ($$3851$$,$$DUTSON DOWNS$$,#{state_id_vic},-38.035548,147.084398), - ($$3851$$,$$FLAMINGO BEACH$$,#{state_id_vic},-38.035548,147.084398), - ($$3851$$,$$FULHAM$$,#{state_id_vic},-38.035548,147.084398), - ($$3851$$,$$GIFFARD$$,#{state_id_vic},-38.035548,147.084398), - ($$3851$$,$$GIFFARD WEST$$,#{state_id_vic},-38.035548,147.084398), - ($$3851$$,$$GLOMAR BEACH$$,#{state_id_vic},-38.035548,147.084398), - ($$3851$$,$$GOLDEN BEACH$$,#{state_id_vic},-38.035548,147.084398), - ($$3851$$,$$KILMANY$$,#{state_id_vic},-38.035548,147.084398), - ($$3851$$,$$LAKE WELLINGTON$$,#{state_id_vic},-38.035548,147.084398), - ($$3851$$,$$LOCH SPORT$$,#{state_id_vic},-38.035548,147.084398), - ($$3851$$,$$LONGFORD$$,#{state_id_vic},-38.035548,147.084398), - ($$3851$$,$$MONTGOMERY$$,#{state_id_vic},-38.035548,147.084398), - ($$3851$$,$$MYRTLEBANK$$,#{state_id_vic},-38.035548,147.084398), - ($$3851$$,$$PARADISE BEACH$$,#{state_id_vic},-38.035548,147.084398), - ($$3851$$,$$PEARSONDALE$$,#{state_id_vic},-38.035548,147.084398), - ($$3851$$,$$SEACOMBE$$,#{state_id_vic},-38.035548,147.084398), - ($$3851$$,$$SEASPRAY$$,#{state_id_vic},-38.035548,147.084398), - ($$3851$$,$$SOMERTON PARK$$,#{state_id_vic},-38.035548,147.084398), - ($$3851$$,$$STRADBROKE$$,#{state_id_vic},-38.035548,147.084398), - ($$3851$$,$$THE HEART$$,#{state_id_vic},-38.035548,147.084398), - ($$3851$$,$$THE HONEYSUCKLES$$,#{state_id_vic},-38.035548,147.084398), - ($$3852$$,$$EAST SALE$$,#{state_id_vic},-38.106731,147.134356), - ($$3852$$,$$SALE EAST RAAF$$,#{state_id_vic},-38.106731,147.134356), - ($$3853$$,$$SALE$$,#{state_id_vic},-38.095569,146.059953), - ($$3854$$,$$GLENGARRY$$,#{state_id_vic},-38.124641,146.573643), - ($$3854$$,$$GLENGARRY NORTH$$,#{state_id_vic},-38.124641,146.573643), - ($$3854$$,$$GLENGARRY WEST$$,#{state_id_vic},-38.124641,146.573643), - ($$3856$$,$$TOONGABBIE$$,#{state_id_vic},-38.066096,146.670202), - ($$3857$$,$$COWWARR$$,#{state_id_vic},-38.003449,146.688721), - ($$3858$$,$$ARBUCKLE$$,#{state_id_vic},-37.974475,146.689838), - ($$3858$$,$$BILLABONG$$,#{state_id_vic},-37.974475,146.689838), - ($$3858$$,$$BURAGWONDUC$$,#{state_id_vic},-37.974475,146.689838), - ($$3858$$,$$CROOKAYAN$$,#{state_id_vic},-37.974475,146.689838), - ($$3858$$,$$DAWSON$$,#{state_id_vic},-37.974475,146.689838), - ($$3858$$,$$DENISON$$,#{state_id_vic},-37.974475,146.689838), - ($$3858$$,$$GILLUM$$,#{state_id_vic},-37.974475,146.689838), - ($$3858$$,$$GLENFALLOCH$$,#{state_id_vic},-37.974475,146.689838), - ($$3858$$,$$GLENMAGGIE$$,#{state_id_vic},-37.974475,146.689838), - ($$3858$$,$$HEYFIELD$$,#{state_id_vic},-37.974475,146.689838), - ($$3858$$,$$HOWITT PLAINS$$,#{state_id_vic},-37.974475,146.689838), - ($$3858$$,$$LICOLA$$,#{state_id_vic},-37.974475,146.689838), - ($$3858$$,$$LICOLA NORTH$$,#{state_id_vic},-37.974475,146.689838), - ($$3858$$,$$REYNARD$$,#{state_id_vic},-37.974475,146.689838), - ($$3858$$,$$SARGOOD$$,#{state_id_vic},-37.974475,146.689838), - ($$3858$$,$$SEATON$$,#{state_id_vic},-37.974475,146.689838), - ($$3858$$,$$TAMBORITHA$$,#{state_id_vic},-37.974475,146.689838), - ($$3858$$,$$WINNINDOO$$,#{state_id_vic},-37.974475,146.689838), - ($$3858$$,$$WORROWING$$,#{state_id_vic},-37.974475,146.689838), - ($$3858$$,$$YANGOURA$$,#{state_id_vic},-37.974475,146.689838), - ($$3859$$,$$MAFFRA WEST UPPER$$,#{state_id_vic},-37.886136,146.848259), - ($$3859$$,$$NEWRY$$,#{state_id_vic},-37.886136,146.848259), - ($$3859$$,$$TINAMBA$$,#{state_id_vic},-37.886136,146.848259), - ($$3859$$,$$TINAMBA WEST$$,#{state_id_vic},-37.886136,146.848259), - ($$3860$$,$$BOISDALE$$,#{state_id_vic},-37.880398,146.987326), - ($$3860$$,$$BRIAGOLONG$$,#{state_id_vic},-37.880398,146.987326), - ($$3860$$,$$BUSHY PARK$$,#{state_id_vic},-37.880398,146.987326), - ($$3860$$,$$COONGULLA$$,#{state_id_vic},-37.880398,146.987326), - ($$3860$$,$$KOOROOL$$,#{state_id_vic},-37.880398,146.987326), - ($$3860$$,$$MAFFRA$$,#{state_id_vic},-37.880398,146.987326), - ($$3860$$,$$MONOMAK$$,#{state_id_vic},-37.880398,146.987326), - ($$3860$$,$$MOROKA$$,#{state_id_vic},-37.880398,146.987326), - ($$3860$$,$$NAP NAP MARRA$$,#{state_id_vic},-37.880398,146.987326), - ($$3860$$,$$RIVERSLEA$$,#{state_id_vic},-37.880398,146.987326), - ($$3860$$,$$TOOLOME$$,#{state_id_vic},-37.880398,146.987326), - ($$3860$$,$$VALENCIA CREEK$$,#{state_id_vic},-37.880398,146.987326), - ($$3860$$,$$WOOLENOOK$$,#{state_id_vic},-37.880398,146.987326), - ($$3860$$,$$WRATHUNG$$,#{state_id_vic},-37.880398,146.987326), - ($$3860$$,$$WRIXON$$,#{state_id_vic},-37.880398,146.987326), - ($$3862$$,$$BUDGEE BUDGEE$$,#{state_id_vic},-37.573541,147.187425), - ($$3862$$,$$COBBANNAH$$,#{state_id_vic},-37.573541,147.187425), - ($$3862$$,$$COWA$$,#{state_id_vic},-37.573541,147.187425), - ($$3862$$,$$CROOKED RIVER$$,#{state_id_vic},-37.573541,147.187425), - ($$3862$$,$$DARGO$$,#{state_id_vic},-37.573541,147.187425), - ($$3862$$,$$HAWKHURST$$,#{state_id_vic},-37.573541,147.187425), - ($$3862$$,$$HOLLANDS LANDING$$,#{state_id_vic},-37.573541,147.187425), - ($$3862$$,$$LLOWALONG$$,#{state_id_vic},-37.573541,147.187425), - ($$3862$$,$$MEERLIEU$$,#{state_id_vic},-37.573541,147.187425), - ($$3862$$,$$MIOWERA$$,#{state_id_vic},-37.573541,147.187425), - ($$3862$$,$$MOORNAPA$$,#{state_id_vic},-37.573541,147.187425), - ($$3862$$,$$MUNRO$$,#{state_id_vic},-37.573541,147.187425), - ($$3862$$,$$PERRY BRIDGE$$,#{state_id_vic},-37.573541,147.187425), - ($$3862$$,$$STOCKDALE$$,#{state_id_vic},-37.573541,147.187425), - ($$3862$$,$$STRATFORD$$,#{state_id_vic},-37.573541,147.187425), - ($$3862$$,$$WATERFORD$$,#{state_id_vic},-37.573541,147.187425), - ($$3862$$,$$WONGUNGARRA$$,#{state_id_vic},-37.573541,147.187425), - ($$3864$$,$$FERNBANK$$,#{state_id_vic},-37.860878,147.341513), - ($$3864$$,$$GLENALADALE$$,#{state_id_vic},-37.860878,147.341513), - ($$3864$$,$$THE FINGERBOARD$$,#{state_id_vic},-37.860878,147.341513), - ($$3865$$,$$LINDENOW$$,#{state_id_vic},-37.800359,147.458491), - ($$3869$$,$$JUMBUK$$,#{state_id_vic},-38.398143,146.424652), - ($$3869$$,$$YINNAR$$,#{state_id_vic},-38.398143,146.424652), - ($$3869$$,$$YINNAR SOUTH$$,#{state_id_vic},-38.398143,146.424652), - ($$3870$$,$$BOOLARRA$$,#{state_id_vic},-38.378924,146.275377), - ($$3870$$,$$BOOLARRA SOUTH$$,#{state_id_vic},-38.378924,146.275377), - ($$3870$$,$$BUDGEREE$$,#{state_id_vic},-38.378924,146.275377), - ($$3870$$,$$GRAND RIDGE$$,#{state_id_vic},-38.378924,146.275377), - ($$3870$$,$$JOHNSTONES HILL$$,#{state_id_vic},-38.378924,146.275377), - ($$3871$$,$$ALLAMBEE RESERVE$$,#{state_id_vic},-38.285262,146.054894), - ($$3871$$,$$ALLAMBEE SOUTH$$,#{state_id_vic},-38.285262,146.054894), - ($$3871$$,$$BAROMI$$,#{state_id_vic},-38.285262,146.054894), - ($$3871$$,$$DARLIMURLA$$,#{state_id_vic},-38.285262,146.054894), - ($$3871$$,$$DELBURN$$,#{state_id_vic},-38.285262,146.054894), - ($$3871$$,$$DOLLAR$$,#{state_id_vic},-38.285262,146.054894), - ($$3871$$,$$MIRBOO$$,#{state_id_vic},-38.285262,146.054894), - ($$3871$$,$$MIRBOO NORTH$$,#{state_id_vic},-38.285262,146.054894), - ($$3873$$,$$GORMANDALE$$,#{state_id_vic},-38.285925,146.719797), - ($$3874$$,$$CHERRILONG$$,#{state_id_vic},-38.379257,146.791268), - ($$3874$$,$$MCLOUGHLINS BEACH$$,#{state_id_vic},-38.379257,146.791268), - ($$3874$$,$$WOODSIDE$$,#{state_id_vic},-38.379257,146.791268), - ($$3874$$,$$WOODSIDE BEACH$$,#{state_id_vic},-38.379257,146.791268), - ($$3874$$,$$WOODSIDE NORTH$$,#{state_id_vic},-38.379257,146.791268), - ($$3875$$,$$BAIRNSDALE$$,#{state_id_vic},-37.825114,147.628962), - ($$3875$$,$$BANKSIA PENINSULA$$,#{state_id_vic},-37.825114,147.628962), - ($$3875$$,$$BENGWORDEN$$,#{state_id_vic},-37.825114,147.628962), - ($$3875$$,$$BROADLANDS$$,#{state_id_vic},-37.825114,147.628962), - ($$3875$$,$$BULLUMWAAL$$,#{state_id_vic},-37.825114,147.628962), - ($$3875$$,$$CALULU$$,#{state_id_vic},-37.825114,147.628962), - ($$3875$$,$$CLIFTON CREEK$$,#{state_id_vic},-37.825114,147.628962), - ($$3875$$,$$DEPTFORD$$,#{state_id_vic},-37.825114,147.628962), - ($$3875$$,$$EAST BAIRNSDALE$$,#{state_id_vic},-37.825114,147.628962), - ($$3875$$,$$ELLASWOOD$$,#{state_id_vic},-37.825114,147.628962), - ($$3875$$,$$FAIRY DELL$$,#{state_id_vic},-37.825114,147.628962), - ($$3875$$,$$FLAGGY CREEK$$,#{state_id_vic},-37.825114,147.628962), - ($$3875$$,$$FORGE CREEK$$,#{state_id_vic},-37.825114,147.628962), - ($$3875$$,$$GOON NURE$$,#{state_id_vic},-37.825114,147.628962), - ($$3875$$,$$GRANITE ROCK$$,#{state_id_vic},-37.825114,147.628962), - ($$3875$$,$$HILLSIDE$$,#{state_id_vic},-37.825114,147.628962), - ($$3875$$,$$IGUANA CREEK$$,#{state_id_vic},-37.825114,147.628962), - ($$3875$$,$$LINDENOW SOUTH$$,#{state_id_vic},-37.825114,147.628962), - ($$3875$$,$$LUCKNOW$$,#{state_id_vic},-37.825114,147.628962), - ($$3875$$,$$MARTHAVALE$$,#{state_id_vic},-37.825114,147.628962), - ($$3875$$,$$MELWOOD$$,#{state_id_vic},-37.825114,147.628962), - ($$3875$$,$$MERRIJIG$$,#{state_id_vic},-37.825114,147.628962), - ($$3875$$,$$MOUNT TAYLOR$$,#{state_id_vic},-37.825114,147.628962), - ($$3875$$,$$NEWLANDS ARM$$,#{state_id_vic},-37.825114,147.628962), - ($$3875$$,$$RYANS$$,#{state_id_vic},-37.825114,147.628962), - ($$3875$$,$$SARSFIELD$$,#{state_id_vic},-37.825114,147.628962), - ($$3875$$,$$TABBERABBERA$$,#{state_id_vic},-37.825114,147.628962), - ($$3875$$,$$WALPA$$,#{state_id_vic},-37.825114,147.628962), - ($$3875$$,$$WATERHOLES$$,#{state_id_vic},-37.825114,147.628962), - ($$3875$$,$$WENTWORTH$$,#{state_id_vic},-37.825114,147.628962), - ($$3875$$,$$WOODGLEN$$,#{state_id_vic},-37.825114,147.628962), - ($$3875$$,$$WUK WUK$$,#{state_id_vic},-37.825114,147.628962), - ($$3875$$,$$WY YUNG$$,#{state_id_vic},-37.825114,147.628962), - ($$3878$$,$$EAGLE POINT$$,#{state_id_vic},-37.89463,147.670253), - ($$3880$$,$$BOOLE POOLE$$,#{state_id_vic},-37.924908,147.693728), - ($$3880$$,$$OCEAN GRANGE$$,#{state_id_vic},-37.924908,147.693728), - ($$3880$$,$$PAYNESVILLE$$,#{state_id_vic},-37.924908,147.693728), - ($$3880$$,$$RAYMOND ISLAND$$,#{state_id_vic},-37.924908,147.693728), - ($$3882$$,$$NICHOLSON$$,#{state_id_vic},-37.815478,147.734243), - ($$3885$$,$$BRUMBY$$,#{state_id_vic},-37.70793,147.831322), - ($$3885$$,$$BRUTHEN$$,#{state_id_vic},-37.70793,147.831322), - ($$3885$$,$$BUCHAN$$,#{state_id_vic},-37.70793,147.831322), - ($$3885$$,$$BUCHAN SOUTH$$,#{state_id_vic},-37.70793,147.831322), - ($$3885$$,$$BUTCHERS RIDGE$$,#{state_id_vic},-37.70793,147.831322), - ($$3885$$,$$GELANTIPY$$,#{state_id_vic},-37.70793,147.831322), - ($$3885$$,$$MOSSIFACE$$,#{state_id_vic},-37.70793,147.831322), - ($$3885$$,$$MURRINDAL$$,#{state_id_vic},-37.70793,147.831322), - ($$3885$$,$$SUGGAN BUGGAN$$,#{state_id_vic},-37.70793,147.831322), - ($$3885$$,$$TAMBO UPPER$$,#{state_id_vic},-37.70793,147.831322), - ($$3885$$,$$TIMBARRA$$,#{state_id_vic},-37.70793,147.831322), - ($$3885$$,$$W TREE$$,#{state_id_vic},-37.70793,147.831322), - ($$3885$$,$$WISELEIGH$$,#{state_id_vic},-37.70793,147.831322), - ($$3885$$,$$WULGULMERANG$$,#{state_id_vic},-37.70793,147.831322), - ($$3885$$,$$WULGULMERANG EAST$$,#{state_id_vic},-37.70793,147.831322), - ($$3885$$,$$WULGULMERANG WEST$$,#{state_id_vic},-37.70793,147.831322), - ($$3885$$,$$YALMY$$,#{state_id_vic},-37.70793,147.831322), - ($$3886$$,$$NEWMERELLA$$,#{state_id_vic},-37.735401,148.433878), - ($$3887$$,$$LAKE TYERS$$,#{state_id_vic},-37.841038,148.121299), - ($$3887$$,$$NOWA NOWA$$,#{state_id_vic},-37.841038,148.121299), - ($$3887$$,$$WAIREWA$$,#{state_id_vic},-37.841038,148.121299), - ($$3888$$,$$BENDOC$$,#{state_id_vic},-37.146838,148.884272), - ($$3888$$,$$BETE BOLONG$$,#{state_id_vic},-37.146838,148.884272), - ($$3888$$,$$BETE BOLONG NORTH$$,#{state_id_vic},-37.146838,148.884272), - ($$3888$$,$$BONANG$$,#{state_id_vic},-37.146838,148.884272), - ($$3888$$,$$BRODRIBB RIVER$$,#{state_id_vic},-37.146838,148.884272), - ($$3888$$,$$CABANANDRA$$,#{state_id_vic},-37.146838,148.884272), - ($$3888$$,$$CAPE CONRAN$$,#{state_id_vic},-37.146838,148.884272), - ($$3888$$,$$CORRINGLE$$,#{state_id_vic},-37.146838,148.884272), - ($$3888$$,$$DEDDICK VALLEY$$,#{state_id_vic},-37.146838,148.884272), - ($$3888$$,$$GOONGERAH$$,#{state_id_vic},-37.146838,148.884272), - ($$3888$$,$$HAYDENS BOG$$,#{state_id_vic},-37.146838,148.884272), - ($$3888$$,$$JARRAHMOND$$,#{state_id_vic},-37.146838,148.884272), - ($$3888$$,$$MARLO$$,#{state_id_vic},-37.146838,148.884272), - ($$3888$$,$$MARTINS CREEK$$,#{state_id_vic},-37.146838,148.884272), - ($$3888$$,$$NURRAN$$,#{state_id_vic},-37.146838,148.884272), - ($$3888$$,$$ORBOST$$,#{state_id_vic},-37.146838,148.884272), - ($$3888$$,$$SIMPSONS CREEK$$,#{state_id_vic},-37.146838,148.884272), - ($$3888$$,$$TOSTAREE$$,#{state_id_vic},-37.146838,148.884272), - ($$3888$$,$$TUBBUT$$,#{state_id_vic},-37.146838,148.884272), - ($$3888$$,$$WAYGARA$$,#{state_id_vic},-37.146838,148.884272), - ($$3888$$,$$WOMBAT CREEK$$,#{state_id_vic},-37.146838,148.884272), - ($$3889$$,$$BELLBIRD CREEK$$,#{state_id_vic},-37.927902,145.906217), - ($$3889$$,$$BEMM RIVER$$,#{state_id_vic},-37.927902,145.906217), - ($$3889$$,$$CABBAGE TREE CREEK$$,#{state_id_vic},-37.927902,145.906217), - ($$3889$$,$$CLUB TERRACE$$,#{state_id_vic},-37.927902,145.906217), - ($$3889$$,$$COMBIENBAR$$,#{state_id_vic},-37.927902,145.906217), - ($$3889$$,$$ERRINUNDRA$$,#{state_id_vic},-37.927902,145.906217), - ($$3889$$,$$MANORINA$$,#{state_id_vic},-37.927902,145.906217), - ($$3890$$,$$BULDAH$$,#{state_id_vic},-37.270391,149.144376), - ($$3890$$,$$CANN RIVER$$,#{state_id_vic},-37.270391,149.144376), - ($$3890$$,$$CHANDLERS CREEK$$,#{state_id_vic},-37.270391,149.144376), - ($$3890$$,$$NOORINBEE$$,#{state_id_vic},-37.270391,149.144376), - ($$3890$$,$$NOORINBEE NORTH$$,#{state_id_vic},-37.270391,149.144376), - ($$3890$$,$$TAMBOON$$,#{state_id_vic},-37.270391,149.144376), - ($$3890$$,$$TONGHI CREEK$$,#{state_id_vic},-37.270391,149.144376), - ($$3891$$,$$GENOA$$,#{state_id_vic},-37.475736,149.592844), - ($$3891$$,$$GIPSY POINT$$,#{state_id_vic},-37.475736,149.592844), - ($$3891$$,$$MARAMINGO CREEK$$,#{state_id_vic},-37.475736,149.592844), - ($$3891$$,$$WALLAGARAUGH$$,#{state_id_vic},-37.475736,149.592844), - ($$3891$$,$$WANGARABELL$$,#{state_id_vic},-37.475736,149.592844), - ($$3891$$,$$WINGAN RIVER$$,#{state_id_vic},-37.475736,149.592844), - ($$3891$$,$$WROXHAM$$,#{state_id_vic},-37.475736,149.592844), - ($$3892$$,$$MALLACOOTA$$,#{state_id_vic},-37.549486,149.729223), - ($$3893$$,$$DOUBLE BRIDGES$$,#{state_id_vic},-37.590191,147.893491), - ($$3893$$,$$STIRLING$$,#{state_id_vic},-37.590191,147.893491), - ($$3893$$,$$TAMBO CROSSING$$,#{state_id_vic},-37.590191,147.893491), - ($$3895$$,$$DOCTORS FLAT$$,#{state_id_vic},-37.298674,147.748627), - ($$3895$$,$$ENSAY$$,#{state_id_vic},-37.298674,147.748627), - ($$3895$$,$$ENSAY NORTH$$,#{state_id_vic},-37.298674,147.748627), - ($$3895$$,$$REEDY FLAT$$,#{state_id_vic},-37.298674,147.748627), - ($$3896$$,$$BINDI$$,#{state_id_vic},-37.112351,147.812864), - ($$3896$$,$$BROOKVILLE$$,#{state_id_vic},-37.112351,147.812864), - ($$3896$$,$$NUNNIONG$$,#{state_id_vic},-37.112351,147.812864), - ($$3896$$,$$SWIFTS CREEK$$,#{state_id_vic},-37.112351,147.812864), - ($$3896$$,$$TONGIO$$,#{state_id_vic},-37.112351,147.812864), - ($$3898$$,$$ANGLERS REST$$,#{state_id_vic},-37.019423,147.470661), - ($$3898$$,$$BINGO MUNJIE$$,#{state_id_vic},-37.019423,147.470661), - ($$3898$$,$$BUNDARA$$,#{state_id_vic},-37.019423,147.470661), - ($$3898$$,$$CASSILIS$$,#{state_id_vic},-37.019423,147.470661), - ($$3898$$,$$COBUNGRA$$,#{state_id_vic},-37.019423,147.470661), - ($$3898$$,$$DINNER PLAIN$$,#{state_id_vic},-37.019423,147.470661), - ($$3898$$,$$GLEN VALLEY$$,#{state_id_vic},-37.019423,147.470661), - ($$3898$$,$$GLEN WILLS$$,#{state_id_vic},-37.019423,147.470661), - ($$3898$$,$$HINNOMUNJIE$$,#{state_id_vic},-37.019423,147.470661), - ($$3898$$,$$OMEO$$,#{state_id_vic},-37.019423,147.470661), - ($$3898$$,$$OMEO VALLEY$$,#{state_id_vic},-37.019423,147.470661), - ($$3898$$,$$SHANNONVALE$$,#{state_id_vic},-37.019423,147.470661), - ($$3900$$,$$BENAMBRA$$,#{state_id_vic},-36.949754,147.703226), - ($$3900$$,$$COBBERAS$$,#{state_id_vic},-36.949754,147.703226), - ($$3902$$,$$BUMBERRAH$$,#{state_id_vic},-37.792719,147.830756), - ($$3902$$,$$JOHNSONVILLE$$,#{state_id_vic},-37.792719,147.830756), - ($$3903$$,$$SWAN REACH$$,#{state_id_vic},-37.810093,147.84454), - ($$3904$$,$$METUNG$$,#{state_id_vic},-37.87152,147.847355), - ($$3909$$,$$KALIMNA$$,#{state_id_vic},-37.857772,147.922922), - ($$3909$$,$$KALIMNA WEST$$,#{state_id_vic},-37.857772,147.922922), - ($$3909$$,$$LAKE BUNGA$$,#{state_id_vic},-37.857772,147.922922), - ($$3909$$,$$LAKE TYERS BEACH$$,#{state_id_vic},-37.857772,147.922922), - ($$3909$$,$$LAKES ENTRANCE$$,#{state_id_vic},-37.857772,147.922922), - ($$3909$$,$$NUNGURNER$$,#{state_id_vic},-37.857772,147.922922), - ($$3909$$,$$NYERIMILANG$$,#{state_id_vic},-37.857772,147.922922), - ($$3909$$,$$TOORLOO ARM$$,#{state_id_vic},-37.857772,147.922922), - ($$3910$$,$$LANGWARRIN$$,#{state_id_vic},-38.153809,145.185851), - ($$3911$$,$$BAXTER$$,#{state_id_vic},-38.195383,145.158007), - ($$3911$$,$$LANGWARRIN SOUTH$$,#{state_id_vic},-38.195383,145.158007), - ($$3912$$,$$PEARCEDALE$$,#{state_id_vic},-38.202617,145.232339), - ($$3912$$,$$SOMERVILLE$$,#{state_id_vic},-38.202617,145.232339), - ($$3913$$,$$TYABB$$,#{state_id_vic},-38.259808,145.186464), - ($$3915$$,$$HASTINGS$$,#{state_id_vic},-38.30771,145.190507), - ($$3915$$,$$TUERONG$$,#{state_id_vic},-38.30771,145.190507), - ($$3916$$,$$MERRICKS$$,#{state_id_vic},-38.392646,145.085801), - ($$3916$$,$$POINT LEO$$,#{state_id_vic},-38.392646,145.085801), - ($$3916$$,$$SHOREHAM$$,#{state_id_vic},-38.392646,145.085801), - ($$3918$$,$$BITTERN$$,#{state_id_vic},-38.337376,145.177993), - ($$3919$$,$$CRIB POINT$$,#{state_id_vic},-38.366119,145.204068), - ($$3920$$,$$HMAS CERBERUS$$,#{state_id_vic},0.0,0.0), - ($$3921$$,$$FRENCH ISLAND$$,#{state_id_vic},-38.342506,145.338749), - ($$3922$$,$$COWES$$,#{state_id_vic},-38.450286,145.239003), - ($$3922$$,$$SILVERLEAVES$$,#{state_id_vic},-38.450286,145.239003), - ($$3922$$,$$SMITHS BEACH$$,#{state_id_vic},-38.450286,145.239003), - ($$3922$$,$$SUMMERLANDS$$,#{state_id_vic},-38.450286,145.239003), - ($$3922$$,$$SUNDERLAND BAY$$,#{state_id_vic},-38.450286,145.239003), - ($$3922$$,$$SUNSET STRIP$$,#{state_id_vic},-38.450286,145.239003), - ($$3922$$,$$SURF BEACH$$,#{state_id_vic},-38.450286,145.239003), - ($$3922$$,$$VENTNOR$$,#{state_id_vic},-38.450286,145.239003), - ($$3922$$,$$WIMBLEDON HEIGHTS$$,#{state_id_vic},-38.450286,145.239003), - ($$3923$$,$$RHYLL$$,#{state_id_vic},-38.460584,145.307964), - ($$3925$$,$$CAPE WOOLAMAI$$,#{state_id_vic},-38.54338,145.340775), - ($$3925$$,$$CHURCHILL ISLAND$$,#{state_id_vic},-38.54338,145.340775), - ($$3925$$,$$NEWHAVEN$$,#{state_id_vic},-38.54338,145.340775), - ($$3925$$,$$SAN REMO$$,#{state_id_vic},-38.54338,145.340775), - ($$3926$$,$$BALNARRING$$,#{state_id_vic},-38.373497,145.126733), - ($$3926$$,$$BALNARRING BEACH$$,#{state_id_vic},-38.373497,145.126733), - ($$3926$$,$$MERRICKS BEACH$$,#{state_id_vic},-38.373497,145.126733), - ($$3926$$,$$MERRICKS NORTH$$,#{state_id_vic},-38.373497,145.126733), - ($$3927$$,$$SOMERS$$,#{state_id_vic},-38.383442,145.147627), - ($$3928$$,$$MAIN RIDGE$$,#{state_id_vic},-38.400438,144.970958), - ($$3929$$,$$FLINDERS$$,#{state_id_vic},-38.473815,145.01628), - ($$3930$$,$$KUNYUNG$$,#{state_id_vic},-38.191618,145.079095), - ($$3930$$,$$MOUNT ELIZA$$,#{state_id_vic},-38.191618,145.079095), - ($$3931$$,$$MORNINGTON$$,#{state_id_vic},-38.238386,145.064195), - ($$3933$$,$$MOOROODUC$$,#{state_id_vic},-38.237072,145.087375), - ($$3934$$,$$MOUNT MARTHA$$,#{state_id_vic},-38.302366,144.994873), - ($$3936$$,$$ARTHURS SEAT$$,#{state_id_vic},-38.354428,144.949793), - ($$3936$$,$$DROMANA$$,#{state_id_vic},-38.354428,144.949793), - ($$3936$$,$$SAFETY BEACH$$,#{state_id_vic},-38.354428,144.949793), - ($$3937$$,$$RED HILL$$,#{state_id_vic},-38.37014,145.012073), - ($$3937$$,$$RED HILL SOUTH$$,#{state_id_vic},-38.37014,145.012073), - ($$3938$$,$$MCCRAE$$,#{state_id_vic},-37.990225,145.21956), - ($$3939$$,$$BONEO$$,#{state_id_vic},-38.398469,144.904098), - ($$3939$$,$$CAPE SCHANCK$$,#{state_id_vic},-38.398469,144.904098), - ($$3939$$,$$FINGAL$$,#{state_id_vic},-38.398469,144.904098), - ($$3939$$,$$ROSEBUD$$,#{state_id_vic},-38.398469,144.904098), - ($$3939$$,$$ROSEBUD PLAZA$$,#{state_id_vic},-38.398469,144.904098), - ($$3940$$,$$ROSEBUD WEST$$,#{state_id_vic},0.0,0.0), - ($$3941$$,$$RYE$$,#{state_id_vic},-38.370692,144.824741), - ($$3941$$,$$ST ANDREWS BEACH$$,#{state_id_vic},-38.370692,144.824741), - ($$3941$$,$$TOOTGAROOK$$,#{state_id_vic},-38.370692,144.824741), - ($$3942$$,$$BLAIRGOWRIE$$,#{state_id_vic},-38.359005,144.768511), - ($$3943$$,$$SORRENTO$$,#{state_id_vic},-38.344297,144.73807), - ($$3944$$,$$PORTSEA$$,#{state_id_vic},-38.338347,144.709285), - ($$3945$$,$$JEETHO$$,#{state_id_vic},-37.970297,145.155125), - ($$3945$$,$$KROWERA$$,#{state_id_vic},-37.970297,145.155125), - ($$3945$$,$$LOCH$$,#{state_id_vic},-37.970297,145.155125), - ($$3945$$,$$WOODLEIGH$$,#{state_id_vic},-37.970297,145.155125), - ($$3946$$,$$BENA$$,#{state_id_vic},-38.433031,145.722184), - ($$3950$$,$$KARDELLA SOUTH$$,#{state_id_vic},-38.449367,145.877524), - ($$3950$$,$$KORUMBURRA$$,#{state_id_vic},-38.449367,145.877524), - ($$3950$$,$$KORUMBURRA SOUTH$$,#{state_id_vic},-38.449367,145.877524), - ($$3950$$,$$STRZELECKI$$,#{state_id_vic},-38.449367,145.877524), - ($$3950$$,$$WHITELAW$$,#{state_id_vic},-38.449367,145.877524), - ($$3951$$,$$ARAWATA$$,#{state_id_vic},-38.394907,145.888218), - ($$3951$$,$$FAIRBANK$$,#{state_id_vic},-38.394907,145.888218), - ($$3951$$,$$JUMBUNNA$$,#{state_id_vic},-38.394907,145.888218), - ($$3951$$,$$KARDELLA$$,#{state_id_vic},-38.394907,145.888218), - ($$3951$$,$$KONGWAK$$,#{state_id_vic},-38.394907,145.888218), - ($$3951$$,$$MOYARRA$$,#{state_id_vic},-38.394907,145.888218), - ($$3951$$,$$OUTTRIM$$,#{state_id_vic},-38.394907,145.888218), - ($$3951$$,$$RANCEBY$$,#{state_id_vic},-38.394907,145.888218), - ($$3953$$,$$BERRYS CREEK$$,#{state_id_vic},-38.406667,146.069703), - ($$3953$$,$$BOOROOL$$,#{state_id_vic},-38.406667,146.069703), - ($$3953$$,$$HALLSTON$$,#{state_id_vic},-38.406667,146.069703), - ($$3953$$,$$KOOROOMAN$$,#{state_id_vic},-38.406667,146.069703), - ($$3953$$,$$LEONGATHA$$,#{state_id_vic},-38.406667,146.069703), - ($$3953$$,$$LEONGATHA NORTH$$,#{state_id_vic},-38.406667,146.069703), - ($$3953$$,$$LEONGATHA SOUTH$$,#{state_id_vic},-38.406667,146.069703), - ($$3953$$,$$MARDAN$$,#{state_id_vic},-38.406667,146.069703), - ($$3953$$,$$MOUNT ECCLES$$,#{state_id_vic},-38.406667,146.069703), - ($$3953$$,$$MOUNT ECCLES SOUTH$$,#{state_id_vic},-38.406667,146.069703), - ($$3953$$,$$NERRENA$$,#{state_id_vic},-38.406667,146.069703), - ($$3953$$,$$RUBY$$,#{state_id_vic},-38.406667,146.069703), - ($$3953$$,$$TRIDA$$,#{state_id_vic},-38.406667,146.069703), - ($$3953$$,$$WILD DOG VALLEY$$,#{state_id_vic},-38.406667,146.069703), - ($$3954$$,$$KOONWARRA$$,#{state_id_vic},-38.547015,145.948111), - ($$3956$$,$$DUMBALK$$,#{state_id_vic},-38.531629,146.095008), - ($$3956$$,$$DUMBALK NORTH$$,#{state_id_vic},-38.531629,146.095008), - ($$3956$$,$$MEENIYAN$$,#{state_id_vic},-38.531629,146.095008), - ($$3956$$,$$MIDDLE TARWIN$$,#{state_id_vic},-38.531629,146.095008), - ($$3956$$,$$TARWIN$$,#{state_id_vic},-38.531629,146.095008), - ($$3956$$,$$TARWIN LOWER$$,#{state_id_vic},-38.531629,146.095008), - ($$3956$$,$$VENUS BAY$$,#{state_id_vic},-38.531629,146.095008), - ($$3956$$,$$WALKERVILLE$$,#{state_id_vic},-38.531629,146.095008), - ($$3956$$,$$WALKERVILLE SOUTH$$,#{state_id_vic},-38.531629,146.095008), - ($$3957$$,$$STONY CREEK$$,#{state_id_vic},-37.944787,145.273438), - ($$3958$$,$$BUFFALO$$,#{state_id_vic},-38.642608,146.074806), - ($$3959$$,$$FISH CREEK$$,#{state_id_vic},-38.694358,146.082083), - ($$3959$$,$$SANDY POINT$$,#{state_id_vic},-38.694358,146.082083), - ($$3959$$,$$WARATAH BAY$$,#{state_id_vic},-38.694358,146.082083), - ($$3960$$,$$BENNISON$$,#{state_id_vic},-38.668395,146.266751), - ($$3960$$,$$BOOLARONG$$,#{state_id_vic},-38.668395,146.266751), - ($$3960$$,$$FOSTER$$,#{state_id_vic},-38.668395,146.266751), - ($$3960$$,$$FOSTER NORTH$$,#{state_id_vic},-38.668395,146.266751), - ($$3960$$,$$GUNYAH$$,#{state_id_vic},-38.668395,146.266751), - ($$3960$$,$$MOUNT BEST$$,#{state_id_vic},-38.668395,146.266751), - ($$3960$$,$$SHALLOW INLET$$,#{state_id_vic},-38.668395,146.266751), - ($$3960$$,$$TIDAL RIVER$$,#{state_id_vic},-38.668395,146.266751), - ($$3960$$,$$TURTONS CREEK$$,#{state_id_vic},-38.668395,146.266751), - ($$3960$$,$$WILSONS PROMONTORY$$,#{state_id_vic},-38.668395,146.266751), - ($$3960$$,$$WONGA$$,#{state_id_vic},-38.668395,146.266751), - ($$3960$$,$$WOORARRA WEST$$,#{state_id_vic},-38.668395,146.266751), - ($$3960$$,$$YANAKIE$$,#{state_id_vic},-38.668395,146.266751), - ($$3962$$,$$AGNES$$,#{state_id_vic},-38.670149,146.396151), - ($$3962$$,$$TOORA$$,#{state_id_vic},-38.670149,146.396151), - ($$3962$$,$$TOORA NORTH$$,#{state_id_vic},-38.670149,146.396151), - ($$3962$$,$$WONYIP$$,#{state_id_vic},-38.670149,146.396151), - ($$3962$$,$$WOORARRA EAST$$,#{state_id_vic},-38.670149,146.396151), - ($$3964$$,$$PORT FRANKLIN$$,#{state_id_vic},-38.679229,146.270581), - ($$3965$$,$$PORT WELSHPOOL$$,#{state_id_vic},-38.691833,146.454398), - ($$3966$$,$$BINGINWARRI$$,#{state_id_vic},-38.584197,146.453313), - ($$3966$$,$$HAZEL PARK$$,#{state_id_vic},-38.584197,146.453313), - ($$3966$$,$$WELSHPOOL$$,#{state_id_vic},-38.584197,146.453313), - ($$3967$$,$$HEDLEY$$,#{state_id_vic},-37.693272,144.961131), - ($$3971$$,$$ALBERTON$$,#{state_id_vic},-38.611394,146.66598), - ($$3971$$,$$ALBERTON WEST$$,#{state_id_vic},-38.611394,146.66598), - ($$3971$$,$$BALOOK$$,#{state_id_vic},-38.611394,146.66598), - ($$3971$$,$$CALROSSIE$$,#{state_id_vic},-38.611394,146.66598), - ($$3971$$,$$DEVON NORTH$$,#{state_id_vic},-38.611394,146.66598), - ($$3971$$,$$GELLIONDALE$$,#{state_id_vic},-38.611394,146.66598), - ($$3971$$,$$HIAWATHA$$,#{state_id_vic},-38.611394,146.66598), - ($$3971$$,$$HUNTERSTON$$,#{state_id_vic},-38.611394,146.66598), - ($$3971$$,$$JACK RIVER$$,#{state_id_vic},-38.611394,146.66598), - ($$3971$$,$$LANGSBOROUGH$$,#{state_id_vic},-38.611394,146.66598), - ($$3971$$,$$MACKS CREEK$$,#{state_id_vic},-38.611394,146.66598), - ($$3971$$,$$MADALYA$$,#{state_id_vic},-38.611394,146.66598), - ($$3971$$,$$MANNS BEACH$$,#{state_id_vic},-38.611394,146.66598), - ($$3971$$,$$PORT ALBERT$$,#{state_id_vic},-38.611394,146.66598), - ($$3971$$,$$ROBERTSONS BEACH$$,#{state_id_vic},-38.611394,146.66598), - ($$3971$$,$$SNAKE ISLAND$$,#{state_id_vic},-38.611394,146.66598), - ($$3971$$,$$STACEYS BRIDGE$$,#{state_id_vic},-38.611394,146.66598), - ($$3971$$,$$TARRA VALLEY$$,#{state_id_vic},-38.611394,146.66598), - ($$3971$$,$$TARRAVILLE$$,#{state_id_vic},-38.611394,146.66598), - ($$3971$$,$$WON WRON$$,#{state_id_vic},-38.611394,146.66598), - ($$3971$$,$$YARRAM$$,#{state_id_vic},-38.611394,146.66598), - ($$3975$$,$$LYNBROOK$$,#{state_id_vic},-38.050457,145.257531), - ($$3975$$,$$LYNDHURST$$,#{state_id_vic},-38.050457,145.257531), - ($$3976$$,$$HAMPTON PARK$$,#{state_id_vic},-38.032221,145.267863), - ($$3977$$,$$BOTANIC RIDGE$$,#{state_id_vic},-38.212992,145.315447), - ($$3977$$,$$CANNONS CREEK$$,#{state_id_vic},-38.212992,145.315447), - ($$3977$$,$$CRANBOURNE$$,#{state_id_vic},-38.212992,145.315447), - ($$3977$$,$$CRANBOURNE EAST$$,#{state_id_vic},-38.212992,145.315447), - ($$3977$$,$$CRANBOURNE NORTH$$,#{state_id_vic},-38.212992,145.315447), - ($$3977$$,$$CRANBOURNE SOUTH$$,#{state_id_vic},-38.212992,145.315447), - ($$3977$$,$$CRANBOURNE WEST$$,#{state_id_vic},-38.212992,145.315447), - ($$3977$$,$$DEVON MEADOWS$$,#{state_id_vic},-38.212992,145.315447), - ($$3977$$,$$JUNCTION VILLAGE$$,#{state_id_vic},-38.212992,145.315447), - ($$3977$$,$$SANDHURST$$,#{state_id_vic},-38.212992,145.315447), - ($$3977$$,$$SKYE$$,#{state_id_vic},-38.212992,145.315447), - ($$3978$$,$$CARDINIA$$,#{state_id_vic},-38.145962,145.423291), - ($$3978$$,$$CLYDE$$,#{state_id_vic},-38.145962,145.423291), - ($$3978$$,$$CLYDE NORTH$$,#{state_id_vic},-38.145962,145.423291), - ($$3979$$,$$ALMURTA$$,#{state_id_vic},-38.434865,145.558415), - ($$3979$$,$$GLEN ALVIE$$,#{state_id_vic},-38.434865,145.558415), - ($$3979$$,$$KERNOT$$,#{state_id_vic},-38.434865,145.558415), - ($$3980$$,$$BLIND BIGHT$$,#{state_id_vic},-38.211236,145.340207), - ($$3980$$,$$TOORADIN$$,#{state_id_vic},-38.211236,145.340207), - ($$3980$$,$$WARNEET$$,#{state_id_vic},-38.211236,145.340207), - ($$3981$$,$$BAYLES$$,#{state_id_vic},-38.179036,145.573638), - ($$3981$$,$$CATANI$$,#{state_id_vic},-38.179036,145.573638), - ($$3981$$,$$DALMORE$$,#{state_id_vic},-38.179036,145.573638), - ($$3981$$,$$HEATH HILL$$,#{state_id_vic},-38.179036,145.573638), - ($$3981$$,$$KOO WEE RUP$$,#{state_id_vic},-38.179036,145.573638), - ($$3981$$,$$KOO WEE RUP NORTH$$,#{state_id_vic},-38.179036,145.573638), - ($$3981$$,$$YANNATHAN$$,#{state_id_vic},-38.179036,145.573638), - ($$3984$$,$$ADAMS ESTATE$$,#{state_id_vic},-38.323843,145.539805), - ($$3984$$,$$CALDERMEADE$$,#{state_id_vic},-38.323843,145.539805), - ($$3984$$,$$CORINELLA$$,#{state_id_vic},-38.323843,145.539805), - ($$3984$$,$$CORONET BAY$$,#{state_id_vic},-38.323843,145.539805), - ($$3984$$,$$GRANTVILLE$$,#{state_id_vic},-38.323843,145.539805), - ($$3984$$,$$JAM JERRUP$$,#{state_id_vic},-38.323843,145.539805), - ($$3984$$,$$LANG LANG$$,#{state_id_vic},-38.323843,145.539805), - ($$3984$$,$$LANG LANG EAST$$,#{state_id_vic},-38.323843,145.539805), - ($$3984$$,$$MONOMEITH$$,#{state_id_vic},-38.323843,145.539805), - ($$3984$$,$$PIONEER BAY$$,#{state_id_vic},-38.323843,145.539805), - ($$3984$$,$$QUEENSFERRY$$,#{state_id_vic},-38.323843,145.539805), - ($$3984$$,$$TENBY POINT$$,#{state_id_vic},-38.323843,145.539805), - ($$3984$$,$$THE GURDIES$$,#{state_id_vic},-38.323843,145.539805), - ($$3987$$,$$NYORA$$,#{state_id_vic},-38.333067,145.670441), - ($$3988$$,$$MOUNTAIN VIEW$$,#{state_id_vic},-38.294339,145.886342), - ($$3988$$,$$POOWONG$$,#{state_id_vic},-38.294339,145.886342), - ($$3988$$,$$POOWONG EAST$$,#{state_id_vic},-38.294339,145.886342), - ($$3988$$,$$POOWONG NORTH$$,#{state_id_vic},-38.294339,145.886342), - ($$3990$$,$$GLEN FORBES$$,#{state_id_vic},-38.449183,145.532297), - ($$3991$$,$$BASS$$,#{state_id_vic},-38.480541,145.467733), - ($$3992$$,$$BLACKWOOD FOREST$$,#{state_id_vic},-38.493513,145.616162), - ($$3992$$,$$DALYSTON$$,#{state_id_vic},-38.493513,145.616162), - ($$3992$$,$$RYANSTON$$,#{state_id_vic},-38.493513,145.616162), - ($$3992$$,$$WEST CREEK$$,#{state_id_vic},-38.493513,145.616162), - ($$3995$$,$$ANDERSON$$,#{state_id_vic},-38.527605,145.446731), - ($$3995$$,$$ARCHIES CREEK$$,#{state_id_vic},-38.527605,145.446731), - ($$3995$$,$$CAPE PATERSON$$,#{state_id_vic},-38.527605,145.446731), - ($$3995$$,$$HARMERS HAVEN$$,#{state_id_vic},-38.527605,145.446731), - ($$3995$$,$$KILCUNDA$$,#{state_id_vic},-38.527605,145.446731), - ($$3995$$,$$LANCE CREEK$$,#{state_id_vic},-38.527605,145.446731), - ($$3995$$,$$NORTH WONTHAGGI$$,#{state_id_vic},-38.527605,145.446731), - ($$3995$$,$$SOUTH DUDLEY$$,#{state_id_vic},-38.527605,145.446731), - ($$3995$$,$$ST CLAIR$$,#{state_id_vic},-38.527605,145.446731), - ($$3995$$,$$WATTLE BANK$$,#{state_id_vic},-38.527605,145.446731), - ($$3995$$,$$WONTHAGGI$$,#{state_id_vic},-38.527605,145.446731), - ($$3995$$,$$WOOLAMAI$$,#{state_id_vic},-38.527605,145.446731), - ($$3996$$,$$INVERLOCH$$,#{state_id_vic},-38.632958,145.729641), - ($$3996$$,$$POUND CREEK$$,#{state_id_vic},-38.632958,145.729641), - ($$4000$$,$$BRISBANE$$,#{state_id_qld},-27.46758,153.027892), - ($$4000$$,$$BRISBANE ADELAIDE STREET$$,#{state_id_qld},-27.46758,153.027892), - ($$4000$$,$$BRISBANE GPO$$,#{state_id_qld},-27.46758,153.027892), - ($$4000$$,$$SPRING HILL$$,#{state_id_qld},-27.46758,153.027892), - ($$4001$$,$$BRISBANE$$,#{state_id_qld},-27.603479,152.823141), - ($$4001$$,$$CENTRAL PLAZA$$,#{state_id_qld},-27.603479,152.823141), - ($$4001$$,$$RIVERSIDE CENTRE$$,#{state_id_qld},-27.603479,152.823141), - ($$4001$$,$$WATERFRONT PLACE$$,#{state_id_qld},-27.603479,152.823141), - ($$4002$$,$$CITY EAST$$,#{state_id_qld},0.0,0.0), - ($$4002$$,$$WINTERGARDEN$$,#{state_id_qld},0.0,0.0), - ($$4003$$,$$GEORGE STREET$$,#{state_id_qld},-24.872431,152.348462), - ($$4004$$,$$SPRING HILL$$,#{state_id_qld},-24.045265,149.316593), - ($$4005$$,$$NEW FARM$$,#{state_id_qld},-27.467878,153.047052), - ($$4006$$,$$BOWEN HILLS$$,#{state_id_qld},-27.444163,153.038407), - ($$4006$$,$$FORTITUDE VALLEY$$,#{state_id_qld},-27.444163,153.038407), - ($$4006$$,$$FORTITUDE VALLEY BC$$,#{state_id_qld},-27.444163,153.038407), - ($$4006$$,$$HERSTON$$,#{state_id_qld},-27.444163,153.038407), - ($$4006$$,$$NEWSTEAD$$,#{state_id_qld},-27.444163,153.038407), - ($$4007$$,$$ASCOT$$,#{state_id_qld},-27.43027,153.058679), - ($$4007$$,$$HAMILTON$$,#{state_id_qld},-27.43027,153.058679), - ($$4007$$,$$HAMILTON CENTRAL$$,#{state_id_qld},-27.43027,153.058679), - ($$4008$$,$$PINKENBA$$,#{state_id_qld},-27.42257,153.118782), - ($$4009$$,$$EAGLE FARM$$,#{state_id_qld},-27.430729,153.091148), - ($$4009$$,$$EAGLE FARM BC$$,#{state_id_qld},-27.430729,153.091148), - ($$4010$$,$$ALBION$$,#{state_id_qld},-27.428026,153.045205), - ($$4010$$,$$ALBION BC$$,#{state_id_qld},-27.428026,153.045205), - ($$4010$$,$$ALBION DC$$,#{state_id_qld},-27.428026,153.045205), - ($$4011$$,$$CLAYFIELD$$,#{state_id_qld},-27.417536,153.056677), - ($$4011$$,$$HENDRA$$,#{state_id_qld},-27.417536,153.056677), - ($$4012$$,$$NUNDAH$$,#{state_id_qld},-27.40344,153.060428), - ($$4012$$,$$TOOMBUL$$,#{state_id_qld},-27.40344,153.060428), - ($$4012$$,$$WAVELL HEIGHTS$$,#{state_id_qld},-27.40344,153.060428), - ($$4012$$,$$WAVELL HEIGHTS NORTH$$,#{state_id_qld},-27.40344,153.060428), - ($$4013$$,$$NORTHGATE$$,#{state_id_qld},-27.389718,153.066343), - ($$4014$$,$$BANYO$$,#{state_id_qld},-27.375825,153.076469), - ($$4014$$,$$NUDGEE$$,#{state_id_qld},-27.375825,153.076469), - ($$4014$$,$$NUDGEE BEACH$$,#{state_id_qld},-27.375825,153.076469), - ($$4014$$,$$VIRGINIA$$,#{state_id_qld},-27.375825,153.076469), - ($$4014$$,$$VIRGINIA BC$$,#{state_id_qld},-27.375825,153.076469), - ($$4014$$,$$VIRGINIA DC$$,#{state_id_qld},-27.375825,153.076469), - ($$4017$$,$$BRACKEN RIDGE$$,#{state_id_qld},-27.327545,153.025938), - ($$4017$$,$$BRIGHTON$$,#{state_id_qld},-27.327545,153.025938), - ($$4017$$,$$BRIGHTON EVENTIDE$$,#{state_id_qld},-27.327545,153.025938), - ($$4017$$,$$BRIGHTON NATHAN STREET$$,#{state_id_qld},-27.327545,153.025938), - ($$4017$$,$$DEAGON$$,#{state_id_qld},-27.327545,153.025938), - ($$4017$$,$$SANDGATE$$,#{state_id_qld},-27.327545,153.025938), - ($$4017$$,$$SANDGATE DC$$,#{state_id_qld},-27.327545,153.025938), - ($$4017$$,$$SHORNCLIFFE$$,#{state_id_qld},-27.327545,153.025938), - ($$4018$$,$$FITZGIBBON$$,#{state_id_qld},-27.349074,153.031804), - ($$4018$$,$$TAIGUM$$,#{state_id_qld},-27.349074,153.031804), - ($$4019$$,$$CLONTARF$$,#{state_id_qld},-27.25958,153.08211), - ($$4019$$,$$CLONTARF BEACH$$,#{state_id_qld},-27.25958,153.08211), - ($$4019$$,$$CLONTARF DC$$,#{state_id_qld},-27.25958,153.08211), - ($$4019$$,$$MARGATE$$,#{state_id_qld},-27.25958,153.08211), - ($$4019$$,$$MARGATE BEACH$$,#{state_id_qld},-27.25958,153.08211), - ($$4019$$,$$WOODY POINT$$,#{state_id_qld},-27.25958,153.08211), - ($$4020$$,$$NEWPORT$$,#{state_id_qld},-27.231153,153.115686), - ($$4020$$,$$REDCLIFFE$$,#{state_id_qld},-27.231153,153.115686), - ($$4020$$,$$REDCLIFFE NORTH$$,#{state_id_qld},-27.231153,153.115686), - ($$4020$$,$$SCARBOROUGH$$,#{state_id_qld},-27.231153,153.115686), - ($$4021$$,$$KIPPA-RING$$,#{state_id_qld},-27.226494,153.085287), - ($$4022$$,$$ROTHWELL$$,#{state_id_qld},-27.213109,153.048135), - ($$4025$$,$$BULWER$$,#{state_id_qld},-27.076847,153.367844), - ($$4025$$,$$CAPE MORETON$$,#{state_id_qld},-27.076847,153.367844), - ($$4025$$,$$COWAN COWAN$$,#{state_id_qld},-27.076847,153.367844), - ($$4025$$,$$KOORINGAL$$,#{state_id_qld},-27.076847,153.367844), - ($$4025$$,$$TANGALOOMA$$,#{state_id_qld},-27.076847,153.367844), - ($$4029$$,$$ROYAL BRISBANE HOSPITAL$$,#{state_id_qld},-27.44768,153.026906), - ($$4030$$,$$LUTWYCHE$$,#{state_id_qld},-27.422714,153.033723), - ($$4030$$,$$WINDSOR$$,#{state_id_qld},-27.422714,153.033723), - ($$4030$$,$$WOOLOOWIN$$,#{state_id_qld},-27.422714,153.033723), - ($$4031$$,$$GORDON PARK$$,#{state_id_qld},-27.414955,153.026877), - ($$4031$$,$$KEDRON$$,#{state_id_qld},-27.414955,153.026877), - ($$4032$$,$$CHERMSIDE$$,#{state_id_qld},-27.38613,153.032142), - ($$4032$$,$$CHERMSIDE BC$$,#{state_id_qld},-27.38613,153.032142), - ($$4032$$,$$CHERMSIDE CENTRE$$,#{state_id_qld},-27.38613,153.032142), - ($$4032$$,$$CHERMSIDE SOUTH$$,#{state_id_qld},-27.38613,153.032142), - ($$4032$$,$$CHERMSIDE WEST$$,#{state_id_qld},-27.38613,153.032142), - ($$4034$$,$$ASPLEY$$,#{state_id_qld},-27.36386,153.015737), - ($$4034$$,$$BOONDALL$$,#{state_id_qld},-27.36386,153.015737), - ($$4034$$,$$CARSELDINE$$,#{state_id_qld},-27.36386,153.015737), - ($$4034$$,$$GEEBUNG$$,#{state_id_qld},-27.36386,153.015737), - ($$4034$$,$$ZILLMERE$$,#{state_id_qld},-27.36386,153.015737), - ($$4035$$,$$ALBANY CREEK$$,#{state_id_qld},-27.345529,152.968403), - ($$4035$$,$$BRIDGEMAN DOWNS$$,#{state_id_qld},-27.345529,152.968403), - ($$4036$$,$$BALD HILLS$$,#{state_id_qld},-27.306169,153.0126), - ($$4037$$,$$EATONS HILL$$,#{state_id_qld},-27.337969,152.949561), - ($$4051$$,$$ALDERLEY$$,#{state_id_qld},-27.424422,153.000517), - ($$4051$$,$$ENOGGERA$$,#{state_id_qld},-27.424422,153.000517), - ($$4051$$,$$GAYTHORNE$$,#{state_id_qld},-27.424422,153.000517), - ($$4051$$,$$GRANGE$$,#{state_id_qld},-27.424422,153.000517), - ($$4051$$,$$NEWMARKET$$,#{state_id_qld},-27.424422,153.000517), - ($$4051$$,$$WILSTON$$,#{state_id_qld},-27.424422,153.000517), - ($$4053$$,$$BROOKSIDE CENTRE$$,#{state_id_qld},-27.409023,152.980333), - ($$4053$$,$$EVERTON HILLS$$,#{state_id_qld},-27.409023,152.980333), - ($$4053$$,$$EVERTON PARK$$,#{state_id_qld},-27.409023,152.980333), - ($$4053$$,$$MCDOWALL$$,#{state_id_qld},-27.409023,152.980333), - ($$4053$$,$$MITCHELTON$$,#{state_id_qld},-27.409023,152.980333), - ($$4053$$,$$STAFFORD$$,#{state_id_qld},-27.409023,152.980333), - ($$4053$$,$$STAFFORD BC$$,#{state_id_qld},-27.409023,152.980333), - ($$4053$$,$$STAFFORD DC$$,#{state_id_qld},-27.409023,152.980333), - ($$4053$$,$$STAFFORD HEIGHTS$$,#{state_id_qld},-27.409023,152.980333), - ($$4054$$,$$ARANA HILLS$$,#{state_id_qld},-27.397523,152.959538), - ($$4054$$,$$KEPERRA$$,#{state_id_qld},-27.397523,152.959538), - ($$4055$$,$$BUNYA$$,#{state_id_qld},-27.369847,152.937598), - ($$4055$$,$$FERNY GROVE$$,#{state_id_qld},-27.369847,152.937598), - ($$4055$$,$$FERNY HILLS$$,#{state_id_qld},-27.369847,152.937598), - ($$4055$$,$$FERNY HILLS DC$$,#{state_id_qld},-27.369847,152.937598), - ($$4055$$,$$UPPER KEDRON$$,#{state_id_qld},-27.369847,152.937598), - ($$4059$$,$$KELVIN GROVE$$,#{state_id_qld},-27.448419,153.013533), - ($$4059$$,$$KELVIN GROVE DC$$,#{state_id_qld},-27.448419,153.013533), - ($$4059$$,$$RED HILL$$,#{state_id_qld},-27.448419,153.013533), - ($$4060$$,$$ASHGROVE$$,#{state_id_qld},-27.445603,152.99212), - ($$4060$$,$$ASHGROVE WEST$$,#{state_id_qld},-27.445603,152.99212), - ($$4061$$,$$THE GAP$$,#{state_id_qld},-27.443306,152.943847), - ($$4064$$,$$MILTON$$,#{state_id_qld},-27.471324,153.004781), - ($$4064$$,$$MILTON BC$$,#{state_id_qld},-27.471324,153.004781), - ($$4064$$,$$PADDINGTON$$,#{state_id_qld},-27.471324,153.004781), - ($$4065$$,$$BARDON$$,#{state_id_qld},-27.463451,152.980035), - ($$4066$$,$$AUCHENFLOWER$$,#{state_id_qld},-27.475531,152.993415), - ($$4066$$,$$MOUNT COOT-THA$$,#{state_id_qld},-27.475531,152.993415), - ($$4066$$,$$TOOWONG$$,#{state_id_qld},-27.475531,152.993415), - ($$4066$$,$$TOOWONG BC$$,#{state_id_qld},-27.475531,152.993415), - ($$4066$$,$$TOOWONG DC$$,#{state_id_qld},-27.475531,152.993415), - ($$4067$$,$$ST LUCIA$$,#{state_id_qld},-27.493869,153.006059), - ($$4067$$,$$ST LUCIA SOUTH$$,#{state_id_qld},-27.493869,153.006059), - ($$4068$$,$$CHELMER$$,#{state_id_qld},-27.514343,152.976732), - ($$4068$$,$$INDOOROOPILLY$$,#{state_id_qld},-27.514343,152.976732), - ($$4068$$,$$INDOOROOPILLY CENTRE$$,#{state_id_qld},-27.514343,152.976732), - ($$4068$$,$$TARINGA$$,#{state_id_qld},-27.514343,152.976732), - ($$4069$$,$$BROOKFIELD$$,#{state_id_qld},-27.490924,152.897741), - ($$4069$$,$$CHAPEL HILL$$,#{state_id_qld},-27.490924,152.897741), - ($$4069$$,$$FIG TREE POCKET$$,#{state_id_qld},-27.490924,152.897741), - ($$4069$$,$$KENMORE$$,#{state_id_qld},-27.490924,152.897741), - ($$4069$$,$$KENMORE DC$$,#{state_id_qld},-27.490924,152.897741), - ($$4069$$,$$KENMORE EAST$$,#{state_id_qld},-27.490924,152.897741), - ($$4069$$,$$KENMORE HILLS$$,#{state_id_qld},-27.490924,152.897741), - ($$4069$$,$$PINJARRA HILLS$$,#{state_id_qld},-27.490924,152.897741), - ($$4069$$,$$PULLENVALE$$,#{state_id_qld},-27.490924,152.897741), - ($$4069$$,$$UPPER BROOKFIELD$$,#{state_id_qld},-27.490924,152.897741), - ($$4070$$,$$ANSTEAD$$,#{state_id_qld},-27.545591,152.87227), - ($$4070$$,$$BELLBOWRIE$$,#{state_id_qld},-27.545591,152.87227), - ($$4070$$,$$MOGGILL$$,#{state_id_qld},-27.545591,152.87227), - ($$4072$$,$$UNIVERSITY OF QUEENSLAND$$,#{state_id_qld},-27.548962,152.330088), - ($$4073$$,$$SEVENTEEN MILE ROCKS$$,#{state_id_qld},-27.550889,152.958818), - ($$4073$$,$$SINNAMON PARK$$,#{state_id_qld},-27.550889,152.958818), - ($$4074$$,$$JAMBOREE HEIGHTS$$,#{state_id_qld},-27.5564,152.93284), - ($$4074$$,$$JINDALEE$$,#{state_id_qld},-27.5564,152.93284), - ($$4074$$,$$MIDDLE PARK$$,#{state_id_qld},-27.5564,152.93284), - ($$4074$$,$$MOUNT OMMANEY$$,#{state_id_qld},-27.5564,152.93284), - ($$4074$$,$$RIVERHILLS$$,#{state_id_qld},-27.5564,152.93284), - ($$4074$$,$$SUMNER$$,#{state_id_qld},-27.5564,152.93284), - ($$4074$$,$$SUMNER PARK BC$$,#{state_id_qld},-27.5564,152.93284), - ($$4074$$,$$WESTLAKE$$,#{state_id_qld},-27.5564,152.93284), - ($$4075$$,$$CORINDA$$,#{state_id_qld},-27.538924,152.981769), - ($$4075$$,$$GRACEVILLE$$,#{state_id_qld},-27.538924,152.981769), - ($$4075$$,$$GRACEVILLE EAST$$,#{state_id_qld},-27.538924,152.981769), - ($$4075$$,$$OXLEY$$,#{state_id_qld},-27.538924,152.981769), - ($$4075$$,$$SHERWOOD$$,#{state_id_qld},-27.538924,152.981769), - ($$4076$$,$$DARRA$$,#{state_id_qld},-27.567027,152.95327), - ($$4076$$,$$WACOL$$,#{state_id_qld},-27.567027,152.95327), - ($$4077$$,$$DOOLANDELLA$$,#{state_id_qld},-27.612032,152.979454), - ($$4077$$,$$DURACK$$,#{state_id_qld},-27.612032,152.979454), - ($$4077$$,$$INALA$$,#{state_id_qld},-27.612032,152.979454), - ($$4077$$,$$INALA EAST$$,#{state_id_qld},-27.612032,152.979454), - ($$4077$$,$$INALA HEIGHTS$$,#{state_id_qld},-27.612032,152.979454), - ($$4077$$,$$RICHLANDS$$,#{state_id_qld},-27.612032,152.979454), - ($$4077$$,$$RICHLANDS DC$$,#{state_id_qld},-27.612032,152.979454), - ($$4078$$,$$ELLEN GROVE$$,#{state_id_qld},-27.612653,152.950579), - ($$4078$$,$$FOREST LAKE$$,#{state_id_qld},-27.612653,152.950579), - ($$4101$$,$$HIGHGATE HILL$$,#{state_id_qld},-27.487699,153.018947), - ($$4101$$,$$SOUTH BRISBANE$$,#{state_id_qld},-27.487699,153.018947), - ($$4101$$,$$SOUTH BRISBANE BC$$,#{state_id_qld},-27.487699,153.018947), - ($$4101$$,$$WEST END$$,#{state_id_qld},-27.487699,153.018947), - ($$4102$$,$$BURANDA$$,#{state_id_qld},-27.499989,153.035349), - ($$4102$$,$$DUTTON PARK$$,#{state_id_qld},-27.499989,153.035349), - ($$4102$$,$$WOOLLOONGABBA$$,#{state_id_qld},-27.499989,153.035349), - ($$4103$$,$$ANNERLEY$$,#{state_id_qld},-27.511609,153.031832), - ($$4103$$,$$ANNERLEY DC$$,#{state_id_qld},-27.511609,153.031832), - ($$4103$$,$$FAIRFIELD$$,#{state_id_qld},-27.511609,153.031832), - ($$4103$$,$$FAIRFIELD GARDENS$$,#{state_id_qld},-27.511609,153.031832), - ($$4104$$,$$YERONGA$$,#{state_id_qld},-27.516831,153.017277), - ($$4105$$,$$MOOROOKA$$,#{state_id_qld},-27.532844,153.025089), - ($$4105$$,$$TENNYSON$$,#{state_id_qld},-27.532844,153.025089), - ($$4105$$,$$YEERONGPILLY$$,#{state_id_qld},-27.532844,153.025089), - ($$4106$$,$$BRISBANE MARKET$$,#{state_id_qld},-27.534195,152.99934), - ($$4106$$,$$ROCKLEA$$,#{state_id_qld},-27.534195,152.99934), - ($$4106$$,$$ROCKLEA DC$$,#{state_id_qld},-27.534195,152.99934), - ($$4107$$,$$SALISBURY$$,#{state_id_qld},-27.544798,153.029585), - ($$4107$$,$$SALISBURY EAST$$,#{state_id_qld},-27.544798,153.029585), - ($$4108$$,$$ARCHERFIELD$$,#{state_id_qld},-27.568388,153.024165), - ($$4108$$,$$ARCHERFIELD BC$$,#{state_id_qld},-27.568388,153.024165), - ($$4108$$,$$COOPERS PLAINS$$,#{state_id_qld},-27.568388,153.024165), - ($$4109$$,$$MACGREGOR$$,#{state_id_qld},-27.567572,153.07787), - ($$4109$$,$$ROBERTSON$$,#{state_id_qld},-27.567572,153.07787), - ($$4109$$,$$SUNNYBANK$$,#{state_id_qld},-27.567572,153.07787), - ($$4109$$,$$SUNNYBANK HILLS$$,#{state_id_qld},-27.567572,153.07787), - ($$4109$$,$$SUNNYBANK SOUTH$$,#{state_id_qld},-27.567572,153.07787), - ($$4110$$,$$ACACIA RIDGE$$,#{state_id_qld},-27.589633,153.02787), - ($$4110$$,$$ACACIA RIDGE BC$$,#{state_id_qld},-27.589633,153.02787), - ($$4110$$,$$ACACIA RIDGE DC$$,#{state_id_qld},-27.589633,153.02787), - ($$4110$$,$$HEATHWOOD$$,#{state_id_qld},-27.589633,153.02787), - ($$4110$$,$$HEATHWOOD DF$$,#{state_id_qld},-27.589633,153.02787), - ($$4110$$,$$LARAPINTA$$,#{state_id_qld},-27.589633,153.02787), - ($$4110$$,$$PALLARA$$,#{state_id_qld},-27.589633,153.02787), - ($$4110$$,$$WILLAWONG$$,#{state_id_qld},-27.589633,153.02787), - ($$4111$$,$$NATHAN$$,#{state_id_qld},-27.553179,153.056433), - ($$4112$$,$$KURABY$$,#{state_id_qld},-27.606379,153.092491), - ($$4113$$,$$EIGHT MILE PLAINS$$,#{state_id_qld},-27.575233,153.087585), - ($$4113$$,$$RUNCORN$$,#{state_id_qld},-27.575233,153.087585), - ($$4114$$,$$KINGSTON$$,#{state_id_qld},-27.660233,153.117675), - ($$4114$$,$$LOGAN CENTRAL$$,#{state_id_qld},-27.660233,153.117675), - ($$4114$$,$$LOGAN CITY BC$$,#{state_id_qld},-27.660233,153.117675), - ($$4114$$,$$LOGAN CITY DC$$,#{state_id_qld},-27.660233,153.117675), - ($$4114$$,$$WOODRIDGE$$,#{state_id_qld},-27.660233,153.117675), - ($$4115$$,$$ALGESTER$$,#{state_id_qld},-27.611303,153.033518), - ($$4115$$,$$PARKINSON$$,#{state_id_qld},-27.611303,153.033518), - ($$4116$$,$$CALAMVALE$$,#{state_id_qld},-27.623928,153.050074), - ($$4116$$,$$DREWVALE$$,#{state_id_qld},-27.623928,153.050074), - ($$4116$$,$$STRETTON$$,#{state_id_qld},-27.623928,153.050074), - ($$4117$$,$$BERRINBA$$,#{state_id_qld},-27.637982,153.083869), - ($$4117$$,$$KARAWATHA$$,#{state_id_qld},-27.637982,153.083869), - ($$4118$$,$$BROWNS PLAINS$$,#{state_id_qld},-27.660763,153.04015), - ($$4118$$,$$BROWNS PLAINS BC$$,#{state_id_qld},-27.660763,153.04015), - ($$4118$$,$$FORESTDALE$$,#{state_id_qld},-27.660763,153.04015), - ($$4118$$,$$HERITAGE PARK$$,#{state_id_qld},-27.660763,153.04015), - ($$4118$$,$$HILLCREST$$,#{state_id_qld},-27.660763,153.04015), - ($$4118$$,$$REGENTS PARK$$,#{state_id_qld},-27.660763,153.04015), - ($$4119$$,$$UNDERWOOD$$,#{state_id_qld},-27.593616,153.108698), - ($$4120$$,$$GREENSLOPES$$,#{state_id_qld},-27.512111,153.053021), - ($$4120$$,$$STONES CORNER$$,#{state_id_qld},-27.512111,153.053021), - ($$4121$$,$$HOLLAND PARK$$,#{state_id_qld},-27.519259,153.061369), - ($$4121$$,$$HOLLAND PARK EAST$$,#{state_id_qld},-27.519259,153.061369), - ($$4121$$,$$HOLLAND PARK WEST$$,#{state_id_qld},-27.519259,153.061369), - ($$4121$$,$$TARRAGINDI$$,#{state_id_qld},-27.519259,153.061369), - ($$4121$$,$$WELLERS HILL$$,#{state_id_qld},-27.519259,153.061369), - ($$4122$$,$$MANSFIELD$$,#{state_id_qld},-27.53938,153.099025), - ($$4122$$,$$MANSFIELD BC$$,#{state_id_qld},-27.53938,153.099025), - ($$4122$$,$$MANSFIELD DC$$,#{state_id_qld},-27.53938,153.099025), - ($$4122$$,$$MOUNT GRAVATT$$,#{state_id_qld},-27.53938,153.099025), - ($$4122$$,$$MOUNT GRAVATT EAST$$,#{state_id_qld},-27.53938,153.099025), - ($$4122$$,$$UPPER MOUNT GRAVATT$$,#{state_id_qld},-27.53938,153.099025), - ($$4122$$,$$UPPER MOUNT GRAVATT BC$$,#{state_id_qld},-27.53938,153.099025), - ($$4122$$,$$WISHART$$,#{state_id_qld},-27.53938,153.099025), - ($$4123$$,$$ROCHEDALE$$,#{state_id_qld},-27.571214,153.12691), - ($$4123$$,$$ROCHEDALE SOUTH$$,#{state_id_qld},-27.571214,153.12691), - ($$4124$$,$$BORONIA HEIGHTS$$,#{state_id_qld},-27.690598,153.022217), - ($$4124$$,$$GREENBANK$$,#{state_id_qld},-27.690598,153.022217), - ($$4124$$,$$LYONS$$,#{state_id_qld},-27.690598,153.022217), - ($$4124$$,$$NEW BEITH$$,#{state_id_qld},-27.690598,153.022217), - ($$4124$$,$$SPRING MOUNTAIN$$,#{state_id_qld},-27.690598,153.022217), - ($$4125$$,$$MUNRUBEN$$,#{state_id_qld},-27.748611,153.025645), - ($$4125$$,$$PARK RIDGE$$,#{state_id_qld},-27.748611,153.025645), - ($$4125$$,$$PARK RIDGE SOUTH$$,#{state_id_qld},-27.748611,153.025645), - ($$4127$$,$$DAISY HILL$$,#{state_id_qld},-27.644265,153.153343), - ($$4127$$,$$PRIESTDALE$$,#{state_id_qld},-27.644265,153.153343), - ($$4127$$,$$SLACKS CREEK$$,#{state_id_qld},-27.644265,153.153343), - ($$4127$$,$$SPRINGWOOD$$,#{state_id_qld},-27.644265,153.153343), - ($$4128$$,$$SHAILER PARK$$,#{state_id_qld},-27.650622,153.165162), - ($$4128$$,$$TANAH MERAH$$,#{state_id_qld},-27.650622,153.165162), - ($$4129$$,$$LOGANHOLME$$,#{state_id_qld},-27.690846,153.178287), - ($$4129$$,$$LOGANHOLME BC$$,#{state_id_qld},-27.690846,153.178287), - ($$4129$$,$$LOGANHOLME DC$$,#{state_id_qld},-27.690846,153.178287), - ($$4130$$,$$CARBROOK$$,#{state_id_qld},-27.687086,153.276026), - ($$4130$$,$$CORNUBIA$$,#{state_id_qld},-27.687086,153.276026), - ($$4131$$,$$LOGANLEA$$,#{state_id_qld},-27.675035,153.127293), - ($$4131$$,$$MEADOWBROOK$$,#{state_id_qld},-27.675035,153.127293), - ($$4132$$,$$CRESTMEAD$$,#{state_id_qld},-27.686848,153.087768), - ($$4132$$,$$CRESTMEAD DC$$,#{state_id_qld},-27.686848,153.087768), - ($$4132$$,$$MARSDEN$$,#{state_id_qld},-27.686848,153.087768), - ($$4133$$,$$CHAMBERS FLAT$$,#{state_id_qld},-27.735955,153.087955), - ($$4133$$,$$LOGAN RESERVE$$,#{state_id_qld},-27.735955,153.087955), - ($$4133$$,$$WATERFORD$$,#{state_id_qld},-27.735955,153.087955), - ($$4133$$,$$WATERFORD WEST$$,#{state_id_qld},-27.735955,153.087955), - ($$4151$$,$$COORPAROO$$,#{state_id_qld},-27.495194,153.057903), - ($$4151$$,$$COORPAROO BC$$,#{state_id_qld},-27.495194,153.057903), - ($$4151$$,$$COORPAROO DC$$,#{state_id_qld},-27.495194,153.057903), - ($$4152$$,$$CAMP HILL$$,#{state_id_qld},-27.493015,153.076507), - ($$4152$$,$$CARINA$$,#{state_id_qld},-27.493015,153.076507), - ($$4152$$,$$CARINA HEIGHTS$$,#{state_id_qld},-27.493015,153.076507), - ($$4152$$,$$CARINDALE$$,#{state_id_qld},-27.493015,153.076507), - ($$4153$$,$$BELMONT$$,#{state_id_qld},-27.504978,153.131443), - ($$4154$$,$$GUMDALE$$,#{state_id_qld},-27.490918,153.153345), - ($$4154$$,$$RANSOME$$,#{state_id_qld},-27.490918,153.153345), - ($$4154$$,$$WAKERLEY$$,#{state_id_qld},-27.490918,153.153345), - ($$4155$$,$$CHANDLER$$,#{state_id_qld},-19.262174,146.779764), - ($$4156$$,$$BURBANK$$,#{state_id_qld},-27.558921,153.163912), - ($$4156$$,$$MACKENZIE$$,#{state_id_qld},-27.558921,153.163912), - ($$4157$$,$$CAPALABA$$,#{state_id_qld},-27.525711,153.19336), - ($$4157$$,$$CAPALABA BC$$,#{state_id_qld},-27.525711,153.19336), - ($$4157$$,$$CAPALABA DC$$,#{state_id_qld},-27.525711,153.19336), - ($$4157$$,$$CAPALABA WEST$$,#{state_id_qld},-27.525711,153.19336), - ($$4157$$,$$SHELDON$$,#{state_id_qld},-27.525711,153.19336), - ($$4158$$,$$THORNESIDE$$,#{state_id_qld},-27.482971,153.20107), - ($$4159$$,$$BIRKDALE$$,#{state_id_qld},-27.498415,153.207434), - ($$4160$$,$$ORMISTON$$,#{state_id_qld},-27.518815,153.255284), - ($$4160$$,$$WELLINGTON POINT$$,#{state_id_qld},-27.518815,153.255284), - ($$4161$$,$$ALEXANDRA HILLS$$,#{state_id_qld},-27.522774,153.220455), - ($$4163$$,$$CLEVELAND$$,#{state_id_qld},-27.525781,153.26484), - ($$4163$$,$$CLEVELAND DC$$,#{state_id_qld},-27.525781,153.26484), - ($$4164$$,$$THORNLANDS$$,#{state_id_qld},-27.563211,153.256977), - ($$4165$$,$$MOUNT COTTON$$,#{state_id_qld},-27.621843,153.235294), - ($$4165$$,$$REDLAND BAY$$,#{state_id_qld},-27.621843,153.235294), - ($$4165$$,$$VICTORIA POINT$$,#{state_id_qld},-27.621843,153.235294), - ($$4165$$,$$VICTORIA POINT WEST$$,#{state_id_qld},-27.621843,153.235294), - ($$4169$$,$$EAST BRISBANE$$,#{state_id_qld},-27.479652,153.045983), - ($$4169$$,$$KANGAROO POINT$$,#{state_id_qld},-27.479652,153.045983), - ($$4170$$,$$CANNON HILL$$,#{state_id_qld},-27.469678,153.097305), - ($$4170$$,$$MORNINGSIDE$$,#{state_id_qld},-27.469678,153.097305), - ($$4170$$,$$NORMAN PARK$$,#{state_id_qld},-27.469678,153.097305), - ($$4170$$,$$SEVEN HILLS$$,#{state_id_qld},-27.469678,153.097305), - ($$4171$$,$$BALMORAL$$,#{state_id_qld},-27.455696,153.06722), - ($$4171$$,$$BULIMBA$$,#{state_id_qld},-27.455696,153.06722), - ($$4171$$,$$HAWTHORNE$$,#{state_id_qld},-27.455696,153.06722), - ($$4172$$,$$MURARRIE$$,#{state_id_qld},-27.464118,153.109052), - ($$4173$$,$$TINGALPA$$,#{state_id_qld},-27.465869,153.133779), - ($$4173$$,$$TINGALPA BC$$,#{state_id_qld},-27.465869,153.133779), - ($$4173$$,$$TINGALPA DC$$,#{state_id_qld},-27.465869,153.133779), - ($$4174$$,$$HEMMANT$$,#{state_id_qld},-27.448586,153.12701), - ($$4178$$,$$LYTTON$$,#{state_id_qld},-27.4243,153.15545), - ($$4178$$,$$PORT OF BRISBANE$$,#{state_id_qld},-27.4243,153.15545), - ($$4178$$,$$WYNNUM$$,#{state_id_qld},-27.4243,153.15545), - ($$4178$$,$$WYNNUM NORTH$$,#{state_id_qld},-27.4243,153.15545), - ($$4178$$,$$WYNNUM PLAZA$$,#{state_id_qld},-27.4243,153.15545), - ($$4178$$,$$WYNNUM WEST$$,#{state_id_qld},-27.4243,153.15545), - ($$4179$$,$$LOTA$$,#{state_id_qld},-27.465277,153.18128), - ($$4179$$,$$MANLY$$,#{state_id_qld},-27.465277,153.18128), - ($$4179$$,$$MANLY WEST$$,#{state_id_qld},-27.465277,153.18128), - ($$4183$$,$$AMITY$$,#{state_id_qld},-27.397888,153.439075), - ($$4183$$,$$AMITY POINT$$,#{state_id_qld},-27.397888,153.439075), - ($$4183$$,$$DUNWICH$$,#{state_id_qld},-27.397888,153.439075), - ($$4183$$,$$NORTH STRADBROKE ISLAND$$,#{state_id_qld},-27.397888,153.439075), - ($$4183$$,$$POINT LOOKOUT$$,#{state_id_qld},-27.397888,153.439075), - ($$4184$$,$$COOCHIEMUDLO ISLAND$$,#{state_id_qld},-27.574426,153.332293), - ($$4184$$,$$KARRAGARRA ISLAND$$,#{state_id_qld},-27.574426,153.332293), - ($$4184$$,$$LAMB ISLAND$$,#{state_id_qld},-27.574426,153.332293), - ($$4184$$,$$MACLEAY ISLAND$$,#{state_id_qld},-27.574426,153.332293), - ($$4184$$,$$PEEL ISLAND$$,#{state_id_qld},-27.574426,153.332293), - ($$4184$$,$$PERULPA ISLAND$$,#{state_id_qld},-27.574426,153.332293), - ($$4184$$,$$RUSSELL ISLAND$$,#{state_id_qld},-27.574426,153.332293), - ($$4205$$,$$BETHANIA$$,#{state_id_qld},-27.694398,153.155502), - ($$4207$$,$$ALBERTON$$,#{state_id_qld},-27.721117,153.270056), - ($$4207$$,$$BAHRS SCRUB$$,#{state_id_qld},-27.721117,153.270056), - ($$4207$$,$$BANNOCKBURN$$,#{state_id_qld},-27.721117,153.270056), - ($$4207$$,$$BEENLEIGH$$,#{state_id_qld},-27.721117,153.270056), - ($$4207$$,$$BELIVAH$$,#{state_id_qld},-27.721117,153.270056), - ($$4207$$,$$BUCCAN$$,#{state_id_qld},-27.721117,153.270056), - ($$4207$$,$$CEDAR CREEK$$,#{state_id_qld},-27.721117,153.270056), - ($$4207$$,$$EAGLEBY$$,#{state_id_qld},-27.721117,153.270056), - ($$4207$$,$$EDENS LANDING$$,#{state_id_qld},-27.721117,153.270056), - ($$4207$$,$$HOLMVIEW$$,#{state_id_qld},-27.721117,153.270056), - ($$4207$$,$$LOGAN VILLAGE$$,#{state_id_qld},-27.721117,153.270056), - ($$4207$$,$$LUSCOMBE$$,#{state_id_qld},-27.721117,153.270056), - ($$4207$$,$$MOUNT WARREN PARK$$,#{state_id_qld},-27.721117,153.270056), - ($$4207$$,$$STAPYLTON$$,#{state_id_qld},-27.721117,153.270056), - ($$4207$$,$$STEIGLITZ$$,#{state_id_qld},-27.721117,153.270056), - ($$4207$$,$$WINDAROO$$,#{state_id_qld},-27.721117,153.270056), - ($$4207$$,$$WOLFFDENE$$,#{state_id_qld},-27.721117,153.270056), - ($$4207$$,$$WOONGOOLBA$$,#{state_id_qld},-27.721117,153.270056), - ($$4207$$,$$YARRABILBA$$,#{state_id_qld},-27.721117,153.270056), - ($$4207$$,$$YATALA$$,#{state_id_qld},-27.721117,153.270056), - ($$4207$$,$$YATALA DC$$,#{state_id_qld},-27.721117,153.270056), - ($$4208$$,$$GILBERTON$$,#{state_id_qld},-27.745013,153.261095), - ($$4208$$,$$JACOBS WELL$$,#{state_id_qld},-27.745013,153.261095), - ($$4208$$,$$KINGSHOLME$$,#{state_id_qld},-27.745013,153.261095), - ($$4208$$,$$NORWELL$$,#{state_id_qld},-27.745013,153.261095), - ($$4208$$,$$ORMEAU$$,#{state_id_qld},-27.745013,153.261095), - ($$4208$$,$$ORMEAU HILLS$$,#{state_id_qld},-27.745013,153.261095), - ($$4209$$,$$COOMERA$$,#{state_id_qld},-27.866592,153.315314), - ($$4209$$,$$PIMPAMA$$,#{state_id_qld},-27.866592,153.315314), - ($$4209$$,$$UPPER COOMERA$$,#{state_id_qld},-27.866592,153.315314), - ($$4209$$,$$WILLOW VALE$$,#{state_id_qld},-27.866592,153.315314), - ($$4210$$,$$GUANABA$$,#{state_id_qld},-27.942395,153.233818), - ($$4210$$,$$MAUDSLAND$$,#{state_id_qld},-27.942395,153.233818), - ($$4210$$,$$OXENFORD$$,#{state_id_qld},-27.942395,153.233818), - ($$4210$$,$$STUDIO VILLAGE$$,#{state_id_qld},-27.942395,153.233818), - ($$4210$$,$$WONGAWALLAN$$,#{state_id_qld},-27.942395,153.233818), - ($$4211$$,$$ADVANCETOWN$$,#{state_id_qld},-28.033052,153.279625), - ($$4211$$,$$BEECHMONT$$,#{state_id_qld},-28.033052,153.279625), - ($$4211$$,$$BINNA BURRA$$,#{state_id_qld},-28.033052,153.279625), - ($$4211$$,$$CARRARA$$,#{state_id_qld},-28.033052,153.279625), - ($$4211$$,$$CLAGIRABA$$,#{state_id_qld},-28.033052,153.279625), - ($$4211$$,$$GAVEN$$,#{state_id_qld},-28.033052,153.279625), - ($$4211$$,$$GILSTON$$,#{state_id_qld},-28.033052,153.279625), - ($$4211$$,$$HIGHLAND PARK$$,#{state_id_qld},-28.033052,153.279625), - ($$4211$$,$$LOWER BEECHMONT$$,#{state_id_qld},-28.033052,153.279625), - ($$4211$$,$$MOUNT NATHAN$$,#{state_id_qld},-28.033052,153.279625), - ($$4211$$,$$NATURAL BRIDGE$$,#{state_id_qld},-28.033052,153.279625), - ($$4211$$,$$NERANG$$,#{state_id_qld},-28.033052,153.279625), - ($$4211$$,$$NERANG BC$$,#{state_id_qld},-28.033052,153.279625), - ($$4211$$,$$NERANG DC$$,#{state_id_qld},-28.033052,153.279625), - ($$4211$$,$$NUMINBAH VALLEY$$,#{state_id_qld},-28.033052,153.279625), - ($$4211$$,$$PACIFIC PINES$$,#{state_id_qld},-28.033052,153.279625), - ($$4211$$,$$SOUTHERN LAMINGTON$$,#{state_id_qld},-28.033052,153.279625), - ($$4212$$,$$HELENSVALE$$,#{state_id_qld},-27.922459,153.334793), - ($$4212$$,$$HELENSVALE TOWN CENTRE$$,#{state_id_qld},-27.922459,153.334793), - ($$4212$$,$$HOPE ISLAND$$,#{state_id_qld},-27.922459,153.334793), - ($$4212$$,$$SANCTUARY COVE$$,#{state_id_qld},-27.922459,153.334793), - ($$4213$$,$$AUSTINVILLE$$,#{state_id_qld},-28.135818,153.317485), - ($$4213$$,$$BONOGIN$$,#{state_id_qld},-28.135818,153.317485), - ($$4213$$,$$MUDGEERABA$$,#{state_id_qld},-28.135818,153.317485), - ($$4213$$,$$NERANWOOD$$,#{state_id_qld},-28.135818,153.317485), - ($$4213$$,$$SPRINGBROOK$$,#{state_id_qld},-28.135818,153.317485), - ($$4213$$,$$TALLAI$$,#{state_id_qld},-28.135818,153.317485), - ($$4213$$,$$WORONGARY$$,#{state_id_qld},-28.135818,153.317485), - ($$4214$$,$$ARUNDEL$$,#{state_id_qld},-27.939708,153.372781), - ($$4214$$,$$ARUNDEL BC$$,#{state_id_qld},-27.939708,153.372781), - ($$4214$$,$$ARUNDEL DC$$,#{state_id_qld},-27.939708,153.372781), - ($$4214$$,$$ASHMORE$$,#{state_id_qld},-27.939708,153.372781), - ($$4214$$,$$ASHMORE CITY$$,#{state_id_qld},-27.939708,153.372781), - ($$4214$$,$$MOLENDINAR$$,#{state_id_qld},-27.939708,153.372781), - ($$4214$$,$$PARKWOOD$$,#{state_id_qld},-27.939708,153.372781), - ($$4215$$,$$AUSTRALIA FAIR$$,#{state_id_qld},-27.968379,153.415136), - ($$4215$$,$$CHIRN PARK$$,#{state_id_qld},-27.968379,153.415136), - ($$4215$$,$$LABRADOR$$,#{state_id_qld},-27.968379,153.415136), - ($$4215$$,$$SOUTHPORT$$,#{state_id_qld},-27.968379,153.415136), - ($$4215$$,$$SOUTHPORT BC$$,#{state_id_qld},-27.968379,153.415136), - ($$4215$$,$$SOUTHPORT PARK$$,#{state_id_qld},-27.968379,153.415136), - ($$4216$$,$$BIGGERA WATERS$$,#{state_id_qld},-27.934226,153.400356), - ($$4216$$,$$COOMBABAH$$,#{state_id_qld},-27.934226,153.400356), - ($$4216$$,$$HOLLYWELL$$,#{state_id_qld},-27.934226,153.400356), - ($$4216$$,$$PARADISE POINT$$,#{state_id_qld},-27.934226,153.400356), - ($$4216$$,$$RUNAWAY BAY$$,#{state_id_qld},-27.934226,153.400356), - ($$4216$$,$$SOUTH STRADBROKE$$,#{state_id_qld},-27.934226,153.400356), - ($$4217$$,$$BENOWA$$,#{state_id_qld},-28.005753,153.387475), - ($$4217$$,$$BUNDALL$$,#{state_id_qld},-28.005753,153.387475), - ($$4217$$,$$BUNDALL BC$$,#{state_id_qld},-28.005753,153.387475), - ($$4217$$,$$BUNDALL DC$$,#{state_id_qld},-28.005753,153.387475), - ($$4217$$,$$CHEVRON ISLAND$$,#{state_id_qld},-28.005753,153.387475), - ($$4217$$,$$GOLD COAST MC$$,#{state_id_qld},-28.005753,153.387475), - ($$4217$$,$$ISLE OF CAPRI$$,#{state_id_qld},-28.005753,153.387475), - ($$4217$$,$$MAIN BEACH$$,#{state_id_qld},-28.005753,153.387475), - ($$4217$$,$$SURFERS PARADISE$$,#{state_id_qld},-28.005753,153.387475), - ($$4218$$,$$BROADBEACH$$,#{state_id_qld},-28.029363,153.4312), - ($$4218$$,$$BROADBEACH WATERS$$,#{state_id_qld},-28.029363,153.4312), - ($$4218$$,$$MERMAID BEACH$$,#{state_id_qld},-28.029363,153.4312), - ($$4218$$,$$MERMAID WATERS$$,#{state_id_qld},-28.029363,153.4312), - ($$4218$$,$$NOBBY BEACH$$,#{state_id_qld},-28.029363,153.4312), - ($$4218$$,$$PACIFIC FAIR$$,#{state_id_qld},-28.029363,153.4312), - ($$4218$$,$$Q SUPERCENTRE$$,#{state_id_qld},-28.029363,153.4312), - ($$4219$$,$$WEST BURLEIGH$$,#{state_id_qld},-28.107294,153.441839), - ($$4220$$,$$BURLEIGH BC$$,#{state_id_qld},-28.088477,153.451921), - ($$4220$$,$$BURLEIGH DC$$,#{state_id_qld},-28.088477,153.451921), - ($$4220$$,$$BURLEIGH HEADS$$,#{state_id_qld},-28.088477,153.451921), - ($$4220$$,$$BURLEIGH TOWN$$,#{state_id_qld},-28.088477,153.451921), - ($$4220$$,$$BURLEIGH WATERS$$,#{state_id_qld},-28.088477,153.451921), - ($$4220$$,$$MIAMI$$,#{state_id_qld},-28.088477,153.451921), - ($$4221$$,$$ELANORA$$,#{state_id_qld},-28.135669,153.470481), - ($$4221$$,$$PALM BEACH$$,#{state_id_qld},-28.135669,153.470481), - ($$4222$$,$$GRIFFITH UNIVERSITY$$,#{state_id_qld},0.0,0.0), - ($$4223$$,$$CURRUMBIN$$,#{state_id_qld},-28.136289,153.477806), - ($$4223$$,$$CURRUMBIN DC$$,#{state_id_qld},-28.136289,153.477806), - ($$4223$$,$$CURRUMBIN VALLEY$$,#{state_id_qld},-28.136289,153.477806), - ($$4223$$,$$CURRUMBIN WATERS$$,#{state_id_qld},-28.136289,153.477806), - ($$4224$$,$$TUGUN$$,#{state_id_qld},-28.143616,153.494865), - ($$4224$$,$$TUGUN HEIGHTS$$,#{state_id_qld},-28.143616,153.494865), - ($$4225$$,$$BILINGA$$,#{state_id_qld},-28.159935,153.510026), - ($$4225$$,$$COOLANGATTA$$,#{state_id_qld},-28.159935,153.510026), - ($$4226$$,$$CLEAR ISLAND WATERS$$,#{state_id_qld},-28.039318,153.401608), - ($$4226$$,$$MERRIMAC$$,#{state_id_qld},-28.039318,153.401608), - ($$4226$$,$$ROBINA$$,#{state_id_qld},-28.039318,153.401608), - ($$4226$$,$$ROBINA DC$$,#{state_id_qld},-28.039318,153.401608), - ($$4227$$,$$REEDY CREEK$$,#{state_id_qld},-28.108599,153.395621), - ($$4227$$,$$VARSITY LAKES$$,#{state_id_qld},-28.108599,153.395621), - ($$4228$$,$$TALLEBUDGERA$$,#{state_id_qld},-28.148226,153.421613), - ($$4228$$,$$TALLEBUDGERA VALLEY$$,#{state_id_qld},-28.148226,153.421613), - ($$4229$$,$$BOND UNIVERSITY$$,#{state_id_qld},-28.075709,153.414682), - ($$4230$$,$$ROBINA TOWN CENTRE$$,#{state_id_qld},-28.077467,153.38531), - ($$4270$$,$$TAMBORINE$$,#{state_id_qld},-27.880883,153.130246), - ($$4271$$,$$EAGLE HEIGHTS$$,#{state_id_qld},-27.922442,153.207493), - ($$4272$$,$$MOUNT TAMBORINE$$,#{state_id_qld},-27.947935,153.191359), - ($$4272$$,$$NORTH TAMBORINE$$,#{state_id_qld},-27.947935,153.191359), - ($$4272$$,$$TAMBORINE MOUNTAIN$$,#{state_id_qld},-27.947935,153.191359), - ($$4275$$,$$BENOBBLE$$,#{state_id_qld},-27.995053,153.162089), - ($$4275$$,$$BIDDADDABA$$,#{state_id_qld},-27.995053,153.162089), - ($$4275$$,$$BOYLAND$$,#{state_id_qld},-27.995053,153.162089), - ($$4275$$,$$CANUNGRA$$,#{state_id_qld},-27.995053,153.162089), - ($$4275$$,$$FERNY GLEN$$,#{state_id_qld},-27.995053,153.162089), - ($$4275$$,$$FLYING FOX$$,#{state_id_qld},-27.995053,153.162089), - ($$4275$$,$$ILLINBAH$$,#{state_id_qld},-27.995053,153.162089), - ($$4275$$,$$LAMINGTON NATIONAL PARK$$,#{state_id_qld},-27.995053,153.162089), - ($$4275$$,$$SARABAH$$,#{state_id_qld},-27.995053,153.162089), - ($$4275$$,$$WITHEREN$$,#{state_id_qld},-27.995053,153.162089), - ($$4275$$,$$WONGLEPONG$$,#{state_id_qld},-27.995053,153.162089), - ($$4280$$,$$JIMBOOMBA$$,#{state_id_qld},-27.831402,153.028469), - ($$4280$$,$$NORTH MACLEAN$$,#{state_id_qld},-27.831402,153.028469), - ($$4280$$,$$SOUTH MACLEAN$$,#{state_id_qld},-27.831402,153.028469), - ($$4280$$,$$STOCKLEIGH$$,#{state_id_qld},-27.831402,153.028469), - ($$4285$$,$$ALLENVIEW$$,#{state_id_qld},-27.901257,152.939081), - ($$4285$$,$$BEAUDESERT$$,#{state_id_qld},-27.901257,152.939081), - ($$4285$$,$$BIRNAM$$,#{state_id_qld},-27.901257,152.939081), - ($$4285$$,$$BROMELTON$$,#{state_id_qld},-27.901257,152.939081), - ($$4285$$,$$CAINBABLE$$,#{state_id_qld},-27.901257,152.939081), - ($$4285$$,$$CEDAR GROVE$$,#{state_id_qld},-27.901257,152.939081), - ($$4285$$,$$CEDAR VALE$$,#{state_id_qld},-27.901257,152.939081), - ($$4285$$,$$CHINGHEE CREEK$$,#{state_id_qld},-27.901257,152.939081), - ($$4285$$,$$CHRISTMAS CREEK$$,#{state_id_qld},-27.901257,152.939081), - ($$4285$$,$$CRYNA$$,#{state_id_qld},-27.901257,152.939081), - ($$4285$$,$$DARLINGTON$$,#{state_id_qld},-27.901257,152.939081), - ($$4285$$,$$GLENEAGLE$$,#{state_id_qld},-27.901257,152.939081), - ($$4285$$,$$HILLVIEW$$,#{state_id_qld},-27.901257,152.939081), - ($$4285$$,$$INNISPLAIN$$,#{state_id_qld},-27.901257,152.939081), - ($$4285$$,$$JOSEPHVILLE$$,#{state_id_qld},-27.901257,152.939081), - ($$4285$$,$$KAGARU$$,#{state_id_qld},-27.901257,152.939081), - ($$4285$$,$$KERRY$$,#{state_id_qld},-27.901257,152.939081), - ($$4285$$,$$KNAPP CREEK$$,#{state_id_qld},-27.901257,152.939081), - ($$4285$$,$$KOORALBYN$$,#{state_id_qld},-27.901257,152.939081), - ($$4285$$,$$LAMINGTON$$,#{state_id_qld},-27.901257,152.939081), - ($$4285$$,$$LARAVALE$$,#{state_id_qld},-27.901257,152.939081), - ($$4285$$,$$MOUNT GIPPS$$,#{state_id_qld},-27.901257,152.939081), - ($$4285$$,$$MUNDOOLUN$$,#{state_id_qld},-27.901257,152.939081), - ($$4285$$,$$NINDOOINBAH$$,#{state_id_qld},-27.901257,152.939081), - ($$4285$$,$$OAKY CREEK$$,#{state_id_qld},-27.901257,152.939081), - ($$4285$$,$$TABOOBA$$,#{state_id_qld},-27.901257,152.939081), - ($$4285$$,$$TABRAGALBA$$,#{state_id_qld},-27.901257,152.939081), - ($$4285$$,$$TAMROOKUM$$,#{state_id_qld},-27.901257,152.939081), - ($$4285$$,$$TAMROOKUM CREEK$$,#{state_id_qld},-27.901257,152.939081), - ($$4285$$,$$UNDULLAH$$,#{state_id_qld},-27.901257,152.939081), - ($$4285$$,$$VERESDALE$$,#{state_id_qld},-27.901257,152.939081), - ($$4285$$,$$VERESDALE SCRUB$$,#{state_id_qld},-27.901257,152.939081), - ($$4285$$,$$WOODHILL$$,#{state_id_qld},-27.901257,152.939081), - ($$4287$$,$$BARNEY VIEW$$,#{state_id_qld},-28.260921,152.781251), - ($$4287$$,$$MOUNT BARNEY$$,#{state_id_qld},-28.260921,152.781251), - ($$4287$$,$$MOUNT LINDESAY$$,#{state_id_qld},-28.260921,152.781251), - ($$4287$$,$$PALEN CREEK$$,#{state_id_qld},-28.260921,152.781251), - ($$4287$$,$$RATHDOWNEY$$,#{state_id_qld},-28.260921,152.781251), - ($$4287$$,$$RUNNING CREEK$$,#{state_id_qld},-28.260921,152.781251), - ($$4300$$,$$AUGUSTINE HEIGHTS$$,#{state_id_qld},-27.669147,152.89354), - ($$4300$$,$$BELLBIRD PARK$$,#{state_id_qld},-27.669147,152.89354), - ($$4300$$,$$BROOKWATER$$,#{state_id_qld},-27.669147,152.89354), - ($$4300$$,$$CAMIRA$$,#{state_id_qld},-27.669147,152.89354), - ($$4300$$,$$CAROLE PARK$$,#{state_id_qld},-27.669147,152.89354), - ($$4300$$,$$GAILES$$,#{state_id_qld},-27.669147,152.89354), - ($$4300$$,$$GOODNA$$,#{state_id_qld},-27.669147,152.89354), - ($$4300$$,$$GOODNA DC$$,#{state_id_qld},-27.669147,152.89354), - ($$4300$$,$$SPRINGFIELD$$,#{state_id_qld},-27.669147,152.89354), - ($$4300$$,$$SPRINGFIELD CENTRAL$$,#{state_id_qld},-27.669147,152.89354), - ($$4300$$,$$SPRINGFIELD LAKES$$,#{state_id_qld},-27.669147,152.89354), - ($$4301$$,$$COLLINGWOOD PARK$$,#{state_id_qld},-27.613211,152.863215), - ($$4301$$,$$REDBANK$$,#{state_id_qld},-27.613211,152.863215), - ($$4301$$,$$REDBANK PLAINS$$,#{state_id_qld},-27.613211,152.863215), - ($$4303$$,$$DINMORE$$,#{state_id_qld},-27.597997,152.831683), - ($$4303$$,$$NEW CHUM$$,#{state_id_qld},-27.597997,152.831683), - ($$4303$$,$$RIVERVIEW$$,#{state_id_qld},-27.597997,152.831683), - ($$4304$$,$$BLACKSTONE$$,#{state_id_qld},-27.62904,152.795901), - ($$4304$$,$$BOOVAL$$,#{state_id_qld},-27.62904,152.795901), - ($$4304$$,$$BOOVAL BC$$,#{state_id_qld},-27.62904,152.795901), - ($$4304$$,$$BOOVAL DC$$,#{state_id_qld},-27.62904,152.795901), - ($$4304$$,$$BOOVAL FAIR$$,#{state_id_qld},-27.62904,152.795901), - ($$4304$$,$$BUNDAMBA$$,#{state_id_qld},-27.62904,152.795901), - ($$4304$$,$$EBBW VALE$$,#{state_id_qld},-27.62904,152.795901), - ($$4304$$,$$NORTH BOOVAL$$,#{state_id_qld},-27.62904,152.795901), - ($$4304$$,$$SILKSTONE$$,#{state_id_qld},-27.62904,152.795901), - ($$4305$$,$$BASIN POCKET$$,#{state_id_qld},-27.601066,152.772672), - ($$4305$$,$$BRASSALL$$,#{state_id_qld},-27.601066,152.772672), - ($$4305$$,$$BREMER$$,#{state_id_qld},-27.601066,152.772672), - ($$4305$$,$$CHURCHILL$$,#{state_id_qld},-27.601066,152.772672), - ($$4305$$,$$COALFALLS$$,#{state_id_qld},-27.601066,152.772672), - ($$4305$$,$$EAST IPSWICH$$,#{state_id_qld},-27.601066,152.772672), - ($$4305$$,$$EASTERN HEIGHTS$$,#{state_id_qld},-27.601066,152.772672), - ($$4305$$,$$FLINDERS VIEW$$,#{state_id_qld},-27.601066,152.772672), - ($$4305$$,$$IPSWICH$$,#{state_id_qld},-27.601066,152.772672), - ($$4305$$,$$LEICHHARDT$$,#{state_id_qld},-27.601066,152.772672), - ($$4305$$,$$LIMESTONE RIDGES$$,#{state_id_qld},-27.601066,152.772672), - ($$4305$$,$$MOORES POCKET$$,#{state_id_qld},-27.601066,152.772672), - ($$4305$$,$$NEWTOWN$$,#{state_id_qld},-27.601066,152.772672), - ($$4305$$,$$NORTH IPSWICH$$,#{state_id_qld},-27.601066,152.772672), - ($$4305$$,$$NORTH TIVOLI$$,#{state_id_qld},-27.601066,152.772672), - ($$4305$$,$$ONE MILE$$,#{state_id_qld},-27.601066,152.772672), - ($$4305$$,$$RACEVIEW$$,#{state_id_qld},-27.601066,152.772672), - ($$4305$$,$$SADLIERS CROSSING$$,#{state_id_qld},-27.601066,152.772672), - ($$4305$$,$$TIVOLI$$,#{state_id_qld},-27.601066,152.772672), - ($$4305$$,$$WEST IPSWICH$$,#{state_id_qld},-27.601066,152.772672), - ($$4305$$,$$WOODEND$$,#{state_id_qld},-27.601066,152.772672), - ($$4305$$,$$WULKURAKA$$,#{state_id_qld},-27.601066,152.772672), - ($$4305$$,$$YAMANTO$$,#{state_id_qld},-27.601066,152.772672), - ($$4306$$,$$AMBERLEY$$,#{state_id_qld},-27.640431,152.702399), - ($$4306$$,$$AVOCA VALE$$,#{state_id_qld},-27.640431,152.702399), - ($$4306$$,$$BANKS CREEK$$,#{state_id_qld},-27.640431,152.702399), - ($$4306$$,$$BARELLAN POINT$$,#{state_id_qld},-27.640431,152.702399), - ($$4306$$,$$BENARKIN$$,#{state_id_qld},-27.640431,152.702399), - ($$4306$$,$$BENARKIN NORTH$$,#{state_id_qld},-27.640431,152.702399), - ($$4306$$,$$BLACKBUTT$$,#{state_id_qld},-27.640431,152.702399), - ($$4306$$,$$BLACKBUTT NORTH$$,#{state_id_qld},-27.640431,152.702399), - ($$4306$$,$$BLACKBUTT SOUTH$$,#{state_id_qld},-27.640431,152.702399), - ($$4306$$,$$BLACKSOIL$$,#{state_id_qld},-27.640431,152.702399), - ($$4306$$,$$BORALLON$$,#{state_id_qld},-27.640431,152.702399), - ($$4306$$,$$CHERRY CREEK$$,#{state_id_qld},-27.640431,152.702399), - ($$4306$$,$$CHUWAR$$,#{state_id_qld},-27.640431,152.702399), - ($$4306$$,$$COLINTON$$,#{state_id_qld},-27.640431,152.702399), - ($$4306$$,$$DEEBING HEIGHTS$$,#{state_id_qld},-27.640431,152.702399), - ($$4306$$,$$DUNDAS$$,#{state_id_qld},-27.640431,152.702399), - ($$4306$$,$$ENGLAND CREEK$$,#{state_id_qld},-27.640431,152.702399), - ($$4306$$,$$FAIRNEY VIEW$$,#{state_id_qld},-27.640431,152.702399), - ($$4306$$,$$FERNVALE$$,#{state_id_qld},-27.640431,152.702399), - ($$4306$$,$$GLAMORGAN VALE$$,#{state_id_qld},-27.640431,152.702399), - ($$4306$$,$$GOOGA CREEK$$,#{state_id_qld},-27.640431,152.702399), - ($$4306$$,$$GOOLMAN$$,#{state_id_qld},-27.640431,152.702399), - ($$4306$$,$$HAIGSLEA$$,#{state_id_qld},-27.640431,152.702399), - ($$4306$$,$$HARLIN$$,#{state_id_qld},-27.640431,152.702399), - ($$4306$$,$$IRONBARK$$,#{state_id_qld},-27.640431,152.702399), - ($$4306$$,$$KARALEE$$,#{state_id_qld},-27.640431,152.702399), - ($$4306$$,$$KARANA DOWNS$$,#{state_id_qld},-27.640431,152.702399), - ($$4306$$,$$KARRABIN$$,#{state_id_qld},-27.640431,152.702399), - ($$4306$$,$$KHOLO$$,#{state_id_qld},-27.640431,152.702399), - ($$4306$$,$$LAKE MANCHESTER$$,#{state_id_qld},-27.640431,152.702399), - ($$4306$$,$$LARK HILL$$,#{state_id_qld},-27.640431,152.702399), - ($$4306$$,$$LINVILLE$$,#{state_id_qld},-27.640431,152.702399), - ($$4306$$,$$MOORE$$,#{state_id_qld},-27.640431,152.702399), - ($$4306$$,$$MOUNT BINGA$$,#{state_id_qld},-27.640431,152.702399), - ($$4306$$,$$MOUNT CROSBY$$,#{state_id_qld},-27.640431,152.702399), - ($$4306$$,$$MOUNT MARROW$$,#{state_id_qld},-27.640431,152.702399), - ($$4306$$,$$MOUNT STANLEY$$,#{state_id_qld},-27.640431,152.702399), - ($$4306$$,$$MUIRLEA$$,#{state_id_qld},-27.640431,152.702399), - ($$4306$$,$$NUKKU$$,#{state_id_qld},-27.640431,152.702399), - ($$4306$$,$$PEAK CROSSING$$,#{state_id_qld},-27.640431,152.702399), - ($$4306$$,$$PINE MOUNTAIN$$,#{state_id_qld},-27.640431,152.702399), - ($$4306$$,$$PURGA$$,#{state_id_qld},-27.640431,152.702399), - ($$4306$$,$$RIPLEY$$,#{state_id_qld},-27.640431,152.702399), - ($$4306$$,$$SOUTH RIPLEY$$,#{state_id_qld},-27.640431,152.702399), - ($$4306$$,$$SPLIT YARD CREEK$$,#{state_id_qld},-27.640431,152.702399), - ($$4306$$,$$SWANBANK$$,#{state_id_qld},-27.640431,152.702399), - ($$4306$$,$$TAROMEO$$,#{state_id_qld},-27.640431,152.702399), - ($$4306$$,$$TEELAH$$,#{state_id_qld},-27.640431,152.702399), - ($$4306$$,$$THAGOONA$$,#{state_id_qld},-27.640431,152.702399), - ($$4306$$,$$VERNOR$$,#{state_id_qld},-27.640431,152.702399), - ($$4306$$,$$WALLOON$$,#{state_id_qld},-27.640431,152.702399), - ($$4306$$,$$WANORA$$,#{state_id_qld},-27.640431,152.702399), - ($$4306$$,$$WASHPOOL$$,#{state_id_qld},-27.640431,152.702399), - ($$4306$$,$$WEST AMBERLEY$$,#{state_id_qld},-27.640431,152.702399), - ($$4306$$,$$WHITE ROCK$$,#{state_id_qld},-27.640431,152.702399), - ($$4306$$,$$WILLOWBANK$$,#{state_id_qld},-27.640431,152.702399), - ($$4306$$,$$WIVENHOE POCKET$$,#{state_id_qld},-27.640431,152.702399), - ($$4307$$,$$COLEYVILLE$$,#{state_id_qld},-27.82052,152.56642), - ($$4307$$,$$HARRISVILLE$$,#{state_id_qld},-27.82052,152.56642), - ($$4307$$,$$MUTDAPILLY$$,#{state_id_qld},-27.82052,152.56642), - ($$4307$$,$$RADFORD$$,#{state_id_qld},-27.82052,152.56642), - ($$4307$$,$$SILVERDALE$$,#{state_id_qld},-27.82052,152.56642), - ($$4307$$,$$WARRILL VIEW$$,#{state_id_qld},-27.82052,152.56642), - ($$4307$$,$$WILSONS PLAINS$$,#{state_id_qld},-27.82052,152.56642), - ($$4309$$,$$ARATULA$$,#{state_id_qld},-27.981456,152.548619), - ($$4309$$,$$CHARLWOOD$$,#{state_id_qld},-27.981456,152.548619), - ($$4309$$,$$CLUMBER$$,#{state_id_qld},-27.981456,152.548619), - ($$4309$$,$$FASSIFERN$$,#{state_id_qld},-27.981456,152.548619), - ($$4309$$,$$FASSIFERN VALLEY$$,#{state_id_qld},-27.981456,152.548619), - ($$4309$$,$$FRAZERVIEW$$,#{state_id_qld},-27.981456,152.548619), - ($$4309$$,$$KALBAR$$,#{state_id_qld},-27.981456,152.548619), - ($$4309$$,$$KENTS LAGOON$$,#{state_id_qld},-27.981456,152.548619), - ($$4309$$,$$KULGUN$$,#{state_id_qld},-27.981456,152.548619), - ($$4309$$,$$MILORA$$,#{state_id_qld},-27.981456,152.548619), - ($$4309$$,$$MOOGERAH$$,#{state_id_qld},-27.981456,152.548619), - ($$4309$$,$$MORWINCHA$$,#{state_id_qld},-27.981456,152.548619), - ($$4309$$,$$MOUNT EDWARDS$$,#{state_id_qld},-27.981456,152.548619), - ($$4309$$,$$MUNBILLA$$,#{state_id_qld},-27.981456,152.548619), - ($$4309$$,$$OBUM OBUM$$,#{state_id_qld},-27.981456,152.548619), - ($$4309$$,$$TAROME$$,#{state_id_qld},-27.981456,152.548619), - ($$4309$$,$$TEVIOTVILLE$$,#{state_id_qld},-27.981456,152.548619), - ($$4310$$,$$ALLANDALE$$,#{state_id_qld},-27.997335,152.714213), - ($$4310$$,$$ANTHONY$$,#{state_id_qld},-27.997335,152.714213), - ($$4310$$,$$BLANTYRE$$,#{state_id_qld},-27.997335,152.714213), - ($$4310$$,$$BOONAH$$,#{state_id_qld},-27.997335,152.714213), - ($$4310$$,$$BUNBURRA$$,#{state_id_qld},-27.997335,152.714213), - ($$4310$$,$$BUNJURGEN$$,#{state_id_qld},-27.997335,152.714213), - ($$4310$$,$$BURNETT CREEK$$,#{state_id_qld},-27.997335,152.714213), - ($$4310$$,$$CANNON CREEK$$,#{state_id_qld},-27.997335,152.714213), - ($$4310$$,$$CARNEYS CREEK$$,#{state_id_qld},-27.997335,152.714213), - ($$4310$$,$$COOCHIN$$,#{state_id_qld},-27.997335,152.714213), - ($$4310$$,$$COULSON$$,#{state_id_qld},-27.997335,152.714213), - ($$4310$$,$$CROFTBY$$,#{state_id_qld},-27.997335,152.714213), - ($$4310$$,$$DUGANDAN$$,#{state_id_qld},-27.997335,152.714213), - ($$4310$$,$$FRENCHES CREEK$$,#{state_id_qld},-27.997335,152.714213), - ($$4310$$,$$HOYA$$,#{state_id_qld},-27.997335,152.714213), - ($$4310$$,$$KENTS POCKET$$,#{state_id_qld},-27.997335,152.714213), - ($$4310$$,$$MAROON$$,#{state_id_qld},-27.997335,152.714213), - ($$4310$$,$$MILBONG$$,#{state_id_qld},-27.997335,152.714213), - ($$4310$$,$$MILFORD$$,#{state_id_qld},-27.997335,152.714213), - ($$4310$$,$$MOUNT ALFORD$$,#{state_id_qld},-27.997335,152.714213), - ($$4310$$,$$MOUNT FRENCH$$,#{state_id_qld},-27.997335,152.714213), - ($$4310$$,$$ROADVALE$$,#{state_id_qld},-27.997335,152.714213), - ($$4310$$,$$TEMPLIN$$,#{state_id_qld},-27.997335,152.714213), - ($$4310$$,$$WALLACES CREEK$$,#{state_id_qld},-27.997335,152.714213), - ($$4310$$,$$WOOLOOMAN$$,#{state_id_qld},-27.997335,152.714213), - ($$4310$$,$$WYARALONG$$,#{state_id_qld},-27.997335,152.714213), - ($$4311$$,$$ATKINSONS DAM$$,#{state_id_qld},-27.421011,152.455509), - ($$4311$$,$$BRIGHTVIEW$$,#{state_id_qld},-27.421011,152.455509), - ($$4311$$,$$BUARABA$$,#{state_id_qld},-27.421011,152.455509), - ($$4311$$,$$BUARABA SOUTH$$,#{state_id_qld},-27.421011,152.455509), - ($$4311$$,$$CHURCHABLE$$,#{state_id_qld},-27.421011,152.455509), - ($$4311$$,$$CLARENDON$$,#{state_id_qld},-27.421011,152.455509), - ($$4311$$,$$COOLANA$$,#{state_id_qld},-27.421011,152.455509), - ($$4311$$,$$COOMINYA$$,#{state_id_qld},-27.421011,152.455509), - ($$4311$$,$$LOCKYER WATERS$$,#{state_id_qld},-27.421011,152.455509), - ($$4311$$,$$LOWOOD$$,#{state_id_qld},-27.421011,152.455509), - ($$4311$$,$$MINDEN$$,#{state_id_qld},-27.421011,152.455509), - ($$4311$$,$$MOUNT TARAMPA$$,#{state_id_qld},-27.421011,152.455509), - ($$4311$$,$$PATRICK ESTATE$$,#{state_id_qld},-27.421011,152.455509), - ($$4311$$,$$PRENZLAU$$,#{state_id_qld},-27.421011,152.455509), - ($$4311$$,$$RIFLE RANGE$$,#{state_id_qld},-27.421011,152.455509), - ($$4311$$,$$TARAMPA$$,#{state_id_qld},-27.421011,152.455509), - ($$4311$$,$$WIVENHOE HILL$$,#{state_id_qld},-27.421011,152.455509), - ($$4312$$,$$BRYDEN$$,#{state_id_qld},-27.269163,152.585337), - ($$4312$$,$$CABOONBAH$$,#{state_id_qld},-27.269163,152.585337), - ($$4312$$,$$COAL CREEK$$,#{state_id_qld},-27.269163,152.585337), - ($$4312$$,$$CROSSDALE$$,#{state_id_qld},-27.269163,152.585337), - ($$4312$$,$$ESK$$,#{state_id_qld},-27.269163,152.585337), - ($$4312$$,$$ESKDALE$$,#{state_id_qld},-27.269163,152.585337), - ($$4312$$,$$GLEN ESK$$,#{state_id_qld},-27.269163,152.585337), - ($$4312$$,$$MOOMBRA$$,#{state_id_qld},-27.269163,152.585337), - ($$4312$$,$$MOUNT BYRON$$,#{state_id_qld},-27.269163,152.585337), - ($$4312$$,$$MOUNT HALLEN$$,#{state_id_qld},-27.269163,152.585337), - ($$4312$$,$$MURRUMBA$$,#{state_id_qld},-27.269163,152.585337), - ($$4312$$,$$REDBANK CREEK$$,#{state_id_qld},-27.269163,152.585337), - ($$4312$$,$$SOMERSET DAM$$,#{state_id_qld},-27.269163,152.585337), - ($$4313$$,$$BIARRA$$,#{state_id_qld},-27.163924,152.327655), - ($$4313$$,$$BRAEMORE$$,#{state_id_qld},-27.163924,152.327655), - ($$4313$$,$$COOEEIMBARDI$$,#{state_id_qld},-27.163924,152.327655), - ($$4313$$,$$CRESSBROOK$$,#{state_id_qld},-27.163924,152.327655), - ($$4313$$,$$FULHAM$$,#{state_id_qld},-27.163924,152.327655), - ($$4313$$,$$GREGORS CREEK$$,#{state_id_qld},-27.163924,152.327655), - ($$4313$$,$$IVORY CREEK$$,#{state_id_qld},-27.163924,152.327655), - ($$4313$$,$$LOWER CRESSBROOK$$,#{state_id_qld},-27.163924,152.327655), - ($$4313$$,$$MOUNT BEPPO$$,#{state_id_qld},-27.163924,152.327655), - ($$4313$$,$$OTTABA$$,#{state_id_qld},-27.163924,152.327655), - ($$4313$$,$$SCRUB CREEK$$,#{state_id_qld},-27.163924,152.327655), - ($$4313$$,$$TOOGOOLAWAH$$,#{state_id_qld},-27.163924,152.327655), - ($$4313$$,$$YIMBUN$$,#{state_id_qld},-27.163924,152.327655), - ($$4340$$,$$ASHWELL$$,#{state_id_qld},-27.628455,152.560103), - ($$4340$$,$$CALVERT$$,#{state_id_qld},-27.628455,152.560103), - ($$4340$$,$$EBENEZER$$,#{state_id_qld},-27.628455,152.560103), - ($$4340$$,$$GRANDCHESTER$$,#{state_id_qld},-27.628455,152.560103), - ($$4340$$,$$JEEBROPILLY$$,#{state_id_qld},-27.628455,152.560103), - ($$4340$$,$$LANEFIELD$$,#{state_id_qld},-27.628455,152.560103), - ($$4340$$,$$LOWER MOUNT WALKER$$,#{state_id_qld},-27.628455,152.560103), - ($$4340$$,$$MERRYVALE$$,#{state_id_qld},-27.628455,152.560103), - ($$4340$$,$$MOORANG$$,#{state_id_qld},-27.628455,152.560103), - ($$4340$$,$$MOUNT FORBES$$,#{state_id_qld},-27.628455,152.560103), - ($$4340$$,$$MOUNT MORT$$,#{state_id_qld},-27.628455,152.560103), - ($$4340$$,$$MOUNT WALKER$$,#{state_id_qld},-27.628455,152.560103), - ($$4340$$,$$MOUNT WALKER WEST$$,#{state_id_qld},-27.628455,152.560103), - ($$4340$$,$$ROSEVALE$$,#{state_id_qld},-27.628455,152.560103), - ($$4340$$,$$ROSEWOOD$$,#{state_id_qld},-27.628455,152.560103), - ($$4340$$,$$TALLEGALLA$$,#{state_id_qld},-27.628455,152.560103), - ($$4340$$,$$THE BLUFF$$,#{state_id_qld},-27.628455,152.560103), - ($$4340$$,$$WOOLSHED$$,#{state_id_qld},-27.628455,152.560103), - ($$4341$$,$$BLENHEIM$$,#{state_id_qld},-27.653658,152.326898), - ($$4341$$,$$HATTON VALE$$,#{state_id_qld},-27.653658,152.326898), - ($$4341$$,$$KENSINGTON GROVE$$,#{state_id_qld},-27.653658,152.326898), - ($$4341$$,$$KENTVILLE$$,#{state_id_qld},-27.653658,152.326898), - ($$4341$$,$$LAIDLEY$$,#{state_id_qld},-27.653658,152.326898), - ($$4341$$,$$LAIDLEY CREEK WEST$$,#{state_id_qld},-27.653658,152.326898), - ($$4341$$,$$LAIDLEY HEIGHTS$$,#{state_id_qld},-27.653658,152.326898), - ($$4341$$,$$LAIDLEY NORTH$$,#{state_id_qld},-27.653658,152.326898), - ($$4341$$,$$LAIDLEY SOUTH$$,#{state_id_qld},-27.653658,152.326898), - ($$4341$$,$$MOUNT BERRYMAN$$,#{state_id_qld},-27.653658,152.326898), - ($$4341$$,$$MULGOWIE$$,#{state_id_qld},-27.653658,152.326898), - ($$4341$$,$$PLAINLAND$$,#{state_id_qld},-27.653658,152.326898), - ($$4341$$,$$REGENCY DOWNS$$,#{state_id_qld},-27.653658,152.326898), - ($$4341$$,$$SUMMERHOLM$$,#{state_id_qld},-27.653658,152.326898), - ($$4341$$,$$THORNTON$$,#{state_id_qld},-27.653658,152.326898), - ($$4341$$,$$TOWNSON$$,#{state_id_qld},-27.653658,152.326898), - ($$4342$$,$$CROWLEY VALE$$,#{state_id_qld},-27.543847,152.377687), - ($$4342$$,$$FOREST HILL$$,#{state_id_qld},-27.543847,152.377687), - ($$4342$$,$$GLEN CAIRN$$,#{state_id_qld},-27.543847,152.377687), - ($$4342$$,$$GLENORE GROVE$$,#{state_id_qld},-27.543847,152.377687), - ($$4342$$,$$LOCKROSE$$,#{state_id_qld},-27.543847,152.377687), - ($$4342$$,$$LYNFORD$$,#{state_id_qld},-27.543847,152.377687), - ($$4343$$,$$ADARE$$,#{state_id_qld},-27.511922,152.296235), - ($$4343$$,$$BLACK DUCK CREEK$$,#{state_id_qld},-27.511922,152.296235), - ($$4343$$,$$CAFFEY$$,#{state_id_qld},-27.511922,152.296235), - ($$4343$$,$$COLLEGE VIEW$$,#{state_id_qld},-27.511922,152.296235), - ($$4343$$,$$EAST HALDON$$,#{state_id_qld},-27.511922,152.296235), - ($$4343$$,$$FORDSDALE$$,#{state_id_qld},-27.511922,152.296235), - ($$4343$$,$$GATTON$$,#{state_id_qld},-27.511922,152.296235), - ($$4343$$,$$INGOLDSBY$$,#{state_id_qld},-27.511922,152.296235), - ($$4343$$,$$JUNCTION VIEW$$,#{state_id_qld},-27.511922,152.296235), - ($$4343$$,$$LAKE CLARENDON$$,#{state_id_qld},-27.511922,152.296235), - ($$4343$$,$$LAWES$$,#{state_id_qld},-27.511922,152.296235), - ($$4343$$,$$LEFTHAND BRANCH$$,#{state_id_qld},-27.511922,152.296235), - ($$4343$$,$$LOWER TENTHILL$$,#{state_id_qld},-27.511922,152.296235), - ($$4343$$,$$MORTON VALE$$,#{state_id_qld},-27.511922,152.296235), - ($$4343$$,$$MOUNT SYLVIA$$,#{state_id_qld},-27.511922,152.296235), - ($$4343$$,$$PLACID HILLS$$,#{state_id_qld},-27.511922,152.296235), - ($$4343$$,$$RINGWOOD$$,#{state_id_qld},-27.511922,152.296235), - ($$4343$$,$$ROCKSIDE$$,#{state_id_qld},-27.511922,152.296235), - ($$4343$$,$$ROPELEY$$,#{state_id_qld},-27.511922,152.296235), - ($$4343$$,$$SPRING CREEK$$,#{state_id_qld},-27.511922,152.296235), - ($$4343$$,$$UPPER TENTHILL$$,#{state_id_qld},-27.511922,152.296235), - ($$4343$$,$$VINEGAR HILL$$,#{state_id_qld},-27.511922,152.296235), - ($$4343$$,$$WOODBINE$$,#{state_id_qld},-27.511922,152.296235), - ($$4343$$,$$WOODLANDS$$,#{state_id_qld},-27.511922,152.296235), - ($$4344$$,$$CARPENDALE$$,#{state_id_qld},-27.589099,152.153257), - ($$4344$$,$$EGYPT$$,#{state_id_qld},-27.589099,152.153257), - ($$4344$$,$$FLAGSTONE CREEK$$,#{state_id_qld},-27.589099,152.153257), - ($$4344$$,$$HELIDON$$,#{state_id_qld},-27.589099,152.153257), - ($$4344$$,$$HELIDON SPA$$,#{state_id_qld},-27.589099,152.153257), - ($$4344$$,$$IREDALE$$,#{state_id_qld},-27.589099,152.153257), - ($$4344$$,$$LILYDALE$$,#{state_id_qld},-27.589099,152.153257), - ($$4344$$,$$LOCKYER$$,#{state_id_qld},-27.589099,152.153257), - ($$4344$$,$$ROCKMOUNT$$,#{state_id_qld},-27.589099,152.153257), - ($$4344$$,$$SEVENTEEN MILE$$,#{state_id_qld},-27.589099,152.153257), - ($$4344$$,$$STOCKYARD$$,#{state_id_qld},-27.589099,152.153257), - ($$4344$$,$$UPPER FLAGSTONE$$,#{state_id_qld},-27.589099,152.153257), - ($$4345$$,$$GATTON COLLEGE$$,#{state_id_qld},-27.549094,152.336382), - ($$4346$$,$$MARBURG$$,#{state_id_qld},-27.56311,152.588618), - ($$4347$$,$$GRANTHAM$$,#{state_id_qld},-27.574836,152.204914), - ($$4347$$,$$MA MA CREEK$$,#{state_id_qld},-27.574836,152.204914), - ($$4347$$,$$MOUNT WHITESTONE$$,#{state_id_qld},-27.574836,152.204914), - ($$4347$$,$$VERADILLA$$,#{state_id_qld},-27.574836,152.204914), - ($$4347$$,$$WINWILL$$,#{state_id_qld},-27.574836,152.204914), - ($$4350$$,$$ATHOL$$,#{state_id_qld},-27.615354,151.774846), - ($$4350$$,$$BLUE MOUNTAIN HEIGHTS$$,#{state_id_qld},-27.615354,151.774846), - ($$4350$$,$$CENTENARY HEIGHTS$$,#{state_id_qld},-27.615354,151.774846), - ($$4350$$,$$CHARLTON$$,#{state_id_qld},-27.615354,151.774846), - ($$4350$$,$$CLIFFORD GARDENS$$,#{state_id_qld},-27.615354,151.774846), - ($$4350$$,$$COTSWOLD HILLS$$,#{state_id_qld},-27.615354,151.774846), - ($$4350$$,$$CRANLEY$$,#{state_id_qld},-27.615354,151.774846), - ($$4350$$,$$DARLING HEIGHTS$$,#{state_id_qld},-27.615354,151.774846), - ($$4350$$,$$DRAYTON$$,#{state_id_qld},-27.615354,151.774846), - ($$4350$$,$$DRAYTON NORTH$$,#{state_id_qld},-27.615354,151.774846), - ($$4350$$,$$EAST TOOWOOMBA$$,#{state_id_qld},-27.615354,151.774846), - ($$4350$$,$$FINNIE$$,#{state_id_qld},-27.615354,151.774846), - ($$4350$$,$$GLENVALE$$,#{state_id_qld},-27.615354,151.774846), - ($$4350$$,$$GOWRIE MOUNTAIN$$,#{state_id_qld},-27.615354,151.774846), - ($$4350$$,$$HARLAXTON$$,#{state_id_qld},-27.615354,151.774846), - ($$4350$$,$$HARRISTOWN$$,#{state_id_qld},-27.615354,151.774846), - ($$4350$$,$$KEARNEYS SPRING$$,#{state_id_qld},-27.615354,151.774846), - ($$4350$$,$$MIDDLE RIDGE$$,#{state_id_qld},-27.615354,151.774846), - ($$4350$$,$$MOUNT KYNOCH$$,#{state_id_qld},-27.615354,151.774846), - ($$4350$$,$$MOUNT LOFTY$$,#{state_id_qld},-27.615354,151.774846), - ($$4350$$,$$MOUNT RASCAL$$,#{state_id_qld},-27.615354,151.774846), - ($$4350$$,$$NEWTOWN$$,#{state_id_qld},-27.615354,151.774846), - ($$4350$$,$$NORTH TOOWOOMBA$$,#{state_id_qld},-27.615354,151.774846), - ($$4350$$,$$NORTHLANDS$$,#{state_id_qld},-27.615354,151.774846), - ($$4350$$,$$PRINCE HENRY HEIGHTS$$,#{state_id_qld},-27.615354,151.774846), - ($$4350$$,$$RANGEVILLE$$,#{state_id_qld},-27.615354,151.774846), - ($$4350$$,$$REDWOOD$$,#{state_id_qld},-27.615354,151.774846), - ($$4350$$,$$ROCKVILLE$$,#{state_id_qld},-27.615354,151.774846), - ($$4350$$,$$SOUTH TOOWOOMBA$$,#{state_id_qld},-27.615354,151.774846), - ($$4350$$,$$SOUTHTOWN$$,#{state_id_qld},-27.615354,151.774846), - ($$4350$$,$$TOOWOOMBA$$,#{state_id_qld},-27.615354,151.774846), - ($$4350$$,$$TOOWOOMBA BC$$,#{state_id_qld},-27.615354,151.774846), - ($$4350$$,$$TOOWOOMBA CITY$$,#{state_id_qld},-27.615354,151.774846), - ($$4350$$,$$TOOWOOMBA DC$$,#{state_id_qld},-27.615354,151.774846), - ($$4350$$,$$TOOWOOMBA EAST$$,#{state_id_qld},-27.615354,151.774846), - ($$4350$$,$$TOOWOOMBA SOUTH$$,#{state_id_qld},-27.615354,151.774846), - ($$4350$$,$$TOOWOOMBA VILLAGE FAIR$$,#{state_id_qld},-27.615354,151.774846), - ($$4350$$,$$TOOWOOMBA WEST$$,#{state_id_qld},-27.615354,151.774846), - ($$4350$$,$$TOP CAMP$$,#{state_id_qld},-27.615354,151.774846), - ($$4350$$,$$TORRINGTON$$,#{state_id_qld},-27.615354,151.774846), - ($$4350$$,$$WELLCAMP$$,#{state_id_qld},-27.615354,151.774846), - ($$4350$$,$$WESTBROOK$$,#{state_id_qld},-27.615354,151.774846), - ($$4350$$,$$WILSONTON$$,#{state_id_qld},-27.615354,151.774846), - ($$4350$$,$$WILSONTON HEIGHTS$$,#{state_id_qld},-27.615354,151.774846), - ($$4350$$,$$WYALLA PLAZA$$,#{state_id_qld},-27.615354,151.774846), - ($$4352$$,$$BALLARD$$,#{state_id_qld},-28.583622,151.801093), - ($$4352$$,$$BAPAUME$$,#{state_id_qld},-28.583622,151.801093), - ($$4352$$,$$BIRNAM$$,#{state_id_qld},-28.583622,151.801093), - ($$4352$$,$$BLANCHVIEW$$,#{state_id_qld},-28.583622,151.801093), - ($$4352$$,$$BRANCHVIEW$$,#{state_id_qld},-28.583622,151.801093), - ($$4352$$,$$CABARLAH$$,#{state_id_qld},-28.583622,151.801093), - ($$4352$$,$$CAWDOR$$,#{state_id_qld},-28.583622,151.801093), - ($$4352$$,$$CEMENT MILLS$$,#{state_id_qld},-28.583622,151.801093), - ($$4352$$,$$COALBANK$$,#{state_id_qld},-28.583622,151.801093), - ($$4352$$,$$CONDAMINE PLAINS$$,#{state_id_qld},-28.583622,151.801093), - ($$4352$$,$$CUTELLA$$,#{state_id_qld},-28.583622,151.801093), - ($$4352$$,$$DERRYMORE$$,#{state_id_qld},-28.583622,151.801093), - ($$4352$$,$$DJUAN$$,#{state_id_qld},-28.583622,151.801093), - ($$4352$$,$$DOCTOR CREEK$$,#{state_id_qld},-28.583622,151.801093), - ($$4352$$,$$EVERGREEN$$,#{state_id_qld},-28.583622,151.801093), - ($$4352$$,$$FIFTEEN MILE$$,#{state_id_qld},-28.583622,151.801093), - ($$4352$$,$$GEHAM$$,#{state_id_qld},-28.583622,151.801093), - ($$4352$$,$$GLENCOE$$,#{state_id_qld},-28.583622,151.801093), - ($$4352$$,$$GORE$$,#{state_id_qld},-28.583622,151.801093), - ($$4352$$,$$GOWRIE JUNCTION$$,#{state_id_qld},-28.583622,151.801093), - ($$4352$$,$$GOWRIE LITTLE PLAIN$$,#{state_id_qld},-28.583622,151.801093), - ($$4352$$,$$GRAPETREE$$,#{state_id_qld},-28.583622,151.801093), - ($$4352$$,$$GROOMSVILLE$$,#{state_id_qld},-28.583622,151.801093), - ($$4352$$,$$HAMPTON$$,#{state_id_qld},-28.583622,151.801093), - ($$4352$$,$$HIGHFIELDS$$,#{state_id_qld},-28.583622,151.801093), - ($$4352$$,$$HIGHGROVE$$,#{state_id_qld},-28.583622,151.801093), - ($$4352$$,$$HODGSON VALE$$,#{state_id_qld},-28.583622,151.801093), - ($$4352$$,$$KARARA$$,#{state_id_qld},-28.583622,151.801093), - ($$4352$$,$$KLEINTON$$,#{state_id_qld},-28.583622,151.801093), - ($$4352$$,$$KULPI$$,#{state_id_qld},-28.583622,151.801093), - ($$4352$$,$$KURROWAH$$,#{state_id_qld},-28.583622,151.801093), - ($$4352$$,$$LILYVALE$$,#{state_id_qld},-28.583622,151.801093), - ($$4352$$,$$MACLAGAN$$,#{state_id_qld},-28.583622,151.801093), - ($$4352$$,$$MALLING$$,#{state_id_qld},-28.583622,151.801093), - ($$4352$$,$$MERINGANDAN$$,#{state_id_qld},-28.583622,151.801093), - ($$4352$$,$$MERINGANDAN WEST$$,#{state_id_qld},-28.583622,151.801093), - ($$4352$$,$$MERRITTS CREEK$$,#{state_id_qld},-28.583622,151.801093), - ($$4352$$,$$MOUNT LUKE$$,#{state_id_qld},-28.583622,151.801093), - ($$4352$$,$$MUNIGANEEN$$,#{state_id_qld},-28.583622,151.801093), - ($$4352$$,$$MURPHYS CREEK$$,#{state_id_qld},-28.583622,151.801093), - ($$4352$$,$$NARKO$$,#{state_id_qld},-28.583622,151.801093), - ($$4352$$,$$NORTH MACLAGAN$$,#{state_id_qld},-28.583622,151.801093), - ($$4352$$,$$NUTGROVE$$,#{state_id_qld},-28.583622,151.801093), - ($$4352$$,$$OMAN AMA$$,#{state_id_qld},-28.583622,151.801093), - ($$4352$$,$$PALMTREE$$,#{state_id_qld},-28.583622,151.801093), - ($$4352$$,$$PAMPAS$$,#{state_id_qld},-28.583622,151.801093), - ($$4352$$,$$PECHEY$$,#{state_id_qld},-28.583622,151.801093), - ($$4352$$,$$PERANGA$$,#{state_id_qld},-28.583622,151.801093), - ($$4352$$,$$PERSEVERANCE$$,#{state_id_qld},-28.583622,151.801093), - ($$4352$$,$$POSTMANS RIDGE$$,#{state_id_qld},-28.583622,151.801093), - ($$4352$$,$$POZIERES$$,#{state_id_qld},-28.583622,151.801093), - ($$4352$$,$$PRESTON$$,#{state_id_qld},-28.583622,151.801093), - ($$4352$$,$$RANGEMORE$$,#{state_id_qld},-28.583622,151.801093), - ($$4352$$,$$RAVENSBOURNE$$,#{state_id_qld},-28.583622,151.801093), - ($$4352$$,$$SILVER RIDGE$$,#{state_id_qld},-28.583622,151.801093), - ($$4352$$,$$SPRING BLUFF$$,#{state_id_qld},-28.583622,151.801093), - ($$4352$$,$$ST AUBYN$$,#{state_id_qld},-28.583622,151.801093), - ($$4352$$,$$THORNVILLE$$,#{state_id_qld},-28.583622,151.801093), - ($$4352$$,$$TOOWOOMBA MC$$,#{state_id_qld},-28.583622,151.801093), - ($$4352$$,$$TUMMAVILLE$$,#{state_id_qld},-28.583622,151.801093), - ($$4352$$,$$UMBIRAM$$,#{state_id_qld},-28.583622,151.801093), - ($$4352$$,$$UPPER LOCKYER$$,#{state_id_qld},-28.583622,151.801093), - ($$4352$$,$$VALE VIEW$$,#{state_id_qld},-28.583622,151.801093), - ($$4352$$,$$WHICHELLO$$,#{state_id_qld},-28.583622,151.801093), - ($$4352$$,$$WHITE MOUNTAIN$$,#{state_id_qld},-28.583622,151.801093), - ($$4352$$,$$WITHCOTT$$,#{state_id_qld},-28.583622,151.801093), - ($$4352$$,$$WOODLEIGH$$,#{state_id_qld},-28.583622,151.801093), - ($$4352$$,$$WOOLMER$$,#{state_id_qld},-28.583622,151.801093), - ($$4352$$,$$WUTUL$$,#{state_id_qld},-28.583622,151.801093), - ($$4352$$,$$WYREEMA$$,#{state_id_qld},-28.583622,151.801093), - ($$4352$$,$$YALANGUR$$,#{state_id_qld},-28.583622,151.801093), - ($$4352$$,$$YANDILLA$$,#{state_id_qld},-28.583622,151.801093), - ($$4353$$,$$BERGEN$$,#{state_id_qld},-27.256984,151.904578), - ($$4353$$,$$EAST COOYAR$$,#{state_id_qld},-27.256984,151.904578), - ($$4353$$,$$HADEN$$,#{state_id_qld},-27.256984,151.904578), - ($$4354$$,$$DOUGLAS$$,#{state_id_qld},-27.322474,151.909698), - ($$4354$$,$$GOOMBUNGEE$$,#{state_id_qld},-27.322474,151.909698), - ($$4354$$,$$KILBIRNIE$$,#{state_id_qld},-27.322474,151.909698), - ($$4355$$,$$ANDURAMBA$$,#{state_id_qld},-27.134613,152.085883), - ($$4355$$,$$CRESSBROOK CREEK$$,#{state_id_qld},-27.134613,152.085883), - ($$4355$$,$$CROWS NEST$$,#{state_id_qld},-27.134613,152.085883), - ($$4355$$,$$EMU CREEK$$,#{state_id_qld},-27.134613,152.085883), - ($$4355$$,$$GLENAVEN$$,#{state_id_qld},-27.134613,152.085883), - ($$4355$$,$$JONES GULLY$$,#{state_id_qld},-27.134613,152.085883), - ($$4355$$,$$MOUNTAIN CAMP$$,#{state_id_qld},-27.134613,152.085883), - ($$4355$$,$$PIERCES CREEK$$,#{state_id_qld},-27.134613,152.085883), - ($$4355$$,$$PINELANDS$$,#{state_id_qld},-27.134613,152.085883), - ($$4355$$,$$PLAINBY$$,#{state_id_qld},-27.134613,152.085883), - ($$4355$$,$$THE BLUFF$$,#{state_id_qld},-27.134613,152.085883), - ($$4355$$,$$UPPER PINELANDS$$,#{state_id_qld},-27.134613,152.085883), - ($$4356$$,$$BONGEEN$$,#{state_id_qld},-27.567709,151.446802), - ($$4356$$,$$BROXBURN$$,#{state_id_qld},-27.567709,151.446802), - ($$4356$$,$$EVANSLEA$$,#{state_id_qld},-27.567709,151.446802), - ($$4356$$,$$IRONGATE$$,#{state_id_qld},-27.567709,151.446802), - ($$4356$$,$$KINCORA$$,#{state_id_qld},-27.567709,151.446802), - ($$4356$$,$$LINTHORPE$$,#{state_id_qld},-27.567709,151.446802), - ($$4356$$,$$MOTLEY$$,#{state_id_qld},-27.567709,151.446802), - ($$4356$$,$$MOUNT TYSON$$,#{state_id_qld},-27.567709,151.446802), - ($$4356$$,$$NORTH BRANCH$$,#{state_id_qld},-27.567709,151.446802), - ($$4356$$,$$NORWIN$$,#{state_id_qld},-27.567709,151.446802), - ($$4356$$,$$PITTSWORTH$$,#{state_id_qld},-27.567709,151.446802), - ($$4356$$,$$PURRAWUNDA$$,#{state_id_qld},-27.567709,151.446802), - ($$4356$$,$$ROSSVALE$$,#{state_id_qld},-27.567709,151.446802), - ($$4356$$,$$SCRUBBY MOUNTAIN$$,#{state_id_qld},-27.567709,151.446802), - ($$4356$$,$$SPRINGSIDE$$,#{state_id_qld},-27.567709,151.446802), - ($$4356$$,$$ST HELENS$$,#{state_id_qld},-27.567709,151.446802), - ($$4356$$,$$STONELEIGH$$,#{state_id_qld},-27.567709,151.446802), - ($$4356$$,$$YARRANLEA$$,#{state_id_qld},-27.567709,151.446802), - ($$4357$$,$$BRINGALILY$$,#{state_id_qld},-28.056801,151.160021), - ($$4357$$,$$BULLI CREEK$$,#{state_id_qld},-28.056801,151.160021), - ($$4357$$,$$CANNING CREEK$$,#{state_id_qld},-28.056801,151.160021), - ($$4357$$,$$CAPTAINS MOUNTAIN$$,#{state_id_qld},-28.056801,151.160021), - ($$4357$$,$$CLONTARF$$,#{state_id_qld},-28.056801,151.160021), - ($$4357$$,$$CONDAMINE FARMS$$,#{state_id_qld},-28.056801,151.160021), - ($$4357$$,$$CYPRESS GARDENS$$,#{state_id_qld},-28.056801,151.160021), - ($$4357$$,$$DOMVILLE$$,#{state_id_qld},-28.056801,151.160021), - ($$4357$$,$$FOREST RIDGE$$,#{state_id_qld},-28.056801,151.160021), - ($$4357$$,$$GRAYS GATE$$,#{state_id_qld},-28.056801,151.160021), - ($$4357$$,$$KOOROONGARRA$$,#{state_id_qld},-28.056801,151.160021), - ($$4357$$,$$LAVELLE$$,#{state_id_qld},-28.056801,151.160021), - ($$4357$$,$$LEMONTREE$$,#{state_id_qld},-28.056801,151.160021), - ($$4357$$,$$MILLMERRAN$$,#{state_id_qld},-28.056801,151.160021), - ($$4357$$,$$MILLMERRAN DOWNS$$,#{state_id_qld},-28.056801,151.160021), - ($$4357$$,$$MILLMERRAN WOODS$$,#{state_id_qld},-28.056801,151.160021), - ($$4357$$,$$MILLWOOD$$,#{state_id_qld},-28.056801,151.160021), - ($$4357$$,$$MOUNT EMLYN$$,#{state_id_qld},-28.056801,151.160021), - ($$4357$$,$$PUNCHS CREEK$$,#{state_id_qld},-28.056801,151.160021), - ($$4357$$,$$ROCKY CREEK$$,#{state_id_qld},-28.056801,151.160021), - ($$4357$$,$$STONEHENGE$$,#{state_id_qld},-28.056801,151.160021), - ($$4357$$,$$THE PINES$$,#{state_id_qld},-28.056801,151.160021), - ($$4357$$,$$TURALLIN$$,#{state_id_qld},-28.056801,151.160021), - ($$4357$$,$$WATTLE RIDGE$$,#{state_id_qld},-28.056801,151.160021), - ($$4357$$,$$WESTERN CREEK$$,#{state_id_qld},-28.056801,151.160021), - ($$4357$$,$$WOONDUL$$,#{state_id_qld},-28.056801,151.160021), - ($$4358$$,$$CAMBOOYA$$,#{state_id_qld},-27.706725,151.863738), - ($$4358$$,$$FELTON$$,#{state_id_qld},-27.706725,151.863738), - ($$4358$$,$$FELTON SOUTH$$,#{state_id_qld},-27.706725,151.863738), - ($$4358$$,$$RAMSAY$$,#{state_id_qld},-27.706725,151.863738), - ($$4359$$,$$BUDGEE$$,#{state_id_qld},-27.785112,152.020111), - ($$4359$$,$$EAST GREENMOUNT$$,#{state_id_qld},-27.785112,152.020111), - ($$4359$$,$$GREENMOUNT$$,#{state_id_qld},-27.785112,152.020111), - ($$4359$$,$$HIRSTGLEN$$,#{state_id_qld},-27.785112,152.020111), - ($$4359$$,$$WEST HALDON$$,#{state_id_qld},-27.785112,152.020111), - ($$4360$$,$$NOBBY$$,#{state_id_qld},-27.838719,151.889972), - ($$4361$$,$$BACK PLAINS$$,#{state_id_qld},-27.894403,151.799775), - ($$4361$$,$$CLIFTON$$,#{state_id_qld},-27.894403,151.799775), - ($$4361$$,$$ELLANGOWAN$$,#{state_id_qld},-27.894403,151.799775), - ($$4361$$,$$ELPHINSTONE$$,#{state_id_qld},-27.894403,151.799775), - ($$4361$$,$$HEADINGTON HILL$$,#{state_id_qld},-27.894403,151.799775), - ($$4361$$,$$KINGS CREEK$$,#{state_id_qld},-27.894403,151.799775), - ($$4361$$,$$MANAPOURI$$,#{state_id_qld},-27.894403,151.799775), - ($$4361$$,$$MISSEN FLAT$$,#{state_id_qld},-27.894403,151.799775), - ($$4361$$,$$MOUNT MOLAR$$,#{state_id_qld},-27.894403,151.799775), - ($$4361$$,$$NEVILTON$$,#{state_id_qld},-27.894403,151.799775), - ($$4361$$,$$PILTON$$,#{state_id_qld},-27.894403,151.799775), - ($$4361$$,$$RYEFORD$$,#{state_id_qld},-27.894403,151.799775), - ($$4361$$,$$SANDY CAMP$$,#{state_id_qld},-27.894403,151.799775), - ($$4361$$,$$SPRING CREEK$$,#{state_id_qld},-27.894403,151.799775), - ($$4361$$,$$UPPER PILTON$$,#{state_id_qld},-27.894403,151.799775), - ($$4361$$,$$VICTORIA HILL$$,#{state_id_qld},-27.894403,151.799775), - ($$4362$$,$$ALLORA$$,#{state_id_qld},-28.035848,151.982723), - ($$4362$$,$$BERAT$$,#{state_id_qld},-28.035848,151.982723), - ($$4362$$,$$DEUCHAR$$,#{state_id_qld},-28.035848,151.982723), - ($$4362$$,$$ELLINTHORP$$,#{state_id_qld},-28.035848,151.982723), - ($$4362$$,$$GOOMBURRA$$,#{state_id_qld},-28.035848,151.982723), - ($$4362$$,$$HENDON$$,#{state_id_qld},-28.035848,151.982723), - ($$4362$$,$$MOUNT MARSHALL$$,#{state_id_qld},-28.035848,151.982723), - ($$4362$$,$$TALGAI$$,#{state_id_qld},-28.035848,151.982723), - ($$4363$$,$$SOUTHBROOK$$,#{state_id_qld},-27.676647,151.739695), - ($$4364$$,$$BROOKSTEAD$$,#{state_id_qld},-27.710492,151.396183), - ($$4365$$,$$LEYBURN$$,#{state_id_qld},-28.017149,151.597527), - ($$4370$$,$$ALLAN$$,#{state_id_qld},-28.195568,151.949533), - ($$4370$$,$$BONY MOUNTAIN$$,#{state_id_qld},-28.195568,151.949533), - ($$4370$$,$$CANNINGVALE$$,#{state_id_qld},-28.195568,151.949533), - ($$4370$$,$$CHERRY GULLY$$,#{state_id_qld},-28.195568,151.949533), - ($$4370$$,$$CLINTONVALE$$,#{state_id_qld},-28.195568,151.949533), - ($$4370$$,$$CUNNINGHAM$$,#{state_id_qld},-28.195568,151.949533), - ($$4370$$,$$DANDEROO$$,#{state_id_qld},-28.195568,151.949533), - ($$4370$$,$$ELBOW VALLEY$$,#{state_id_qld},-28.195568,151.949533), - ($$4370$$,$$FREESTONE$$,#{state_id_qld},-28.195568,151.949533), - ($$4370$$,$$GLADFIELD$$,#{state_id_qld},-28.195568,151.949533), - ($$4370$$,$$GLENGALLAN$$,#{state_id_qld},-28.195568,151.949533), - ($$4370$$,$$GREYMARE$$,#{state_id_qld},-28.195568,151.949533), - ($$4370$$,$$JUNABEE$$,#{state_id_qld},-28.195568,151.949533), - ($$4370$$,$$LESLIE$$,#{state_id_qld},-28.195568,151.949533), - ($$4370$$,$$LESLIE DAM$$,#{state_id_qld},-28.195568,151.949533), - ($$4370$$,$$LOCH LOMOND$$,#{state_id_qld},-28.195568,151.949533), - ($$4370$$,$$MARYVALE$$,#{state_id_qld},-28.195568,151.949533), - ($$4370$$,$$MASSIE$$,#{state_id_qld},-28.195568,151.949533), - ($$4370$$,$$MONTROSE$$,#{state_id_qld},-28.195568,151.949533), - ($$4370$$,$$MORGAN PARK$$,#{state_id_qld},-28.195568,151.949533), - ($$4370$$,$$MOUNT COLLIERY$$,#{state_id_qld},-28.195568,151.949533), - ($$4370$$,$$MOUNT STURT$$,#{state_id_qld},-28.195568,151.949533), - ($$4370$$,$$MOUNT TABOR$$,#{state_id_qld},-28.195568,151.949533), - ($$4370$$,$$MURRAYS BRIDGE$$,#{state_id_qld},-28.195568,151.949533), - ($$4370$$,$$NORTH BRANCH$$,#{state_id_qld},-28.195568,151.949533), - ($$4370$$,$$PRATTEN$$,#{state_id_qld},-28.195568,151.949533), - ($$4370$$,$$RODGERS CREEK$$,#{state_id_qld},-28.195568,151.949533), - ($$4370$$,$$ROSEHILL$$,#{state_id_qld},-28.195568,151.949533), - ($$4370$$,$$ROSENTHAL HEIGHTS$$,#{state_id_qld},-28.195568,151.949533), - ($$4370$$,$$SILVERWOOD$$,#{state_id_qld},-28.195568,151.949533), - ($$4370$$,$$SLADEVALE$$,#{state_id_qld},-28.195568,151.949533), - ($$4370$$,$$SWAN CREEK$$,#{state_id_qld},-28.195568,151.949533), - ($$4370$$,$$THANE$$,#{state_id_qld},-28.195568,151.949533), - ($$4370$$,$$THANES CREEK$$,#{state_id_qld},-28.195568,151.949533), - ($$4370$$,$$THE GLEN$$,#{state_id_qld},-28.195568,151.949533), - ($$4370$$,$$THE HERMITAGE$$,#{state_id_qld},-28.195568,151.949533), - ($$4370$$,$$TOOLBURRA$$,#{state_id_qld},-28.195568,151.949533), - ($$4370$$,$$TREGONY$$,#{state_id_qld},-28.195568,151.949533), - ($$4370$$,$$UPPER FREESTONE$$,#{state_id_qld},-28.195568,151.949533), - ($$4370$$,$$UPPER WHEATVALE$$,#{state_id_qld},-28.195568,151.949533), - ($$4370$$,$$WARWICK$$,#{state_id_qld},-28.195568,151.949533), - ($$4370$$,$$WARWICK DC$$,#{state_id_qld},-28.195568,151.949533), - ($$4370$$,$$WHEATVALE$$,#{state_id_qld},-28.195568,151.949533), - ($$4370$$,$$WILDASH$$,#{state_id_qld},-28.195568,151.949533), - ($$4370$$,$$WILLOWVALE$$,#{state_id_qld},-28.195568,151.949533), - ($$4370$$,$$WIYARRA$$,#{state_id_qld},-28.195568,151.949533), - ($$4370$$,$$WOMINA$$,#{state_id_qld},-28.195568,151.949533), - ($$4371$$,$$EMU VALE$$,#{state_id_qld},-28.230733,152.245531), - ($$4371$$,$$SWANFELS$$,#{state_id_qld},-28.230733,152.245531), - ($$4371$$,$$YANGAN$$,#{state_id_qld},-28.230733,152.245531), - ($$4372$$,$$TANNYMOREL$$,#{state_id_qld},-28.303067,152.233089), - ($$4373$$,$$KILLARNEY$$,#{state_id_qld},-28.340099,152.294194), - ($$4373$$,$$THE FALLS$$,#{state_id_qld},-28.340099,152.294194), - ($$4373$$,$$THE HEAD$$,#{state_id_qld},-28.340099,152.294194), - ($$4374$$,$$DALVEEN$$,#{state_id_qld},-28.488431,151.968689), - ($$4375$$,$$COTTONVALE$$,#{state_id_qld},-28.529507,151.947124), - ($$4375$$,$$FLEURBAIX$$,#{state_id_qld},-28.529507,151.947124), - ($$4376$$,$$THULIMBAH$$,#{state_id_qld},-28.541494,151.933783), - ($$4377$$,$$GLEN NIVEN$$,#{state_id_qld},-28.580891,151.97341), - ($$4377$$,$$MARYLAND$$,#{state_id_nsw},-28.580891,151.97341), - ($$4377$$,$$THE SUMMIT$$,#{state_id_qld},-28.580891,151.97341), - ($$4378$$,$$APPLETHORPE$$,#{state_id_qld},-28.613948,151.955491), - ($$4380$$,$$AMIENS$$,#{state_id_qld},-28.614072,151.903705), - ($$4380$$,$$BROADWATER$$,#{state_id_qld},-28.614072,151.903705), - ($$4380$$,$$CANNON CREEK$$,#{state_id_qld},-28.614072,151.903705), - ($$4380$$,$$DALCOUTH$$,#{state_id_qld},-28.614072,151.903705), - ($$4380$$,$$EUKEY$$,#{state_id_qld},-28.614072,151.903705), - ($$4380$$,$$GLENLYON$$,#{state_id_qld},-28.614072,151.903705), - ($$4380$$,$$GREENLANDS$$,#{state_id_qld},-28.614072,151.903705), - ($$4380$$,$$KYOOMBA$$,#{state_id_qld},-28.614072,151.903705), - ($$4380$$,$$MINGOOLA$$,#{state_id_qld},-28.614072,151.903705), - ($$4380$$,$$MOUNT TULLY$$,#{state_id_qld},-28.614072,151.903705), - ($$4380$$,$$NUNDUBBERMERE$$,#{state_id_qld},-28.614072,151.903705), - ($$4380$$,$$PIKEDALE$$,#{state_id_qld},-28.614072,151.903705), - ($$4380$$,$$PIKES CREEK$$,#{state_id_qld},-28.614072,151.903705), - ($$4380$$,$$SEVERNLEA$$,#{state_id_qld},-28.614072,151.903705), - ($$4380$$,$$SPRINGDALE$$,#{state_id_qld},-28.614072,151.903705), - ($$4380$$,$$STANTHORPE$$,#{state_id_qld},-28.614072,151.903705), - ($$4380$$,$$STORM KING$$,#{state_id_qld},-28.614072,151.903705), - ($$4380$$,$$SUGARLOAF$$,#{state_id_qld},-28.614072,151.903705), - ($$4380$$,$$THORNDALE$$,#{state_id_qld},-28.614072,151.903705), - ($$4381$$,$$GLEN APLIN$$,#{state_id_qld},-28.739941,151.874983), - ($$4382$$,$$BALLANDEAN$$,#{state_id_qld},-28.798985,151.842238), - ($$4382$$,$$GIRRAWEEN$$,#{state_id_qld},-28.798985,151.842238), - ($$4382$$,$$LYRA$$,#{state_id_qld},-28.798985,151.842238), - ($$4382$$,$$SOMME$$,#{state_id_qld},-28.798985,151.842238), - ($$4382$$,$$WYBERBA$$,#{state_id_qld},-28.798985,151.842238), - ($$4383$$,$$JENNINGS$$,#{state_id_nsw},-28.882394,151.910698), - ($$4383$$,$$WALLANGARRA$$,#{state_id_qld},-28.882394,151.910698), - ($$4384$$,$$LIMEVALE$$,#{state_id_qld},-28.728006,151.183208), - ($$4385$$,$$BEEBO$$,#{state_id_qld},-28.722738,150.967237), - ($$4385$$,$$BONSHAW$$,#{state_id_qld},-28.722738,150.967237), - ($$4385$$,$$CAMP CREEK$$,#{state_id_nsw},-28.722738,150.967237), - ($$4385$$,$$GLENARBON$$,#{state_id_qld},-28.722738,150.967237), - ($$4385$$,$$MAIDENHEAD$$,#{state_id_qld},-28.722738,150.967237), - ($$4385$$,$$RIVERTON$$,#{state_id_qld},-28.722738,150.967237), - ($$4385$$,$$SILVER SPUR$$,#{state_id_qld},-28.722738,150.967237), - ($$4385$$,$$SMITHLEA$$,#{state_id_qld},-28.722738,150.967237), - ($$4385$$,$$TEXAS$$,#{state_id_qld},-28.722738,150.967237), - ($$4385$$,$$TEXAS$$,#{state_id_nsw},-28.722738,150.967237), - ($$4385$$,$$WATSONS CROSSING$$,#{state_id_qld},-28.722738,150.967237), - ($$4387$$,$$BRUSH CREEK$$,#{state_id_qld},-28.606093,151.126671), - ($$4387$$,$$BYBERA$$,#{state_id_qld},-28.606093,151.126671), - ($$4387$$,$$COOLMUNDA$$,#{state_id_qld},-28.606093,151.126671), - ($$4387$$,$$GREENUP$$,#{state_id_qld},-28.606093,151.126671), - ($$4387$$,$$INGLEWOOD$$,#{state_id_qld},-28.606093,151.126671), - ($$4387$$,$$MOSQUITO CREEK$$,#{state_id_qld},-28.606093,151.126671), - ($$4387$$,$$TERRICA$$,#{state_id_qld},-28.606093,151.126671), - ($$4387$$,$$WARROO$$,#{state_id_qld},-28.606093,151.126671), - ($$4387$$,$$WHETSTONE$$,#{state_id_qld},-28.606093,151.126671), - ($$4388$$,$$KURUMBUL$$,#{state_id_qld},-28.615672,150.554538), - ($$4388$$,$$YELARBON$$,#{state_id_qld},-28.615672,150.554538), - ($$4390$$,$$BILLA BILLA$$,#{state_id_qld},-28.145163,150.288342), - ($$4390$$,$$CALINGUNEE$$,#{state_id_qld},-28.145163,150.288342), - ($$4390$$,$$CALLANDOON$$,#{state_id_qld},-28.145163,150.288342), - ($$4390$$,$$GOODAR$$,#{state_id_qld},-28.145163,150.288342), - ($$4390$$,$$GOONDIWINDI$$,#{state_id_qld},-28.145163,150.288342), - ($$4390$$,$$KINDON$$,#{state_id_qld},-28.145163,150.288342), - ($$4390$$,$$LUNDAVRA$$,#{state_id_qld},-28.145163,150.288342), - ($$4390$$,$$WONDALLI$$,#{state_id_qld},-28.145163,150.288342), - ($$4390$$,$$WYAGA$$,#{state_id_qld},-28.145163,150.288342), - ($$4390$$,$$YAGABURNE$$,#{state_id_qld},-28.145163,150.288342), - ($$4400$$,$$KINGSTHORPE$$,#{state_id_qld},-27.495768,151.796847), - ($$4401$$,$$ACLAND$$,#{state_id_qld},-27.303702,151.691102), - ($$4401$$,$$AUBIGNY$$,#{state_id_qld},-27.303702,151.691102), - ($$4401$$,$$BALGOWAN$$,#{state_id_qld},-27.303702,151.691102), - ($$4401$$,$$BIDDESTON$$,#{state_id_qld},-27.303702,151.691102), - ($$4401$$,$$BOODUA$$,#{state_id_qld},-27.303702,151.691102), - ($$4401$$,$$DEVON PARK$$,#{state_id_qld},-27.303702,151.691102), - ($$4401$$,$$GREENWOOD$$,#{state_id_qld},-27.303702,151.691102), - ($$4401$$,$$HIGHLAND PLAINS$$,#{state_id_qld},-27.303702,151.691102), - ($$4401$$,$$KELVINHAUGH$$,#{state_id_qld},-27.303702,151.691102), - ($$4401$$,$$MOUNT IRVING$$,#{state_id_qld},-27.303702,151.691102), - ($$4401$$,$$MULDU$$,#{state_id_qld},-27.303702,151.691102), - ($$4401$$,$$OAKEY$$,#{state_id_qld},-27.303702,151.691102), - ($$4401$$,$$ROSALIE PLAINS$$,#{state_id_qld},-27.303702,151.691102), - ($$4401$$,$$SABINE$$,#{state_id_qld},-27.303702,151.691102), - ($$4401$$,$$SILVERLEIGH$$,#{state_id_qld},-27.303702,151.691102), - ($$4401$$,$$YARGULLEN$$,#{state_id_qld},-27.303702,151.691102), - ($$4402$$,$$COOYAR$$,#{state_id_qld},-26.983086,151.832336), - ($$4402$$,$$KOORALGIN$$,#{state_id_qld},-26.983086,151.832336), - ($$4402$$,$$UPPER COOYAR CREEK$$,#{state_id_qld},-26.983086,151.832336), - ($$4403$$,$$BRYMAROO$$,#{state_id_qld},-27.217717,151.591852), - ($$4403$$,$$JONDARYAN$$,#{state_id_qld},-27.217717,151.591852), - ($$4403$$,$$MALU$$,#{state_id_qld},-27.217717,151.591852), - ($$4403$$,$$MOUNT MORIAH$$,#{state_id_qld},-27.217717,151.591852), - ($$4403$$,$$QUINALOW$$,#{state_id_qld},-27.217717,151.591852), - ($$4403$$,$$WEST PRAIRIE$$,#{state_id_qld},-27.217717,151.591852), - ($$4404$$,$$BOWENVILLE$$,#{state_id_qld},-27.305817,151.490518), - ($$4404$$,$$FORMARTIN$$,#{state_id_qld},-27.305817,151.490518), - ($$4404$$,$$IRVINGDALE$$,#{state_id_qld},-27.305817,151.490518), - ($$4404$$,$$WAINUI$$,#{state_id_qld},-27.305817,151.490518), - ($$4405$$,$$BLAXLAND$$,#{state_id_qld},-27.192428,151.396625), - ($$4405$$,$$BUNYA MOUNTAINS$$,#{state_id_qld},-27.192428,151.396625), - ($$4405$$,$$DALBY$$,#{state_id_qld},-27.192428,151.396625), - ($$4405$$,$$DUCKLO$$,#{state_id_qld},-27.192428,151.396625), - ($$4405$$,$$GRASSDALE$$,#{state_id_qld},-27.192428,151.396625), - ($$4405$$,$$MARMADUA$$,#{state_id_qld},-27.192428,151.396625), - ($$4405$$,$$PIRRINUAN$$,#{state_id_qld},-27.192428,151.396625), - ($$4405$$,$$RANGES BRIDGE$$,#{state_id_qld},-27.192428,151.396625), - ($$4405$$,$$ST RUTH$$,#{state_id_qld},-27.192428,151.396625), - ($$4405$$,$$TIPTON$$,#{state_id_qld},-27.192428,151.396625), - ($$4405$$,$$WERANGA$$,#{state_id_qld},-27.192428,151.396625), - ($$4406$$,$$BOONDANDILLA$$,#{state_id_qld},-27.894641,150.596584), - ($$4406$$,$$HANNAFORD$$,#{state_id_qld},-27.894641,150.596584), - ($$4406$$,$$JIMBOUR$$,#{state_id_qld},-27.894641,150.596584), - ($$4406$$,$$KAIMKILLENBUN$$,#{state_id_qld},-27.894641,150.596584), - ($$4406$$,$$KOGAN$$,#{state_id_qld},-27.894641,150.596584), - ($$4406$$,$$MACALISTER$$,#{state_id_qld},-27.894641,150.596584), - ($$4406$$,$$MOONIE$$,#{state_id_qld},-27.894641,150.596584), - ($$4406$$,$$SOUTHWOOD$$,#{state_id_qld},-27.894641,150.596584), - ($$4406$$,$$THE GUMS$$,#{state_id_qld},-27.894641,150.596584), - ($$4406$$,$$WEIR RIVER$$,#{state_id_qld},-27.894641,150.596584), - ($$4407$$,$$CATTLE CREEK$$,#{state_id_qld},-27.6483,150.836678), - ($$4407$$,$$CECIL PLAINS$$,#{state_id_qld},-27.6483,150.836678), - ($$4407$$,$$DUNMORE$$,#{state_id_qld},-27.6483,150.836678), - ($$4407$$,$$NANGWEE$$,#{state_id_qld},-27.6483,150.836678), - ($$4408$$,$$BELL$$,#{state_id_qld},-26.93254,151.448511), - ($$4410$$,$$JANDOWAE$$,#{state_id_qld},-26.781197,151.109816), - ($$4411$$,$$WARRA$$,#{state_id_qld},-26.929329,150.919183), - ($$4412$$,$$BRIGALOW$$,#{state_id_qld},-26.843852,150.790571), - ($$4413$$,$$BAKING BOARD$$,#{state_id_qld},-26.706834,150.545144), - ($$4413$$,$$BOONARGA$$,#{state_id_qld},-26.706834,150.545144), - ($$4413$$,$$BURNCLUITH$$,#{state_id_qld},-26.706834,150.545144), - ($$4413$$,$$CANAGA$$,#{state_id_qld},-26.706834,150.545144), - ($$4413$$,$$CHANCES PLAIN$$,#{state_id_qld},-26.706834,150.545144), - ($$4413$$,$$CHINCHILLA$$,#{state_id_qld},-26.706834,150.545144), - ($$4413$$,$$DURAH$$,#{state_id_qld},-26.706834,150.545144), - ($$4413$$,$$HOPELAND$$,#{state_id_qld},-26.706834,150.545144), - ($$4413$$,$$MONTROSE$$,#{state_id_qld},-26.706834,150.545144), - ($$4413$$,$$WIEAMBILLA$$,#{state_id_qld},-26.706834,150.545144), - ($$4415$$,$$COLUMBOOLA$$,#{state_id_qld},-26.672867,150.332141), - ($$4415$$,$$DALWOGON$$,#{state_id_qld},-26.672867,150.332141), - ($$4415$$,$$GURULMUNDI$$,#{state_id_qld},-26.672867,150.332141), - ($$4415$$,$$HOOKSWOOD$$,#{state_id_qld},-26.672867,150.332141), - ($$4415$$,$$KOWGURAN$$,#{state_id_qld},-26.672867,150.332141), - ($$4415$$,$$MILES$$,#{state_id_qld},-26.672867,150.332141), - ($$4415$$,$$MYALL PARK$$,#{state_id_qld},-26.672867,150.332141), - ($$4415$$,$$PELHAM$$,#{state_id_qld},-26.672867,150.332141), - ($$4416$$,$$BARRAMORNIE$$,#{state_id_qld},-27.013259,150.060549), - ($$4416$$,$$CONDAMINE$$,#{state_id_qld},-27.013259,150.060549), - ($$4416$$,$$MORABY$$,#{state_id_qld},-27.013259,150.060549), - ($$4416$$,$$NANGRAM$$,#{state_id_qld},-27.013259,150.060549), - ($$4416$$,$$PINE HILLS$$,#{state_id_qld},-27.013259,150.060549), - ($$4416$$,$$SUNNYSIDE$$,#{state_id_qld},-27.013259,150.060549), - ($$4416$$,$$YULABILLA$$,#{state_id_qld},-27.013259,150.060549), - ($$4417$$,$$NOORINDOO$$,#{state_id_qld},-27.15617,149.158851), - ($$4417$$,$$OBERINA$$,#{state_id_qld},-27.15617,149.158851), - ($$4417$$,$$PARKNOOK$$,#{state_id_qld},-27.15617,149.158851), - ($$4417$$,$$SURAT$$,#{state_id_qld},-27.15617,149.158851), - ($$4417$$,$$WARKON$$,#{state_id_qld},-27.15617,149.158851), - ($$4417$$,$$WELLESLEY$$,#{state_id_qld},-27.15617,149.158851), - ($$4417$$,$$WERIBONE$$,#{state_id_qld},-27.15617,149.158851), - ($$4418$$,$$GULUGUBA$$,#{state_id_qld},-26.260063,150.048929), - ($$4419$$,$$COCKATOO$$,#{state_id_qld},-25.72569,150.28164), - ($$4419$$,$$GROSMONT$$,#{state_id_qld},-25.72569,150.28164), - ($$4419$$,$$WANDOAN$$,#{state_id_qld},-25.72569,150.28164), - ($$4420$$,$$BROADMERE$$,#{state_id_qld},-25.494713,149.525293), - ($$4420$$,$$COORADA$$,#{state_id_qld},-25.494713,149.525293), - ($$4420$$,$$GHINGHINDA$$,#{state_id_qld},-25.494713,149.525293), - ($$4420$$,$$GLENHAUGHTON$$,#{state_id_qld},-25.494713,149.525293), - ($$4420$$,$$GWAMBEGWINE$$,#{state_id_qld},-25.494713,149.525293), - ($$4420$$,$$HORNET BANK$$,#{state_id_qld},-25.494713,149.525293), - ($$4420$$,$$PEEK-A-DOO$$,#{state_id_qld},-25.494713,149.525293), - ($$4420$$,$$SPRING CREEK$$,#{state_id_qld},-25.494713,149.525293), - ($$4420$$,$$TAROOM$$,#{state_id_qld},-25.494713,149.525293), - ($$4421$$,$$GORANBA$$,#{state_id_qld},-27.285342,150.599138), - ($$4421$$,$$TARA$$,#{state_id_qld},-27.285342,150.599138), - ($$4422$$,$$COOMRITH$$,#{state_id_qld},-27.562837,149.612212), - ($$4422$$,$$FLINTON$$,#{state_id_qld},-27.562837,149.612212), - ($$4422$$,$$INGLESTONE$$,#{state_id_qld},-27.562837,149.612212), - ($$4422$$,$$MEANDARRA$$,#{state_id_qld},-27.562837,149.612212), - ($$4422$$,$$WESTMAR$$,#{state_id_qld},-27.562837,149.612212), - ($$4423$$,$$GLENMORGAN$$,#{state_id_qld},-27.249625,149.679008), - ($$4423$$,$$TEELBA$$,#{state_id_qld},-27.249625,149.679008), - ($$4424$$,$$DRILLHAM$$,#{state_id_qld},-26.640357,149.982992), - ($$4424$$,$$DRILLHAM SOUTH$$,#{state_id_qld},-26.640357,149.982992), - ($$4424$$,$$GLENAUBYN$$,#{state_id_qld},-26.640357,149.982992), - ($$4425$$,$$BOGANDILLA$$,#{state_id_qld},-26.517858,149.783594), - ($$4425$$,$$DULACCA$$,#{state_id_qld},-26.517858,149.783594), - ($$4426$$,$$JACKSON$$,#{state_id_qld},-26.642228,149.623168), - ($$4426$$,$$JACKSON NORTH$$,#{state_id_qld},-26.642228,149.623168), - ($$4426$$,$$JACKSON SOUTH$$,#{state_id_qld},-26.642228,149.623168), - ($$4427$$,$$CLIFFORD$$,#{state_id_qld},-26.128626,149.367449), - ($$4427$$,$$YULEBA$$,#{state_id_qld},-26.128626,149.367449), - ($$4427$$,$$YULEBA NORTH$$,#{state_id_qld},-26.128626,149.367449), - ($$4427$$,$$YULEBA SOUTH$$,#{state_id_qld},-26.128626,149.367449), - ($$4428$$,$$PICKANJINNIE$$,#{state_id_qld},-26.58117,149.114285), - ($$4428$$,$$WALLUMBILLA$$,#{state_id_qld},-26.58117,149.114285), - ($$4428$$,$$WALLUMBILLA NORTH$$,#{state_id_qld},-26.58117,149.114285), - ($$4428$$,$$WALLUMBILLA SOUTH$$,#{state_id_qld},-26.58117,149.114285), - ($$4454$$,$$BAFFLE WEST$$,#{state_id_qld},-25.724492,148.672028), - ($$4454$$,$$BEILBA$$,#{state_id_qld},-25.724492,148.672028), - ($$4454$$,$$DURHAM DOWNS$$,#{state_id_qld},-25.724492,148.672028), - ($$4454$$,$$HIGHLAND PLAINS$$,#{state_id_qld},-25.724492,148.672028), - ($$4454$$,$$HUTTON CREEK$$,#{state_id_qld},-25.724492,148.672028), - ($$4454$$,$$INJUNE$$,#{state_id_qld},-25.724492,148.672028), - ($$4454$$,$$MOUNT HUTTON$$,#{state_id_qld},-25.724492,148.672028), - ($$4454$$,$$PONY HILLS$$,#{state_id_qld},-25.724492,148.672028), - ($$4454$$,$$SIMMIE$$,#{state_id_qld},-25.724492,148.672028), - ($$4454$$,$$UPPER DAWSON$$,#{state_id_qld},-25.724492,148.672028), - ($$4454$$,$$WESTGROVE$$,#{state_id_qld},-25.724492,148.672028), - ($$4455$$,$$BALLAROO$$,#{state_id_qld},-27.116299,148.616963), - ($$4455$$,$$BLYTHDALE$$,#{state_id_qld},-27.116299,148.616963), - ($$4455$$,$$BUNGEWORGORAI$$,#{state_id_qld},-27.116299,148.616963), - ($$4455$$,$$BUNGIL$$,#{state_id_qld},-27.116299,148.616963), - ($$4455$$,$$BYMOUNT$$,#{state_id_qld},-27.116299,148.616963), - ($$4455$$,$$CORNWALL$$,#{state_id_qld},-27.116299,148.616963), - ($$4455$$,$$DARGAL ROAD$$,#{state_id_qld},-27.116299,148.616963), - ($$4455$$,$$EUMAMURRIN$$,#{state_id_qld},-27.116299,148.616963), - ($$4455$$,$$EUTHULLA$$,#{state_id_qld},-27.116299,148.616963), - ($$4455$$,$$GUNNEWIN$$,#{state_id_qld},-27.116299,148.616963), - ($$4455$$,$$HODGSON$$,#{state_id_qld},-27.116299,148.616963), - ($$4455$$,$$MOOGA$$,#{state_id_qld},-27.116299,148.616963), - ($$4455$$,$$MOUNT ABUNDANCE$$,#{state_id_qld},-27.116299,148.616963), - ($$4455$$,$$MOUNT BINDANGO$$,#{state_id_qld},-27.116299,148.616963), - ($$4455$$,$$ORALLO$$,#{state_id_qld},-27.116299,148.616963), - ($$4455$$,$$ORANGE HILL$$,#{state_id_qld},-27.116299,148.616963), - ($$4455$$,$$ROMA$$,#{state_id_qld},-27.116299,148.616963), - ($$4455$$,$$TINGUN$$,#{state_id_qld},-27.116299,148.616963), - ($$4455$$,$$WYCOMBE$$,#{state_id_qld},-27.116299,148.616963), - ($$4461$$,$$MUCKADILLA$$,#{state_id_qld},-26.602637,148.363184), - ($$4462$$,$$AMBY$$,#{state_id_qld},-26.549209,148.186897), - ($$4465$$,$$DUNKELD$$,#{state_id_qld},-26.968591,148.062548), - ($$4465$$,$$FORESTVALE$$,#{state_id_qld},-26.968591,148.062548), - ($$4465$$,$$MITCHELL$$,#{state_id_qld},-26.968591,148.062548), - ($$4465$$,$$V GATE$$,#{state_id_qld},-26.968591,148.062548), - ($$4465$$,$$WOMALILLA$$,#{state_id_qld},-26.968591,148.062548), - ($$4467$$,$$MUNGALLALA$$,#{state_id_qld},-26.446905,147.543855), - ($$4467$$,$$REDFORD$$,#{state_id_qld},-26.446905,147.543855), - ($$4467$$,$$TYRCONNEL$$,#{state_id_qld},-26.446905,147.543855), - ($$4468$$,$$CLARA CREEK$$,#{state_id_qld},-26.087266,146.833194), - ($$4468$$,$$MORVEN$$,#{state_id_qld},-26.087266,146.833194), - ($$4470$$,$$BAKERS BEND$$,#{state_id_qld},-26.647239,146.176789), - ($$4470$$,$$CHARLEVILLE$$,#{state_id_qld},-26.647239,146.176789), - ($$4470$$,$$GOWRIE STATION$$,#{state_id_qld},-26.647239,146.176789), - ($$4470$$,$$LANGLO$$,#{state_id_qld},-26.647239,146.176789), - ($$4470$$,$$MURWEH$$,#{state_id_qld},-26.647239,146.176789), - ($$4470$$,$$RIVERSLEIGH$$,#{state_id_qld},-26.647239,146.176789), - ($$4471$$,$$CLAVERTON$$,#{state_id_qld},-27.37093,145.959624), - ($$4471$$,$$NARDOO SIDING$$,#{state_id_qld},-27.37093,145.959624), - ($$4472$$,$$BLACKALL$$,#{state_id_qld},-24.424637,145.465854), - ($$4472$$,$$MOUNT ENNISKILLEN$$,#{state_id_qld},-24.424637,145.465854), - ($$4474$$,$$ADAVALE$$,#{state_id_qld},-25.909385,144.598632), - ($$4475$$,$$CHEEPIE$$,#{state_id_qld},-26.630652,145.01525), - ($$4477$$,$$AUGATHELLA$$,#{state_id_qld},-25.794685,146.586608), - ($$4477$$,$$UPPER WARREGO$$,#{state_id_qld},-25.794685,146.586608), - ($$4478$$,$$BAYRICK$$,#{state_id_qld},-25.479054,146.011909), - ($$4478$$,$$CALDERVALE$$,#{state_id_qld},-25.479054,146.011909), - ($$4478$$,$$LANSDOWNE$$,#{state_id_qld},-25.479054,146.011909), - ($$4478$$,$$LUMEAH$$,#{state_id_qld},-25.479054,146.011909), - ($$4478$$,$$MACFARLANE$$,#{state_id_qld},-25.479054,146.011909), - ($$4478$$,$$MINNIE DOWNS$$,#{state_id_qld},-25.479054,146.011909), - ($$4478$$,$$SCRUBBY CREEK$$,#{state_id_qld},-25.479054,146.011909), - ($$4478$$,$$TAMBO$$,#{state_id_qld},-25.479054,146.011909), - ($$4478$$,$$WINDEYER$$,#{state_id_qld},-25.479054,146.011909), - ($$4478$$,$$YANDARLO$$,#{state_id_qld},-25.479054,146.011909), - ($$4479$$,$$COOLADDI$$,#{state_id_qld},-26.645522,145.460966), - ($$4480$$,$$EROMANGA$$,#{state_id_qld},-26.668654,143.267376), - ($$4480$$,$$QUILPIE$$,#{state_id_qld},-26.668654,143.267376), - ($$4481$$,$$FARRARS CREEK$$,#{state_id_qld},-25.393607,141.55373), - ($$4481$$,$$TANBAR$$,#{state_id_qld},-25.393607,141.55373), - ($$4481$$,$$WINDORAH$$,#{state_id_qld},-25.393607,141.55373), - ($$4482$$,$$BIRDSVILLE$$,#{state_id_qld},-25.898355,139.351645), - ($$4486$$,$$DIRRANBANDI$$,#{state_id_qld},-28.585815,148.227609), - ($$4486$$,$$HEBEL$$,#{state_id_qld},-28.585815,148.227609), - ($$4487$$,$$BEGONIA$$,#{state_id_qld},-27.505231,148.31586), - ($$4487$$,$$ST GEORGE$$,#{state_id_qld},-27.505231,148.31586), - ($$4488$$,$$BOLLON$$,#{state_id_qld},-28.031524,147.47786), - ($$4488$$,$$NEBINE$$,#{state_id_qld},-28.031524,147.47786), - ($$4489$$,$$WYANDRA$$,#{state_id_qld},-27.24739,145.981713), - ($$4490$$,$$BARRINGUN$$,#{state_id_qld},-28.843124,145.816249), - ($$4490$$,$$COONGOOLA$$,#{state_id_qld},-28.843124,145.816249), - ($$4490$$,$$CUNNAMULLA$$,#{state_id_qld},-28.843124,145.816249), - ($$4490$$,$$CUTTABURRA$$,#{state_id_qld},-28.843124,145.816249), - ($$4490$$,$$HUMEBURN$$,#{state_id_qld},-28.843124,145.816249), - ($$4490$$,$$JOBS GATE$$,#{state_id_qld},-28.843124,145.816249), - ($$4490$$,$$LINDEN$$,#{state_id_qld},-28.843124,145.816249), - ($$4490$$,$$NOORAMA$$,#{state_id_qld},-28.843124,145.816249), - ($$4490$$,$$TUEN$$,#{state_id_qld},-28.843124,145.816249), - ($$4490$$,$$WIDGEEGOARA$$,#{state_id_qld},-28.843124,145.816249), - ($$4490$$,$$YOWAH$$,#{state_id_qld},-28.843124,145.816249), - ($$4491$$,$$EULO$$,#{state_id_qld},-28.169275,145.042128), - ($$4492$$,$$BULLAWARRA$$,#{state_id_qld},-27.783565,143.373975), - ($$4492$$,$$BULLOO DOWNS$$,#{state_id_qld},-27.783565,143.373975), - ($$4492$$,$$DYNEVOR$$,#{state_id_qld},-27.783565,143.373975), - ($$4492$$,$$NOCKATUNGA$$,#{state_id_qld},-27.783565,143.373975), - ($$4492$$,$$NORLEY$$,#{state_id_qld},-27.783565,143.373975), - ($$4492$$,$$THARGOMINDAH$$,#{state_id_qld},-27.783565,143.373975), - ($$4493$$,$$HUNGERFORD$$,#{state_id_qld},-28.996339,144.406584), - ($$4494$$,$$BUNGUNYA$$,#{state_id_qld},-28.420865,149.658021), - ($$4494$$,$$NORTH BUNGUNYA$$,#{state_id_qld},-28.420865,149.658021), - ($$4494$$,$$TARAWERA$$,#{state_id_qld},-28.420865,149.658021), - ($$4496$$,$$NORTH TALWOOD$$,#{state_id_qld},-28.443959,149.467729), - ($$4496$$,$$SOUTH TALWOOD$$,#{state_id_qld},-28.443959,149.467729), - ($$4496$$,$$TALWOOD$$,#{state_id_qld},-28.443959,149.467729), - ($$4497$$,$$DAYMAR$$,#{state_id_qld},-28.604238,148.986787), - ($$4497$$,$$THALLON$$,#{state_id_qld},-28.604238,148.986787), - ($$4497$$,$$WEENGALLON$$,#{state_id_qld},-28.604238,148.986787), - ($$4498$$,$$KIOMA$$,#{state_id_qld},-28.214948,149.794723), - ($$4498$$,$$TOOBEAH$$,#{state_id_qld},-28.214948,149.794723), - ($$4500$$,$$BRAY PARK$$,#{state_id_qld},-27.292807,152.962376), - ($$4500$$,$$BRENDALE$$,#{state_id_qld},-27.292807,152.962376), - ($$4500$$,$$BRENDALE BC$$,#{state_id_qld},-27.292807,152.962376), - ($$4500$$,$$BRENDALE DC$$,#{state_id_qld},-27.292807,152.962376), - ($$4500$$,$$CASHMERE$$,#{state_id_qld},-27.292807,152.962376), - ($$4500$$,$$CLEAR MOUNTAIN$$,#{state_id_qld},-27.292807,152.962376), - ($$4500$$,$$JOYNER$$,#{state_id_qld},-27.292807,152.962376), - ($$4500$$,$$STRATHPINE$$,#{state_id_qld},-27.292807,152.962376), - ($$4500$$,$$STRATHPINE CENTRE$$,#{state_id_qld},-27.292807,152.962376), - ($$4500$$,$$WARNER$$,#{state_id_qld},-27.292807,152.962376), - ($$4501$$,$$LAWNTON$$,#{state_id_qld},-27.281605,152.980942), - ($$4502$$,$$PETRIE$$,#{state_id_qld},-27.26876,152.975593), - ($$4503$$,$$DAKABIN$$,#{state_id_qld},-27.226474,152.980732), - ($$4503$$,$$GRIFFIN$$,#{state_id_qld},-27.226474,152.980732), - ($$4503$$,$$KALLANGUR$$,#{state_id_qld},-27.226474,152.980732), - ($$4503$$,$$KURWONGBAH$$,#{state_id_qld},-27.226474,152.980732), - ($$4503$$,$$MURRUMBA DOWNS$$,#{state_id_qld},-27.226474,152.980732), - ($$4503$$,$$WHITESIDE$$,#{state_id_qld},-27.226474,152.980732), - ($$4504$$,$$NARANGBA$$,#{state_id_qld},-27.186964,152.928542), - ($$4505$$,$$BURPENGARY$$,#{state_id_qld},-27.17079,152.954767), - ($$4505$$,$$BURPENGARY DC$$,#{state_id_qld},-27.17079,152.954767), - ($$4506$$,$$MOORINA$$,#{state_id_qld},-27.147649,152.860468), - ($$4506$$,$$MORAYFIELD$$,#{state_id_qld},-27.147649,152.860468), - ($$4507$$,$$BANKSIA BEACH$$,#{state_id_qld},-27.045188,153.140879), - ($$4507$$,$$BELLARA$$,#{state_id_qld},-27.045188,153.140879), - ($$4507$$,$$BONGAREE$$,#{state_id_qld},-27.045188,153.140879), - ($$4507$$,$$BRIBIE ISLAND$$,#{state_id_qld},-27.045188,153.140879), - ($$4507$$,$$BRIBIE ISLAND NORTH$$,#{state_id_qld},-27.045188,153.140879), - ($$4507$$,$$WELSBY$$,#{state_id_qld},-27.045188,153.140879), - ($$4507$$,$$WHITE PATCH$$,#{state_id_qld},-27.045188,153.140879), - ($$4507$$,$$WOORIM$$,#{state_id_qld},-27.045188,153.140879), - ($$4508$$,$$DECEPTION BAY$$,#{state_id_qld},-27.196702,153.029501), - ($$4509$$,$$MANGO HILL$$,#{state_id_qld},-27.232691,153.017319), - ($$4509$$,$$NORTH LAKES$$,#{state_id_qld},-27.232691,153.017319), - ($$4510$$,$$BEACHMERE$$,#{state_id_qld},-27.129058,153.052258), - ($$4510$$,$$BELLMERE$$,#{state_id_qld},-27.129058,153.052258), - ($$4510$$,$$CABOOLTURE$$,#{state_id_qld},-27.129058,153.052258), - ($$4510$$,$$CABOOLTURE BC$$,#{state_id_qld},-27.129058,153.052258), - ($$4510$$,$$CABOOLTURE SOUTH$$,#{state_id_qld},-27.129058,153.052258), - ($$4510$$,$$DONNYBROOK$$,#{state_id_qld},-27.129058,153.052258), - ($$4510$$,$$MELDALE$$,#{state_id_qld},-27.129058,153.052258), - ($$4510$$,$$MOODLU$$,#{state_id_qld},-27.129058,153.052258), - ($$4510$$,$$ROCKSBERG$$,#{state_id_qld},-27.129058,153.052258), - ($$4510$$,$$TOORBUL$$,#{state_id_qld},-27.129058,153.052258), - ($$4510$$,$$UPPER CABOOLTURE$$,#{state_id_qld},-27.129058,153.052258), - ($$4511$$,$$GODWIN BEACH$$,#{state_id_qld},-27.084803,153.113383), - ($$4511$$,$$NINGI$$,#{state_id_qld},-27.084803,153.113383), - ($$4511$$,$$SANDSTONE POINT$$,#{state_id_qld},-27.084803,153.113383), - ($$4512$$,$$BRACALBA$$,#{state_id_qld},-27.011117,152.841028), - ($$4512$$,$$WAMURAN$$,#{state_id_qld},-27.011117,152.841028), - ($$4512$$,$$WAMURAN BASIN$$,#{state_id_qld},-27.011117,152.841028), - ($$4514$$,$$BELLTHORPE$$,#{state_id_qld},-26.848046,152.715349), - ($$4514$$,$$CEDARTON$$,#{state_id_qld},-26.848046,152.715349), - ($$4514$$,$$COMMISSIONERS FLAT$$,#{state_id_qld},-26.848046,152.715349), - ($$4514$$,$$D'AGUILAR$$,#{state_id_qld},-26.848046,152.715349), - ($$4514$$,$$DELANEYS CREEK$$,#{state_id_qld},-26.848046,152.715349), - ($$4514$$,$$MOUNT ARCHER$$,#{state_id_qld},-26.848046,152.715349), - ($$4514$$,$$MOUNT DELANEY$$,#{state_id_qld},-26.848046,152.715349), - ($$4514$$,$$NEURUM$$,#{state_id_qld},-26.848046,152.715349), - ($$4514$$,$$STANMORE$$,#{state_id_qld},-26.848046,152.715349), - ($$4514$$,$$STONY CREEK$$,#{state_id_qld},-26.848046,152.715349), - ($$4514$$,$$VILLENEUVE$$,#{state_id_qld},-26.848046,152.715349), - ($$4514$$,$$WOODFORD$$,#{state_id_qld},-26.848046,152.715349), - ($$4515$$,$$GLENFERN$$,#{state_id_qld},-26.959741,152.609621), - ($$4515$$,$$HAZELDEAN$$,#{state_id_qld},-26.959741,152.609621), - ($$4515$$,$$JIMNA$$,#{state_id_qld},-26.959741,152.609621), - ($$4515$$,$$KILCOY$$,#{state_id_qld},-26.959741,152.609621), - ($$4515$$,$$KINGAHAM$$,#{state_id_qld},-26.959741,152.609621), - ($$4515$$,$$MONSILDALE$$,#{state_id_qld},-26.959741,152.609621), - ($$4515$$,$$MOUNT KILCOY$$,#{state_id_qld},-26.959741,152.609621), - ($$4515$$,$$ROYSTON$$,#{state_id_qld},-26.959741,152.609621), - ($$4515$$,$$SANDY CREEK$$,#{state_id_qld},-26.959741,152.609621), - ($$4515$$,$$SHEEP STATION CREEK$$,#{state_id_qld},-26.959741,152.609621), - ($$4515$$,$$WINYA$$,#{state_id_qld},-26.959741,152.609621), - ($$4515$$,$$WOOLMAR$$,#{state_id_qld},-26.959741,152.609621), - ($$4516$$,$$ELIMBAH$$,#{state_id_qld},-27.015065,152.944523), - ($$4517$$,$$BEERBURRUM$$,#{state_id_qld},-26.952598,152.963955), - ($$4518$$,$$GLASS HOUSE MOUNTAINS$$,#{state_id_qld},-26.897918,152.959204), - ($$4519$$,$$BEERWAH$$,#{state_id_qld},-26.857043,152.957162), - ($$4519$$,$$COOCHIN CREEK$$,#{state_id_qld},-26.857043,152.957162), - ($$4519$$,$$CROHAMHURST$$,#{state_id_qld},-26.857043,152.957162), - ($$4519$$,$$PEACHESTER$$,#{state_id_qld},-26.857043,152.957162), - ($$4520$$,$$ARMSTRONG CREEK$$,#{state_id_qld},-27.225617,152.797905), - ($$4520$$,$$CAMP MOUNTAIN$$,#{state_id_qld},-27.225617,152.797905), - ($$4520$$,$$CEDAR CREEK$$,#{state_id_qld},-27.225617,152.797905), - ($$4520$$,$$CLOSEBURN$$,#{state_id_qld},-27.225617,152.797905), - ($$4520$$,$$DRAPER$$,#{state_id_qld},-27.225617,152.797905), - ($$4520$$,$$ENOGGERA RESERVOIR$$,#{state_id_qld},-27.225617,152.797905), - ($$4520$$,$$HIGHVALE$$,#{state_id_qld},-27.225617,152.797905), - ($$4520$$,$$JOLLYS LOOKOUT$$,#{state_id_qld},-27.225617,152.797905), - ($$4520$$,$$KOBBLE CREEK$$,#{state_id_qld},-27.225617,152.797905), - ($$4520$$,$$MOUNT GLORIOUS$$,#{state_id_qld},-27.225617,152.797905), - ($$4520$$,$$MOUNT NEBO$$,#{state_id_qld},-27.225617,152.797905), - ($$4520$$,$$MOUNT SAMSON$$,#{state_id_qld},-27.225617,152.797905), - ($$4520$$,$$SAMFORD$$,#{state_id_qld},-27.225617,152.797905), - ($$4520$$,$$SAMFORD VALLEY$$,#{state_id_qld},-27.225617,152.797905), - ($$4520$$,$$SAMFORD VILLAGE$$,#{state_id_qld},-27.225617,152.797905), - ($$4520$$,$$SAMSONVALE$$,#{state_id_qld},-27.225617,152.797905), - ($$4520$$,$$WIGHTS MOUNTAIN$$,#{state_id_qld},-27.225617,152.797905), - ($$4520$$,$$YUGAR$$,#{state_id_qld},-27.225617,152.797905), - ($$4521$$,$$CAMPBELLS POCKET$$,#{state_id_qld},-27.07184,152.808774), - ($$4521$$,$$DAYBORO$$,#{state_id_qld},-27.07184,152.808774), - ($$4521$$,$$KING SCRUB$$,#{state_id_qld},-27.07184,152.808774), - ($$4521$$,$$LACEYS CREEK$$,#{state_id_qld},-27.07184,152.808774), - ($$4521$$,$$MOUNT MEE$$,#{state_id_qld},-27.07184,152.808774), - ($$4521$$,$$MOUNT PLEASANT$$,#{state_id_qld},-27.07184,152.808774), - ($$4521$$,$$OCEAN VIEW$$,#{state_id_qld},-27.07184,152.808774), - ($$4521$$,$$RUSH CREEK$$,#{state_id_qld},-27.07184,152.808774), - ($$4550$$,$$LANDSBOROUGH$$,#{state_id_qld},-26.808565,152.963941), - ($$4550$$,$$MOUNT MELLUM$$,#{state_id_qld},-26.808565,152.963941), - ($$4551$$,$$AROONA$$,#{state_id_qld},-26.775377,153.113173), - ($$4551$$,$$BATTERY HILL$$,#{state_id_qld},-26.775377,153.113173), - ($$4551$$,$$BELLS CREEK$$,#{state_id_qld},-26.775377,153.113173), - ($$4551$$,$$CALOUNDRA$$,#{state_id_qld},-26.775377,153.113173), - ($$4551$$,$$CALOUNDRA BC$$,#{state_id_qld},-26.775377,153.113173), - ($$4551$$,$$CALOUNDRA DC$$,#{state_id_qld},-26.775377,153.113173), - ($$4551$$,$$CALOUNDRA WEST$$,#{state_id_qld},-26.775377,153.113173), - ($$4551$$,$$CURRIMUNDI$$,#{state_id_qld},-26.775377,153.113173), - ($$4551$$,$$DICKY BEACH$$,#{state_id_qld},-26.775377,153.113173), - ($$4551$$,$$GOLDEN BEACH$$,#{state_id_qld},-26.775377,153.113173), - ($$4551$$,$$KINGS BEACH$$,#{state_id_qld},-26.775377,153.113173), - ($$4551$$,$$LITTLE MOUNTAIN$$,#{state_id_qld},-26.775377,153.113173), - ($$4551$$,$$MERIDAN PLAINS$$,#{state_id_qld},-26.775377,153.113173), - ($$4551$$,$$MOFFAT BEACH$$,#{state_id_qld},-26.775377,153.113173), - ($$4551$$,$$PELICAN WATERS$$,#{state_id_qld},-26.775377,153.113173), - ($$4551$$,$$SHELLY BEACH$$,#{state_id_qld},-26.775377,153.113173), - ($$4552$$,$$BALD KNOB$$,#{state_id_qld},-26.775957,152.892993), - ($$4552$$,$$BALMORAL RIDGE$$,#{state_id_qld},-26.775957,152.892993), - ($$4552$$,$$BOOROOBIN$$,#{state_id_qld},-26.775957,152.892993), - ($$4552$$,$$CAMBROON$$,#{state_id_qld},-26.775957,152.892993), - ($$4552$$,$$CONONDALE$$,#{state_id_qld},-26.775957,152.892993), - ($$4552$$,$$CRYSTAL WATERS$$,#{state_id_qld},-26.775957,152.892993), - ($$4552$$,$$CURRAMORE$$,#{state_id_qld},-26.775957,152.892993), - ($$4552$$,$$ELAMAN CREEK$$,#{state_id_qld},-26.775957,152.892993), - ($$4552$$,$$HARPER CREEK$$,#{state_id_qld},-26.775957,152.892993), - ($$4552$$,$$MALENY$$,#{state_id_qld},-26.775957,152.892993), - ($$4552$$,$$NORTH MALENY$$,#{state_id_qld},-26.775957,152.892993), - ($$4552$$,$$REESVILLE$$,#{state_id_qld},-26.775957,152.892993), - ($$4552$$,$$WITTA$$,#{state_id_qld},-26.775957,152.892993), - ($$4552$$,$$WOOTHA$$,#{state_id_qld},-26.775957,152.892993), - ($$4553$$,$$DIAMOND VALLEY$$,#{state_id_qld},-26.756517,152.932651), - ($$4553$$,$$GLENVIEW$$,#{state_id_qld},-26.756517,152.932651), - ($$4553$$,$$MOOLOOLAH$$,#{state_id_qld},-26.756517,152.932651), - ($$4553$$,$$MOOLOOLAH VALLEY$$,#{state_id_qld},-26.756517,152.932651), - ($$4553$$,$$PALMVIEW$$,#{state_id_qld},-26.756517,152.932651), - ($$4554$$,$$EUDLO$$,#{state_id_qld},-26.725961,152.957869), - ($$4554$$,$$ILKLEY$$,#{state_id_qld},-26.725961,152.957869), - ($$4555$$,$$CHEVALLUM$$,#{state_id_qld},-26.695853,152.987885), - ($$4555$$,$$HUNCHY$$,#{state_id_qld},-26.695853,152.987885), - ($$4555$$,$$LANDERS SHOOT$$,#{state_id_qld},-26.695853,152.987885), - ($$4555$$,$$PALMWOODS$$,#{state_id_qld},-26.695853,152.987885), - ($$4556$$,$$BUDERIM$$,#{state_id_qld},-26.685821,153.050524), - ($$4556$$,$$FOREST GLEN$$,#{state_id_qld},-26.685821,153.050524), - ($$4556$$,$$KUNDA PARK$$,#{state_id_qld},-26.685821,153.050524), - ($$4556$$,$$MONS$$,#{state_id_qld},-26.685821,153.050524), - ($$4556$$,$$SIPPY DOWNS$$,#{state_id_qld},-26.685821,153.050524), - ($$4556$$,$$TANAWHA$$,#{state_id_qld},-26.685821,153.050524), - ($$4557$$,$$MOOLOOLABA$$,#{state_id_qld},-26.677686,153.117168), - ($$4557$$,$$MOUNTAIN CREEK$$,#{state_id_qld},-26.677686,153.117168), - ($$4558$$,$$COTTON TREE$$,#{state_id_qld},-26.655187,153.098747), - ($$4558$$,$$KULUIN$$,#{state_id_qld},-26.655187,153.098747), - ($$4558$$,$$MAROOCHYDORE$$,#{state_id_qld},-26.655187,153.098747), - ($$4558$$,$$MAROOCHYDORE BC$$,#{state_id_qld},-26.655187,153.098747), - ($$4558$$,$$MAROOCHYDORE DC$$,#{state_id_qld},-26.655187,153.098747), - ($$4558$$,$$SUNSHINE PLAZA$$,#{state_id_qld},-26.655187,153.098747), - ($$4559$$,$$DIDDILLIBAH$$,#{state_id_qld},-26.646035,153.035002), - ($$4559$$,$$KIELS MOUNTAIN$$,#{state_id_qld},-26.646035,153.035002), - ($$4559$$,$$WEST WOOMBYE$$,#{state_id_qld},-26.646035,153.035002), - ($$4559$$,$$WOOMBYE$$,#{state_id_qld},-26.646035,153.035002), - ($$4560$$,$$BLI BLI$$,#{state_id_qld},-26.6223,153.041663), - ($$4560$$,$$BURNSIDE$$,#{state_id_qld},-26.6223,153.041663), - ($$4560$$,$$COES CREEK$$,#{state_id_qld},-26.6223,153.041663), - ($$4560$$,$$COOLOOLABIN$$,#{state_id_qld},-26.6223,153.041663), - ($$4560$$,$$DULONG$$,#{state_id_qld},-26.6223,153.041663), - ($$4560$$,$$FLAXTON$$,#{state_id_qld},-26.6223,153.041663), - ($$4560$$,$$HIGHWORTH$$,#{state_id_qld},-26.6223,153.041663), - ($$4560$$,$$IMAGE FLAT$$,#{state_id_qld},-26.6223,153.041663), - ($$4560$$,$$KIAMBA$$,#{state_id_qld},-26.6223,153.041663), - ($$4560$$,$$KULANGOOR$$,#{state_id_qld},-26.6223,153.041663), - ($$4560$$,$$KUREELPA$$,#{state_id_qld},-26.6223,153.041663), - ($$4560$$,$$MAPLETON$$,#{state_id_qld},-26.6223,153.041663), - ($$4560$$,$$MONTVILLE$$,#{state_id_qld},-26.6223,153.041663), - ($$4560$$,$$NAMBOUR$$,#{state_id_qld},-26.6223,153.041663), - ($$4560$$,$$NAMBOUR BC$$,#{state_id_qld},-26.6223,153.041663), - ($$4560$$,$$NAMBOUR DC$$,#{state_id_qld},-26.6223,153.041663), - ($$4560$$,$$NAMBOUR WEST$$,#{state_id_qld},-26.6223,153.041663), - ($$4560$$,$$PARKLANDS$$,#{state_id_qld},-26.6223,153.041663), - ($$4560$$,$$PERWILLOWEN$$,#{state_id_qld},-26.6223,153.041663), - ($$4560$$,$$ROSEMOUNT$$,#{state_id_qld},-26.6223,153.041663), - ($$4560$$,$$SUNSHINE COAST MC$$,#{state_id_qld},-26.6223,153.041663), - ($$4560$$,$$TOWEN MOUNTAIN$$,#{state_id_qld},-26.6223,153.041663), - ($$4561$$,$$BRIDGES$$,#{state_id_qld},-26.525907,152.928936), - ($$4561$$,$$MAROOCHY RIVER$$,#{state_id_qld},-26.525907,152.928936), - ($$4561$$,$$NINDERRY$$,#{state_id_qld},-26.525907,152.928936), - ($$4561$$,$$NORTH ARM$$,#{state_id_qld},-26.525907,152.928936), - ($$4561$$,$$VALDORA$$,#{state_id_qld},-26.525907,152.928936), - ($$4561$$,$$YANDINA$$,#{state_id_qld},-26.525907,152.928936), - ($$4561$$,$$YANDINA CREEK$$,#{state_id_qld},-26.525907,152.928936), - ($$4562$$,$$BELLI PARK$$,#{state_id_qld},-26.475165,152.796338), - ($$4562$$,$$DOONAN$$,#{state_id_qld},-26.475165,152.796338), - ($$4562$$,$$EERWAH VALE$$,#{state_id_qld},-26.475165,152.796338), - ($$4562$$,$$EUMUNDI$$,#{state_id_qld},-26.475165,152.796338), - ($$4562$$,$$VERRIERDALE$$,#{state_id_qld},-26.475165,152.796338), - ($$4562$$,$$WEYBA DOWNS$$,#{state_id_qld},-26.475165,152.796338), - ($$4563$$,$$BLACK MOUNTAIN$$,#{state_id_qld},-26.418798,152.854218), - ($$4563$$,$$CARTERS RIDGE$$,#{state_id_qld},-26.418798,152.854218), - ($$4563$$,$$COOROY$$,#{state_id_qld},-26.418798,152.854218), - ($$4563$$,$$COOROY MOUNTAIN$$,#{state_id_qld},-26.418798,152.854218), - ($$4563$$,$$LAKE MACDONALD$$,#{state_id_qld},-26.418798,152.854218), - ($$4563$$,$$RIDGEWOOD$$,#{state_id_qld},-26.418798,152.854218), - ($$4563$$,$$TINBEERWAH$$,#{state_id_qld},-26.418798,152.854218), - ($$4564$$,$$MARCOOLA$$,#{state_id_qld},-26.583856,153.096211), - ($$4564$$,$$MUDJIMBA$$,#{state_id_qld},-26.583856,153.096211), - ($$4564$$,$$PACIFIC PARADISE$$,#{state_id_qld},-26.583856,153.096211), - ($$4564$$,$$TWIN WATERS$$,#{state_id_qld},-26.583856,153.096211), - ($$4565$$,$$BOREEN POINT$$,#{state_id_qld},-26.28624,152.993922), - ($$4565$$,$$COOROIBAH$$,#{state_id_qld},-26.28624,152.993922), - ($$4565$$,$$COOTHARABA$$,#{state_id_qld},-26.28624,152.993922), - ($$4565$$,$$NOOSA NORTH SHORE$$,#{state_id_qld},-26.28624,152.993922), - ($$4565$$,$$RINGTAIL CREEK$$,#{state_id_qld},-26.28624,152.993922), - ($$4565$$,$$TEWANTIN$$,#{state_id_qld},-26.28624,152.993922), - ($$4566$$,$$NOOSAVILLE$$,#{state_id_qld},-26.402039,153.064626), - ($$4566$$,$$NOOSAVILLE BC$$,#{state_id_qld},-26.402039,153.064626), - ($$4566$$,$$NOOSAVILLE DC$$,#{state_id_qld},-26.402039,153.064626), - ($$4567$$,$$CASTAWAYS BEACH$$,#{state_id_qld},-26.430258,153.105728), - ($$4567$$,$$NOOSA HEADS$$,#{state_id_qld},-26.430258,153.105728), - ($$4567$$,$$SUNRISE BEACH$$,#{state_id_qld},-26.430258,153.105728), - ($$4567$$,$$SUNSHINE BEACH$$,#{state_id_qld},-26.430258,153.105728), - ($$4568$$,$$FEDERAL$$,#{state_id_qld},-26.328915,152.858705), - ($$4568$$,$$PINBARREN$$,#{state_id_qld},-26.328915,152.858705), - ($$4568$$,$$POMONA$$,#{state_id_qld},-26.328915,152.858705), - ($$4569$$,$$COORAN$$,#{state_id_qld},-26.334574,152.822844), - ($$4570$$,$$AMAMOOR$$,#{state_id_qld},-26.34524,152.674989), - ($$4570$$,$$AMAMOOR CREEK$$,#{state_id_qld},-26.34524,152.674989), - ($$4570$$,$$ANDERLEIGH$$,#{state_id_qld},-26.34524,152.674989), - ($$4570$$,$$ARALUEN$$,#{state_id_qld},-26.34524,152.674989), - ($$4570$$,$$BANKS POCKET$$,#{state_id_qld},-26.34524,152.674989), - ($$4570$$,$$BEENAAM VALLEY$$,#{state_id_qld},-26.34524,152.674989), - ($$4570$$,$$BELLA CREEK$$,#{state_id_qld},-26.34524,152.674989), - ($$4570$$,$$BELLS BRIDGE$$,#{state_id_qld},-26.34524,152.674989), - ($$4570$$,$$BOLLIER$$,#{state_id_qld},-26.34524,152.674989), - ($$4570$$,$$BROOLOO$$,#{state_id_qld},-26.34524,152.674989), - ($$4570$$,$$CALGOA$$,#{state_id_qld},-26.34524,152.674989), - ($$4570$$,$$CALICO CREEK$$,#{state_id_qld},-26.34524,152.674989), - ($$4570$$,$$CANINA$$,#{state_id_qld},-26.34524,152.674989), - ($$4570$$,$$CEDAR POCKET$$,#{state_id_qld},-26.34524,152.674989), - ($$4570$$,$$CHATSWORTH$$,#{state_id_qld},-26.34524,152.674989), - ($$4570$$,$$COLES CREEK$$,#{state_id_qld},-26.34524,152.674989), - ($$4570$$,$$COONDOO$$,#{state_id_qld},-26.34524,152.674989), - ($$4570$$,$$CORELLA$$,#{state_id_qld},-26.34524,152.674989), - ($$4570$$,$$CURRA$$,#{state_id_qld},-26.34524,152.674989), - ($$4570$$,$$DAGUN$$,#{state_id_qld},-26.34524,152.674989), - ($$4570$$,$$DOWNSFIELD$$,#{state_id_qld},-26.34524,152.674989), - ($$4570$$,$$EAST DEEP CREEK$$,#{state_id_qld},-26.34524,152.674989), - ($$4570$$,$$FISHERMANS POCKET$$,#{state_id_qld},-26.34524,152.674989), - ($$4570$$,$$GILLDORA$$,#{state_id_qld},-26.34524,152.674989), - ($$4570$$,$$GLANMIRE$$,#{state_id_qld},-26.34524,152.674989), - ($$4570$$,$$GLASTONBURY$$,#{state_id_qld},-26.34524,152.674989), - ($$4570$$,$$GLEN ECHO$$,#{state_id_qld},-26.34524,152.674989), - ($$4570$$,$$GLENWOOD$$,#{state_id_qld},-26.34524,152.674989), - ($$4570$$,$$GOOMBOORIAN$$,#{state_id_qld},-26.34524,152.674989), - ($$4570$$,$$GREENS CREEK$$,#{state_id_qld},-26.34524,152.674989), - ($$4570$$,$$GUNALDA$$,#{state_id_qld},-26.34524,152.674989), - ($$4570$$,$$GYMPIE$$,#{state_id_qld},-26.34524,152.674989), - ($$4570$$,$$GYMPIE DC$$,#{state_id_qld},-26.34524,152.674989), - ($$4570$$,$$IMBIL$$,#{state_id_qld},-26.34524,152.674989), - ($$4570$$,$$JONES HILL$$,#{state_id_qld},-26.34524,152.674989), - ($$4570$$,$$KANDANGA$$,#{state_id_qld},-26.34524,152.674989), - ($$4570$$,$$KANDANGA CREEK$$,#{state_id_qld},-26.34524,152.674989), - ($$4570$$,$$KANIGAN$$,#{state_id_qld},-26.34524,152.674989), - ($$4570$$,$$KIA ORA$$,#{state_id_qld},-26.34524,152.674989), - ($$4570$$,$$KYBONG$$,#{state_id_qld},-26.34524,152.674989), - ($$4570$$,$$LAGOON POCKET$$,#{state_id_qld},-26.34524,152.674989), - ($$4570$$,$$LAKE BORUMBA$$,#{state_id_qld},-26.34524,152.674989), - ($$4570$$,$$LANGSHAW$$,#{state_id_qld},-26.34524,152.674989), - ($$4570$$,$$LONG FLAT$$,#{state_id_qld},-26.34524,152.674989), - ($$4570$$,$$LOWER WONGA$$,#{state_id_qld},-26.34524,152.674989), - ($$4570$$,$$MARODIAN$$,#{state_id_qld},-26.34524,152.674989), - ($$4570$$,$$MARYS CREEK$$,#{state_id_qld},-26.34524,152.674989), - ($$4570$$,$$MCINTOSH CREEK$$,#{state_id_qld},-26.34524,152.674989), - ($$4570$$,$$MELAWONDI$$,#{state_id_qld},-26.34524,152.674989), - ($$4570$$,$$MIVA$$,#{state_id_qld},-26.34524,152.674989), - ($$4570$$,$$MONKLAND$$,#{state_id_qld},-26.34524,152.674989), - ($$4570$$,$$MOOLOO$$,#{state_id_qld},-26.34524,152.674989), - ($$4570$$,$$MOTHAR MOUNTAIN$$,#{state_id_qld},-26.34524,152.674989), - ($$4570$$,$$MUNNA CREEK$$,#{state_id_qld},-26.34524,152.674989), - ($$4570$$,$$NAHRUNDA$$,#{state_id_qld},-26.34524,152.674989), - ($$4570$$,$$NEERDIE$$,#{state_id_qld},-26.34524,152.674989), - ($$4570$$,$$NEUSA VALE$$,#{state_id_qld},-26.34524,152.674989), - ($$4570$$,$$NORTH DEEP CREEK$$,#{state_id_qld},-26.34524,152.674989), - ($$4570$$,$$PATERSON$$,#{state_id_qld},-26.34524,152.674989), - ($$4570$$,$$PIE CREEK$$,#{state_id_qld},-26.34524,152.674989), - ($$4570$$,$$ROSS CREEK$$,#{state_id_qld},-26.34524,152.674989), - ($$4570$$,$$SCOTCHY POCKET$$,#{state_id_qld},-26.34524,152.674989), - ($$4570$$,$$SCRUBBY CREEK$$,#{state_id_qld},-26.34524,152.674989), - ($$4570$$,$$SEXTON$$,#{state_id_qld},-26.34524,152.674989), - ($$4570$$,$$SOUTHSIDE$$,#{state_id_qld},-26.34524,152.674989), - ($$4570$$,$$TAMAREE$$,#{state_id_qld},-26.34524,152.674989), - ($$4570$$,$$TANDUR$$,#{state_id_qld},-26.34524,152.674989), - ($$4570$$,$$THE DAWN$$,#{state_id_qld},-26.34524,152.674989), - ($$4570$$,$$THE PALMS$$,#{state_id_qld},-26.34524,152.674989), - ($$4570$$,$$THEEBINE$$,#{state_id_qld},-26.34524,152.674989), - ($$4570$$,$$TOOLARA FOREST$$,#{state_id_qld},-26.34524,152.674989), - ($$4570$$,$$TRAVESTON$$,#{state_id_qld},-26.34524,152.674989), - ($$4570$$,$$TUCHEKOI$$,#{state_id_qld},-26.34524,152.674989), - ($$4570$$,$$TWO MILE$$,#{state_id_qld},-26.34524,152.674989), - ($$4570$$,$$UPPER GLASTONBURY$$,#{state_id_qld},-26.34524,152.674989), - ($$4570$$,$$UPPER KANDANGA$$,#{state_id_qld},-26.34524,152.674989), - ($$4570$$,$$VETERAN$$,#{state_id_qld},-26.34524,152.674989), - ($$4570$$,$$VICTORY HEIGHTS$$,#{state_id_qld},-26.34524,152.674989), - ($$4570$$,$$WALLU$$,#{state_id_qld},-26.34524,152.674989), - ($$4570$$,$$WIDGEE$$,#{state_id_qld},-26.34524,152.674989), - ($$4570$$,$$WIDGEE CROSSING NORTH$$,#{state_id_qld},-26.34524,152.674989), - ($$4570$$,$$WIDGEE CROSSING SOUTH$$,#{state_id_qld},-26.34524,152.674989), - ($$4570$$,$$WILSONS POCKET$$,#{state_id_qld},-26.34524,152.674989), - ($$4570$$,$$WOLVI$$,#{state_id_qld},-26.34524,152.674989), - ($$4570$$,$$WOOLOOGA$$,#{state_id_qld},-26.34524,152.674989), - ($$4570$$,$$WOONDUM$$,#{state_id_qld},-26.34524,152.674989), - ($$4571$$,$$COMO$$,#{state_id_qld},-26.219743,152.931885), - ($$4571$$,$$KIN KIN$$,#{state_id_qld},-26.219743,152.931885), - ($$4572$$,$$ALEXANDRA HEADLAND$$,#{state_id_qld},-26.670635,153.108029), - ($$4573$$,$$COOLUM BEACH$$,#{state_id_qld},-26.527219,153.090485), - ($$4573$$,$$MARCUS BEACH$$,#{state_id_qld},-26.527219,153.090485), - ($$4573$$,$$MOUNT COOLUM$$,#{state_id_qld},-26.527219,153.090485), - ($$4573$$,$$PEREGIAN BEACH$$,#{state_id_qld},-26.527219,153.090485), - ($$4573$$,$$PEREGIAN BEACH SOUTH$$,#{state_id_qld},-26.527219,153.090485), - ($$4573$$,$$PEREGIAN SPRINGS$$,#{state_id_qld},-26.527219,153.090485), - ($$4573$$,$$POINT ARKWRIGHT$$,#{state_id_qld},-26.527219,153.090485), - ($$4573$$,$$YAROOMBA$$,#{state_id_qld},-26.527219,153.090485), - ($$4574$$,$$COOLABINE$$,#{state_id_qld},-26.600831,152.762607), - ($$4574$$,$$GHEERULLA$$,#{state_id_qld},-26.600831,152.762607), - ($$4574$$,$$KENILWORTH$$,#{state_id_qld},-26.600831,152.762607), - ($$4574$$,$$KIDAMAN CREEK$$,#{state_id_qld},-26.600831,152.762607), - ($$4574$$,$$MOY POCKET$$,#{state_id_qld},-26.600831,152.762607), - ($$4574$$,$$OBI OBI$$,#{state_id_qld},-26.600831,152.762607), - ($$4575$$,$$BIRTINYA$$,#{state_id_qld},-26.745513,153.117015), - ($$4575$$,$$BOKARINA$$,#{state_id_qld},-26.745513,153.117015), - ($$4575$$,$$BUDDINA$$,#{state_id_qld},-26.745513,153.117015), - ($$4575$$,$$MINYAMA$$,#{state_id_qld},-26.745513,153.117015), - ($$4575$$,$$PARREARRA$$,#{state_id_qld},-26.745513,153.117015), - ($$4575$$,$$WARANA$$,#{state_id_qld},-26.745513,153.117015), - ($$4575$$,$$WURTULLA$$,#{state_id_qld},-26.745513,153.117015), - ($$4580$$,$$COOLOOLA$$,#{state_id_qld},-26.005381,153.057271), - ($$4580$$,$$COOLOOLA COVE$$,#{state_id_qld},-26.005381,153.057271), - ($$4580$$,$$TIN CAN BAY$$,#{state_id_qld},-26.005381,153.057271), - ($$4581$$,$$EURONG$$,#{state_id_qld},-25.51083,153.123577), - ($$4581$$,$$FRASER ISLAND$$,#{state_id_qld},-25.51083,153.123577), - ($$4581$$,$$INSKIP$$,#{state_id_qld},-25.51083,153.123577), - ($$4581$$,$$ORCHID BEACH$$,#{state_id_qld},-25.51083,153.123577), - ($$4581$$,$$RAINBOW BEACH$$,#{state_id_qld},-25.51083,153.123577), - ($$4600$$,$$BLACK SNAKE$$,#{state_id_qld},-26.224964,152.268781), - ($$4600$$,$$CINNABAR$$,#{state_id_qld},-26.224964,152.268781), - ($$4600$$,$$KILKIVAN$$,#{state_id_qld},-26.224964,152.268781), - ($$4600$$,$$MUDLO$$,#{state_id_qld},-26.224964,152.268781), - ($$4600$$,$$OAKVIEW$$,#{state_id_qld},-26.224964,152.268781), - ($$4601$$,$$BARAMBAH$$,#{state_id_qld},-26.325215,152.166608), - ($$4601$$,$$BOONARA$$,#{state_id_qld},-26.325215,152.166608), - ($$4601$$,$$BOOUBYJAN$$,#{state_id_qld},-26.325215,152.166608), - ($$4601$$,$$GOOMERI$$,#{state_id_qld},-26.325215,152.166608), - ($$4601$$,$$GOOMERIBONG$$,#{state_id_qld},-26.325215,152.166608), - ($$4601$$,$$KINBOMBI$$,#{state_id_qld},-26.325215,152.166608), - ($$4601$$,$$MANUMBAR$$,#{state_id_qld},-26.325215,152.166608), - ($$4601$$,$$TANSEY$$,#{state_id_qld},-26.325215,152.166608), - ($$4601$$,$$WRATTENS FOREST$$,#{state_id_qld},-26.325215,152.166608), - ($$4605$$,$$BARLIL$$,#{state_id_qld},-26.204516,151.885957), - ($$4605$$,$$BYEE$$,#{state_id_qld},-26.204516,151.885957), - ($$4605$$,$$CHERBOURG$$,#{state_id_qld},-26.204516,151.885957), - ($$4605$$,$$CLOYNA$$,#{state_id_qld},-26.204516,151.885957), - ($$4605$$,$$COBBS HILL$$,#{state_id_qld},-26.204516,151.885957), - ($$4605$$,$$CROWNTHORPE$$,#{state_id_qld},-26.204516,151.885957), - ($$4605$$,$$GLENROCK$$,#{state_id_qld},-26.204516,151.885957), - ($$4605$$,$$KITOBA$$,#{state_id_qld},-26.204516,151.885957), - ($$4605$$,$$MANYUNG$$,#{state_id_qld},-26.204516,151.885957), - ($$4605$$,$$MERLWOOD$$,#{state_id_qld},-26.204516,151.885957), - ($$4605$$,$$MOFFATDALE$$,#{state_id_qld},-26.204516,151.885957), - ($$4605$$,$$MOONDOONER$$,#{state_id_qld},-26.204516,151.885957), - ($$4605$$,$$MURGON$$,#{state_id_qld},-26.204516,151.885957), - ($$4605$$,$$OAKDALE$$,#{state_id_qld},-26.204516,151.885957), - ($$4605$$,$$REDGATE$$,#{state_id_qld},-26.204516,151.885957), - ($$4605$$,$$SILVERLEAF$$,#{state_id_qld},-26.204516,151.885957), - ($$4605$$,$$SUNNY NOOK$$,#{state_id_qld},-26.204516,151.885957), - ($$4605$$,$$TABLELANDS$$,#{state_id_qld},-26.204516,151.885957), - ($$4605$$,$$WARNUNG$$,#{state_id_qld},-26.204516,151.885957), - ($$4605$$,$$WINDERA$$,#{state_id_qld},-26.204516,151.885957), - ($$4605$$,$$WOOROONDEN$$,#{state_id_qld},-26.204516,151.885957), - ($$4606$$,$$CHELMSFORD$$,#{state_id_qld},-26.253777,151.812709), - ($$4606$$,$$FAIRDALE$$,#{state_id_qld},-26.253777,151.812709), - ($$4606$$,$$FICKS CROSSING$$,#{state_id_qld},-26.253777,151.812709), - ($$4606$$,$$GREENVIEW$$,#{state_id_qld},-26.253777,151.812709), - ($$4606$$,$$LEAFDALE$$,#{state_id_qld},-26.253777,151.812709), - ($$4606$$,$$MOUNT MCEUEN$$,#{state_id_qld},-26.253777,151.812709), - ($$4606$$,$$MP CREEK$$,#{state_id_qld},-26.253777,151.812709), - ($$4606$$,$$WHEATLANDS$$,#{state_id_qld},-26.253777,151.812709), - ($$4606$$,$$WONDAI$$,#{state_id_qld},-26.253777,151.812709), - ($$4608$$,$$CHARLESTOWN$$,#{state_id_qld},-26.369266,151.938598), - ($$4608$$,$$CUSHNIE$$,#{state_id_qld},-26.369266,151.938598), - ($$4608$$,$$TINGOORA$$,#{state_id_qld},-26.369266,151.938598), - ($$4608$$,$$WILKESDALE$$,#{state_id_qld},-26.369266,151.938598), - ($$4608$$,$$WOOROOLIN$$,#{state_id_qld},-26.369266,151.938598), - ($$4610$$,$$ALICE CREEK$$,#{state_id_qld},-26.760017,151.631701), - ($$4610$$,$$BALLOGIE$$,#{state_id_qld},-26.760017,151.631701), - ($$4610$$,$$BENAIR$$,#{state_id_qld},-26.760017,151.631701), - ($$4610$$,$$BOOIE$$,#{state_id_qld},-26.760017,151.631701), - ($$4610$$,$$BOONENNE$$,#{state_id_qld},-26.760017,151.631701), - ($$4610$$,$$BOYNESIDE$$,#{state_id_qld},-26.760017,151.631701), - ($$4610$$,$$CHAHPINGAH$$,#{state_id_qld},-26.760017,151.631701), - ($$4610$$,$$COOLABUNIA$$,#{state_id_qld},-26.760017,151.631701), - ($$4610$$,$$CORNDALE$$,#{state_id_qld},-26.760017,151.631701), - ($$4610$$,$$CRAWFORD$$,#{state_id_qld},-26.760017,151.631701), - ($$4610$$,$$DANGORE$$,#{state_id_qld},-26.760017,151.631701), - ($$4610$$,$$DURONG$$,#{state_id_qld},-26.760017,151.631701), - ($$4610$$,$$DURONG SOUTH$$,#{state_id_qld},-26.760017,151.631701), - ($$4610$$,$$ELLESMERE$$,#{state_id_qld},-26.760017,151.631701), - ($$4610$$,$$GOODGER$$,#{state_id_qld},-26.760017,151.631701), - ($$4610$$,$$GORDONBROOK$$,#{state_id_qld},-26.760017,151.631701), - ($$4610$$,$$HALY CREEK$$,#{state_id_qld},-26.760017,151.631701), - ($$4610$$,$$HODGLEIGH$$,#{state_id_qld},-26.760017,151.631701), - ($$4610$$,$$INVERLAW$$,#{state_id_qld},-26.760017,151.631701), - ($$4610$$,$$IRONPOT$$,#{state_id_qld},-26.760017,151.631701), - ($$4610$$,$$KINGAROY$$,#{state_id_qld},-26.760017,151.631701), - ($$4610$$,$$KINGAROY DC$$,#{state_id_qld},-26.760017,151.631701), - ($$4610$$,$$KUMBIA$$,#{state_id_qld},-26.760017,151.631701), - ($$4610$$,$$MANNUEM$$,#{state_id_qld},-26.760017,151.631701), - ($$4610$$,$$MEMERAMBI$$,#{state_id_qld},-26.760017,151.631701), - ($$4610$$,$$TAABINGA$$,#{state_id_qld},-26.760017,151.631701), - ($$4610$$,$$TAABINGA VILLAGE$$,#{state_id_qld},-26.760017,151.631701), - ($$4610$$,$$WATTLE GROVE$$,#{state_id_qld},-26.760017,151.631701), - ($$4611$$,$$MARSHLANDS$$,#{state_id_qld},-26.156007,151.750509), - ($$4611$$,$$MONDURE$$,#{state_id_qld},-26.156007,151.750509), - ($$4612$$,$$HIVESVILLE$$,#{state_id_qld},-26.176778,151.69149), - ($$4612$$,$$KAWL KAWL$$,#{state_id_qld},-26.176778,151.69149), - ($$4612$$,$$KEYSLAND$$,#{state_id_qld},-26.176778,151.69149), - ($$4612$$,$$STONELANDS$$,#{state_id_qld},-26.176778,151.69149), - ($$4612$$,$$WIGTON$$,#{state_id_qld},-26.176778,151.69149), - ($$4613$$,$$ABBEYWOOD$$,#{state_id_qld},-26.106878,151.6288), - ($$4613$$,$$BOONDOOMA$$,#{state_id_qld},-26.106878,151.6288), - ($$4613$$,$$BRIGOODA$$,#{state_id_qld},-26.106878,151.6288), - ($$4613$$,$$COVERTY$$,#{state_id_qld},-26.106878,151.6288), - ($$4613$$,$$KINLEYMORE$$,#{state_id_qld},-26.106878,151.6288), - ($$4613$$,$$MELROSE$$,#{state_id_qld},-26.106878,151.6288), - ($$4613$$,$$OKEDEN$$,#{state_id_qld},-26.106878,151.6288), - ($$4613$$,$$PROSTON$$,#{state_id_qld},-26.106878,151.6288), - ($$4613$$,$$SPEEDWELL$$,#{state_id_qld},-26.106878,151.6288), - ($$4613$$,$$STALWORTH$$,#{state_id_qld},-26.106878,151.6288), - ($$4614$$,$$NEUMGNA$$,#{state_id_qld},-26.819705,151.895063), - ($$4614$$,$$UPPER YARRAMAN$$,#{state_id_qld},-26.819705,151.895063), - ($$4614$$,$$YARRAMAN$$,#{state_id_qld},-26.819705,151.895063), - ($$4615$$,$$BARKER CREEK FLAT$$,#{state_id_qld},-26.648199,151.946486), - ($$4615$$,$$BROOKLANDS$$,#{state_id_qld},-26.648199,151.946486), - ($$4615$$,$$BULLCAMP$$,#{state_id_qld},-26.648199,151.946486), - ($$4615$$,$$EAST NANANGO$$,#{state_id_qld},-26.648199,151.946486), - ($$4615$$,$$ELGIN VALE$$,#{state_id_qld},-26.648199,151.946486), - ($$4615$$,$$GLAN DEVON$$,#{state_id_qld},-26.648199,151.946486), - ($$4615$$,$$JOHNSTOWN$$,#{state_id_qld},-26.648199,151.946486), - ($$4615$$,$$KUNIOON$$,#{state_id_qld},-26.648199,151.946486), - ($$4615$$,$$MAIDENWELL$$,#{state_id_qld},-26.648199,151.946486), - ($$4615$$,$$NANANGO$$,#{state_id_qld},-26.648199,151.946486), - ($$4615$$,$$PIMPIMBUDGEE$$,#{state_id_qld},-26.648199,151.946486), - ($$4615$$,$$RUNNYMEDE$$,#{state_id_qld},-26.648199,151.946486), - ($$4615$$,$$SANDY RIDGES$$,#{state_id_qld},-26.648199,151.946486), - ($$4615$$,$$SOUTH EAST NANANGO$$,#{state_id_qld},-26.648199,151.946486), - ($$4615$$,$$SOUTH NANANGO$$,#{state_id_qld},-26.648199,151.946486), - ($$4615$$,$$TARONG$$,#{state_id_qld},-26.648199,151.946486), - ($$4615$$,$$WATTLE CAMP$$,#{state_id_qld},-26.648199,151.946486), - ($$4615$$,$$WENGENVILLE$$,#{state_id_qld},-26.648199,151.946486), - ($$4615$$,$$WYALLA$$,#{state_id_qld},-26.648199,151.946486), - ($$4620$$,$$ARAMARA$$,#{state_id_qld},-25.611275,152.325924), - ($$4620$$,$$BROOWEENA$$,#{state_id_qld},-25.611275,152.325924), - ($$4620$$,$$DOONGUL$$,#{state_id_qld},-25.611275,152.325924), - ($$4620$$,$$GIGOOMGAN$$,#{state_id_qld},-25.611275,152.325924), - ($$4620$$,$$GLENBAR$$,#{state_id_qld},-25.611275,152.325924), - ($$4620$$,$$GUNGALOON$$,#{state_id_qld},-25.611275,152.325924), - ($$4620$$,$$MALARGA$$,#{state_id_qld},-25.611275,152.325924), - ($$4620$$,$$NORTH ARAMARA$$,#{state_id_qld},-25.611275,152.325924), - ($$4620$$,$$TEEBAR$$,#{state_id_qld},-25.611275,152.325924), - ($$4620$$,$$WOOCOO$$,#{state_id_qld},-25.611275,152.325924), - ($$4621$$,$$BIGGENDEN$$,#{state_id_qld},-25.510983,152.045629), - ($$4621$$,$$BOOMPA$$,#{state_id_qld},-25.510983,152.045629), - ($$4621$$,$$COALSTOUN LAKES$$,#{state_id_qld},-25.510983,152.045629), - ($$4621$$,$$CORINGA$$,#{state_id_qld},-25.510983,152.045629), - ($$4621$$,$$DALLARNIL$$,#{state_id_qld},-25.510983,152.045629), - ($$4621$$,$$DEGILBO$$,#{state_id_qld},-25.510983,152.045629), - ($$4621$$,$$DIDCOT$$,#{state_id_qld},-25.510983,152.045629), - ($$4621$$,$$GOLDEN FLEECE$$,#{state_id_qld},-25.510983,152.045629), - ($$4621$$,$$LAKESIDE$$,#{state_id_qld},-25.510983,152.045629), - ($$4621$$,$$WATERANGA$$,#{state_id_qld},-25.510983,152.045629), - ($$4621$$,$$WOOWOONGA$$,#{state_id_qld},-25.510983,152.045629), - ($$4625$$,$$ARANBANGA$$,#{state_id_qld},-25.806808,151.670067), - ($$4625$$,$$BAN BAN$$,#{state_id_qld},-25.806808,151.670067), - ($$4625$$,$$BAN BAN SPRINGS$$,#{state_id_qld},-25.806808,151.670067), - ($$4625$$,$$BARLYNE$$,#{state_id_qld},-25.806808,151.670067), - ($$4625$$,$$BINJOUR$$,#{state_id_qld},-25.806808,151.670067), - ($$4625$$,$$BLAIRMORE$$,#{state_id_qld},-25.806808,151.670067), - ($$4625$$,$$BON ACCORD$$,#{state_id_qld},-25.806808,151.670067), - ($$4625$$,$$BRANCH CREEK$$,#{state_id_qld},-25.806808,151.670067), - ($$4625$$,$$BYRNESTOWN$$,#{state_id_qld},-25.806808,151.670067), - ($$4625$$,$$CAMPBELL CREEK$$,#{state_id_qld},-25.806808,151.670067), - ($$4625$$,$$DEEP CREEK$$,#{state_id_qld},-25.806808,151.670067), - ($$4625$$,$$DIRNBIR$$,#{state_id_qld},-25.806808,151.670067), - ($$4625$$,$$DUNDARRAH$$,#{state_id_qld},-25.806808,151.670067), - ($$4625$$,$$GAYNDAH$$,#{state_id_qld},-25.806808,151.670067), - ($$4625$$,$$GINOONDAN$$,#{state_id_qld},-25.806808,151.670067), - ($$4625$$,$$GOOROOLBA$$,#{state_id_qld},-25.806808,151.670067), - ($$4625$$,$$HARRIET$$,#{state_id_qld},-25.806808,151.670067), - ($$4625$$,$$HUMPHERY$$,#{state_id_qld},-25.806808,151.670067), - ($$4625$$,$$IDERAWAY$$,#{state_id_qld},-25.806808,151.670067), - ($$4625$$,$$MINGO$$,#{state_id_qld},-25.806808,151.670067), - ($$4625$$,$$MOUNT DEBATEABLE$$,#{state_id_qld},-25.806808,151.670067), - ($$4625$$,$$MOUNT LAWLESS$$,#{state_id_qld},-25.806808,151.670067), - ($$4625$$,$$MOUNT STEADMAN$$,#{state_id_qld},-25.806808,151.670067), - ($$4625$$,$$PENWHAUPELL$$,#{state_id_qld},-25.806808,151.670067), - ($$4625$$,$$PILE GULLY$$,#{state_id_qld},-25.806808,151.670067), - ($$4625$$,$$REIDS CREEK$$,#{state_id_qld},-25.806808,151.670067), - ($$4625$$,$$STOCKHAVEN$$,#{state_id_qld},-25.806808,151.670067), - ($$4625$$,$$THE LIMITS$$,#{state_id_qld},-25.806808,151.670067), - ($$4625$$,$$TOONDAHRA$$,#{state_id_qld},-25.806808,151.670067), - ($$4625$$,$$WAHOON$$,#{state_id_qld},-25.806808,151.670067), - ($$4625$$,$$WETHERON$$,#{state_id_qld},-25.806808,151.670067), - ($$4625$$,$$WILSON VALLEY$$,#{state_id_qld},-25.806808,151.670067), - ($$4625$$,$$WOODMILLAR$$,#{state_id_qld},-25.806808,151.670067), - ($$4625$$,$$YENDA$$,#{state_id_qld},-25.806808,151.670067), - ($$4626$$,$$BEERON$$,#{state_id_qld},-25.813563,151.205469), - ($$4626$$,$$BOYNEWOOD$$,#{state_id_qld},-25.813563,151.205469), - ($$4626$$,$$BROVINIA$$,#{state_id_qld},-25.813563,151.205469), - ($$4626$$,$$CATTLE CREEK$$,#{state_id_qld},-25.813563,151.205469), - ($$4626$$,$$COONAMBULA$$,#{state_id_qld},-25.813563,151.205469), - ($$4626$$,$$DERRI DERRA$$,#{state_id_qld},-25.813563,151.205469), - ($$4626$$,$$DYKEHEAD$$,#{state_id_qld},-25.813563,151.205469), - ($$4626$$,$$GLENRAE$$,#{state_id_qld},-25.813563,151.205469), - ($$4626$$,$$GURGEENA$$,#{state_id_qld},-25.813563,151.205469), - ($$4626$$,$$HAWKWOOD$$,#{state_id_qld},-25.813563,151.205469), - ($$4626$$,$$MONOGORILBY$$,#{state_id_qld},-25.813563,151.205469), - ($$4626$$,$$MUNDOWRAN$$,#{state_id_qld},-25.813563,151.205469), - ($$4626$$,$$MUNDUBBERA$$,#{state_id_qld},-25.813563,151.205469), - ($$4626$$,$$O'BIL BIL$$,#{state_id_qld},-25.813563,151.205469), - ($$4626$$,$$OLD COORANGA$$,#{state_id_qld},-25.813563,151.205469), - ($$4626$$,$$PHILPOTT$$,#{state_id_qld},-25.813563,151.205469), - ($$4626$$,$$RIVERLEIGH$$,#{state_id_qld},-25.813563,151.205469), - ($$4627$$,$$ABERCORN$$,#{state_id_qld},-25.136637,151.129347), - ($$4627$$,$$CERATODUS$$,#{state_id_qld},-25.136637,151.129347), - ($$4627$$,$$CHELTENHAM$$,#{state_id_qld},-25.136637,151.129347), - ($$4627$$,$$CYNTHIA$$,#{state_id_qld},-25.136637,151.129347), - ($$4627$$,$$EIDSVOLD$$,#{state_id_qld},-25.136637,151.129347), - ($$4627$$,$$EIDSVOLD EAST$$,#{state_id_qld},-25.136637,151.129347), - ($$4627$$,$$EIDSVOLD WEST$$,#{state_id_qld},-25.136637,151.129347), - ($$4627$$,$$GROSVENOR$$,#{state_id_qld},-25.136637,151.129347), - ($$4627$$,$$MALMOE$$,#{state_id_qld},-25.136637,151.129347), - ($$4627$$,$$WURUMA DAM$$,#{state_id_qld},-25.136637,151.129347), - ($$4630$$,$$BANCROFT$$,#{state_id_qld},-24.788266,151.231904), - ($$4630$$,$$BUKALI$$,#{state_id_qld},-24.788266,151.231904), - ($$4630$$,$$CANIA$$,#{state_id_qld},-24.788266,151.231904), - ($$4630$$,$$CANNINDAH$$,#{state_id_qld},-24.788266,151.231904), - ($$4630$$,$$COOMINGLAH$$,#{state_id_qld},-24.788266,151.231904), - ($$4630$$,$$COOMINGLAH FOREST$$,#{state_id_qld},-24.788266,151.231904), - ($$4630$$,$$DALGA$$,#{state_id_qld},-24.788266,151.231904), - ($$4630$$,$$GLENLEIGH$$,#{state_id_qld},-24.788266,151.231904), - ($$4630$$,$$HARRAMI$$,#{state_id_qld},-24.788266,151.231904), - ($$4630$$,$$KALPOWAR$$,#{state_id_qld},-24.788266,151.231904), - ($$4630$$,$$KAPALDO$$,#{state_id_qld},-24.788266,151.231904), - ($$4630$$,$$LANGLEY$$,#{state_id_qld},-24.788266,151.231904), - ($$4630$$,$$MONAL$$,#{state_id_qld},-24.788266,151.231904), - ($$4630$$,$$MONTO$$,#{state_id_qld},-24.788266,151.231904), - ($$4630$$,$$MOONFORD$$,#{state_id_qld},-24.788266,151.231904), - ($$4630$$,$$MULGILDIE$$,#{state_id_qld},-24.788266,151.231904), - ($$4630$$,$$MUNGUNGO$$,#{state_id_qld},-24.788266,151.231904), - ($$4630$$,$$RAWBELLE$$,#{state_id_qld},-24.788266,151.231904), - ($$4630$$,$$SELENE$$,#{state_id_qld},-24.788266,151.231904), - ($$4630$$,$$SPLINTER CREEK$$,#{state_id_qld},-24.788266,151.231904), - ($$4630$$,$$TELLEBANG$$,#{state_id_qld},-24.788266,151.231904), - ($$4630$$,$$THREE MOON$$,#{state_id_qld},-24.788266,151.231904), - ($$4630$$,$$VENTNOR$$,#{state_id_qld},-24.788266,151.231904), - ($$4630$$,$$YARROL$$,#{state_id_qld},-24.788266,151.231904), - ($$4650$$,$$ALDERSHOT$$,#{state_id_qld},-25.466599,152.656881), - ($$4650$$,$$ANTIGUA$$,#{state_id_qld},-25.466599,152.656881), - ($$4650$$,$$BAUPLE$$,#{state_id_qld},-25.466599,152.656881), - ($$4650$$,$$BAUPLE FOREST$$,#{state_id_qld},-25.466599,152.656881), - ($$4650$$,$$BEAVER ROCK$$,#{state_id_qld},-25.466599,152.656881), - ($$4650$$,$$BIDWILL$$,#{state_id_qld},-25.466599,152.656881), - ($$4650$$,$$BOONOOROO$$,#{state_id_qld},-25.466599,152.656881), - ($$4650$$,$$BOONOOROO PLAINS$$,#{state_id_qld},-25.466599,152.656881), - ($$4650$$,$$DUCKINWILLA$$,#{state_id_qld},-25.466599,152.656881), - ($$4650$$,$$DUNDATHU$$,#{state_id_qld},-25.466599,152.656881), - ($$4650$$,$$DUNMORA$$,#{state_id_qld},-25.466599,152.656881), - ($$4650$$,$$FERNEY$$,#{state_id_qld},-25.466599,152.656881), - ($$4650$$,$$GLENORCHY$$,#{state_id_qld},-25.466599,152.656881), - ($$4650$$,$$GOOTCHIE$$,#{state_id_qld},-25.466599,152.656881), - ($$4650$$,$$GRAHAMS CREEK$$,#{state_id_qld},-25.466599,152.656881), - ($$4650$$,$$GRANVILLE$$,#{state_id_qld},-25.466599,152.656881), - ($$4650$$,$$GUNDIAH$$,#{state_id_qld},-25.466599,152.656881), - ($$4650$$,$$ISLAND PLANTATION$$,#{state_id_qld},-25.466599,152.656881), - ($$4650$$,$$MAAROOM$$,#{state_id_qld},-25.466599,152.656881), - ($$4650$$,$$MAGNOLIA$$,#{state_id_qld},-25.466599,152.656881), - ($$4650$$,$$MARYBOROUGH$$,#{state_id_qld},-25.466599,152.656881), - ($$4650$$,$$MARYBOROUGH DC$$,#{state_id_qld},-25.466599,152.656881), - ($$4650$$,$$MARYBOROUGH WEST$$,#{state_id_qld},-25.466599,152.656881), - ($$4650$$,$$MOUNT URAH$$,#{state_id_qld},-25.466599,152.656881), - ($$4650$$,$$MUNGAR$$,#{state_id_qld},-25.466599,152.656881), - ($$4650$$,$$NETHERBY$$,#{state_id_qld},-25.466599,152.656881), - ($$4650$$,$$OAKHURST$$,#{state_id_qld},-25.466599,152.656881), - ($$4650$$,$$OWANYILLA$$,#{state_id_qld},-25.466599,152.656881), - ($$4650$$,$$PALLAS STREET MARYBOROUGH$$,#{state_id_qld},-25.466599,152.656881), - ($$4650$$,$$PILERWA$$,#{state_id_qld},-25.466599,152.656881), - ($$4650$$,$$PIONEERS REST$$,#{state_id_qld},-25.466599,152.656881), - ($$4650$$,$$POONA$$,#{state_id_qld},-25.466599,152.656881), - ($$4650$$,$$PRAWLE$$,#{state_id_qld},-25.466599,152.656881), - ($$4650$$,$$ST HELENS$$,#{state_id_qld},-25.466599,152.656881), - ($$4650$$,$$ST MARY$$,#{state_id_qld},-25.466599,152.656881), - ($$4650$$,$$TALEGALLA WEIR$$,#{state_id_qld},-25.466599,152.656881), - ($$4650$$,$$TANDORA$$,#{state_id_qld},-25.466599,152.656881), - ($$4650$$,$$TEDDINGTON$$,#{state_id_qld},-25.466599,152.656881), - ($$4650$$,$$THE DIMONDS$$,#{state_id_qld},-25.466599,152.656881), - ($$4650$$,$$THINOOMBA$$,#{state_id_qld},-25.466599,152.656881), - ($$4650$$,$$TIARO$$,#{state_id_qld},-25.466599,152.656881), - ($$4650$$,$$TINANA$$,#{state_id_qld},-25.466599,152.656881), - ($$4650$$,$$TINANA SOUTH$$,#{state_id_qld},-25.466599,152.656881), - ($$4650$$,$$TINNANBAR$$,#{state_id_qld},-25.466599,152.656881), - ($$4650$$,$$TUAN$$,#{state_id_qld},-25.466599,152.656881), - ($$4650$$,$$TUAN FOREST$$,#{state_id_qld},-25.466599,152.656881), - ($$4650$$,$$WALKERS POINT$$,#{state_id_qld},-25.466599,152.656881), - ($$4650$$,$$YENGARIE$$,#{state_id_qld},-25.466599,152.656881), - ($$4650$$,$$YERRA$$,#{state_id_qld},-25.466599,152.656881), - ($$4655$$,$$BOORAL$$,#{state_id_qld},-25.352719,152.890792), - ($$4655$$,$$BUNYA CREEK$$,#{state_id_qld},-25.352719,152.890792), - ($$4655$$,$$CRAIGNISH$$,#{state_id_qld},-25.352719,152.890792), - ($$4655$$,$$DUNDOWRAN$$,#{state_id_qld},-25.352719,152.890792), - ($$4655$$,$$DUNDOWRAN BEACH$$,#{state_id_qld},-25.352719,152.890792), - ($$4655$$,$$ELI WATERS$$,#{state_id_qld},-25.352719,152.890792), - ($$4655$$,$$GREAT SANDY STRAIT$$,#{state_id_qld},-25.352719,152.890792), - ($$4655$$,$$HAPPY VALLEY$$,#{state_id_qld},-25.352719,152.890792), - ($$4655$$,$$HERVEY BAY$$,#{state_id_qld},-25.352719,152.890792), - ($$4655$$,$$HERVEY BAY DC$$,#{state_id_qld},-25.352719,152.890792), - ($$4655$$,$$KAWUNGAN$$,#{state_id_qld},-25.352719,152.890792), - ($$4655$$,$$KINGFISHER BAY RESORT$$,#{state_id_qld},-25.352719,152.890792), - ($$4655$$,$$NIKENBAH$$,#{state_id_qld},-25.352719,152.890792), - ($$4655$$,$$PIALBA$$,#{state_id_qld},-25.352719,152.890792), - ($$4655$$,$$POINT VERNON$$,#{state_id_qld},-25.352719,152.890792), - ($$4655$$,$$RIVER HEADS$$,#{state_id_qld},-25.352719,152.890792), - ($$4655$$,$$SCARNESS$$,#{state_id_qld},-25.352719,152.890792), - ($$4655$$,$$SUNSHINE ACRES$$,#{state_id_qld},-25.352719,152.890792), - ($$4655$$,$$SUSAN RIVER$$,#{state_id_qld},-25.352719,152.890792), - ($$4655$$,$$TAKURA$$,#{state_id_qld},-25.352719,152.890792), - ($$4655$$,$$TOOGOOM$$,#{state_id_qld},-25.352719,152.890792), - ($$4655$$,$$TORQUAY$$,#{state_id_qld},-25.352719,152.890792), - ($$4655$$,$$URANGAN$$,#{state_id_qld},-25.352719,152.890792), - ($$4655$$,$$URRAWEEN$$,#{state_id_qld},-25.352719,152.890792), - ($$4655$$,$$WALLIEBUM$$,#{state_id_qld},-25.352719,152.890792), - ($$4655$$,$$WALLIGAN$$,#{state_id_qld},-25.352719,152.890792), - ($$4655$$,$$WONDUNNA$$,#{state_id_qld},-25.352719,152.890792), - ($$4659$$,$$BEELBI CREEK$$,#{state_id_qld},-25.29264,152.626943), - ($$4659$$,$$BURGOWAN$$,#{state_id_qld},-25.29264,152.626943), - ($$4659$$,$$BURRUM$$,#{state_id_qld},-25.29264,152.626943), - ($$4659$$,$$BURRUM HEADS$$,#{state_id_qld},-25.29264,152.626943), - ($$4659$$,$$BURRUM RIVER$$,#{state_id_qld},-25.29264,152.626943), - ($$4659$$,$$BURRUM TOWN$$,#{state_id_qld},-25.29264,152.626943), - ($$4659$$,$$HOWARD$$,#{state_id_qld},-25.29264,152.626943), - ($$4659$$,$$PACIFIC HAVEN$$,#{state_id_qld},-25.29264,152.626943), - ($$4660$$,$$ABINGTON$$,#{state_id_qld},-25.203235,152.328931), - ($$4660$$,$$APPLE TREE CREEK$$,#{state_id_qld},-25.203235,152.328931), - ($$4660$$,$$BUXTON$$,#{state_id_qld},-25.203235,152.328931), - ($$4660$$,$$CHERWELL$$,#{state_id_qld},-25.203235,152.328931), - ($$4660$$,$$CHILDERS$$,#{state_id_qld},-25.203235,152.328931), - ($$4660$$,$$CORDALBA$$,#{state_id_qld},-25.203235,152.328931), - ($$4660$$,$$DOOLBI$$,#{state_id_qld},-25.203235,152.328931), - ($$4660$$,$$EUREKA$$,#{state_id_qld},-25.203235,152.328931), - ($$4660$$,$$FARNSFIELD$$,#{state_id_qld},-25.203235,152.328931), - ($$4660$$,$$GOODWOOD$$,#{state_id_qld},-25.203235,152.328931), - ($$4660$$,$$GREGORY RIVER$$,#{state_id_qld},-25.203235,152.328931), - ($$4660$$,$$HORTON$$,#{state_id_qld},-25.203235,152.328931), - ($$4660$$,$$ISIS CENTRAL$$,#{state_id_qld},-25.203235,152.328931), - ($$4660$$,$$ISIS RIVER$$,#{state_id_qld},-25.203235,152.328931), - ($$4660$$,$$KULLOGUM$$,#{state_id_qld},-25.203235,152.328931), - ($$4660$$,$$NORTH GREGORY$$,#{state_id_qld},-25.203235,152.328931), - ($$4660$$,$$NORTH ISIS$$,#{state_id_qld},-25.203235,152.328931), - ($$4660$$,$$PROMISEDLAND$$,#{state_id_qld},-25.203235,152.328931), - ($$4660$$,$$REDRIDGE$$,#{state_id_qld},-25.203235,152.328931), - ($$4660$$,$$SOUTH ISIS$$,#{state_id_qld},-25.203235,152.328931), - ($$4660$$,$$WOODGATE$$,#{state_id_qld},-25.203235,152.328931), - ($$4662$$,$$TORBANLEA$$,#{state_id_qld},-25.347212,152.59878), - ($$4670$$,$$ABBOTSFORD$$,#{state_id_qld},-24.843687,152.024909), - ($$4670$$,$$ALLOWAY$$,#{state_id_qld},-24.843687,152.024909), - ($$4670$$,$$ASHFIELD$$,#{state_id_qld},-24.843687,152.024909), - ($$4670$$,$$AVENELL HEIGHTS$$,#{state_id_qld},-24.843687,152.024909), - ($$4670$$,$$AVOCA$$,#{state_id_qld},-24.843687,152.024909), - ($$4670$$,$$AVONDALE$$,#{state_id_qld},-24.843687,152.024909), - ($$4670$$,$$BARGARA$$,#{state_id_qld},-24.843687,152.024909), - ($$4670$$,$$BRANYAN$$,#{state_id_qld},-24.843687,152.024909), - ($$4670$$,$$BUCCA$$,#{state_id_qld},-24.843687,152.024909), - ($$4670$$,$$BUNDABERG$$,#{state_id_qld},-24.843687,152.024909), - ($$4670$$,$$BUNDABERG CENTRAL$$,#{state_id_qld},-24.843687,152.024909), - ($$4670$$,$$BUNDABERG DC$$,#{state_id_qld},-24.843687,152.024909), - ($$4670$$,$$BUNDABERG EAST$$,#{state_id_qld},-24.843687,152.024909), - ($$4670$$,$$BUNDABERG NORTH$$,#{state_id_qld},-24.843687,152.024909), - ($$4670$$,$$BUNDABERG SOUTH$$,#{state_id_qld},-24.843687,152.024909), - ($$4670$$,$$BUNDABERG WEST$$,#{state_id_qld},-24.843687,152.024909), - ($$4670$$,$$BURNETT HEADS$$,#{state_id_qld},-24.843687,152.024909), - ($$4670$$,$$CALAVOS$$,#{state_id_qld},-24.843687,152.024909), - ($$4670$$,$$COONARR$$,#{state_id_qld},-24.843687,152.024909), - ($$4670$$,$$CORAL COVE$$,#{state_id_qld},-24.843687,152.024909), - ($$4670$$,$$ELECTRA$$,#{state_id_qld},-24.843687,152.024909), - ($$4670$$,$$ELLIOTT$$,#{state_id_qld},-24.843687,152.024909), - ($$4670$$,$$ELLIOTT HEADS$$,#{state_id_qld},-24.843687,152.024909), - ($$4670$$,$$FAIRYMEAD$$,#{state_id_qld},-24.843687,152.024909), - ($$4670$$,$$GIVELDA$$,#{state_id_qld},-24.843687,152.024909), - ($$4670$$,$$GOOBURRUM$$,#{state_id_qld},-24.843687,152.024909), - ($$4670$$,$$INNES PARK$$,#{state_id_qld},-24.843687,152.024909), - ($$4670$$,$$KALKIE$$,#{state_id_qld},-24.843687,152.024909), - ($$4670$$,$$KENSINGTON$$,#{state_id_qld},-24.843687,152.024909), - ($$4670$$,$$KEPNOCK$$,#{state_id_qld},-24.843687,152.024909), - ($$4670$$,$$KINKUNA$$,#{state_id_qld},-24.843687,152.024909), - ($$4670$$,$$MEADOWVALE$$,#{state_id_qld},-24.843687,152.024909), - ($$4670$$,$$MILLBANK$$,#{state_id_qld},-24.843687,152.024909), - ($$4670$$,$$MON REPOS$$,#{state_id_qld},-24.843687,152.024909), - ($$4670$$,$$MOORE PARK BEACH$$,#{state_id_qld},-24.843687,152.024909), - ($$4670$$,$$MOORLAND$$,#{state_id_qld},-24.843687,152.024909), - ($$4670$$,$$MULLETT CREEK$$,#{state_id_qld},-24.843687,152.024909), - ($$4670$$,$$NORVILLE$$,#{state_id_qld},-24.843687,152.024909), - ($$4670$$,$$OAKWOOD$$,#{state_id_qld},-24.843687,152.024909), - ($$4670$$,$$PINE CREEK$$,#{state_id_qld},-24.843687,152.024909), - ($$4670$$,$$QUNABA$$,#{state_id_qld},-24.843687,152.024909), - ($$4670$$,$$RUBYANNA$$,#{state_id_qld},-24.843687,152.024909), - ($$4670$$,$$SHARON$$,#{state_id_qld},-24.843687,152.024909), - ($$4670$$,$$SOUTH BINGERA$$,#{state_id_qld},-24.843687,152.024909), - ($$4670$$,$$SOUTH KOLAN$$,#{state_id_qld},-24.843687,152.024909), - ($$4670$$,$$SVENSSON HEIGHTS$$,#{state_id_qld},-24.843687,152.024909), - ($$4670$$,$$THABEBAN$$,#{state_id_qld},-24.843687,152.024909), - ($$4670$$,$$WALKERVALE$$,#{state_id_qld},-24.843687,152.024909), - ($$4670$$,$$WATALGAN$$,#{state_id_qld},-24.843687,152.024909), - ($$4670$$,$$WELCOME CREEK$$,#{state_id_qld},-24.843687,152.024909), - ($$4670$$,$$WINDERMERE$$,#{state_id_qld},-24.843687,152.024909), - ($$4670$$,$$WINFIELD$$,#{state_id_qld},-24.843687,152.024909), - ($$4670$$,$$WOONGARRA$$,#{state_id_qld},-24.843687,152.024909), - ($$4671$$,$$BOOLBOONDA$$,#{state_id_qld},-25.079428,151.67995), - ($$4671$$,$$BOOYAL$$,#{state_id_qld},-25.079428,151.67995), - ($$4671$$,$$BULLYARD$$,#{state_id_qld},-25.079428,151.67995), - ($$4671$$,$$BUNGADOO$$,#{state_id_qld},-25.079428,151.67995), - ($$4671$$,$$DALYSFORD$$,#{state_id_qld},-25.079428,151.67995), - ($$4671$$,$$DAMASCUS$$,#{state_id_qld},-25.079428,151.67995), - ($$4671$$,$$DELAN$$,#{state_id_qld},-25.079428,151.67995), - ($$4671$$,$$DOUGHBOY$$,#{state_id_qld},-25.079428,151.67995), - ($$4671$$,$$DRINAN$$,#{state_id_qld},-25.079428,151.67995), - ($$4671$$,$$DUINGAL$$,#{state_id_qld},-25.079428,151.67995), - ($$4671$$,$$GAETA$$,#{state_id_qld},-25.079428,151.67995), - ($$4671$$,$$GIN GIN$$,#{state_id_qld},-25.079428,151.67995), - ($$4671$$,$$GOOD NIGHT$$,#{state_id_qld},-25.079428,151.67995), - ($$4671$$,$$HORSE CAMP$$,#{state_id_qld},-25.079428,151.67995), - ($$4671$$,$$KOLONGA$$,#{state_id_qld},-25.079428,151.67995), - ($$4671$$,$$LAKE MONDURAN$$,#{state_id_qld},-25.079428,151.67995), - ($$4671$$,$$MAROONDAN$$,#{state_id_qld},-25.079428,151.67995), - ($$4671$$,$$MCILWRAITH$$,#{state_id_qld},-25.079428,151.67995), - ($$4671$$,$$MOLANGUL$$,#{state_id_qld},-25.079428,151.67995), - ($$4671$$,$$MONDURAN$$,#{state_id_qld},-25.079428,151.67995), - ($$4671$$,$$MOOLBOOLAMAN$$,#{state_id_qld},-25.079428,151.67995), - ($$4671$$,$$MORGANVILLE$$,#{state_id_qld},-25.079428,151.67995), - ($$4671$$,$$MOUNT PERRY$$,#{state_id_qld},-25.079428,151.67995), - ($$4671$$,$$MUNGY$$,#{state_id_qld},-25.079428,151.67995), - ($$4671$$,$$NEARUM$$,#{state_id_qld},-25.079428,151.67995), - ($$4671$$,$$NEW MOONTA$$,#{state_id_qld},-25.079428,151.67995), - ($$4671$$,$$REDHILL FARMS$$,#{state_id_qld},-25.079428,151.67995), - ($$4671$$,$$SKYRING RESERVE$$,#{state_id_qld},-25.079428,151.67995), - ($$4671$$,$$ST AGNES$$,#{state_id_qld},-25.079428,151.67995), - ($$4671$$,$$ST KILDA$$,#{state_id_qld},-25.079428,151.67995), - ($$4671$$,$$TAKILBERAN$$,#{state_id_qld},-25.079428,151.67995), - ($$4671$$,$$TIRROAN$$,#{state_id_qld},-25.079428,151.67995), - ($$4671$$,$$WALLAVILLE$$,#{state_id_qld},-25.079428,151.67995), - ($$4671$$,$$WONBAH$$,#{state_id_qld},-25.079428,151.67995), - ($$4671$$,$$WONBAH FOREST$$,#{state_id_qld},-25.079428,151.67995), - ($$4673$$,$$MIARA$$,#{state_id_qld},-24.686568,152.165161), - ($$4673$$,$$WATERLOO$$,#{state_id_qld},-24.686568,152.165161), - ($$4673$$,$$YANDARAN$$,#{state_id_qld},-24.686568,152.165161), - ($$4674$$,$$BAFFLE CREEK$$,#{state_id_qld},-24.473289,151.948748), - ($$4674$$,$$BERAJONDO$$,#{state_id_qld},-24.473289,151.948748), - ($$4674$$,$$DEEPWATER$$,#{state_id_qld},-24.473289,151.948748), - ($$4674$$,$$EULEILAH$$,#{state_id_qld},-24.473289,151.948748), - ($$4674$$,$$MOUNT MARIA$$,#{state_id_qld},-24.473289,151.948748), - ($$4674$$,$$OYSTER CREEK$$,#{state_id_qld},-24.473289,151.948748), - ($$4674$$,$$ROSEDALE$$,#{state_id_qld},-24.473289,151.948748), - ($$4674$$,$$RULES BEACH$$,#{state_id_qld},-24.473289,151.948748), - ($$4674$$,$$TAUNTON$$,#{state_id_qld},-24.473289,151.948748), - ($$4676$$,$$GINDORAN$$,#{state_id_qld},-24.62626,151.59192), - ($$4676$$,$$LOWMEAD$$,#{state_id_qld},-24.62626,151.59192), - ($$4677$$,$$AGNES WATER$$,#{state_id_qld},-24.212505,151.903352), - ($$4677$$,$$CAPTAIN CREEK$$,#{state_id_qld},-24.212505,151.903352), - ($$4677$$,$$COLOSSEUM$$,#{state_id_qld},-24.212505,151.903352), - ($$4677$$,$$EURIMBULA$$,#{state_id_qld},-24.212505,151.903352), - ($$4677$$,$$MIRIAM VALE$$,#{state_id_qld},-24.212505,151.903352), - ($$4677$$,$$MOUNT TOM$$,#{state_id_qld},-24.212505,151.903352), - ($$4677$$,$$ROUND HILL$$,#{state_id_qld},-24.212505,151.903352), - ($$4677$$,$$SEVENTEEN SEVENTY$$,#{state_id_qld},-24.212505,151.903352), - ($$4678$$,$$BOROREN$$,#{state_id_qld},-24.243194,151.495847), - ($$4678$$,$$FORESHORES$$,#{state_id_qld},-24.243194,151.495847), - ($$4678$$,$$RODDS BAY$$,#{state_id_qld},-24.243194,151.495847), - ($$4678$$,$$TURKEY BEACH$$,#{state_id_qld},-24.243194,151.495847), - ($$4680$$,$$BARNEY POINT$$,#{state_id_qld},-23.856785,151.271287), - ($$4680$$,$$BEECHER$$,#{state_id_qld},-23.856785,151.271287), - ($$4680$$,$$BENARABY$$,#{state_id_qld},-23.856785,151.271287), - ($$4680$$,$$BOYNE ISLAND$$,#{state_id_qld},-23.856785,151.271287), - ($$4680$$,$$BOYNE VALLEY$$,#{state_id_qld},-23.856785,151.271287), - ($$4680$$,$$BOYNEDALE$$,#{state_id_qld},-23.856785,151.271287), - ($$4680$$,$$BUILYAN$$,#{state_id_qld},-23.856785,151.271287), - ($$4680$$,$$BURUA$$,#{state_id_qld},-23.856785,151.271287), - ($$4680$$,$$BYELLEE$$,#{state_id_qld},-23.856785,151.271287), - ($$4680$$,$$CALLEMONDAH$$,#{state_id_qld},-23.856785,151.271287), - ($$4680$$,$$CALLIOPE$$,#{state_id_qld},-23.856785,151.271287), - ($$4680$$,$$CLINTON$$,#{state_id_qld},-23.856785,151.271287), - ($$4680$$,$$CURTIS ISLAND$$,#{state_id_qld},-23.856785,151.271287), - ($$4680$$,$$DIGLUM$$,#{state_id_qld},-23.856785,151.271287), - ($$4680$$,$$GLADSTONE$$,#{state_id_qld},-23.856785,151.271287), - ($$4680$$,$$GLADSTONE BC$$,#{state_id_qld},-23.856785,151.271287), - ($$4680$$,$$GLADSTONE DC$$,#{state_id_qld},-23.856785,151.271287), - ($$4680$$,$$GLADSTONE SOUTH$$,#{state_id_qld},-23.856785,151.271287), - ($$4680$$,$$GLADSTONE-CITY$$,#{state_id_qld},-23.856785,151.271287), - ($$4680$$,$$GLEN EDEN$$,#{state_id_qld},-23.856785,151.271287), - ($$4680$$,$$HERON ISLAND$$,#{state_id_qld},-23.856785,151.271287), - ($$4680$$,$$IVERAGH$$,#{state_id_qld},-23.856785,151.271287), - ($$4680$$,$$KIN KORA$$,#{state_id_qld},-23.856785,151.271287), - ($$4680$$,$$KIRKWOOD$$,#{state_id_qld},-23.856785,151.271287), - ($$4680$$,$$MANY PEAKS$$,#{state_id_qld},-23.856785,151.271287), - ($$4680$$,$$MOUNT ALMA$$,#{state_id_qld},-23.856785,151.271287), - ($$4680$$,$$NEW AUCKLAND$$,#{state_id_qld},-23.856785,151.271287), - ($$4680$$,$$O'CONNELL$$,#{state_id_qld},-23.856785,151.271287), - ($$4680$$,$$RIVER RANCH$$,#{state_id_qld},-23.856785,151.271287), - ($$4680$$,$$SOUTH END$$,#{state_id_qld},-23.856785,151.271287), - ($$4680$$,$$SOUTH GLADSTONE$$,#{state_id_qld},-23.856785,151.271287), - ($$4680$$,$$SOUTH TREES$$,#{state_id_qld},-23.856785,151.271287), - ($$4680$$,$$SUN VALLEY$$,#{state_id_qld},-23.856785,151.271287), - ($$4680$$,$$TABLELANDS$$,#{state_id_qld},-23.856785,151.271287), - ($$4680$$,$$TANNUM SANDS$$,#{state_id_qld},-23.856785,151.271287), - ($$4680$$,$$TARAGOOLA$$,#{state_id_qld},-23.856785,151.271287), - ($$4680$$,$$TELINA$$,#{state_id_qld},-23.856785,151.271287), - ($$4680$$,$$TOOLOOA$$,#{state_id_qld},-23.856785,151.271287), - ($$4680$$,$$UBOBO$$,#{state_id_qld},-23.856785,151.271287), - ($$4680$$,$$WEST GLADSTONE$$,#{state_id_qld},-23.856785,151.271287), - ($$4680$$,$$WEST STOWE$$,#{state_id_qld},-23.856785,151.271287), - ($$4680$$,$$WOODERSON$$,#{state_id_qld},-23.856785,151.271287), - ($$4680$$,$$WURDONG HEIGHTS$$,#{state_id_qld},-23.856785,151.271287), - ($$4694$$,$$ALDOGA$$,#{state_id_qld},-23.835292,151.071334), - ($$4694$$,$$TARGINIE$$,#{state_id_qld},-23.835292,151.071334), - ($$4694$$,$$YARWUN$$,#{state_id_qld},-23.835292,151.071334), - ($$4695$$,$$AMBROSE$$,#{state_id_qld},-23.785682,150.926796), - ($$4695$$,$$BRACEWELL$$,#{state_id_qld},-23.785682,150.926796), - ($$4695$$,$$DARTS CREEK$$,#{state_id_qld},-23.785682,150.926796), - ($$4695$$,$$EAST END$$,#{state_id_qld},-23.785682,150.926796), - ($$4695$$,$$MACHINE CREEK$$,#{state_id_qld},-23.785682,150.926796), - ($$4695$$,$$MOUNT LARCOM$$,#{state_id_qld},-23.785682,150.926796), - ($$4695$$,$$THE NARROWS$$,#{state_id_qld},-23.785682,150.926796), - ($$4697$$,$$RAGLAN$$,#{state_id_qld},-23.717454,150.819346), - ($$4699$$,$$BAJOOL$$,#{state_id_qld},-23.651894,150.641144), - ($$4699$$,$$PORT ALMA$$,#{state_id_qld},-23.651894,150.641144), - ($$4700$$,$$ALLENSTOWN$$,#{state_id_qld},-23.388939,150.502867), - ($$4700$$,$$DEPOT HILL$$,#{state_id_qld},-23.388939,150.502867), - ($$4700$$,$$FAIRY BOWER$$,#{state_id_qld},-23.388939,150.502867), - ($$4700$$,$$GREAT KEPPEL ISLAND$$,#{state_id_qld},-23.388939,150.502867), - ($$4700$$,$$PORT CURTIS$$,#{state_id_qld},-23.388939,150.502867), - ($$4700$$,$$ROCKHAMPTON$$,#{state_id_qld},-23.388939,150.502867), - ($$4700$$,$$ROCKHAMPTON CITY$$,#{state_id_qld},-23.388939,150.502867), - ($$4700$$,$$ROCKHAMPTON HOSPITAL$$,#{state_id_qld},-23.388939,150.502867), - ($$4700$$,$$THE KEPPELS$$,#{state_id_qld},-23.388939,150.502867), - ($$4700$$,$$THE RANGE$$,#{state_id_qld},-23.388939,150.502867), - ($$4700$$,$$WANDAL$$,#{state_id_qld},-23.388939,150.502867), - ($$4700$$,$$WEST ROCKHAMPTON$$,#{state_id_qld},-23.388939,150.502867), - ($$4701$$,$$BERSERKER$$,#{state_id_qld},-23.362809,150.522691), - ($$4701$$,$$CENTRAL QUEENSLAND UNIVERSITY$$,#{state_id_qld},-23.362809,150.522691), - ($$4701$$,$$FRENCHVILLE$$,#{state_id_qld},-23.362809,150.522691), - ($$4701$$,$$GREENLAKE$$,#{state_id_qld},-23.362809,150.522691), - ($$4701$$,$$IRONPOT$$,#{state_id_qld},-23.362809,150.522691), - ($$4701$$,$$KAWANA$$,#{state_id_qld},-23.362809,150.522691), - ($$4701$$,$$KOONGAL$$,#{state_id_qld},-23.362809,150.522691), - ($$4701$$,$$LAKES CREEK$$,#{state_id_qld},-23.362809,150.522691), - ($$4701$$,$$LIMESTONE CREEK$$,#{state_id_qld},-23.362809,150.522691), - ($$4701$$,$$MOUNT ARCHER$$,#{state_id_qld},-23.362809,150.522691), - ($$4701$$,$$NANKIN$$,#{state_id_qld},-23.362809,150.522691), - ($$4701$$,$$NERIMBERA$$,#{state_id_qld},-23.362809,150.522691), - ($$4701$$,$$NORMAN GARDENS$$,#{state_id_qld},-23.362809,150.522691), - ($$4701$$,$$NORTH ROCKHAMPTON$$,#{state_id_qld},-23.362809,150.522691), - ($$4701$$,$$PARK AVENUE$$,#{state_id_qld},-23.362809,150.522691), - ($$4701$$,$$RED HILL ROCKHAMPTON$$,#{state_id_qld},-23.362809,150.522691), - ($$4701$$,$$ROCKHAMPTON DC$$,#{state_id_qld},-23.362809,150.522691), - ($$4701$$,$$ROCKHAMPTON NORTH$$,#{state_id_qld},-23.362809,150.522691), - ($$4701$$,$$ROCKYVIEW$$,#{state_id_qld},-23.362809,150.522691), - ($$4701$$,$$SANDRINGHAM$$,#{state_id_qld},-23.362809,150.522691), - ($$4701$$,$$THE COMMON$$,#{state_id_qld},-23.362809,150.522691), - ($$4702$$,$$ALBERTA$$,#{state_id_qld},-24.325475,149.782526), - ($$4702$$,$$ALSACE$$,#{state_id_qld},-24.325475,149.782526), - ($$4702$$,$$ALTON DOWNS$$,#{state_id_qld},-24.325475,149.782526), - ($$4702$$,$$ANAKIE$$,#{state_id_qld},-24.325475,149.782526), - ($$4702$$,$$ARGOON$$,#{state_id_qld},-24.325475,149.782526), - ($$4702$$,$$BALCOMBA$$,#{state_id_qld},-24.325475,149.782526), - ($$4702$$,$$BANANA$$,#{state_id_qld},-24.325475,149.782526), - ($$4702$$,$$BARALABA$$,#{state_id_qld},-24.325475,149.782526), - ($$4702$$,$$BARNARD$$,#{state_id_qld},-24.325475,149.782526), - ($$4702$$,$$BINGEGANG$$,#{state_id_qld},-24.325475,149.782526), - ($$4702$$,$$BLACKDOWN$$,#{state_id_qld},-24.325475,149.782526), - ($$4702$$,$$BLUFF$$,#{state_id_qld},-24.325475,149.782526), - ($$4702$$,$$BOOLBURRA$$,#{state_id_qld},-24.325475,149.782526), - ($$4702$$,$$BOULDERCOMBE$$,#{state_id_qld},-24.325475,149.782526), - ($$4702$$,$$BUSHLEY$$,#{state_id_qld},-24.325475,149.782526), - ($$4702$$,$$CANAL CREEK$$,#{state_id_qld},-24.325475,149.782526), - ($$4702$$,$$CANOONA$$,#{state_id_qld},-24.325475,149.782526), - ($$4702$$,$$CAWARRAL$$,#{state_id_qld},-24.325475,149.782526), - ($$4702$$,$$CENTRAL QUEENSLAND MC$$,#{state_id_qld},-24.325475,149.782526), - ($$4702$$,$$COMET$$,#{state_id_qld},-24.325475,149.782526), - ($$4702$$,$$CONSUELO$$,#{state_id_qld},-24.325475,149.782526), - ($$4702$$,$$COOMOO$$,#{state_id_qld},-24.325475,149.782526), - ($$4702$$,$$COOROOMAN$$,#{state_id_qld},-24.325475,149.782526), - ($$4702$$,$$COORUMBENE$$,#{state_id_qld},-24.325475,149.782526), - ($$4702$$,$$COOWONGA$$,#{state_id_qld},-24.325475,149.782526), - ($$4702$$,$$DALMA$$,#{state_id_qld},-24.325475,149.782526), - ($$4702$$,$$DINGO$$,#{state_id_qld},-24.325475,149.782526), - ($$4702$$,$$DIXALEA$$,#{state_id_qld},-24.325475,149.782526), - ($$4702$$,$$DULULU$$,#{state_id_qld},-24.325475,149.782526), - ($$4702$$,$$DUMPY CREEK$$,#{state_id_qld},-24.325475,149.782526), - ($$4702$$,$$ETNA CREEK$$,#{state_id_qld},-24.325475,149.782526), - ($$4702$$,$$FERNLEES$$,#{state_id_qld},-24.325475,149.782526), - ($$4702$$,$$GAINSFORD$$,#{state_id_qld},-24.325475,149.782526), - ($$4702$$,$$GARNANT$$,#{state_id_qld},-24.325475,149.782526), - ($$4702$$,$$GINDIE$$,#{state_id_qld},-24.325475,149.782526), - ($$4702$$,$$GLENROY$$,#{state_id_qld},-24.325475,149.782526), - ($$4702$$,$$GOGANGO$$,#{state_id_qld},-24.325475,149.782526), - ($$4702$$,$$GOOMALLY$$,#{state_id_qld},-24.325475,149.782526), - ($$4702$$,$$GOOVIGEN$$,#{state_id_qld},-24.325475,149.782526), - ($$4702$$,$$GOOWARRA$$,#{state_id_qld},-24.325475,149.782526), - ($$4702$$,$$GRACEMERE$$,#{state_id_qld},-24.325475,149.782526), - ($$4702$$,$$JAMBIN$$,#{state_id_qld},-24.325475,149.782526), - ($$4702$$,$$JARDINE$$,#{state_id_qld},-24.325475,149.782526), - ($$4702$$,$$JELLINBAH$$,#{state_id_qld},-24.325475,149.782526), - ($$4702$$,$$JOSKELEIGH$$,#{state_id_qld},-24.325475,149.782526), - ($$4702$$,$$KABRA$$,#{state_id_qld},-24.325475,149.782526), - ($$4702$$,$$KALAPA$$,#{state_id_qld},-24.325475,149.782526), - ($$4702$$,$$KEPPEL SANDS$$,#{state_id_qld},-24.325475,149.782526), - ($$4702$$,$$KOKOTUNGO$$,#{state_id_qld},-24.325475,149.782526), - ($$4702$$,$$KUNWARARA$$,#{state_id_qld},-24.325475,149.782526), - ($$4702$$,$$LOWESBY$$,#{state_id_qld},-24.325475,149.782526), - ($$4702$$,$$MACKENZIE$$,#{state_id_qld},-24.325475,149.782526), - ($$4702$$,$$MARMOR$$,#{state_id_qld},-24.325475,149.782526), - ($$4702$$,$$MIDGEE$$,#{state_id_qld},-24.325475,149.782526), - ($$4702$$,$$MILMAN$$,#{state_id_qld},-24.325475,149.782526), - ($$4702$$,$$MIMOSA$$,#{state_id_qld},-24.325475,149.782526), - ($$4702$$,$$MOONMERA$$,#{state_id_qld},-24.325475,149.782526), - ($$4702$$,$$MORINISH$$,#{state_id_qld},-24.325475,149.782526), - ($$4702$$,$$MORINISH SOUTH$$,#{state_id_qld},-24.325475,149.782526), - ($$4702$$,$$MOUNT CHALMERS$$,#{state_id_qld},-24.325475,149.782526), - ($$4702$$,$$NINE MILE$$,#{state_id_qld},-24.325475,149.782526), - ($$4702$$,$$PARKHURST$$,#{state_id_qld},-24.325475,149.782526), - ($$4702$$,$$PHEASANT CREEK$$,#{state_id_qld},-24.325475,149.782526), - ($$4702$$,$$PINK LILY$$,#{state_id_qld},-24.325475,149.782526), - ($$4702$$,$$PLUM TREE$$,#{state_id_qld},-24.325475,149.782526), - ($$4702$$,$$RIDGELANDS$$,#{state_id_qld},-24.325475,149.782526), - ($$4702$$,$$ROLLESTON$$,#{state_id_qld},-24.325475,149.782526), - ($$4702$$,$$ROSSMOYA$$,#{state_id_qld},-24.325475,149.782526), - ($$4702$$,$$RUBYVALE$$,#{state_id_qld},-24.325475,149.782526), - ($$4702$$,$$SAPPHIRE$$,#{state_id_qld},-24.325475,149.782526), - ($$4702$$,$$SHOALWATER$$,#{state_id_qld},-24.325475,149.782526), - ($$4702$$,$$SMOKY CREEK$$,#{state_id_qld},-24.325475,149.782526), - ($$4702$$,$$SOUTH YAAMBA$$,#{state_id_qld},-24.325475,149.782526), - ($$4702$$,$$STANAGE$$,#{state_id_qld},-24.325475,149.782526), - ($$4702$$,$$STANWELL$$,#{state_id_qld},-24.325475,149.782526), - ($$4702$$,$$STEWARTON$$,#{state_id_qld},-24.325475,149.782526), - ($$4702$$,$$THE CAVES$$,#{state_id_qld},-24.325475,149.782526), - ($$4702$$,$$THOMPSON POINT$$,#{state_id_qld},-24.325475,149.782526), - ($$4702$$,$$TUNGAMULL$$,#{state_id_qld},-24.325475,149.782526), - ($$4702$$,$$ULOGIE$$,#{state_id_qld},-24.325475,149.782526), - ($$4702$$,$$WALLAROO$$,#{state_id_qld},-24.325475,149.782526), - ($$4702$$,$$WESTWOOD$$,#{state_id_qld},-24.325475,149.782526), - ($$4702$$,$$WILLOWS$$,#{state_id_qld},-24.325475,149.782526), - ($$4702$$,$$WILLOWS GEMFIELDS$$,#{state_id_qld},-24.325475,149.782526), - ($$4702$$,$$WOOLEIN$$,#{state_id_qld},-24.325475,149.782526), - ($$4702$$,$$WOOROONA$$,#{state_id_qld},-24.325475,149.782526), - ($$4702$$,$$WOWAN$$,#{state_id_qld},-24.325475,149.782526), - ($$4702$$,$$WYCARBAH$$,#{state_id_qld},-24.325475,149.782526), - ($$4702$$,$$YARAKA$$,#{state_id_qld},-24.325475,149.782526), - ($$4703$$,$$ADELAIDE PARK$$,#{state_id_qld},-23.114684,150.696731), - ($$4703$$,$$BANGALEE$$,#{state_id_qld},-23.114684,150.696731), - ($$4703$$,$$BARLOWS HILL$$,#{state_id_qld},-23.114684,150.696731), - ($$4703$$,$$BARMARYEE$$,#{state_id_qld},-23.114684,150.696731), - ($$4703$$,$$BARMOYA$$,#{state_id_qld},-23.114684,150.696731), - ($$4703$$,$$BONDOOLA$$,#{state_id_qld},-23.114684,150.696731), - ($$4703$$,$$BUNGUNDARRA$$,#{state_id_qld},-23.114684,150.696731), - ($$4703$$,$$BYFIELD$$,#{state_id_qld},-23.114684,150.696731), - ($$4703$$,$$CAUSEWAY LAKE$$,#{state_id_qld},-23.114684,150.696731), - ($$4703$$,$$COBRABALL$$,#{state_id_qld},-23.114684,150.696731), - ($$4703$$,$$COOEE BAY$$,#{state_id_qld},-23.114684,150.696731), - ($$4703$$,$$FARNBOROUGH$$,#{state_id_qld},-23.114684,150.696731), - ($$4703$$,$$HIDDEN VALLEY$$,#{state_id_qld},-23.114684,150.696731), - ($$4703$$,$$INVERNESS$$,#{state_id_qld},-23.114684,150.696731), - ($$4703$$,$$KINKA BEACH$$,#{state_id_qld},-23.114684,150.696731), - ($$4703$$,$$LAKE MARY$$,#{state_id_qld},-23.114684,150.696731), - ($$4703$$,$$LAMMERMOOR$$,#{state_id_qld},-23.114684,150.696731), - ($$4703$$,$$MARYVALE$$,#{state_id_qld},-23.114684,150.696731), - ($$4703$$,$$MEIKLEVILLE HILL$$,#{state_id_qld},-23.114684,150.696731), - ($$4703$$,$$MULAMBIN$$,#{state_id_qld},-23.114684,150.696731), - ($$4703$$,$$MULARA$$,#{state_id_qld},-23.114684,150.696731), - ($$4703$$,$$PACIFIC HEIGHTS$$,#{state_id_qld},-23.114684,150.696731), - ($$4703$$,$$ROSSLYN$$,#{state_id_qld},-23.114684,150.696731), - ($$4703$$,$$STOCKYARD$$,#{state_id_qld},-23.114684,150.696731), - ($$4703$$,$$TANBY$$,#{state_id_qld},-23.114684,150.696731), - ($$4703$$,$$TARANGANBA$$,#{state_id_qld},-23.114684,150.696731), - ($$4703$$,$$TAROOMBALL$$,#{state_id_qld},-23.114684,150.696731), - ($$4703$$,$$WEERRIBA$$,#{state_id_qld},-23.114684,150.696731), - ($$4703$$,$$WOODBURY$$,#{state_id_qld},-23.114684,150.696731), - ($$4703$$,$$YEPPOON$$,#{state_id_qld},-23.114684,150.696731), - ($$4704$$,$$WATTLEBANK$$,#{state_id_qld},-23.119953,150.462544), - ($$4704$$,$$YAAMBA$$,#{state_id_qld},-23.119953,150.462544), - ($$4705$$,$$CLARKE CREEK$$,#{state_id_qld},-22.743208,149.313523), - ($$4705$$,$$LOTUS CREEK$$,#{state_id_qld},-22.743208,149.313523), - ($$4705$$,$$MACKENZIE RIVER$$,#{state_id_qld},-22.743208,149.313523), - ($$4705$$,$$MARLBOROUGH$$,#{state_id_qld},-22.743208,149.313523), - ($$4705$$,$$MOUNT GARDINER$$,#{state_id_qld},-22.743208,149.313523), - ($$4706$$,$$OGMORE$$,#{state_id_qld},-22.592329,149.635224), - ($$4707$$,$$COLLAROY$$,#{state_id_qld},-21.868843,149.143033), - ($$4707$$,$$ST LAWRENCE$$,#{state_id_qld},-21.868843,149.143033), - ($$4707$$,$$THE PERCY GROUP$$,#{state_id_qld},-21.868843,149.143033), - ($$4709$$,$$TIERI$$,#{state_id_qld},-23.038374,148.344727), - ($$4710$$,$$EMU PARK$$,#{state_id_qld},-23.257223,150.82635), - ($$4710$$,$$ZILZIE$$,#{state_id_qld},-23.257223,150.82635), - ($$4711$$,$$GLENDALE$$,#{state_id_qld},-23.252534,150.476229), - ($$4711$$,$$GLENLEE$$,#{state_id_qld},-23.252534,150.476229), - ($$4712$$,$$DUARINGA$$,#{state_id_qld},-23.69521,149.673726), - ($$4713$$,$$WOORABINDA$$,#{state_id_qld},-24.114138,149.430965), - ($$4714$$,$$BAREE$$,#{state_id_qld},-23.624671,150.391364), - ($$4714$$,$$BOULDER CREEK$$,#{state_id_qld},-23.624671,150.391364), - ($$4714$$,$$FLETCHER CREEK$$,#{state_id_qld},-23.624671,150.391364), - ($$4714$$,$$HAMILTON CREEK$$,#{state_id_qld},-23.624671,150.391364), - ($$4714$$,$$HORSE CREEK$$,#{state_id_qld},-23.624671,150.391364), - ($$4714$$,$$JOHNSONS HILL$$,#{state_id_qld},-23.624671,150.391364), - ($$4714$$,$$LEYDENS HILL$$,#{state_id_qld},-23.624671,150.391364), - ($$4714$$,$$LIMESTONE$$,#{state_id_qld},-23.624671,150.391364), - ($$4714$$,$$MOONGAN$$,#{state_id_qld},-23.624671,150.391364), - ($$4714$$,$$MOUNT MORGAN$$,#{state_id_qld},-23.624671,150.391364), - ($$4714$$,$$NINE MILE CREEK$$,#{state_id_qld},-23.624671,150.391364), - ($$4714$$,$$OAKEY CREEK$$,#{state_id_qld},-23.624671,150.391364), - ($$4714$$,$$STRUCK OIL$$,#{state_id_qld},-23.624671,150.391364), - ($$4714$$,$$THE MINE$$,#{state_id_qld},-23.624671,150.391364), - ($$4714$$,$$TROTTER CREEK$$,#{state_id_qld},-23.624671,150.391364), - ($$4714$$,$$WALMUL$$,#{state_id_qld},-23.624671,150.391364), - ($$4714$$,$$WALTERHALL$$,#{state_id_qld},-23.624671,150.391364), - ($$4714$$,$$WURA$$,#{state_id_qld},-23.624671,150.391364), - ($$4715$$,$$BILOELA$$,#{state_id_qld},-24.401296,150.511308), - ($$4715$$,$$CALLIDE$$,#{state_id_qld},-24.401296,150.511308), - ($$4715$$,$$CASTLE CREEK$$,#{state_id_qld},-24.401296,150.511308), - ($$4715$$,$$DAKENBA$$,#{state_id_qld},-24.401296,150.511308), - ($$4715$$,$$DUMGREE$$,#{state_id_qld},-24.401296,150.511308), - ($$4715$$,$$GREYCLIFFE$$,#{state_id_qld},-24.401296,150.511308), - ($$4715$$,$$MOUNT MURCHISON$$,#{state_id_qld},-24.401296,150.511308), - ($$4715$$,$$ORANGE CREEK$$,#{state_id_qld},-24.401296,150.511308), - ($$4715$$,$$PROSPECT$$,#{state_id_qld},-24.401296,150.511308), - ($$4715$$,$$TARRAMBA$$,#{state_id_qld},-24.401296,150.511308), - ($$4715$$,$$VALENTINE PLAINS$$,#{state_id_qld},-24.401296,150.511308), - ($$4716$$,$$LAWGI DAWES$$,#{state_id_qld},-24.579542,150.668297), - ($$4716$$,$$THANGOOL$$,#{state_id_qld},-24.579542,150.668297), - ($$4717$$,$$BLACKWATER$$,#{state_id_qld},-23.585701,148.880408), - ($$4718$$,$$BAUHINIA$$,#{state_id_qld},-24.568117,149.297694), - ($$4718$$,$$DROMEDARY$$,#{state_id_qld},-24.568117,149.297694), - ($$4718$$,$$KIANGA$$,#{state_id_qld},-24.568117,149.297694), - ($$4718$$,$$MOURA$$,#{state_id_qld},-24.568117,149.297694), - ($$4718$$,$$MUNGABUNDA$$,#{state_id_qld},-24.568117,149.297694), - ($$4718$$,$$OOMBABEER$$,#{state_id_qld},-24.568117,149.297694), - ($$4718$$,$$RHYDDING$$,#{state_id_qld},-24.568117,149.297694), - ($$4718$$,$$ROUNDSTONE$$,#{state_id_qld},-24.568117,149.297694), - ($$4718$$,$$WARNOAH$$,#{state_id_qld},-24.568117,149.297694), - ($$4719$$,$$CAMBOON$$,#{state_id_qld},-25.049303,150.163389), - ($$4719$$,$$CRACOW$$,#{state_id_qld},-25.049303,150.163389), - ($$4719$$,$$GLENMORAL$$,#{state_id_qld},-25.049303,150.163389), - ($$4719$$,$$ISLA$$,#{state_id_qld},-25.049303,150.163389), - ($$4719$$,$$LONESOME CREEK$$,#{state_id_qld},-25.049303,150.163389), - ($$4719$$,$$THEODORE$$,#{state_id_qld},-25.049303,150.163389), - ($$4720$$,$$EMERALD$$,#{state_id_qld},-23.52685,148.161076), - ($$4720$$,$$YAMALA$$,#{state_id_qld},-23.52685,148.161076), - ($$4721$$,$$ARGYLL$$,#{state_id_qld},-23.277321,147.562498), - ($$4721$$,$$CLERMONT$$,#{state_id_qld},-23.277321,147.562498), - ($$4721$$,$$FRANKFIELD$$,#{state_id_qld},-23.277321,147.562498), - ($$4721$$,$$GEMINI MOUNTAINS$$,#{state_id_qld},-23.277321,147.562498), - ($$4721$$,$$KILCUMMIN$$,#{state_id_qld},-23.277321,147.562498), - ($$4721$$,$$PASHA$$,#{state_id_qld},-23.277321,147.562498), - ($$4721$$,$$THERESA CREEK$$,#{state_id_qld},-23.277321,147.562498), - ($$4721$$,$$WOLFANG$$,#{state_id_qld},-23.277321,147.562498), - ($$4722$$,$$BUCKLAND$$,#{state_id_qld},-24.506491,147.41651), - ($$4722$$,$$CAIRDBEIGN$$,#{state_id_qld},-24.506491,147.41651), - ($$4722$$,$$CONA CREEK$$,#{state_id_qld},-24.506491,147.41651), - ($$4722$$,$$NANDOWRIE$$,#{state_id_qld},-24.506491,147.41651), - ($$4722$$,$$ORION$$,#{state_id_qld},-24.506491,147.41651), - ($$4722$$,$$SPRINGSURE$$,#{state_id_qld},-24.506491,147.41651), - ($$4722$$,$$WEALWANDANGIE$$,#{state_id_qld},-24.506491,147.41651), - ($$4723$$,$$BELCONG$$,#{state_id_qld},-23.010007,148.231917), - ($$4723$$,$$CAPELLA$$,#{state_id_qld},-23.010007,148.231917), - ($$4723$$,$$CARBINE CREEK$$,#{state_id_qld},-23.010007,148.231917), - ($$4723$$,$$CHIRNSIDE$$,#{state_id_qld},-23.010007,148.231917), - ($$4723$$,$$CRINUM$$,#{state_id_qld},-23.010007,148.231917), - ($$4723$$,$$HIBERNIA$$,#{state_id_qld},-23.010007,148.231917), - ($$4723$$,$$KHOSH BULDUK$$,#{state_id_qld},-23.010007,148.231917), - ($$4723$$,$$LILYVALE$$,#{state_id_qld},-23.010007,148.231917), - ($$4723$$,$$LOWESTOFF$$,#{state_id_qld},-23.010007,148.231917), - ($$4723$$,$$MOUNT MACARTHUR$$,#{state_id_qld},-23.010007,148.231917), - ($$4723$$,$$RETRO$$,#{state_id_qld},-23.010007,148.231917), - ($$4724$$,$$ALPHA$$,#{state_id_qld},-23.649656,146.641074), - ($$4724$$,$$BEAUFORT$$,#{state_id_qld},-23.649656,146.641074), - ($$4724$$,$$DRUMMONDSLOPE$$,#{state_id_qld},-23.649656,146.641074), - ($$4724$$,$$HOBARTVILLE$$,#{state_id_qld},-23.649656,146.641074), - ($$4724$$,$$PINE HILL$$,#{state_id_qld},-23.649656,146.641074), - ($$4724$$,$$PORT WINE$$,#{state_id_qld},-23.649656,146.641074), - ($$4724$$,$$SEDGEFORD$$,#{state_id_qld},-23.649656,146.641074), - ($$4724$$,$$SURBITON$$,#{state_id_qld},-23.649656,146.641074), - ($$4725$$,$$BARCALDINE$$,#{state_id_qld},-23.551987,145.289074), - ($$4725$$,$$BARCALDINE DOWNS$$,#{state_id_qld},-23.551987,145.289074), - ($$4725$$,$$PATRICK$$,#{state_id_qld},-23.551987,145.289074), - ($$4725$$,$$TARA STATION$$,#{state_id_qld},-23.551987,145.289074), - ($$4726$$,$$ARAMAC$$,#{state_id_qld},-22.97216,145.24543), - ($$4726$$,$$PELICAN CREEK$$,#{state_id_qld},-22.97216,145.24543), - ($$4727$$,$$ILFRACOMBE$$,#{state_id_qld},-23.849803,144.472886), - ($$4728$$,$$DUNROBIN$$,#{state_id_qld},-22.68504,146.150547), - ($$4728$$,$$GARFIELD$$,#{state_id_qld},-22.68504,146.150547), - ($$4728$$,$$JERICHO$$,#{state_id_qld},-22.68504,146.150547), - ($$4728$$,$$MEXICO$$,#{state_id_qld},-22.68504,146.150547), - ($$4730$$,$$CAMOOLA$$,#{state_id_qld},-23.044622,144.525193), - ($$4730$$,$$CHORREGON$$,#{state_id_qld},-23.044622,144.525193), - ($$4730$$,$$ERNESTINA$$,#{state_id_qld},-23.044622,144.525193), - ($$4730$$,$$LONGREACH$$,#{state_id_qld},-23.044622,144.525193), - ($$4730$$,$$MANEROO$$,#{state_id_qld},-23.044622,144.525193), - ($$4730$$,$$MORELLA$$,#{state_id_qld},-23.044622,144.525193), - ($$4730$$,$$STONEHENGE$$,#{state_id_qld},-23.044622,144.525193), - ($$4730$$,$$TOCAL$$,#{state_id_qld},-23.044622,144.525193), - ($$4730$$,$$VERGEMONT$$,#{state_id_qld},-23.044622,144.525193), - ($$4731$$,$$ISISFORD$$,#{state_id_qld},-24.260492,144.440754), - ($$4732$$,$$MUTTABURRA$$,#{state_id_qld},-22.593523,144.546108), - ($$4732$$,$$TABLEDERRY$$,#{state_id_qld},-22.593523,144.546108), - ($$4733$$,$$CORFIELD$$,#{state_id_qld},-21.712701,143.374169), - ($$4735$$,$$DIAMANTINA LAKES$$,#{state_id_qld},-23.763837,141.139094), - ($$4735$$,$$MIDDLETON$$,#{state_id_qld},-23.763837,141.139094), - ($$4735$$,$$OPALTON$$,#{state_id_qld},-23.763837,141.139094), - ($$4735$$,$$WINTON$$,#{state_id_qld},-23.763837,141.139094), - ($$4736$$,$$JUNDAH$$,#{state_id_qld},-24.83083,143.058389), - ($$4737$$,$$ARMSTRONG BEACH$$,#{state_id_qld},-21.454914,149.289562), - ($$4737$$,$$BLUE MOUNTAIN$$,#{state_id_qld},-21.454914,149.289562), - ($$4737$$,$$CAMPWIN BEACH$$,#{state_id_qld},-21.454914,149.289562), - ($$4737$$,$$FRESHWATER POINT$$,#{state_id_qld},-21.454914,149.289562), - ($$4737$$,$$SARINA$$,#{state_id_qld},-21.454914,149.289562), - ($$4737$$,$$SARINA BEACH$$,#{state_id_qld},-21.454914,149.289562), - ($$4737$$,$$SARINA RANGE$$,#{state_id_qld},-21.454914,149.289562), - ($$4737$$,$$SUNNYSIDE$$,#{state_id_qld},-21.454914,149.289562), - ($$4738$$,$$ILBILBIE$$,#{state_id_qld},-21.704347,149.356583), - ($$4738$$,$$KOUMALA$$,#{state_id_qld},-21.704347,149.356583), - ($$4739$$,$$CARMILA$$,#{state_id_qld},-21.910044,149.416599), - ($$4740$$,$$ALEXANDRA$$,#{state_id_qld},-21.164465,149.099609), - ($$4740$$,$$ALLIGATOR CREEK$$,#{state_id_qld},-21.164465,149.099609), - ($$4740$$,$$ANDERGROVE$$,#{state_id_qld},-21.164465,149.099609), - ($$4740$$,$$BAKERS CREEK$$,#{state_id_qld},-21.164465,149.099609), - ($$4740$$,$$BALBERRA$$,#{state_id_qld},-21.164465,149.099609), - ($$4740$$,$$BALNAGOWAN$$,#{state_id_qld},-21.164465,149.099609), - ($$4740$$,$$BEACONSFIELD$$,#{state_id_qld},-21.164465,149.099609), - ($$4740$$,$$BELMUNDA$$,#{state_id_qld},-21.164465,149.099609), - ($$4740$$,$$BLACKS BEACH$$,#{state_id_qld},-21.164465,149.099609), - ($$4740$$,$$CAPE HILLSBOROUGH$$,#{state_id_qld},-21.164465,149.099609), - ($$4740$$,$$CHELONA$$,#{state_id_qld},-21.164465,149.099609), - ($$4740$$,$$CREMORNE$$,#{state_id_qld},-21.164465,149.099609), - ($$4740$$,$$DOLPHIN HEADS$$,#{state_id_qld},-21.164465,149.099609), - ($$4740$$,$$DUMBLETON$$,#{state_id_qld},-21.164465,149.099609), - ($$4740$$,$$DUNDULA$$,#{state_id_qld},-21.164465,149.099609), - ($$4740$$,$$DUNNROCK$$,#{state_id_qld},-21.164465,149.099609), - ($$4740$$,$$EAST MACKAY$$,#{state_id_qld},-21.164465,149.099609), - ($$4740$$,$$EIMEO$$,#{state_id_qld},-21.164465,149.099609), - ($$4740$$,$$ERAKALA$$,#{state_id_qld},-21.164465,149.099609), - ($$4740$$,$$FOULDEN$$,#{state_id_qld},-21.164465,149.099609), - ($$4740$$,$$GLENELLA$$,#{state_id_qld},-21.164465,149.099609), - ($$4740$$,$$GRASSTREE BEACH$$,#{state_id_qld},-21.164465,149.099609), - ($$4740$$,$$HABANA$$,#{state_id_qld},-21.164465,149.099609), - ($$4740$$,$$HALIDAY BAY$$,#{state_id_qld},-21.164465,149.099609), - ($$4740$$,$$HAY POINT$$,#{state_id_qld},-21.164465,149.099609), - ($$4740$$,$$HOMEBUSH$$,#{state_id_qld},-21.164465,149.099609), - ($$4740$$,$$MACKAY$$,#{state_id_qld},-21.164465,149.099609), - ($$4740$$,$$MACKAY BC$$,#{state_id_qld},-21.164465,149.099609), - ($$4740$$,$$MACKAY CANELAND$$,#{state_id_qld},-21.164465,149.099609), - ($$4740$$,$$MACKAY DC$$,#{state_id_qld},-21.164465,149.099609), - ($$4740$$,$$MACKAY HARBOUR$$,#{state_id_qld},-21.164465,149.099609), - ($$4740$$,$$MACKAY NORTH$$,#{state_id_qld},-21.164465,149.099609), - ($$4740$$,$$MACKAY SOUTH$$,#{state_id_qld},-21.164465,149.099609), - ($$4740$$,$$MACKAY WEST$$,#{state_id_qld},-21.164465,149.099609), - ($$4740$$,$$MCEWENS BEACH$$,#{state_id_qld},-21.164465,149.099609), - ($$4740$$,$$MOUNT JUKES$$,#{state_id_qld},-21.164465,149.099609), - ($$4740$$,$$MOUNT PLEASANT$$,#{state_id_qld},-21.164465,149.099609), - ($$4740$$,$$MUNBURA$$,#{state_id_qld},-21.164465,149.099609), - ($$4740$$,$$NINDAROO$$,#{state_id_qld},-21.164465,149.099609), - ($$4740$$,$$NORTH MACKAY$$,#{state_id_qld},-21.164465,149.099609), - ($$4740$$,$$OORALEA$$,#{state_id_qld},-21.164465,149.099609), - ($$4740$$,$$PAGET$$,#{state_id_qld},-21.164465,149.099609), - ($$4740$$,$$RACECOURSE$$,#{state_id_qld},-21.164465,149.099609), - ($$4740$$,$$RICHMOND$$,#{state_id_qld},-21.164465,149.099609), - ($$4740$$,$$ROSELLA$$,#{state_id_qld},-21.164465,149.099609), - ($$4740$$,$$RURAL VIEW$$,#{state_id_qld},-21.164465,149.099609), - ($$4740$$,$$SANDIFORD$$,#{state_id_qld},-21.164465,149.099609), - ($$4740$$,$$SLADE POINT$$,#{state_id_qld},-21.164465,149.099609), - ($$4740$$,$$SOUTH MACKAY$$,#{state_id_qld},-21.164465,149.099609), - ($$4740$$,$$TE KOWAI$$,#{state_id_qld},-21.164465,149.099609), - ($$4740$$,$$THE LEAP$$,#{state_id_qld},-21.164465,149.099609), - ($$4740$$,$$WEST MACKAY$$,#{state_id_qld},-21.164465,149.099609), - ($$4741$$,$$BALL BAY$$,#{state_id_qld},-20.90451,148.99513), - ($$4741$$,$$BRIGHTLY$$,#{state_id_qld},-20.90451,148.99513), - ($$4741$$,$$CLAIRVIEW$$,#{state_id_qld},-20.90451,148.99513), - ($$4741$$,$$COPPABELLA$$,#{state_id_qld},-20.90451,148.99513), - ($$4741$$,$$DAYDREAM ISLAND$$,#{state_id_qld},-20.90451,148.99513), - ($$4741$$,$$EPSOM$$,#{state_id_qld},-20.90451,148.99513), - ($$4741$$,$$ETON$$,#{state_id_qld},-20.90451,148.99513), - ($$4741$$,$$ETON NORTH$$,#{state_id_qld},-20.90451,148.99513), - ($$4741$$,$$EUNGELLA HINTERLAND$$,#{state_id_qld},-20.90451,148.99513), - ($$4741$$,$$FARLEIGH$$,#{state_id_qld},-20.90451,148.99513), - ($$4741$$,$$GARGETT$$,#{state_id_qld},-20.90451,148.99513), - ($$4741$$,$$HAMPDEN$$,#{state_id_qld},-20.90451,148.99513), - ($$4741$$,$$HAZLEDEAN$$,#{state_id_qld},-20.90451,148.99513), - ($$4741$$,$$HOOK ISLAND$$,#{state_id_qld},-20.90451,148.99513), - ($$4741$$,$$KALARKA$$,#{state_id_qld},-20.90451,148.99513), - ($$4741$$,$$KINCHANT DAM$$,#{state_id_qld},-20.90451,148.99513), - ($$4741$$,$$KUTTABUL$$,#{state_id_qld},-20.90451,148.99513), - ($$4741$$,$$LINDEMAN ISLAND$$,#{state_id_qld},-20.90451,148.99513), - ($$4741$$,$$LONG ISLAND$$,#{state_id_qld},-20.90451,148.99513), - ($$4741$$,$$MACKAY MC$$,#{state_id_qld},-20.90451,148.99513), - ($$4741$$,$$MOUNT CHARLTON$$,#{state_id_qld},-20.90451,148.99513), - ($$4741$$,$$MOUNT OSSA$$,#{state_id_qld},-20.90451,148.99513), - ($$4741$$,$$MOUNT PELION$$,#{state_id_qld},-20.90451,148.99513), - ($$4741$$,$$NORTH ETON$$,#{state_id_qld},-20.90451,148.99513), - ($$4741$$,$$OAKENDEN$$,#{state_id_qld},-20.90451,148.99513), - ($$4741$$,$$ORKABIE$$,#{state_id_qld},-20.90451,148.99513), - ($$4741$$,$$OWENS CREEK$$,#{state_id_qld},-20.90451,148.99513), - ($$4741$$,$$PINNACLE$$,#{state_id_qld},-20.90451,148.99513), - ($$4741$$,$$PLEYSTOWE$$,#{state_id_qld},-20.90451,148.99513), - ($$4741$$,$$SEAFORTH$$,#{state_id_qld},-20.90451,148.99513), - ($$4741$$,$$SOUTH MOLLE$$,#{state_id_qld},-20.90451,148.99513), - ($$4741$$,$$YALBOROO$$,#{state_id_qld},-20.90451,148.99513), - ($$4742$$,$$BURTON$$,#{state_id_qld},-21.538291,148.075393), - ($$4742$$,$$EAGLEFIELD$$,#{state_id_qld},-21.538291,148.075393), - ($$4742$$,$$ELPHINSTONE$$,#{state_id_qld},-21.538291,148.075393), - ($$4742$$,$$HAIL CREEK$$,#{state_id_qld},-21.538291,148.075393), - ($$4742$$,$$KEMMIS$$,#{state_id_qld},-21.538291,148.075393), - ($$4742$$,$$MOUNT BRITTON$$,#{state_id_qld},-21.538291,148.075393), - ($$4742$$,$$NEBO$$,#{state_id_qld},-21.538291,148.075393), - ($$4742$$,$$OXFORD$$,#{state_id_qld},-21.538291,148.075393), - ($$4742$$,$$STRATHFIELD$$,#{state_id_qld},-21.538291,148.075393), - ($$4742$$,$$TURRAWULLA$$,#{state_id_qld},-21.538291,148.075393), - ($$4742$$,$$VALKYRIE$$,#{state_id_qld},-21.538291,148.075393), - ($$4743$$,$$GLENDEN$$,#{state_id_qld},-21.355672,148.115193), - ($$4743$$,$$SUTTOR$$,#{state_id_qld},-21.355672,148.115193), - ($$4744$$,$$MORANBAH$$,#{state_id_qld},-22.002169,148.046238), - ($$4745$$,$$DYSART$$,#{state_id_qld},-22.588153,148.348719), - ($$4746$$,$$MAY DOWNS$$,#{state_id_qld},-22.640498,148.916434), - ($$4746$$,$$MIDDLEMOUNT$$,#{state_id_qld},-22.640498,148.916434), - ($$4750$$,$$BUCASIA$$,#{state_id_qld},-21.043768,149.15747), - ($$4750$$,$$SHOAL POINT$$,#{state_id_qld},-21.043768,149.15747), - ($$4751$$,$$GREENMOUNT$$,#{state_id_qld},-21.188625,149.03059), - ($$4751$$,$$PALMYRA$$,#{state_id_qld},-21.188625,149.03059), - ($$4751$$,$$VICTORIA PLAINS$$,#{state_id_qld},-21.188625,149.03059), - ($$4751$$,$$WALKERSTON$$,#{state_id_qld},-21.188625,149.03059), - ($$4753$$,$$DEVEREUX CREEK$$,#{state_id_qld},-21.116487,148.890812), - ($$4753$$,$$MARIAN$$,#{state_id_qld},-21.116487,148.890812), - ($$4754$$,$$BENHOLME$$,#{state_id_qld},-21.158348,148.813809), - ($$4754$$,$$DOWS CREEK$$,#{state_id_qld},-21.158348,148.813809), - ($$4754$$,$$MIA MIA$$,#{state_id_qld},-21.158348,148.813809), - ($$4754$$,$$MIRANI$$,#{state_id_qld},-21.158348,148.813809), - ($$4754$$,$$MOUNT MARTIN$$,#{state_id_qld},-21.158348,148.813809), - ($$4754$$,$$PINEVALE$$,#{state_id_qld},-21.158348,148.813809), - ($$4754$$,$$SEPTIMUS$$,#{state_id_qld},-21.158348,148.813809), - ($$4756$$,$$FINCH HATTON$$,#{state_id_qld},-21.142455,148.63221), - ($$4756$$,$$NETHERDALE$$,#{state_id_qld},-21.142455,148.63221), - ($$4757$$,$$BROKEN RIVER$$,#{state_id_qld},-21.167011,148.50552), - ($$4757$$,$$CREDITON$$,#{state_id_qld},-21.167011,148.50552), - ($$4757$$,$$DALRYMPLE HEIGHTS$$,#{state_id_qld},-21.167011,148.50552), - ($$4757$$,$$EUNGELLA$$,#{state_id_qld},-21.167011,148.50552), - ($$4757$$,$$EUNGELLA DAM$$,#{state_id_qld},-21.167011,148.50552), - ($$4798$$,$$CALEN$$,#{state_id_qld},-20.897518,148.771151), - ($$4798$$,$$MENTMORE$$,#{state_id_qld},-20.897518,148.771151), - ($$4798$$,$$PINDI PINDI$$,#{state_id_qld},-20.897518,148.771151), - ($$4798$$,$$ST HELENS BEACH$$,#{state_id_qld},-20.897518,148.771151), - ($$4799$$,$$BLOOMSBURY$$,#{state_id_qld},-20.705298,148.595763), - ($$4799$$,$$MIDGE POINT$$,#{state_id_qld},-20.705298,148.595763), - ($$4800$$,$$ANDROMACHE$$,#{state_id_qld},-20.584521,148.420002), - ($$4800$$,$$BRANDY CREEK$$,#{state_id_qld},-20.584521,148.420002), - ($$4800$$,$$BREADALBANE$$,#{state_id_qld},-20.584521,148.420002), - ($$4800$$,$$CANNON VALLEY$$,#{state_id_qld},-20.584521,148.420002), - ($$4800$$,$$CAPE CONWAY$$,#{state_id_qld},-20.584521,148.420002), - ($$4800$$,$$CAPE GLOUCESTER$$,#{state_id_qld},-20.584521,148.420002), - ($$4800$$,$$CONWAY$$,#{state_id_qld},-20.584521,148.420002), - ($$4800$$,$$CONWAY BEACH$$,#{state_id_qld},-20.584521,148.420002), - ($$4800$$,$$CRYSTAL BROOK$$,#{state_id_qld},-20.584521,148.420002), - ($$4800$$,$$DINGO BEACH$$,#{state_id_qld},-20.584521,148.420002), - ($$4800$$,$$DITTMER$$,#{state_id_qld},-20.584521,148.420002), - ($$4800$$,$$FOXDALE$$,#{state_id_qld},-20.584521,148.420002), - ($$4800$$,$$GLEN ISLA$$,#{state_id_qld},-20.584521,148.420002), - ($$4800$$,$$GOORGANGA CREEK$$,#{state_id_qld},-20.584521,148.420002), - ($$4800$$,$$GOORGANGA PLAINS$$,#{state_id_qld},-20.584521,148.420002), - ($$4800$$,$$GREGORY RIVER$$,#{state_id_qld},-20.584521,148.420002), - ($$4800$$,$$GUNYARRA$$,#{state_id_qld},-20.584521,148.420002), - ($$4800$$,$$HAMILTON PLAINS$$,#{state_id_qld},-20.584521,148.420002), - ($$4800$$,$$HIDEAWAY BAY$$,#{state_id_qld},-20.584521,148.420002), - ($$4800$$,$$KELSEY CREEK$$,#{state_id_qld},-20.584521,148.420002), - ($$4800$$,$$LAGUNA QUAYS$$,#{state_id_qld},-20.584521,148.420002), - ($$4800$$,$$LAKE PROSERPINE$$,#{state_id_qld},-20.584521,148.420002), - ($$4800$$,$$LETHEBROOK$$,#{state_id_qld},-20.584521,148.420002), - ($$4800$$,$$MOUNT JULIAN$$,#{state_id_qld},-20.584521,148.420002), - ($$4800$$,$$MOUNT MARLOW$$,#{state_id_qld},-20.584521,148.420002), - ($$4800$$,$$MOUNT PLUTO$$,#{state_id_qld},-20.584521,148.420002), - ($$4800$$,$$MYRTLEVALE$$,#{state_id_qld},-20.584521,148.420002), - ($$4800$$,$$NORTH GREGORY$$,#{state_id_qld},-20.584521,148.420002), - ($$4800$$,$$PALM GROVE$$,#{state_id_qld},-20.584521,148.420002), - ($$4800$$,$$PAULS POCKET$$,#{state_id_qld},-20.584521,148.420002), - ($$4800$$,$$PRESTON$$,#{state_id_qld},-20.584521,148.420002), - ($$4800$$,$$PROSERPINE$$,#{state_id_qld},-20.584521,148.420002), - ($$4800$$,$$RIORDANVALE$$,#{state_id_qld},-20.584521,148.420002), - ($$4800$$,$$SILVER CREEK$$,#{state_id_qld},-20.584521,148.420002), - ($$4800$$,$$STRATHDICKIE$$,#{state_id_qld},-20.584521,148.420002), - ($$4800$$,$$SUGARLOAF$$,#{state_id_qld},-20.584521,148.420002), - ($$4800$$,$$THOOPARA$$,#{state_id_qld},-20.584521,148.420002), - ($$4800$$,$$WILSON BEACH$$,#{state_id_qld},-20.584521,148.420002), - ($$4801$$,$$HAYMAN ISLAND$$,#{state_id_qld},-20.049795,148.888223), - ($$4802$$,$$AIRLIE BEACH$$,#{state_id_qld},-20.267869,148.716223), - ($$4802$$,$$CANNONVALE$$,#{state_id_qld},-20.267869,148.716223), - ($$4802$$,$$FLAMETREE$$,#{state_id_qld},-20.267869,148.716223), - ($$4802$$,$$JUBILEE POCKET$$,#{state_id_qld},-20.267869,148.716223), - ($$4802$$,$$MANDALAY$$,#{state_id_qld},-20.267869,148.716223), - ($$4802$$,$$MOUNT ROOPER$$,#{state_id_qld},-20.267869,148.716223), - ($$4802$$,$$SHUTE HARBOUR$$,#{state_id_qld},-20.267869,148.716223), - ($$4802$$,$$WHITSUNDAYS$$,#{state_id_qld},-20.267869,148.716223), - ($$4802$$,$$WOODWARK$$,#{state_id_qld},-20.267869,148.716223), - ($$4803$$,$$HAMILTON ISLAND$$,#{state_id_qld},-20.354415,148.963683), - ($$4804$$,$$COLLINSVILLE$$,#{state_id_qld},-20.553361,147.845464), - ($$4804$$,$$MOUNT COOLON$$,#{state_id_qld},-20.553361,147.845464), - ($$4804$$,$$MOUNT WYATT$$,#{state_id_qld},-20.553361,147.845464), - ($$4804$$,$$NEWLANDS$$,#{state_id_qld},-20.553361,147.845464), - ($$4804$$,$$SPRINGLANDS$$,#{state_id_qld},-20.553361,147.845464), - ($$4805$$,$$BINBEE$$,#{state_id_qld},-20.382548,147.885867), - ($$4805$$,$$BOGIE$$,#{state_id_qld},-20.382548,147.885867), - ($$4805$$,$$BOWEN$$,#{state_id_qld},-20.382548,147.885867), - ($$4805$$,$$BRISK BAY$$,#{state_id_qld},-20.382548,147.885867), - ($$4805$$,$$DELTA$$,#{state_id_qld},-20.382548,147.885867), - ($$4805$$,$$GUMLU$$,#{state_id_qld},-20.382548,147.885867), - ($$4805$$,$$GUTHALUNGRA$$,#{state_id_qld},-20.382548,147.885867), - ($$4805$$,$$MERINDA$$,#{state_id_qld},-20.382548,147.885867), - ($$4805$$,$$QUEENS BEACH$$,#{state_id_qld},-20.382548,147.885867), - ($$4806$$,$$CARSTAIRS$$,#{state_id_qld},-19.669608,147.446511), - ($$4806$$,$$FREDERICKSFIELD$$,#{state_id_qld},-19.669608,147.446511), - ($$4806$$,$$HOME HILL$$,#{state_id_qld},-19.669608,147.446511), - ($$4806$$,$$INKERMAN$$,#{state_id_qld},-19.669608,147.446511), - ($$4806$$,$$KIRKNIE$$,#{state_id_qld},-19.669608,147.446511), - ($$4806$$,$$OSBORNE$$,#{state_id_qld},-19.669608,147.446511), - ($$4806$$,$$RANGEMORE$$,#{state_id_qld},-19.669608,147.446511), - ($$4806$$,$$WANGARATTA$$,#{state_id_qld},-19.669608,147.446511), - ($$4806$$,$$WUNJUNGA$$,#{state_id_qld},-19.669608,147.446511), - ($$4807$$,$$AIRDMILLAN$$,#{state_id_qld},-19.546861,147.443697), - ($$4807$$,$$AIRVILLE$$,#{state_id_qld},-19.546861,147.443697), - ($$4807$$,$$ALVA$$,#{state_id_qld},-19.546861,147.443697), - ($$4807$$,$$AYR$$,#{state_id_qld},-19.546861,147.443697), - ($$4807$$,$$CLARE$$,#{state_id_qld},-19.546861,147.443697), - ($$4807$$,$$CLAREDALE$$,#{state_id_qld},-19.546861,147.443697), - ($$4807$$,$$DALBEG$$,#{state_id_qld},-19.546861,147.443697), - ($$4807$$,$$EIGHT MILE CREEK$$,#{state_id_qld},-19.546861,147.443697), - ($$4807$$,$$JARVISFIELD$$,#{state_id_qld},-19.546861,147.443697), - ($$4807$$,$$MCDESME$$,#{state_id_qld},-19.546861,147.443697), - ($$4807$$,$$MILLAROO$$,#{state_id_qld},-19.546861,147.443697), - ($$4807$$,$$MONA PARK$$,#{state_id_qld},-19.546861,147.443697), - ($$4807$$,$$MOUNT KELLY$$,#{state_id_qld},-19.546861,147.443697), - ($$4807$$,$$MULGRAVE$$,#{state_id_qld},-19.546861,147.443697), - ($$4807$$,$$RITA ISLAND$$,#{state_id_qld},-19.546861,147.443697), - ($$4807$$,$$SWANS LAGOON$$,#{state_id_qld},-19.546861,147.443697), - ($$4808$$,$$BRANDON$$,#{state_id_qld},-19.554272,147.353705), - ($$4808$$,$$COLEVALE$$,#{state_id_qld},-19.554272,147.353705), - ($$4809$$,$$BARRATTA$$,#{state_id_qld},-19.531139,147.213564), - ($$4809$$,$$CROMARTY$$,#{state_id_qld},-19.531139,147.213564), - ($$4809$$,$$GIRU$$,#{state_id_qld},-19.531139,147.213564), - ($$4809$$,$$HORSESHOE LAGOON$$,#{state_id_qld},-19.531139,147.213564), - ($$4809$$,$$JERONA$$,#{state_id_qld},-19.531139,147.213564), - ($$4809$$,$$MOUNT SURROUND$$,#{state_id_qld},-19.531139,147.213564), - ($$4809$$,$$SHIRBOURNE$$,#{state_id_qld},-19.531139,147.213564), - ($$4809$$,$$UPPER HAUGHTON$$,#{state_id_qld},-19.531139,147.213564), - ($$4810$$,$$BELGIAN GARDENS$$,#{state_id_qld},-19.245827,146.795727), - ($$4810$$,$$CAPE CLEVELAND$$,#{state_id_qld},-19.245827,146.795727), - ($$4810$$,$$CASTLE HILL$$,#{state_id_qld},-19.245827,146.795727), - ($$4810$$,$$NORTH WARD$$,#{state_id_qld},-19.245827,146.795727), - ($$4810$$,$$PALLARENDA$$,#{state_id_qld},-19.245827,146.795727), - ($$4810$$,$$RAILWAY ESTATE$$,#{state_id_qld},-19.245827,146.795727), - ($$4810$$,$$ROWES BAY$$,#{state_id_qld},-19.245827,146.795727), - ($$4810$$,$$SHELLY BEACH$$,#{state_id_qld},-19.245827,146.795727), - ($$4810$$,$$SOUTH TOWNSVILLE$$,#{state_id_qld},-19.245827,146.795727), - ($$4810$$,$$TOWN COMMON$$,#{state_id_qld},-19.245827,146.795727), - ($$4810$$,$$TOWNSVILLE$$,#{state_id_qld},-19.245827,146.795727), - ($$4810$$,$$TOWNSVILLE CITY$$,#{state_id_qld},-19.245827,146.795727), - ($$4810$$,$$TOWNSVILLE DC$$,#{state_id_qld},-19.245827,146.795727), - ($$4810$$,$$TOWNSVILLE MC$$,#{state_id_qld},-19.245827,146.795727), - ($$4810$$,$$WEST END$$,#{state_id_qld},-19.245827,146.795727), - ($$4811$$,$$CLUDEN$$,#{state_id_qld},-19.316209,146.82358), - ($$4811$$,$$IDALIA$$,#{state_id_qld},-19.316209,146.82358), - ($$4811$$,$$JAMES COOK UNIVERSITY$$,#{state_id_qld},-19.316209,146.82358), - ($$4811$$,$$MOUNT STUART$$,#{state_id_qld},-19.316209,146.82358), - ($$4811$$,$$OAK VALLEY$$,#{state_id_qld},-19.316209,146.82358), - ($$4811$$,$$OONOONBA$$,#{state_id_qld},-19.316209,146.82358), - ($$4811$$,$$ROSENEATH$$,#{state_id_qld},-19.316209,146.82358), - ($$4811$$,$$STUART$$,#{state_id_qld},-19.316209,146.82358), - ($$4811$$,$$WULGURU$$,#{state_id_qld},-19.316209,146.82358), - ($$4812$$,$$CURRAJONG$$,#{state_id_qld},-19.274924,146.776217), - ($$4812$$,$$GULLIVER$$,#{state_id_qld},-19.274924,146.776217), - ($$4812$$,$$HERMIT PARK$$,#{state_id_qld},-19.274924,146.776217), - ($$4812$$,$$HYDE PARK$$,#{state_id_qld},-19.274924,146.776217), - ($$4812$$,$$HYDE PARK CASTLETOWN$$,#{state_id_qld},-19.274924,146.776217), - ($$4812$$,$$MUNDINGBURRA$$,#{state_id_qld},-19.274924,146.776217), - ($$4812$$,$$MYSTERTON$$,#{state_id_qld},-19.274924,146.776217), - ($$4812$$,$$PIMLICO$$,#{state_id_qld},-19.274924,146.776217), - ($$4812$$,$$ROSSLEA$$,#{state_id_qld},-19.274924,146.776217), - ($$4813$$,$$TOWNSVILLE MILPO$$,#{state_id_qld},0.0,0.0), - ($$4814$$,$$AITKENVALE$$,#{state_id_qld},-19.297714,146.764341), - ($$4814$$,$$AITKENVALE BC$$,#{state_id_qld},-19.297714,146.764341), - ($$4814$$,$$ANNANDALE$$,#{state_id_qld},-19.297714,146.764341), - ($$4814$$,$$CRANBROOK$$,#{state_id_qld},-19.297714,146.764341), - ($$4814$$,$$DOUGLAS$$,#{state_id_qld},-19.297714,146.764341), - ($$4814$$,$$GARBUTT$$,#{state_id_qld},-19.297714,146.764341), - ($$4814$$,$$GARBUTT BC$$,#{state_id_qld},-19.297714,146.764341), - ($$4814$$,$$GARBUTT EAST$$,#{state_id_qld},-19.297714,146.764341), - ($$4814$$,$$HEATLEY$$,#{state_id_qld},-19.297714,146.764341), - ($$4814$$,$$MOUNT LOUISA$$,#{state_id_qld},-19.297714,146.764341), - ($$4814$$,$$MURRAY$$,#{state_id_qld},-19.297714,146.764341), - ($$4814$$,$$THURINGOWA DC$$,#{state_id_qld},-19.297714,146.764341), - ($$4814$$,$$VINCENT$$,#{state_id_qld},-19.297714,146.764341), - ($$4815$$,$$CONDON$$,#{state_id_qld},-19.330397,146.727724), - ($$4815$$,$$GRANITE VALE$$,#{state_id_qld},-19.330397,146.727724), - ($$4815$$,$$GUMLOW$$,#{state_id_qld},-19.330397,146.727724), - ($$4815$$,$$KELSO$$,#{state_id_qld},-19.330397,146.727724), - ($$4815$$,$$PINNACLES$$,#{state_id_qld},-19.330397,146.727724), - ($$4815$$,$$RASMUSSEN$$,#{state_id_qld},-19.330397,146.727724), - ($$4816$$,$$ALLIGATOR CREEK$$,#{state_id_qld},-19.424915,146.947198), - ($$4816$$,$$BALGAL BEACH$$,#{state_id_qld},-19.424915,146.947198), - ($$4816$$,$$BARRINGHA$$,#{state_id_qld},-19.424915,146.947198), - ($$4816$$,$$BROOKHILL$$,#{state_id_qld},-19.424915,146.947198), - ($$4816$$,$$BUCHANAN$$,#{state_id_qld},-19.424915,146.947198), - ($$4816$$,$$CALCIUM$$,#{state_id_qld},-19.424915,146.947198), - ($$4816$$,$$CARRUCHAN$$,#{state_id_qld},-19.424915,146.947198), - ($$4816$$,$$CLEMANT$$,#{state_id_qld},-19.424915,146.947198), - ($$4816$$,$$CRIMEA$$,#{state_id_qld},-19.424915,146.947198), - ($$4816$$,$$CRYSTAL CREEK$$,#{state_id_qld},-19.424915,146.947198), - ($$4816$$,$$CUNGULLA$$,#{state_id_qld},-19.424915,146.947198), - ($$4816$$,$$ELLERBECK$$,#{state_id_qld},-19.424915,146.947198), - ($$4816$$,$$GREENVALE$$,#{state_id_qld},-19.424915,146.947198), - ($$4816$$,$$HIDDEN VALLEY$$,#{state_id_qld},-19.424915,146.947198), - ($$4816$$,$$HOMESTEAD$$,#{state_id_qld},-19.424915,146.947198), - ($$4816$$,$$JULAGO$$,#{state_id_qld},-19.424915,146.947198), - ($$4816$$,$$KENNEDY$$,#{state_id_qld},-19.424915,146.947198), - ($$4816$$,$$MACROSSAN$$,#{state_id_qld},-19.424915,146.947198), - ($$4816$$,$$MAJORS CREEK$$,#{state_id_qld},-19.424915,146.947198), - ($$4816$$,$$MALPAS-TRENTON$$,#{state_id_qld},-19.424915,146.947198), - ($$4816$$,$$MINGELA$$,#{state_id_qld},-19.424915,146.947198), - ($$4816$$,$$MOUNT ELLIOT$$,#{state_id_qld},-19.424915,146.947198), - ($$4816$$,$$MUTARNEE$$,#{state_id_qld},-19.424915,146.947198), - ($$4816$$,$$NELIA$$,#{state_id_qld},-19.424915,146.947198), - ($$4816$$,$$NOME$$,#{state_id_qld},-19.424915,146.947198), - ($$4816$$,$$PALM ISLAND$$,#{state_id_qld},-19.424915,146.947198), - ($$4816$$,$$PALUMA$$,#{state_id_qld},-19.424915,146.947198), - ($$4816$$,$$PENTLAND$$,#{state_id_qld},-19.424915,146.947198), - ($$4816$$,$$PRAIRIE$$,#{state_id_qld},-19.424915,146.947198), - ($$4816$$,$$RAVENSWOOD$$,#{state_id_qld},-19.424915,146.947198), - ($$4816$$,$$REID RIVER$$,#{state_id_qld},-19.424915,146.947198), - ($$4816$$,$$ROLLINGSTONE$$,#{state_id_qld},-19.424915,146.947198), - ($$4816$$,$$ROSS RIVER$$,#{state_id_qld},-19.424915,146.947198), - ($$4816$$,$$SAVANNAH$$,#{state_id_qld},-19.424915,146.947198), - ($$4816$$,$$SELLHEIM$$,#{state_id_qld},-19.424915,146.947198), - ($$4816$$,$$THE CAPE$$,#{state_id_qld},-19.424915,146.947198), - ($$4816$$,$$TOOMULLA$$,#{state_id_qld},-19.424915,146.947198), - ($$4816$$,$$TOONPAN$$,#{state_id_qld},-19.424915,146.947198), - ($$4816$$,$$TORRENS CREEK$$,#{state_id_qld},-19.424915,146.947198), - ($$4816$$,$$WOODSTOCK$$,#{state_id_qld},-19.424915,146.947198), - ($$4817$$,$$ALICE RIVER$$,#{state_id_qld},-19.32661,146.59971), - ($$4817$$,$$BOHLE PLAINS$$,#{state_id_qld},-19.32661,146.59971), - ($$4817$$,$$HERVEY RANGE$$,#{state_id_qld},-19.32661,146.59971), - ($$4817$$,$$KIRWAN$$,#{state_id_qld},-19.32661,146.59971), - ($$4817$$,$$RANGEWOOD$$,#{state_id_qld},-19.32661,146.59971), - ($$4817$$,$$THURINGOWA CENTRAL$$,#{state_id_qld},-19.32661,146.59971), - ($$4818$$,$$BEACH HOLM$$,#{state_id_qld},-19.202291,146.659747), - ($$4818$$,$$BLACK RIVER$$,#{state_id_qld},-19.202291,146.659747), - ($$4818$$,$$BLUE HILLS$$,#{state_id_qld},-19.202291,146.659747), - ($$4818$$,$$BLUEWATER$$,#{state_id_qld},-19.202291,146.659747), - ($$4818$$,$$BLUEWATER PARK$$,#{state_id_qld},-19.202291,146.659747), - ($$4818$$,$$BOHLE$$,#{state_id_qld},-19.202291,146.659747), - ($$4818$$,$$BURDELL$$,#{state_id_qld},-19.202291,146.659747), - ($$4818$$,$$BUSHLAND BEACH$$,#{state_id_qld},-19.202291,146.659747), - ($$4818$$,$$COSGROVE$$,#{state_id_qld},-19.202291,146.659747), - ($$4818$$,$$DEERAGUN$$,#{state_id_qld},-19.202291,146.659747), - ($$4818$$,$$JENSEN$$,#{state_id_qld},-19.202291,146.659747), - ($$4818$$,$$LYNAM$$,#{state_id_qld},-19.202291,146.659747), - ($$4818$$,$$MOUNT LOW$$,#{state_id_qld},-19.202291,146.659747), - ($$4818$$,$$MOUNT ST JOHN$$,#{state_id_qld},-19.202291,146.659747), - ($$4818$$,$$SAUNDERS BEACH$$,#{state_id_qld},-19.202291,146.659747), - ($$4818$$,$$SHAW$$,#{state_id_qld},-19.202291,146.659747), - ($$4818$$,$$TOOLAKEA$$,#{state_id_qld},-19.202291,146.659747), - ($$4818$$,$$YABULU$$,#{state_id_qld},-19.202291,146.659747), - ($$4819$$,$$ARCADIA$$,#{state_id_qld},-19.151037,146.86423), - ($$4819$$,$$FLORENCE BAY$$,#{state_id_qld},-19.151037,146.86423), - ($$4819$$,$$HORSESHOE BAY$$,#{state_id_qld},-19.151037,146.86423), - ($$4819$$,$$MAGNETIC ISLAND$$,#{state_id_qld},-19.151037,146.86423), - ($$4819$$,$$NELLY BAY$$,#{state_id_qld},-19.151037,146.86423), - ($$4819$$,$$PICNIC BAY$$,#{state_id_qld},-19.151037,146.86423), - ($$4819$$,$$WEST POINT$$,#{state_id_qld},-19.151037,146.86423), - ($$4820$$,$$ALABAMA HILL$$,#{state_id_qld},-20.082915,146.249367), - ($$4820$$,$$BALFES CREEK$$,#{state_id_qld},-20.082915,146.249367), - ($$4820$$,$$BASALT$$,#{state_id_qld},-20.082915,146.249367), - ($$4820$$,$$BLACK JACK$$,#{state_id_qld},-20.082915,146.249367), - ($$4820$$,$$BREDDAN$$,#{state_id_qld},-20.082915,146.249367), - ($$4820$$,$$BROUGHTON$$,#{state_id_qld},-20.082915,146.249367), - ($$4820$$,$$CAMPASPE$$,#{state_id_qld},-20.082915,146.249367), - ($$4820$$,$$CHARTERS TOWERS$$,#{state_id_qld},-20.082915,146.249367), - ($$4820$$,$$COLUMBIA$$,#{state_id_qld},-20.082915,146.249367), - ($$4820$$,$$DOTSWOOD$$,#{state_id_qld},-20.082915,146.249367), - ($$4820$$,$$GRAND SECRET$$,#{state_id_qld},-20.082915,146.249367), - ($$4820$$,$$LISSNER$$,#{state_id_qld},-20.082915,146.249367), - ($$4820$$,$$LLANARTH$$,#{state_id_qld},-20.082915,146.249367), - ($$4820$$,$$MILLCHESTER$$,#{state_id_qld},-20.082915,146.249367), - ($$4820$$,$$MOSMAN PARK$$,#{state_id_qld},-20.082915,146.249367), - ($$4820$$,$$QUEENTON$$,#{state_id_qld},-20.082915,146.249367), - ($$4820$$,$$RICHMOND HILL$$,#{state_id_qld},-20.082915,146.249367), - ($$4820$$,$$SEVENTY MILE$$,#{state_id_qld},-20.082915,146.249367), - ($$4820$$,$$SOUTHERN CROSS$$,#{state_id_qld},-20.082915,146.249367), - ($$4820$$,$$TOLL$$,#{state_id_qld},-20.082915,146.249367), - ($$4820$$,$$TOWERS HILL$$,#{state_id_qld},-20.082915,146.249367), - ($$4821$$,$$DUTTON RIVER$$,#{state_id_qld},-20.281399,143.87636), - ($$4821$$,$$HUGHENDEN$$,#{state_id_qld},-20.281399,143.87636), - ($$4821$$,$$PORCUPINE$$,#{state_id_qld},-20.281399,143.87636), - ($$4821$$,$$STAMFORD$$,#{state_id_qld},-20.281399,143.87636), - ($$4821$$,$$TANGORIN$$,#{state_id_qld},-20.281399,143.87636), - ($$4822$$,$$ALBION$$,#{state_id_qld},-21.50903,142.69317), - ($$4822$$,$$BELLFIELD$$,#{state_id_qld},-21.50903,142.69317), - ($$4822$$,$$BURLEIGH$$,#{state_id_qld},-21.50903,142.69317), - ($$4822$$,$$CAMBRIDGE$$,#{state_id_qld},-21.50903,142.69317), - ($$4822$$,$$MAXWELTON$$,#{state_id_qld},-21.50903,142.69317), - ($$4822$$,$$NONDA$$,#{state_id_qld},-21.50903,142.69317), - ($$4822$$,$$RICHMOND$$,#{state_id_qld},-21.50903,142.69317), - ($$4822$$,$$SAXBY$$,#{state_id_qld},-21.50903,142.69317), - ($$4822$$,$$VICTORIA VALE$$,#{state_id_qld},-21.50903,142.69317), - ($$4822$$,$$WOOLGAR$$,#{state_id_qld},-21.50903,142.69317), - ($$4823$$,$$CARPENTARIA$$,#{state_id_qld},-18.127441,139.959459), - ($$4823$$,$$JULIA CREEK$$,#{state_id_qld},-18.127441,139.959459), - ($$4823$$,$$KYNUNA$$,#{state_id_qld},-18.127441,139.959459), - ($$4823$$,$$MCKINLAY$$,#{state_id_qld},-18.127441,139.959459), - ($$4823$$,$$STOKES$$,#{state_id_qld},-18.127441,139.959459), - ($$4823$$,$$TALDORA$$,#{state_id_qld},-18.127441,139.959459), - ($$4823$$,$$WARBURTON$$,#{state_id_qld},-18.127441,139.959459), - ($$4824$$,$$CLONCURRY$$,#{state_id_qld},-20.704158,140.505319), - ($$4824$$,$$FOUR WAYS$$,#{state_id_qld},-20.704158,140.505319), - ($$4824$$,$$GIDYA$$,#{state_id_qld},-20.704158,140.505319), - ($$4824$$,$$KURIDALA$$,#{state_id_qld},-20.704158,140.505319), - ($$4824$$,$$OORINDI$$,#{state_id_qld},-20.704158,140.505319), - ($$4824$$,$$THREE RIVERS$$,#{state_id_qld},-20.704158,140.505319), - ($$4825$$,$$ALEXANDRIA$$,#{state_id_qld},-21.164318,149.098918), - ($$4825$$,$$ALPURRURULAM$$,#{state_id_nt},-21.164318,149.098918), - ($$4825$$,$$BARKLY$$,#{state_id_qld},-21.164318,149.098918), - ($$4825$$,$$BREAKAWAY$$,#{state_id_qld},-21.164318,149.098918), - ($$4825$$,$$BUCKINGHAM$$,#{state_id_qld},-21.164318,149.098918), - ($$4825$$,$$CARRANDOTTA$$,#{state_id_qld},-21.164318,149.098918), - ($$4825$$,$$DAJARRA$$,#{state_id_qld},-21.164318,149.098918), - ($$4825$$,$$DUCHESS$$,#{state_id_qld},-21.164318,149.098918), - ($$4825$$,$$FIELDING$$,#{state_id_qld},-21.164318,149.098918), - ($$4825$$,$$FISHER$$,#{state_id_qld},-21.164318,149.098918), - ($$4825$$,$$GEORGINA$$,#{state_id_qld},-21.164318,149.098918), - ($$4825$$,$$GUNPOWDER$$,#{state_id_qld},-21.164318,149.098918), - ($$4825$$,$$HAPPY VALLEY$$,#{state_id_qld},-21.164318,149.098918), - ($$4825$$,$$HEALY$$,#{state_id_qld},-21.164318,149.098918), - ($$4825$$,$$KALKADOON$$,#{state_id_qld},-21.164318,149.098918), - ($$4825$$,$$LANSKEY$$,#{state_id_qld},-21.164318,149.098918), - ($$4825$$,$$LAWN HILL$$,#{state_id_qld},-21.164318,149.098918), - ($$4825$$,$$MENZIES$$,#{state_id_qld},-21.164318,149.098918), - ($$4825$$,$$MICA CREEK$$,#{state_id_qld},-21.164318,149.098918), - ($$4825$$,$$MILES END$$,#{state_id_qld},-21.164318,149.098918), - ($$4825$$,$$MORNINGTON$$,#{state_id_qld},-21.164318,149.098918), - ($$4825$$,$$MOUNT ISA$$,#{state_id_qld},-21.164318,149.098918), - ($$4825$$,$$MOUNT ISA CITY$$,#{state_id_qld},-21.164318,149.098918), - ($$4825$$,$$MOUNT ISA DC$$,#{state_id_qld},-21.164318,149.098918), - ($$4825$$,$$MOUNT ISA EAST$$,#{state_id_qld},-21.164318,149.098918), - ($$4825$$,$$PARKSIDE$$,#{state_id_qld},-21.164318,149.098918), - ($$4825$$,$$PIONEER$$,#{state_id_qld},-21.164318,149.098918), - ($$4825$$,$$PITURIE$$,#{state_id_qld},-21.164318,149.098918), - ($$4825$$,$$RANKEN$$,#{state_id_nt},-21.164318,149.098918), - ($$4825$$,$$RYAN$$,#{state_id_qld},-21.164318,149.098918), - ($$4825$$,$$SOLDIERS HILL$$,#{state_id_qld},-21.164318,149.098918), - ($$4825$$,$$SPREADBOROUGH$$,#{state_id_qld},-21.164318,149.098918), - ($$4825$$,$$SUNSET$$,#{state_id_qld},-21.164318,149.098918), - ($$4825$$,$$THE GAP$$,#{state_id_qld},-21.164318,149.098918), - ($$4825$$,$$THE MONUMENT$$,#{state_id_qld},-21.164318,149.098918), - ($$4825$$,$$TOWNVIEW$$,#{state_id_qld},-21.164318,149.098918), - ($$4825$$,$$WAVERLEY$$,#{state_id_qld},-21.164318,149.098918), - ($$4825$$,$$WINSTON$$,#{state_id_qld},-21.164318,149.098918), - ($$4828$$,$$CAMOOWEAL$$,#{state_id_qld},-19.921773,138.119119), - ($$4829$$,$$AMAROO$$,#{state_id_qld},-23.393865,139.673121), - ($$4829$$,$$BEDOURIE$$,#{state_id_qld},-23.393865,139.673121), - ($$4829$$,$$BOULIA$$,#{state_id_qld},-23.393865,139.673121), - ($$4829$$,$$MIN MIN$$,#{state_id_qld},-23.393865,139.673121), - ($$4829$$,$$STURT$$,#{state_id_qld},-23.393865,139.673121), - ($$4829$$,$$TOKO$$,#{state_id_qld},-23.393865,139.673121), - ($$4829$$,$$WARENDA$$,#{state_id_qld},-23.393865,139.673121), - ($$4829$$,$$WILLS$$,#{state_id_qld},-23.393865,139.673121), - ($$4830$$,$$BURKETOWN$$,#{state_id_qld},-17.741706,139.547865), - ($$4830$$,$$DOOMADGEE$$,#{state_id_qld},-17.741706,139.547865), - ($$4830$$,$$GREGORY$$,#{state_id_qld},-17.741706,139.547865), - ($$4830$$,$$GREGORY DOWNS$$,#{state_id_qld},-17.741706,139.547865), - ($$4830$$,$$NICHOLSON$$,#{state_id_qld},-17.741706,139.547865), - ($$4849$$,$$CARDWELL$$,#{state_id_qld},-18.265652,146.027929), - ($$4849$$,$$DAMPER CREEK$$,#{state_id_qld},-18.265652,146.027929), - ($$4849$$,$$HINCHINBROOK$$,#{state_id_qld},-18.265652,146.027929), - ($$4849$$,$$LUMHOLTZ$$,#{state_id_qld},-18.265652,146.027929), - ($$4849$$,$$RUNGOO$$,#{state_id_qld},-18.265652,146.027929), - ($$4850$$,$$ABERGOWRIE$$,#{state_id_qld},-18.474994,145.885791), - ($$4850$$,$$ALLINGHAM$$,#{state_id_qld},-18.474994,145.885791), - ($$4850$$,$$BAMBAROO$$,#{state_id_qld},-18.474994,145.885791), - ($$4850$$,$$BEMERSIDE$$,#{state_id_qld},-18.474994,145.885791), - ($$4850$$,$$BLACKROCK$$,#{state_id_qld},-18.474994,145.885791), - ($$4850$$,$$BRAEMEADOWS$$,#{state_id_qld},-18.474994,145.885791), - ($$4850$$,$$COOLBIE$$,#{state_id_qld},-18.474994,145.885791), - ($$4850$$,$$CORDELIA$$,#{state_id_qld},-18.474994,145.885791), - ($$4850$$,$$DALRYMPLE CREEK$$,#{state_id_qld},-18.474994,145.885791), - ($$4850$$,$$FORESTHOME$$,#{state_id_qld},-18.474994,145.885791), - ($$4850$$,$$FORREST BEACH$$,#{state_id_qld},-18.474994,145.885791), - ($$4850$$,$$GAIRLOCH$$,#{state_id_qld},-18.474994,145.885791), - ($$4850$$,$$GARRAWALT$$,#{state_id_qld},-18.474994,145.885791), - ($$4850$$,$$HALIFAX$$,#{state_id_qld},-18.474994,145.885791), - ($$4850$$,$$HAWKINS CREEK$$,#{state_id_qld},-18.474994,145.885791), - ($$4850$$,$$HELENS HILL$$,#{state_id_qld},-18.474994,145.885791), - ($$4850$$,$$INGHAM$$,#{state_id_qld},-18.474994,145.885791), - ($$4850$$,$$LANNERCOST$$,#{state_id_qld},-18.474994,145.885791), - ($$4850$$,$$LONG POCKET$$,#{state_id_qld},-18.474994,145.885791), - ($$4850$$,$$LUCINDA$$,#{state_id_qld},-18.474994,145.885791), - ($$4850$$,$$MACKNADE$$,#{state_id_qld},-18.474994,145.885791), - ($$4850$$,$$MOUNT FOX$$,#{state_id_qld},-18.474994,145.885791), - ($$4850$$,$$ORIENT$$,#{state_id_qld},-18.474994,145.885791), - ($$4850$$,$$PEACOCK SIDING$$,#{state_id_qld},-18.474994,145.885791), - ($$4850$$,$$TAYLORS BEACH$$,#{state_id_qld},-18.474994,145.885791), - ($$4850$$,$$TOOBANNA$$,#{state_id_qld},-18.474994,145.885791), - ($$4850$$,$$TREBONNE$$,#{state_id_qld},-18.474994,145.885791), - ($$4850$$,$$UPPER STONE$$,#{state_id_qld},-18.474994,145.885791), - ($$4850$$,$$VALLEY OF LAGOONS$$,#{state_id_qld},-18.474994,145.885791), - ($$4850$$,$$VICTORIA ESTATE$$,#{state_id_qld},-18.474994,145.885791), - ($$4850$$,$$VICTORIA PLANTATION$$,#{state_id_qld},-18.474994,145.885791), - ($$4850$$,$$WALLAMAN$$,#{state_id_qld},-18.474994,145.885791), - ($$4850$$,$$WHARPS$$,#{state_id_qld},-18.474994,145.885791), - ($$4850$$,$$YURUGA$$,#{state_id_qld},-18.474994,145.885791), - ($$4852$$,$$BINGIL BAY$$,#{state_id_qld},-17.830091,146.09996), - ($$4852$$,$$CARMOO$$,#{state_id_qld},-17.830091,146.09996), - ($$4852$$,$$DJIRU$$,#{state_id_qld},-17.830091,146.09996), - ($$4852$$,$$DUNK$$,#{state_id_qld},-17.830091,146.09996), - ($$4852$$,$$GARNERS BEACH$$,#{state_id_qld},-17.830091,146.09996), - ($$4852$$,$$MIDGEREE BAR$$,#{state_id_qld},-17.830091,146.09996), - ($$4852$$,$$MISSION BEACH$$,#{state_id_qld},-17.830091,146.09996), - ($$4852$$,$$SOUTH MISSION BEACH$$,#{state_id_qld},-17.830091,146.09996), - ($$4852$$,$$TAM O'SHANTER$$,#{state_id_qld},-17.830091,146.09996), - ($$4852$$,$$WONGALING BEACH$$,#{state_id_qld},-17.830091,146.09996), - ($$4854$$,$$BILYANA$$,#{state_id_qld},-18.119359,145.913114), - ($$4854$$,$$BIRKALLA$$,#{state_id_qld},-18.119359,145.913114), - ($$4854$$,$$BULGUN$$,#{state_id_qld},-18.119359,145.913114), - ($$4854$$,$$CARDSTONE$$,#{state_id_qld},-18.119359,145.913114), - ($$4854$$,$$DINGO POCKET$$,#{state_id_qld},-18.119359,145.913114), - ($$4854$$,$$DJARAWONG$$,#{state_id_qld},-18.119359,145.913114), - ($$4854$$,$$EAST FELUGA$$,#{state_id_qld},-18.119359,145.913114), - ($$4854$$,$$EURAMO$$,#{state_id_qld},-18.119359,145.913114), - ($$4854$$,$$FELUGA$$,#{state_id_qld},-18.119359,145.913114), - ($$4854$$,$$HULL HEADS$$,#{state_id_qld},-18.119359,145.913114), - ($$4854$$,$$JARRA CREEK$$,#{state_id_qld},-18.119359,145.913114), - ($$4854$$,$$KOOROOMOOL$$,#{state_id_qld},-18.119359,145.913114), - ($$4854$$,$$LOWER TULLY$$,#{state_id_qld},-18.119359,145.913114), - ($$4854$$,$$MERRYBURN$$,#{state_id_qld},-18.119359,145.913114), - ($$4854$$,$$MIDGENOO$$,#{state_id_qld},-18.119359,145.913114), - ($$4854$$,$$MOUNT MACKAY$$,#{state_id_qld},-18.119359,145.913114), - ($$4854$$,$$MUNRO PLAINS$$,#{state_id_qld},-18.119359,145.913114), - ($$4854$$,$$MURRAY UPPER$$,#{state_id_qld},-18.119359,145.913114), - ($$4854$$,$$MURRIGAL$$,#{state_id_qld},-18.119359,145.913114), - ($$4854$$,$$ROCKINGHAM$$,#{state_id_qld},-18.119359,145.913114), - ($$4854$$,$$SILKY OAK$$,#{state_id_qld},-18.119359,145.913114), - ($$4854$$,$$TULLY$$,#{state_id_qld},-18.119359,145.913114), - ($$4854$$,$$TULLY HEADS$$,#{state_id_qld},-18.119359,145.913114), - ($$4854$$,$$WALTER HILL$$,#{state_id_qld},-18.119359,145.913114), - ($$4854$$,$$WARRAMI$$,#{state_id_qld},-18.119359,145.913114), - ($$4855$$,$$DAVESON$$,#{state_id_qld},-17.786658,146.021722), - ($$4855$$,$$EL ARISH$$,#{state_id_qld},-17.786658,146.021722), - ($$4855$$,$$FRIDAY POCKET$$,#{state_id_qld},-17.786658,146.021722), - ($$4855$$,$$GRANADILLA$$,#{state_id_qld},-17.786658,146.021722), - ($$4855$$,$$JAFFA$$,#{state_id_qld},-17.786658,146.021722), - ($$4855$$,$$MAADI$$,#{state_id_qld},-17.786658,146.021722), - ($$4855$$,$$MARIA CREEKS$$,#{state_id_qld},-17.786658,146.021722), - ($$4855$$,$$SHELL POCKET$$,#{state_id_qld},-17.786658,146.021722), - ($$4856$$,$$GOOLBOO$$,#{state_id_qld},-17.719188,146.026968), - ($$4856$$,$$JAPOONVALE$$,#{state_id_qld},-17.719188,146.026968), - ($$4856$$,$$MCCUTCHEON$$,#{state_id_qld},-17.719188,146.026968), - ($$4856$$,$$NO 4 BRANCH$$,#{state_id_qld},-17.719188,146.026968), - ($$4856$$,$$NO 5 BRANCH$$,#{state_id_qld},-17.719188,146.026968), - ($$4856$$,$$SILKWOOD$$,#{state_id_qld},-17.719188,146.026968), - ($$4856$$,$$WALTER LEVER ESTATE$$,#{state_id_qld},-17.719188,146.026968), - ($$4857$$,$$SILKWOOD EAST$$,#{state_id_qld},-17.747122,146.019451), - ($$4858$$,$$COMOON LOOP$$,#{state_id_qld},-17.566621,146.035946), - ($$4858$$,$$ETTY BAY$$,#{state_id_qld},-17.566621,146.035946), - ($$4858$$,$$MARTYVILLE$$,#{state_id_qld},-17.566621,146.035946), - ($$4858$$,$$MOURILYAN$$,#{state_id_qld},-17.566621,146.035946), - ($$4858$$,$$MOURILYAN HARBOUR$$,#{state_id_qld},-17.566621,146.035946), - ($$4858$$,$$NEW HARBOURLINE$$,#{state_id_qld},-17.566621,146.035946), - ($$4859$$,$$NO 6 BRANCH$$,#{state_id_qld},-17.596413,145.971405), - ($$4859$$,$$SOUTH JOHNSTONE$$,#{state_id_qld},-17.596413,145.971405), - ($$4860$$,$$BAMBOO CREEK$$,#{state_id_qld},-17.56422,145.996277), - ($$4860$$,$$BELVEDERE$$,#{state_id_qld},-17.56422,145.996277), - ($$4860$$,$$COCONUTS$$,#{state_id_qld},-17.56422,145.996277), - ($$4860$$,$$COOROO LANDS$$,#{state_id_qld},-17.56422,145.996277), - ($$4860$$,$$COORUMBA$$,#{state_id_qld},-17.56422,145.996277), - ($$4860$$,$$COQUETTE POINT$$,#{state_id_qld},-17.56422,145.996277), - ($$4860$$,$$CULLINANE$$,#{state_id_qld},-17.56422,145.996277), - ($$4860$$,$$DARADGEE$$,#{state_id_qld},-17.56422,145.996277), - ($$4860$$,$$EAST INNISFAIL$$,#{state_id_qld},-17.56422,145.996277), - ($$4860$$,$$EAST PALMERSTON$$,#{state_id_qld},-17.56422,145.996277), - ($$4860$$,$$EATON$$,#{state_id_qld},-17.56422,145.996277), - ($$4860$$,$$EUBENANGEE$$,#{state_id_qld},-17.56422,145.996277), - ($$4860$$,$$FITZGERALD CREEK$$,#{state_id_qld},-17.56422,145.996277), - ($$4860$$,$$FLYING FISH POINT$$,#{state_id_qld},-17.56422,145.996277), - ($$4860$$,$$GARRADUNGA$$,#{state_id_qld},-17.56422,145.996277), - ($$4860$$,$$GOONDI$$,#{state_id_qld},-17.56422,145.996277), - ($$4860$$,$$GOONDI BEND$$,#{state_id_qld},-17.56422,145.996277), - ($$4860$$,$$GOONDI HILL$$,#{state_id_qld},-17.56422,145.996277), - ($$4860$$,$$HUDSON$$,#{state_id_qld},-17.56422,145.996277), - ($$4860$$,$$INNISFAIL$$,#{state_id_qld},-17.56422,145.996277), - ($$4860$$,$$INNISFAIL ESTATE$$,#{state_id_qld},-17.56422,145.996277), - ($$4860$$,$$JUBILEE HEIGHTS$$,#{state_id_qld},-17.56422,145.996277), - ($$4860$$,$$MIGHELL$$,#{state_id_qld},-17.56422,145.996277), - ($$4860$$,$$MUNDOO$$,#{state_id_qld},-17.56422,145.996277), - ($$4860$$,$$NERADA$$,#{state_id_qld},-17.56422,145.996277), - ($$4860$$,$$NGATJAN$$,#{state_id_qld},-17.56422,145.996277), - ($$4860$$,$$O'BRIENS HILL$$,#{state_id_qld},-17.56422,145.996277), - ($$4860$$,$$PALMERSTON$$,#{state_id_qld},-17.56422,145.996277), - ($$4860$$,$$PIN GIN HILL$$,#{state_id_qld},-17.56422,145.996277), - ($$4860$$,$$SOUTH INNISFAIL$$,#{state_id_qld},-17.56422,145.996277), - ($$4860$$,$$STOTERS HILL$$,#{state_id_qld},-17.56422,145.996277), - ($$4860$$,$$SUNDOWN$$,#{state_id_qld},-17.56422,145.996277), - ($$4860$$,$$UPPER DARADGEE$$,#{state_id_qld},-17.56422,145.996277), - ($$4860$$,$$VASA VIEWS$$,#{state_id_qld},-17.56422,145.996277), - ($$4860$$,$$WANJURU$$,#{state_id_qld},-17.56422,145.996277), - ($$4860$$,$$WEBB$$,#{state_id_qld},-17.56422,145.996277), - ($$4860$$,$$WOOROONOORAN$$,#{state_id_qld},-17.56422,145.996277), - ($$4861$$,$$BABINDA$$,#{state_id_qld},-17.344418,145.924815), - ($$4861$$,$$BARTLE FRERE$$,#{state_id_qld},-17.344418,145.924815), - ($$4861$$,$$EAST RUSSELL$$,#{state_id_qld},-17.344418,145.924815), - ($$4865$$,$$GOLDSBOROUGH$$,#{state_id_qld},-17.152995,145.743914), - ($$4865$$,$$GORDONVALE$$,#{state_id_qld},-17.152995,145.743914), - ($$4865$$,$$GREEN HILL$$,#{state_id_qld},-17.152995,145.743914), - ($$4865$$,$$KAMMA$$,#{state_id_qld},-17.152995,145.743914), - ($$4865$$,$$LITTLE MULGRAVE$$,#{state_id_qld},-17.152995,145.743914), - ($$4865$$,$$PACKERS CAMP$$,#{state_id_qld},-17.152995,145.743914), - ($$4868$$,$$BAYVIEW HEIGHTS$$,#{state_id_qld},-16.962827,145.730429), - ($$4868$$,$$MOUNT SHERIDAN$$,#{state_id_qld},-16.962827,145.730429), - ($$4868$$,$$WHITE ROCK$$,#{state_id_qld},-16.962827,145.730429), - ($$4868$$,$$WOREE$$,#{state_id_qld},-16.962827,145.730429), - ($$4869$$,$$BENTLEY PARK$$,#{state_id_qld},-17.004072,145.738835), - ($$4869$$,$$EDMONTON$$,#{state_id_qld},-17.004072,145.738835), - ($$4869$$,$$MOUNT PETER$$,#{state_id_qld},-17.004072,145.738835), - ($$4869$$,$$WRIGHTS CREEK$$,#{state_id_qld},-17.004072,145.738835), - ($$4870$$,$$AEROGLEN$$,#{state_id_qld},-16.883938,145.746732), - ($$4870$$,$$BARRON GORGE$$,#{state_id_qld},-16.883938,145.746732), - ($$4870$$,$$BRINSMEAD$$,#{state_id_qld},-16.883938,145.746732), - ($$4870$$,$$BUNGALOW$$,#{state_id_qld},-16.883938,145.746732), - ($$4870$$,$$CAIRNS$$,#{state_id_qld},-16.883938,145.746732), - ($$4870$$,$$CAIRNS CENTRAL$$,#{state_id_qld},-16.883938,145.746732), - ($$4870$$,$$CAIRNS CITY$$,#{state_id_qld},-16.883938,145.746732), - ($$4870$$,$$CAIRNS DC$$,#{state_id_qld},-16.883938,145.746732), - ($$4870$$,$$CAIRNS MC$$,#{state_id_qld},-16.883938,145.746732), - ($$4870$$,$$CAIRNS NORTH$$,#{state_id_qld},-16.883938,145.746732), - ($$4870$$,$$CAIRNS ORCHID PLAZA$$,#{state_id_qld},-16.883938,145.746732), - ($$4870$$,$$EARLVILLE$$,#{state_id_qld},-16.883938,145.746732), - ($$4870$$,$$EARLVILLE BC$$,#{state_id_qld},-16.883938,145.746732), - ($$4870$$,$$EDGE HILL$$,#{state_id_qld},-16.883938,145.746732), - ($$4870$$,$$FRESHWATER$$,#{state_id_qld},-16.883938,145.746732), - ($$4870$$,$$KAMERUNGA$$,#{state_id_qld},-16.883938,145.746732), - ($$4870$$,$$KANIMBLA$$,#{state_id_qld},-16.883938,145.746732), - ($$4870$$,$$LAMB RANGE$$,#{state_id_qld},-16.883938,145.746732), - ($$4870$$,$$MANOORA$$,#{state_id_qld},-16.883938,145.746732), - ($$4870$$,$$MANUNDA$$,#{state_id_qld},-16.883938,145.746732), - ($$4870$$,$$MARTYNVALE$$,#{state_id_qld},-16.883938,145.746732), - ($$4870$$,$$MOOROOBOOL$$,#{state_id_qld},-16.883938,145.746732), - ($$4870$$,$$NORTH CAIRNS$$,#{state_id_qld},-16.883938,145.746732), - ($$4870$$,$$PARRAMATTA PARK$$,#{state_id_qld},-16.883938,145.746732), - ($$4870$$,$$PORTSMITH$$,#{state_id_qld},-16.883938,145.746732), - ($$4870$$,$$REDLYNCH$$,#{state_id_qld},-16.883938,145.746732), - ($$4870$$,$$STRATFORD$$,#{state_id_qld},-16.883938,145.746732), - ($$4870$$,$$WESTCOURT$$,#{state_id_qld},-16.883938,145.746732), - ($$4870$$,$$WHITFIELD$$,#{state_id_qld},-16.883938,145.746732), - ($$4871$$,$$ABINGDON DOWNS$$,#{state_id_qld},-17.608738,143.185168), - ($$4871$$,$$ALMADEN$$,#{state_id_qld},-17.608738,143.185168), - ($$4871$$,$$ALOOMBA$$,#{state_id_qld},-17.608738,143.185168), - ($$4871$$,$$AMBER$$,#{state_id_qld},-17.608738,143.185168), - ($$4871$$,$$ARBOUIN$$,#{state_id_qld},-17.608738,143.185168), - ($$4871$$,$$ARCHER RIVER$$,#{state_id_qld},-17.608738,143.185168), - ($$4871$$,$$AURUKUN$$,#{state_id_qld},-17.608738,143.185168), - ($$4871$$,$$BASILISK$$,#{state_id_qld},-17.608738,143.185168), - ($$4871$$,$$BELLENDEN KER$$,#{state_id_qld},-17.608738,143.185168), - ($$4871$$,$$BELLEVUE$$,#{state_id_qld},-17.608738,143.185168), - ($$4871$$,$$BLACKBULL$$,#{state_id_qld},-17.608738,143.185168), - ($$4871$$,$$BOLWARRA$$,#{state_id_qld},-17.608738,143.185168), - ($$4871$$,$$BOMBEETA$$,#{state_id_qld},-17.608738,143.185168), - ($$4871$$,$$BOOGAN$$,#{state_id_qld},-17.608738,143.185168), - ($$4871$$,$$BRAMSTON BEACH$$,#{state_id_qld},-17.608738,143.185168), - ($$4871$$,$$BULLERINGA$$,#{state_id_qld},-17.608738,143.185168), - ($$4871$$,$$CAMP CREEK$$,#{state_id_qld},-17.608738,143.185168), - ($$4871$$,$$CHILLAGOE$$,#{state_id_qld},-17.608738,143.185168), - ($$4871$$,$$CLARAVILLE$$,#{state_id_qld},-17.608738,143.185168), - ($$4871$$,$$COEN$$,#{state_id_qld},-17.608738,143.185168), - ($$4871$$,$$CONJUBOY$$,#{state_id_qld},-17.608738,143.185168), - ($$4871$$,$$CORALIE$$,#{state_id_qld},-17.608738,143.185168), - ($$4871$$,$$COWLEY$$,#{state_id_qld},-17.608738,143.185168), - ($$4871$$,$$COWLEY BEACH$$,#{state_id_qld},-17.608738,143.185168), - ($$4871$$,$$COWLEY CREEK$$,#{state_id_qld},-17.608738,143.185168), - ($$4871$$,$$CROYDON$$,#{state_id_qld},-17.608738,143.185168), - ($$4871$$,$$CRYSTALBROOK$$,#{state_id_qld},-17.608738,143.185168), - ($$4871$$,$$CURRAJAH$$,#{state_id_qld},-17.608738,143.185168), - ($$4871$$,$$DEERAL$$,#{state_id_qld},-17.608738,143.185168), - ($$4871$$,$$DESAILLY$$,#{state_id_qld},-17.608738,143.185168), - ($$4871$$,$$DIXIE$$,#{state_id_qld},-17.608738,143.185168), - ($$4871$$,$$EAST CREEK$$,#{state_id_qld},-17.608738,143.185168), - ($$4871$$,$$EAST TRINITY$$,#{state_id_qld},-17.608738,143.185168), - ($$4871$$,$$EDWARD RIVER$$,#{state_id_qld},-17.608738,143.185168), - ($$4871$$,$$EINASLEIGH$$,#{state_id_qld},-17.608738,143.185168), - ($$4871$$,$$ESMERALDA$$,#{state_id_qld},-17.608738,143.185168), - ($$4871$$,$$FISHERY FALLS$$,#{state_id_qld},-17.608738,143.185168), - ($$4871$$,$$FITZROY ISLAND$$,#{state_id_qld},-17.608738,143.185168), - ($$4871$$,$$FORSAYTH$$,#{state_id_qld},-17.608738,143.185168), - ($$4871$$,$$FOSSILBROOK$$,#{state_id_qld},-17.608738,143.185168), - ($$4871$$,$$GAMBOOLA$$,#{state_id_qld},-17.608738,143.185168), - ($$4871$$,$$GEORGETOWN$$,#{state_id_qld},-17.608738,143.185168), - ($$4871$$,$$GERMANTOWN$$,#{state_id_qld},-17.608738,143.185168), - ($$4871$$,$$GILBERT RIVER$$,#{state_id_qld},-17.608738,143.185168), - ($$4871$$,$$GLEN BOUGHTON$$,#{state_id_qld},-17.608738,143.185168), - ($$4871$$,$$GREEN ISLAND$$,#{state_id_qld},-17.608738,143.185168), - ($$4871$$,$$GROGANVILLE$$,#{state_id_qld},-17.608738,143.185168), - ($$4871$$,$$GUNUNA$$,#{state_id_qld},-17.608738,143.185168), - ($$4871$$,$$HIGHBURY$$,#{state_id_qld},-17.608738,143.185168), - ($$4871$$,$$HOLROYD RIVER$$,#{state_id_qld},-17.608738,143.185168), - ($$4871$$,$$HURRICANE$$,#{state_id_qld},-17.608738,143.185168), - ($$4871$$,$$JULATTEN$$,#{state_id_qld},-17.608738,143.185168), - ($$4871$$,$$KARRON$$,#{state_id_qld},-17.608738,143.185168), - ($$4871$$,$$KOWANYAMA$$,#{state_id_qld},-17.608738,143.185168), - ($$4871$$,$$KURRIMINE BEACH$$,#{state_id_qld},-17.608738,143.185168), - ($$4871$$,$$LAKEFIELD$$,#{state_id_qld},-17.608738,143.185168), - ($$4871$$,$$LAKELAND$$,#{state_id_qld},-17.608738,143.185168), - ($$4871$$,$$LAKELAND DOWNS$$,#{state_id_qld},-17.608738,143.185168), - ($$4871$$,$$LAURA$$,#{state_id_qld},-17.608738,143.185168), - ($$4871$$,$$LIZARD$$,#{state_id_qld},-17.608738,143.185168), - ($$4871$$,$$LOCKHART$$,#{state_id_qld},-17.608738,143.185168), - ($$4871$$,$$LOWER COWLEY$$,#{state_id_qld},-17.608738,143.185168), - ($$4871$$,$$LYNDHURST$$,#{state_id_qld},-17.608738,143.185168), - ($$4871$$,$$LYNDSIDE$$,#{state_id_qld},-17.608738,143.185168), - ($$4871$$,$$MACALISTER RANGE$$,#{state_id_qld},-17.608738,143.185168), - ($$4871$$,$$MARAMIE$$,#{state_id_qld},-17.608738,143.185168), - ($$4871$$,$$MENA CREEK$$,#{state_id_qld},-17.608738,143.185168), - ($$4871$$,$$MIRIWINNI$$,#{state_id_qld},-17.608738,143.185168), - ($$4871$$,$$MORESBY$$,#{state_id_qld},-17.608738,143.185168), - ($$4871$$,$$MORNINGTON ISLAND$$,#{state_id_qld},-17.608738,143.185168), - ($$4871$$,$$MOUNT CARBINE$$,#{state_id_qld},-17.608738,143.185168), - ($$4871$$,$$MOUNT MOLLOY$$,#{state_id_qld},-17.608738,143.185168), - ($$4871$$,$$MOUNT MULGRAVE$$,#{state_id_qld},-17.608738,143.185168), - ($$4871$$,$$MOUNT MULLIGAN$$,#{state_id_qld},-17.608738,143.185168), - ($$4871$$,$$MOUNT SURPRISE$$,#{state_id_qld},-17.608738,143.185168), - ($$4871$$,$$NORTHHEAD$$,#{state_id_qld},-17.608738,143.185168), - ($$4871$$,$$NYCHUM$$,#{state_id_qld},-17.608738,143.185168), - ($$4871$$,$$PALMER$$,#{state_id_qld},-17.608738,143.185168), - ($$4871$$,$$PETFORD$$,#{state_id_qld},-17.608738,143.185168), - ($$4871$$,$$PORMPURAAW$$,#{state_id_qld},-17.608738,143.185168), - ($$4871$$,$$PORTLAND ROADS$$,#{state_id_qld},-17.608738,143.185168), - ($$4871$$,$$RAVENSWORTH$$,#{state_id_qld},-17.608738,143.185168), - ($$4871$$,$$RED RIVER$$,#{state_id_qld},-17.608738,143.185168), - ($$4871$$,$$ROOKWOOD$$,#{state_id_qld},-17.608738,143.185168), - ($$4871$$,$$SANDY POCKET$$,#{state_id_qld},-17.608738,143.185168), - ($$4871$$,$$SOUTH WELLESLEY ISLANDS$$,#{state_id_qld},-17.608738,143.185168), - ($$4871$$,$$SOUTHEDGE$$,#{state_id_qld},-17.608738,143.185168), - ($$4871$$,$$SPRINGFIELD$$,#{state_id_qld},-17.608738,143.185168), - ($$4871$$,$$STAATEN$$,#{state_id_qld},-17.608738,143.185168), - ($$4871$$,$$STOCKTON$$,#{state_id_qld},-17.608738,143.185168), - ($$4871$$,$$STRATHMORE$$,#{state_id_qld},-17.608738,143.185168), - ($$4871$$,$$TALAROO$$,#{state_id_qld},-17.608738,143.185168), - ($$4871$$,$$THORNBOROUGH$$,#{state_id_qld},-17.608738,143.185168), - ($$4871$$,$$UTCHEE CREEK$$,#{state_id_qld},-17.608738,143.185168), - ($$4871$$,$$WANGAN$$,#{state_id_qld},-17.608738,143.185168), - ($$4871$$,$$WARRUBULLEN$$,#{state_id_qld},-17.608738,143.185168), - ($$4871$$,$$WAUGH POCKET$$,#{state_id_qld},-17.608738,143.185168), - ($$4871$$,$$WELLESLEY ISLANDS$$,#{state_id_qld},-17.608738,143.185168), - ($$4871$$,$$WEST WELLESLEY ISLANDS$$,#{state_id_qld},-17.608738,143.185168), - ($$4871$$,$$WOOPEN CREEK$$,#{state_id_qld},-17.608738,143.185168), - ($$4871$$,$$WROTHAM$$,#{state_id_qld},-17.608738,143.185168), - ($$4871$$,$$YAGOONYA$$,#{state_id_qld},-17.608738,143.185168), - ($$4871$$,$$YARRABAH$$,#{state_id_qld},-17.608738,143.185168), - ($$4871$$,$$YARRADEN$$,#{state_id_qld},-17.608738,143.185168), - ($$4872$$,$$BARRINE$$,#{state_id_qld},-17.231966,145.62332), - ($$4872$$,$$BARWIDGI$$,#{state_id_qld},-17.231966,145.62332), - ($$4872$$,$$CAIRNS MC$$,#{state_id_qld},-17.231966,145.62332), - ($$4872$$,$$DANBULLA$$,#{state_id_qld},-17.231966,145.62332), - ($$4872$$,$$DIMBULAH$$,#{state_id_qld},-17.231966,145.62332), - ($$4872$$,$$FORTY MILE$$,#{state_id_qld},-17.231966,145.62332), - ($$4872$$,$$GLEN RUTH$$,#{state_id_qld},-17.231966,145.62332), - ($$4872$$,$$GUNNAWARRA$$,#{state_id_qld},-17.231966,145.62332), - ($$4872$$,$$INNOT HOT SPRINGS$$,#{state_id_qld},-17.231966,145.62332), - ($$4872$$,$$KAIRI$$,#{state_id_qld},-17.231966,145.62332), - ($$4872$$,$$KIRRAMA$$,#{state_id_qld},-17.231966,145.62332), - ($$4872$$,$$KOOMBOOLOOMBA$$,#{state_id_qld},-17.231966,145.62332), - ($$4872$$,$$KOWROWA$$,#{state_id_qld},-17.231966,145.62332), - ($$4872$$,$$LAKE TINAROO$$,#{state_id_qld},-17.231966,145.62332), - ($$4872$$,$$MINNAMOOLKA$$,#{state_id_qld},-17.231966,145.62332), - ($$4872$$,$$MOUNT GARNET$$,#{state_id_qld},-17.231966,145.62332), - ($$4872$$,$$MUNDERRA$$,#{state_id_qld},-17.231966,145.62332), - ($$4872$$,$$MUTCHILBA$$,#{state_id_qld},-17.231966,145.62332), - ($$4872$$,$$SILVER VALLEY$$,#{state_id_qld},-17.231966,145.62332), - ($$4872$$,$$TINAROO$$,#{state_id_qld},-17.231966,145.62332), - ($$4872$$,$$WAIRUNA$$,#{state_id_qld},-17.231966,145.62332), - ($$4872$$,$$WALKAMIN$$,#{state_id_qld},-17.231966,145.62332), - ($$4873$$,$$BAMBOO$$,#{state_id_qld},-16.372831,145.384849), - ($$4873$$,$$BONNIE DOON$$,#{state_id_qld},-16.372831,145.384849), - ($$4873$$,$$CAPE TRIBULATION$$,#{state_id_qld},-16.372831,145.384849), - ($$4873$$,$$CASSOWARY$$,#{state_id_qld},-16.372831,145.384849), - ($$4873$$,$$COOYA BEACH$$,#{state_id_qld},-16.372831,145.384849), - ($$4873$$,$$COW BAY$$,#{state_id_qld},-16.372831,145.384849), - ($$4873$$,$$DAGMAR$$,#{state_id_qld},-16.372831,145.384849), - ($$4873$$,$$DAINTREE$$,#{state_id_qld},-16.372831,145.384849), - ($$4873$$,$$DEDIN$$,#{state_id_qld},-16.372831,145.384849), - ($$4873$$,$$DIWAN$$,#{state_id_qld},-16.372831,145.384849), - ($$4873$$,$$FINLAY VALE$$,#{state_id_qld},-16.372831,145.384849), - ($$4873$$,$$FOREST CREEK$$,#{state_id_qld},-16.372831,145.384849), - ($$4873$$,$$KIMBERLEY$$,#{state_id_qld},-16.372831,145.384849), - ($$4873$$,$$LOW ISLES$$,#{state_id_qld},-16.372831,145.384849), - ($$4873$$,$$LOWER DAINTREE$$,#{state_id_qld},-16.372831,145.384849), - ($$4873$$,$$MIALLO$$,#{state_id_qld},-16.372831,145.384849), - ($$4873$$,$$MOSSMAN$$,#{state_id_qld},-16.372831,145.384849), - ($$4873$$,$$MOSSMAN GORGE$$,#{state_id_qld},-16.372831,145.384849), - ($$4873$$,$$NEWELL$$,#{state_id_qld},-16.372831,145.384849), - ($$4873$$,$$NOAH$$,#{state_id_qld},-16.372831,145.384849), - ($$4873$$,$$ROCKY POINT$$,#{state_id_qld},-16.372831,145.384849), - ($$4873$$,$$SHANNONVALE$$,#{state_id_qld},-16.372831,145.384849), - ($$4873$$,$$SPURGEON$$,#{state_id_qld},-16.372831,145.384849), - ($$4873$$,$$STEWART CREEK VALLEY$$,#{state_id_qld},-16.372831,145.384849), - ($$4873$$,$$SYNDICATE$$,#{state_id_qld},-16.372831,145.384849), - ($$4873$$,$$THORNTON BEACH$$,#{state_id_qld},-16.372831,145.384849), - ($$4873$$,$$UPPER DAINTREE$$,#{state_id_qld},-16.372831,145.384849), - ($$4873$$,$$WHYANBEEL$$,#{state_id_qld},-16.372831,145.384849), - ($$4873$$,$$WONGA$$,#{state_id_qld},-16.372831,145.384849), - ($$4874$$,$$EVANS LANDING$$,#{state_id_qld},-12.662368,141.849076), - ($$4874$$,$$JARDINE RIVER$$,#{state_id_qld},-12.662368,141.849076), - ($$4874$$,$$MAPOON$$,#{state_id_qld},-12.662368,141.849076), - ($$4874$$,$$MISSION RIVER$$,#{state_id_qld},-12.662368,141.849076), - ($$4874$$,$$NANUM$$,#{state_id_qld},-12.662368,141.849076), - ($$4874$$,$$NAPRANUM$$,#{state_id_qld},-12.662368,141.849076), - ($$4874$$,$$ROCKY POINT$$,#{state_id_qld},-12.662368,141.849076), - ($$4874$$,$$SHELBURNE$$,#{state_id_qld},-12.662368,141.849076), - ($$4874$$,$$TRUNDING$$,#{state_id_qld},-12.662368,141.849076), - ($$4874$$,$$WEIPA$$,#{state_id_qld},-12.662368,141.849076), - ($$4874$$,$$WEIPA AIRPORT$$,#{state_id_qld},-12.662368,141.849076), - ($$4874$$,$$WENLOCK$$,#{state_id_qld},-12.662368,141.849076), - ($$4875$$,$$BADU ISLAND$$,#{state_id_qld},-10.120146,142.139423), - ($$4875$$,$$BANKS ISLAND$$,#{state_id_qld},-10.120146,142.139423), - ($$4875$$,$$BOIGU ISLAND$$,#{state_id_qld},-10.120146,142.139423), - ($$4875$$,$$COCONUT ISLAND$$,#{state_id_qld},-10.120146,142.139423), - ($$4875$$,$$DARNLEY ISLAND$$,#{state_id_qld},-10.120146,142.139423), - ($$4875$$,$$DAUAN ISLAND$$,#{state_id_qld},-10.120146,142.139423), - ($$4875$$,$$ERUB$$,#{state_id_qld},-10.120146,142.139423), - ($$4875$$,$$HORN ISLAND$$,#{state_id_qld},-10.120146,142.139423), - ($$4875$$,$$JERVIS ISLAND$$,#{state_id_qld},-10.120146,142.139423), - ($$4875$$,$$KUBIN VILLAGE$$,#{state_id_qld},-10.120146,142.139423), - ($$4875$$,$$MABUIAG ISLAND$$,#{state_id_qld},-10.120146,142.139423), - ($$4875$$,$$MOA ISLAND$$,#{state_id_qld},-10.120146,142.139423), - ($$4875$$,$$MULGRAVE ISLAND$$,#{state_id_qld},-10.120146,142.139423), - ($$4875$$,$$MURRAY ISLAND$$,#{state_id_qld},-10.120146,142.139423), - ($$4875$$,$$SAIBAI ISLAND$$,#{state_id_qld},-10.120146,142.139423), - ($$4875$$,$$STEPHENS ISLAND$$,#{state_id_qld},-10.120146,142.139423), - ($$4875$$,$$TALBOT ISLAND$$,#{state_id_qld},-10.120146,142.139423), - ($$4875$$,$$THURSDAY ISLAND$$,#{state_id_qld},-10.120146,142.139423), - ($$4875$$,$$WARRABER ISLAND$$,#{state_id_qld},-10.120146,142.139423), - ($$4875$$,$$YAM ISLAND$$,#{state_id_qld},-10.120146,142.139423), - ($$4875$$,$$YORKE ISLAND$$,#{state_id_qld},-10.120146,142.139423), - ($$4876$$,$$BAMAGA$$,#{state_id_qld},-10.851692,142.427014), - ($$4876$$,$$INJINOO$$,#{state_id_qld},-10.851692,142.427014), - ($$4876$$,$$NEW MAPOON$$,#{state_id_qld},-10.851692,142.427014), - ($$4876$$,$$SEISIA$$,#{state_id_qld},-10.851692,142.427014), - ($$4876$$,$$UMAGICO$$,#{state_id_qld},-10.851692,142.427014), - ($$4877$$,$$CRAIGLIE$$,#{state_id_qld},-16.535286,145.467604), - ($$4877$$,$$KILLALOE$$,#{state_id_qld},-16.535286,145.467604), - ($$4877$$,$$MOWBRAY$$,#{state_id_qld},-16.535286,145.467604), - ($$4877$$,$$OAK BEACH$$,#{state_id_qld},-16.535286,145.467604), - ($$4877$$,$$PORT DOUGLAS$$,#{state_id_qld},-16.535286,145.467604), - ($$4877$$,$$WANGETTI$$,#{state_id_qld},-16.535286,145.467604), - ($$4878$$,$$BARRON$$,#{state_id_qld},-16.838291,145.708683), - ($$4878$$,$$CARAVONICA$$,#{state_id_qld},-16.838291,145.708683), - ($$4878$$,$$HOLLOWAYS BEACH$$,#{state_id_qld},-16.838291,145.708683), - ($$4878$$,$$MACHANS BEACH$$,#{state_id_qld},-16.838291,145.708683), - ($$4878$$,$$SMITHFIELD$$,#{state_id_qld},-16.838291,145.708683), - ($$4878$$,$$YORKEYS KNOB$$,#{state_id_qld},-16.838291,145.708683), - ($$4879$$,$$CLIFTON BEACH$$,#{state_id_qld},-16.765776,145.668652), - ($$4879$$,$$ELLIS BEACH$$,#{state_id_qld},-16.765776,145.668652), - ($$4879$$,$$KEWARRA BEACH$$,#{state_id_qld},-16.765776,145.668652), - ($$4879$$,$$PALM COVE$$,#{state_id_qld},-16.765776,145.668652), - ($$4879$$,$$TRINITY BEACH$$,#{state_id_qld},-16.765776,145.668652), - ($$4879$$,$$TRINITY PARK$$,#{state_id_qld},-16.765776,145.668652), - ($$4880$$,$$ARRIGA$$,#{state_id_qld},-17.109908,145.301993), - ($$4880$$,$$BIBOOHRA$$,#{state_id_qld},-17.109908,145.301993), - ($$4880$$,$$CHEWKO$$,#{state_id_qld},-17.109908,145.301993), - ($$4880$$,$$GLEN RUSSELL$$,#{state_id_qld},-17.109908,145.301993), - ($$4880$$,$$MAREEBA$$,#{state_id_qld},-17.109908,145.301993), - ($$4880$$,$$PADDYS GREEN$$,#{state_id_qld},-17.109908,145.301993), - ($$4881$$,$$KOAH$$,#{state_id_qld},-16.824388,145.509799), - ($$4881$$,$$KURANDA$$,#{state_id_qld},-16.824388,145.509799), - ($$4881$$,$$MONA MONA$$,#{state_id_qld},-16.824388,145.509799), - ($$4881$$,$$SPEEWAH$$,#{state_id_qld},-16.824388,145.509799), - ($$4882$$,$$TOLGA$$,#{state_id_qld},-17.185835,145.463033), - ($$4883$$,$$ATHERTON$$,#{state_id_qld},-17.268273,145.47457), - ($$4883$$,$$CARRINGTON$$,#{state_id_qld},-17.268273,145.47457), - ($$4883$$,$$EAST BARRON$$,#{state_id_qld},-17.268273,145.47457), - ($$4883$$,$$UPPER BARRON$$,#{state_id_qld},-17.268273,145.47457), - ($$4883$$,$$WONGABEL$$,#{state_id_qld},-17.268273,145.47457), - ($$4884$$,$$GADGARRA$$,#{state_id_qld},-17.219315,145.696889), - ($$4884$$,$$LAKE BARRINE$$,#{state_id_qld},-17.219315,145.696889), - ($$4884$$,$$LAKE EACHAM$$,#{state_id_qld},-17.219315,145.696889), - ($$4884$$,$$YUNGABURRA$$,#{state_id_qld},-17.219315,145.696889), - ($$4885$$,$$BUTCHERS CREEK$$,#{state_id_qld},-17.363155,145.69377), - ($$4885$$,$$GLEN ALLYN$$,#{state_id_qld},-17.363155,145.69377), - ($$4885$$,$$JAGGAN$$,#{state_id_qld},-17.363155,145.69377), - ($$4885$$,$$KUREEN$$,#{state_id_qld},-17.363155,145.69377), - ($$4885$$,$$MALANDA$$,#{state_id_qld},-17.363155,145.69377), - ($$4885$$,$$NORTH JOHNSTONE$$,#{state_id_qld},-17.363155,145.69377), - ($$4885$$,$$PEERAMON$$,#{state_id_qld},-17.363155,145.69377), - ($$4885$$,$$TARZALI$$,#{state_id_qld},-17.363155,145.69377), - ($$4885$$,$$TOPAZ$$,#{state_id_qld},-17.363155,145.69377), - ($$4886$$,$$BEATRICE$$,#{state_id_qld},-17.542146,145.605722), - ($$4886$$,$$ELLINJAA$$,#{state_id_qld},-17.542146,145.605722), - ($$4886$$,$$MAALAN$$,#{state_id_qld},-17.542146,145.605722), - ($$4886$$,$$MIDDLEBROOK$$,#{state_id_qld},-17.542146,145.605722), - ($$4886$$,$$MILLAA MILLAA$$,#{state_id_qld},-17.542146,145.605722), - ($$4886$$,$$MINBUN$$,#{state_id_qld},-17.542146,145.605722), - ($$4886$$,$$MOREGATTA$$,#{state_id_qld},-17.542146,145.605722), - ($$4886$$,$$MUNGALLI$$,#{state_id_qld},-17.542146,145.605722), - ($$4887$$,$$HERBERTON$$,#{state_id_qld},-17.384931,145.386636), - ($$4887$$,$$IRVINEBANK$$,#{state_id_qld},-17.384931,145.386636), - ($$4887$$,$$KALUNGA$$,#{state_id_qld},-17.384931,145.386636), - ($$4887$$,$$MOOMIN$$,#{state_id_qld},-17.384931,145.386636), - ($$4887$$,$$WATSONVILLE$$,#{state_id_qld},-17.384931,145.386636), - ($$4887$$,$$WONDECLA$$,#{state_id_qld},-17.384931,145.386636), - ($$4888$$,$$EVELYN$$,#{state_id_qld},-17.499336,145.491052), - ($$4888$$,$$KABAN$$,#{state_id_qld},-17.499336,145.491052), - ($$4888$$,$$MILLSTREAM$$,#{state_id_qld},-17.499336,145.491052), - ($$4888$$,$$RAVENSHOE$$,#{state_id_qld},-17.499336,145.491052), - ($$4888$$,$$TUMOULIN$$,#{state_id_qld},-17.499336,145.491052), - ($$4890$$,$$HOWITT$$,#{state_id_qld},-17.281509,141.625841), - ($$4890$$,$$NORMAN$$,#{state_id_qld},-17.281509,141.625841), - ($$4890$$,$$NORMANTON$$,#{state_id_qld},-17.281509,141.625841), - ($$4891$$,$$KARUMBA$$,#{state_id_qld},-17.483843,140.839772), - ($$4895$$,$$AYTON$$,#{state_id_qld},-16.013552,145.263051), - ($$4895$$,$$BLOOMFIELD$$,#{state_id_qld},-16.013552,145.263051), - ($$4895$$,$$COOKTOWN$$,#{state_id_qld},-16.013552,145.263051), - ($$4895$$,$$DEGARRA$$,#{state_id_qld},-16.013552,145.263051), - ($$4895$$,$$ENDEAVOUR FALLS$$,#{state_id_qld},-16.013552,145.263051), - ($$4895$$,$$HELENVALE$$,#{state_id_qld},-16.013552,145.263051), - ($$4895$$,$$HOPE VALE$$,#{state_id_qld},-16.013552,145.263051), - ($$4895$$,$$ROSSVILLE$$,#{state_id_qld},-16.013552,145.263051), - ($$4895$$,$$STARCKE$$,#{state_id_qld},-16.013552,145.263051), - ($$4895$$,$$WUJAL WUJAL$$,#{state_id_qld},-16.013552,145.263051), - ($$5000$$,$$ADELAIDE$$,#{state_id_sa},-34.92577,138.599732), - ($$5000$$,$$ADELAIDE BC$$,#{state_id_sa},-34.92577,138.599732), - ($$5000$$,$$CITY WEST CAMPUS$$,#{state_id_sa},-34.92577,138.599732), - ($$5000$$,$$HALIFAX STREET$$,#{state_id_sa},-34.92577,138.599732), - ($$5000$$,$$HUTT STREET$$,#{state_id_sa},-34.92577,138.599732), - ($$5000$$,$$PARLIAMENT HOUSE$$,#{state_id_sa},-34.92577,138.599732), - ($$5000$$,$$RUNDLE MALL$$,#{state_id_sa},-34.92577,138.599732), - ($$5000$$,$$STATION ARCADE$$,#{state_id_sa},-34.92577,138.599732), - ($$5000$$,$$STURT STREET$$,#{state_id_sa},-34.92577,138.599732), - ($$5001$$,$$ADELAIDE$$,#{state_id_sa},-35.120097,139.273782), - ($$5005$$,$$ADELAIDE UNIVERSITY$$,#{state_id_sa},-34.919398,138.60351), - ($$5005$$,$$THE UNIVERSITY OF ADELAIDE$$,#{state_id_sa},-34.919398,138.60351), - ($$5005$$,$$UNIVERSITY OF ADELAIDE$$,#{state_id_sa},-34.919398,138.60351), - ($$5006$$,$$NORTH ADELAIDE$$,#{state_id_sa},-34.921568,138.599136), - ($$5006$$,$$NORTH ADELAIDE MELBOURNE ST$$,#{state_id_sa},-34.921568,138.599136), - ($$5007$$,$$BOWDEN$$,#{state_id_sa},-34.904277,138.578712), - ($$5007$$,$$BROMPTON$$,#{state_id_sa},-34.904277,138.578712), - ($$5007$$,$$HINDMARSH$$,#{state_id_sa},-34.904277,138.578712), - ($$5007$$,$$WELLAND$$,#{state_id_sa},-34.904277,138.578712), - ($$5007$$,$$WEST HINDMARSH$$,#{state_id_sa},-34.904277,138.578712), - ($$5008$$,$$CROYDON$$,#{state_id_sa},-34.89845,138.56358), - ($$5008$$,$$CROYDON PARK$$,#{state_id_sa},-34.89845,138.56358), - ($$5008$$,$$CROYDON PARK SOUTH$$,#{state_id_sa},-34.89845,138.56358), - ($$5008$$,$$DEVON PARK$$,#{state_id_sa},-34.89845,138.56358), - ($$5008$$,$$DUDLEY PARK$$,#{state_id_sa},-34.89845,138.56358), - ($$5008$$,$$RENOWN PARK$$,#{state_id_sa},-34.89845,138.56358), - ($$5008$$,$$RIDLEYTON$$,#{state_id_sa},-34.89845,138.56358), - ($$5008$$,$$WEST CROYDON$$,#{state_id_sa},-34.89845,138.56358), - ($$5009$$,$$ALLENBY GARDENS$$,#{state_id_sa},-34.904334,138.555148), - ($$5009$$,$$BEVERLEY$$,#{state_id_sa},-34.904334,138.555148), - ($$5009$$,$$KILKENNY$$,#{state_id_sa},-34.904334,138.555148), - ($$5010$$,$$ANGLE PARK$$,#{state_id_sa},-34.859133,138.560673), - ($$5010$$,$$FERRYDEN PARK$$,#{state_id_sa},-34.859133,138.560673), - ($$5010$$,$$REGENCY PARK$$,#{state_id_sa},-34.859133,138.560673), - ($$5010$$,$$REGENCY PARK BC$$,#{state_id_sa},-34.859133,138.560673), - ($$5011$$,$$WOODVILLE$$,#{state_id_sa},-34.877605,138.538261), - ($$5011$$,$$WOODVILLE PARK$$,#{state_id_sa},-34.877605,138.538261), - ($$5011$$,$$WOODVILLE SOUTH$$,#{state_id_sa},-34.877605,138.538261), - ($$5011$$,$$WOODVILLE WEST$$,#{state_id_sa},-34.877605,138.538261), - ($$5012$$,$$ATHOL PARK$$,#{state_id_sa},-34.857676,138.542627), - ($$5012$$,$$MANSFIELD PARK$$,#{state_id_sa},-34.857676,138.542627), - ($$5012$$,$$WOODVILLE GARDENS$$,#{state_id_sa},-34.857676,138.542627), - ($$5012$$,$$WOODVILLE NORTH$$,#{state_id_sa},-34.857676,138.542627), - ($$5013$$,$$GILLMAN$$,#{state_id_sa},-34.840426,138.52745), - ($$5013$$,$$OTTOWAY$$,#{state_id_sa},-34.840426,138.52745), - ($$5013$$,$$PENNINGTON$$,#{state_id_sa},-34.840426,138.52745), - ($$5013$$,$$ROSEWATER$$,#{state_id_sa},-34.840426,138.52745), - ($$5013$$,$$ROSEWATER EAST$$,#{state_id_sa},-34.840426,138.52745), - ($$5013$$,$$WINGFIELD$$,#{state_id_sa},-34.840426,138.52745), - ($$5014$$,$$ALBERT PARK$$,#{state_id_sa},-34.878311,138.521348), - ($$5014$$,$$ALBERTON$$,#{state_id_sa},-34.878311,138.521348), - ($$5014$$,$$CHELTENHAM$$,#{state_id_sa},-34.878311,138.521348), - ($$5014$$,$$HENDON$$,#{state_id_sa},-34.878311,138.521348), - ($$5014$$,$$QUEENSTOWN$$,#{state_id_sa},-34.878311,138.521348), - ($$5014$$,$$ROYAL PARK$$,#{state_id_sa},-34.878311,138.521348), - ($$5015$$,$$BIRKENHEAD$$,#{state_id_sa},-34.840209,138.496803), - ($$5015$$,$$ETHELTON$$,#{state_id_sa},-34.840209,138.496803), - ($$5015$$,$$GLANVILLE$$,#{state_id_sa},-34.840209,138.496803), - ($$5015$$,$$NEW PORT$$,#{state_id_sa},-34.840209,138.496803), - ($$5015$$,$$PORT ADELAIDE$$,#{state_id_sa},-34.840209,138.496803), - ($$5015$$,$$PORT ADELAIDE BC$$,#{state_id_sa},-34.840209,138.496803), - ($$5015$$,$$PORT ADELAIDE DC$$,#{state_id_sa},-34.840209,138.496803), - ($$5016$$,$$LARGS BAY$$,#{state_id_sa},-34.821123,138.494309), - ($$5016$$,$$LARGS NORTH$$,#{state_id_sa},-34.821123,138.494309), - ($$5016$$,$$PETERHEAD$$,#{state_id_sa},-34.821123,138.494309), - ($$5017$$,$$OSBORNE$$,#{state_id_sa},-34.798477,138.491768), - ($$5017$$,$$TAPEROO$$,#{state_id_sa},-34.798477,138.491768), - ($$5018$$,$$NORTH HAVEN$$,#{state_id_sa},-34.792468,138.497748), - ($$5018$$,$$OUTER HARBOR$$,#{state_id_sa},-34.792468,138.497748), - ($$5019$$,$$EXETER$$,#{state_id_sa},-34.842086,138.489704), - ($$5019$$,$$SEMAPHORE$$,#{state_id_sa},-34.842086,138.489704), - ($$5019$$,$$SEMAPHORE PARK$$,#{state_id_sa},-34.842086,138.489704), - ($$5019$$,$$SEMAPHORE SOUTH$$,#{state_id_sa},-34.842086,138.489704), - ($$5020$$,$$WEST LAKES SHORE$$,#{state_id_sa},-34.871388,138.483659), - ($$5021$$,$$WEST LAKES$$,#{state_id_sa},-34.871659,138.48215), - ($$5022$$,$$GRANGE$$,#{state_id_sa},-34.899814,138.489405), - ($$5022$$,$$HENLEY BEACH$$,#{state_id_sa},-34.899814,138.489405), - ($$5022$$,$$HENLEY BEACH SOUTH$$,#{state_id_sa},-34.899814,138.489405), - ($$5022$$,$$KIRKCALDY$$,#{state_id_sa},-34.899814,138.489405), - ($$5022$$,$$TENNYSON$$,#{state_id_sa},-34.899814,138.489405), - ($$5023$$,$$FINDON$$,#{state_id_sa},-34.90548,138.532561), - ($$5023$$,$$SEATON$$,#{state_id_sa},-34.90548,138.532561), - ($$5023$$,$$SEATON NORTH$$,#{state_id_sa},-34.90548,138.532561), - ($$5024$$,$$FULHAM$$,#{state_id_sa},-34.926634,138.512231), - ($$5024$$,$$FULHAM GARDENS$$,#{state_id_sa},-34.926634,138.512231), - ($$5024$$,$$WEST BEACH$$,#{state_id_sa},-34.926634,138.512231), - ($$5025$$,$$FLINDERS PARK$$,#{state_id_sa},-34.907738,138.546435), - ($$5025$$,$$KIDMAN PARK$$,#{state_id_sa},-34.907738,138.546435), - ($$5031$$,$$MILE END$$,#{state_id_sa},-34.925146,138.56092), - ($$5031$$,$$MILE END SOUTH$$,#{state_id_sa},-34.925146,138.56092), - ($$5031$$,$$THEBARTON$$,#{state_id_sa},-34.925146,138.56092), - ($$5031$$,$$TORRENSVILLE$$,#{state_id_sa},-34.925146,138.56092), - ($$5031$$,$$TORRENSVILLE PLAZA$$,#{state_id_sa},-34.925146,138.56092), - ($$5032$$,$$BROOKLYN PARK$$,#{state_id_sa},-34.928848,138.542773), - ($$5032$$,$$LOCKLEYS$$,#{state_id_sa},-34.928848,138.542773), - ($$5032$$,$$UNDERDALE$$,#{state_id_sa},-34.928848,138.542773), - ($$5033$$,$$COWANDILLA$$,#{state_id_sa},-34.932483,138.556885), - ($$5033$$,$$HILTON$$,#{state_id_sa},-34.932483,138.556885), - ($$5033$$,$$HILTON PLAZA$$,#{state_id_sa},-34.932483,138.556885), - ($$5033$$,$$MARLESTON$$,#{state_id_sa},-34.932483,138.556885), - ($$5033$$,$$MARLESTON DC$$,#{state_id_sa},-34.932483,138.556885), - ($$5033$$,$$RICHMOND$$,#{state_id_sa},-34.932483,138.556885), - ($$5033$$,$$WEST RICHMOND$$,#{state_id_sa},-34.932483,138.556885), - ($$5034$$,$$CLARENCE PARK$$,#{state_id_sa},-34.962993,138.586161), - ($$5034$$,$$GOODWOOD$$,#{state_id_sa},-34.962993,138.586161), - ($$5034$$,$$KINGS PARK$$,#{state_id_sa},-34.962993,138.586161), - ($$5034$$,$$MILLSWOOD$$,#{state_id_sa},-34.962993,138.586161), - ($$5034$$,$$WAYVILLE$$,#{state_id_sa},-34.962993,138.586161), - ($$5035$$,$$ASHFORD$$,#{state_id_sa},-34.948705,138.57395), - ($$5035$$,$$BLACK FOREST$$,#{state_id_sa},-34.948705,138.57395), - ($$5035$$,$$EVERARD PARK$$,#{state_id_sa},-34.948705,138.57395), - ($$5035$$,$$FORESTVILLE$$,#{state_id_sa},-34.948705,138.57395), - ($$5035$$,$$KESWICK$$,#{state_id_sa},-34.948705,138.57395), - ($$5035$$,$$KESWICK TERMINAL$$,#{state_id_sa},-34.948705,138.57395), - ($$5037$$,$$GLANDORE$$,#{state_id_sa},-34.960393,138.566426), - ($$5037$$,$$KURRALTA PARK$$,#{state_id_sa},-34.960393,138.566426), - ($$5037$$,$$NETLEY$$,#{state_id_sa},-34.960393,138.566426), - ($$5037$$,$$NORTH PLYMPTON$$,#{state_id_sa},-34.960393,138.566426), - ($$5038$$,$$CAMDEN PARK$$,#{state_id_sa},-34.96838,138.543432), - ($$5038$$,$$PLYMPTON$$,#{state_id_sa},-34.96838,138.543432), - ($$5038$$,$$PLYMPTON PARK$$,#{state_id_sa},-34.96838,138.543432), - ($$5038$$,$$SOUTH PLYMPTON$$,#{state_id_sa},-34.96838,138.543432), - ($$5039$$,$$CLARENCE GARDENS$$,#{state_id_sa},-34.971683,138.577772), - ($$5039$$,$$EDWARDSTOWN$$,#{state_id_sa},-34.971683,138.577772), - ($$5039$$,$$MELROSE PARK$$,#{state_id_sa},-34.971683,138.577772), - ($$5039$$,$$MELROSE PARK DC$$,#{state_id_sa},-34.971683,138.577772), - ($$5040$$,$$NOVAR GARDENS$$,#{state_id_sa},-34.961584,138.528204), - ($$5041$$,$$COLONEL LIGHT GARDENS$$,#{state_id_sa},-34.983666,138.592125), - ($$5041$$,$$CUMBERLAND PARK$$,#{state_id_sa},-34.983666,138.592125), - ($$5041$$,$$DAW PARK$$,#{state_id_sa},-34.983666,138.592125), - ($$5041$$,$$PANORAMA$$,#{state_id_sa},-34.983666,138.592125), - ($$5041$$,$$WESTBOURNE PARK$$,#{state_id_sa},-34.983666,138.592125), - ($$5042$$,$$BEDFORD PARK$$,#{state_id_sa},-35.019843,138.568127), - ($$5042$$,$$CLOVELLY PARK$$,#{state_id_sa},-35.019843,138.568127), - ($$5042$$,$$FLINDERS UNIVERSITY$$,#{state_id_sa},-35.019843,138.568127), - ($$5042$$,$$PASADENA$$,#{state_id_sa},-35.019843,138.568127), - ($$5042$$,$$ST MARYS$$,#{state_id_sa},-35.019843,138.568127), - ($$5043$$,$$ASCOT PARK$$,#{state_id_sa},-34.986929,138.564635), - ($$5043$$,$$MARION$$,#{state_id_sa},-34.986929,138.564635), - ($$5043$$,$$MITCHELL PARK$$,#{state_id_sa},-34.986929,138.564635), - ($$5043$$,$$MORPHETTVILLE$$,#{state_id_sa},-34.986929,138.564635), - ($$5043$$,$$PARK HOLME$$,#{state_id_sa},-34.986929,138.564635), - ($$5044$$,$$GLENGOWRIE$$,#{state_id_sa},-34.986272,138.530886), - ($$5044$$,$$SOMERTON PARK$$,#{state_id_sa},-34.986272,138.530886), - ($$5045$$,$$GLENELG$$,#{state_id_sa},-34.980154,138.517721), - ($$5045$$,$$GLENELG EAST$$,#{state_id_sa},-34.980154,138.517721), - ($$5045$$,$$GLENELG JETTY ROAD$$,#{state_id_sa},-34.980154,138.517721), - ($$5045$$,$$GLENELG NORTH$$,#{state_id_sa},-34.980154,138.517721), - ($$5045$$,$$GLENELG SOUTH$$,#{state_id_sa},-34.980154,138.517721), - ($$5046$$,$$OAKLANDS PARK$$,#{state_id_sa},-35.014317,138.545262), - ($$5046$$,$$WARRADALE$$,#{state_id_sa},-35.014317,138.545262), - ($$5046$$,$$WARRADALE NORTH$$,#{state_id_sa},-35.014317,138.545262), - ($$5047$$,$$DARLINGTON$$,#{state_id_sa},-35.029907,138.556645), - ($$5047$$,$$SEACOMBE GARDENS$$,#{state_id_sa},-35.029907,138.556645), - ($$5047$$,$$SEACOMBE HEIGHTS$$,#{state_id_sa},-35.029907,138.556645), - ($$5047$$,$$STURT$$,#{state_id_sa},-35.029907,138.556645), - ($$5048$$,$$BRIGHTON$$,#{state_id_sa},-35.020996,138.523266), - ($$5048$$,$$DOVER GARDENS$$,#{state_id_sa},-35.020996,138.523266), - ($$5048$$,$$HOVE$$,#{state_id_sa},-35.020996,138.523266), - ($$5048$$,$$NORTH BRIGHTON$$,#{state_id_sa},-35.020996,138.523266), - ($$5048$$,$$SOUTH BRIGHTON$$,#{state_id_sa},-35.020996,138.523266), - ($$5049$$,$$KINGSTON PARK$$,#{state_id_sa},-35.040043,138.519618), - ($$5049$$,$$MARINO$$,#{state_id_sa},-35.040043,138.519618), - ($$5049$$,$$SEACLIFF$$,#{state_id_sa},-35.040043,138.519618), - ($$5049$$,$$SEACLIFF PARK$$,#{state_id_sa},-35.040043,138.519618), - ($$5049$$,$$SEAVIEW DOWNS$$,#{state_id_sa},-35.040043,138.519618), - ($$5050$$,$$BELLEVUE HEIGHTS$$,#{state_id_sa},-35.032941,138.586754), - ($$5050$$,$$EDEN HILLS$$,#{state_id_sa},-35.032941,138.586754), - ($$5051$$,$$BLACKWOOD$$,#{state_id_sa},-35.021155,138.616756), - ($$5051$$,$$COROMANDEL VALLEY$$,#{state_id_sa},-35.021155,138.616756), - ($$5051$$,$$CRAIGBURN FARM$$,#{state_id_sa},-35.021155,138.616756), - ($$5051$$,$$HAWTHORNDENE$$,#{state_id_sa},-35.021155,138.616756), - ($$5052$$,$$BELAIR$$,#{state_id_sa},-34.997952,138.622116), - ($$5052$$,$$GLENALTA$$,#{state_id_sa},-34.997952,138.622116), - ($$5061$$,$$HYDE PARK$$,#{state_id_sa},-34.957389,138.614822), - ($$5061$$,$$MALVERN$$,#{state_id_sa},-34.957389,138.614822), - ($$5061$$,$$UNLEY$$,#{state_id_sa},-34.957389,138.614822), - ($$5061$$,$$UNLEY BC$$,#{state_id_sa},-34.957389,138.614822), - ($$5061$$,$$UNLEY DC$$,#{state_id_sa},-34.957389,138.614822), - ($$5061$$,$$UNLEY PARK$$,#{state_id_sa},-34.957389,138.614822), - ($$5062$$,$$BROWN HILL CREEK$$,#{state_id_sa},-34.987009,138.653218), - ($$5062$$,$$CLAPHAM$$,#{state_id_sa},-34.987009,138.653218), - ($$5062$$,$$HAWTHORN$$,#{state_id_sa},-34.987009,138.653218), - ($$5062$$,$$KINGSWOOD$$,#{state_id_sa},-34.987009,138.653218), - ($$5062$$,$$LOWER MITCHAM$$,#{state_id_sa},-34.987009,138.653218), - ($$5062$$,$$LYNTON$$,#{state_id_sa},-34.987009,138.653218), - ($$5062$$,$$MITCHAM$$,#{state_id_sa},-34.987009,138.653218), - ($$5062$$,$$MITCHAM SHOPPING CENTRE$$,#{state_id_sa},-34.987009,138.653218), - ($$5062$$,$$NETHERBY$$,#{state_id_sa},-34.987009,138.653218), - ($$5062$$,$$SPRINGFIELD$$,#{state_id_sa},-34.987009,138.653218), - ($$5062$$,$$TORRENS PARK$$,#{state_id_sa},-34.987009,138.653218), - ($$5063$$,$$EASTWOOD$$,#{state_id_sa},-34.942432,138.620875), - ($$5063$$,$$FREWVILLE$$,#{state_id_sa},-34.942432,138.620875), - ($$5063$$,$$FULLARTON$$,#{state_id_sa},-34.942432,138.620875), - ($$5063$$,$$HIGHGATE$$,#{state_id_sa},-34.942432,138.620875), - ($$5063$$,$$PARKSIDE$$,#{state_id_sa},-34.942432,138.620875), - ($$5064$$,$$GLEN OSMOND$$,#{state_id_sa},-34.96026,138.64144), - ($$5064$$,$$GLENUNGA$$,#{state_id_sa},-34.96026,138.64144), - ($$5064$$,$$MOUNT OSMOND$$,#{state_id_sa},-34.96026,138.64144), - ($$5064$$,$$MYRTLE BANK$$,#{state_id_sa},-34.96026,138.64144), - ($$5064$$,$$ST GEORGES$$,#{state_id_sa},-34.96026,138.64144), - ($$5064$$,$$URRBRAE$$,#{state_id_sa},-34.96026,138.64144), - ($$5065$$,$$DULWICH$$,#{state_id_sa},-34.935144,138.629321), - ($$5065$$,$$GLENSIDE$$,#{state_id_sa},-34.935144,138.629321), - ($$5065$$,$$LINDEN PARK$$,#{state_id_sa},-34.935144,138.629321), - ($$5065$$,$$TOORAK GARDENS$$,#{state_id_sa},-34.935144,138.629321), - ($$5065$$,$$TUSMORE$$,#{state_id_sa},-34.935144,138.629321), - ($$5066$$,$$BEAUMONT$$,#{state_id_sa},-34.950576,138.661084), - ($$5066$$,$$BURNSIDE$$,#{state_id_sa},-34.950576,138.661084), - ($$5066$$,$$ERINDALE$$,#{state_id_sa},-34.950576,138.661084), - ($$5066$$,$$HAZELWOOD PARK$$,#{state_id_sa},-34.950576,138.661084), - ($$5066$$,$$STONYFELL$$,#{state_id_sa},-34.950576,138.661084), - ($$5066$$,$$WATERFALL GULLY$$,#{state_id_sa},-34.950576,138.661084), - ($$5066$$,$$WATTLE PARK$$,#{state_id_sa},-34.950576,138.661084), - ($$5067$$,$$BEULAH PARK$$,#{state_id_sa},-34.91772,138.643572), - ($$5067$$,$$KENT TOWN$$,#{state_id_sa},-34.91772,138.643572), - ($$5067$$,$$NORWOOD$$,#{state_id_sa},-34.91772,138.643572), - ($$5067$$,$$NORWOOD SOUTH$$,#{state_id_sa},-34.91772,138.643572), - ($$5067$$,$$ROSE PARK$$,#{state_id_sa},-34.91772,138.643572), - ($$5068$$,$$HEATHPOOL$$,#{state_id_sa},-34.930524,138.648349), - ($$5068$$,$$KENSINGTON$$,#{state_id_sa},-34.930524,138.648349), - ($$5068$$,$$KENSINGTON GARDENS$$,#{state_id_sa},-34.930524,138.648349), - ($$5068$$,$$KENSINGTON PARK$$,#{state_id_sa},-34.930524,138.648349), - ($$5068$$,$$LEABROOK$$,#{state_id_sa},-34.930524,138.648349), - ($$5068$$,$$MARRYATVILLE$$,#{state_id_sa},-34.930524,138.648349), - ($$5068$$,$$ST MORRIS$$,#{state_id_sa},-34.930524,138.648349), - ($$5068$$,$$TRINITY GARDENS$$,#{state_id_sa},-34.930524,138.648349), - ($$5069$$,$$COLLEGE PARK$$,#{state_id_sa},-34.912714,138.621705), - ($$5069$$,$$EVANDALE$$,#{state_id_sa},-34.912714,138.621705), - ($$5069$$,$$HACKNEY$$,#{state_id_sa},-34.912714,138.621705), - ($$5069$$,$$MAYLANDS$$,#{state_id_sa},-34.912714,138.621705), - ($$5069$$,$$ST PETERS$$,#{state_id_sa},-34.912714,138.621705), - ($$5069$$,$$STEPNEY$$,#{state_id_sa},-34.912714,138.621705), - ($$5070$$,$$FELIXSTOW$$,#{state_id_sa},-34.890867,138.643967), - ($$5070$$,$$FIRLE$$,#{state_id_sa},-34.890867,138.643967), - ($$5070$$,$$GLYNDE$$,#{state_id_sa},-34.890867,138.643967), - ($$5070$$,$$GLYNDE DC$$,#{state_id_sa},-34.890867,138.643967), - ($$5070$$,$$GLYNDE PLAZA$$,#{state_id_sa},-34.890867,138.643967), - ($$5070$$,$$JOSLIN$$,#{state_id_sa},-34.890867,138.643967), - ($$5070$$,$$MARDEN$$,#{state_id_sa},-34.890867,138.643967), - ($$5070$$,$$PAYNEHAM$$,#{state_id_sa},-34.890867,138.643967), - ($$5070$$,$$PAYNEHAM SOUTH$$,#{state_id_sa},-34.890867,138.643967), - ($$5070$$,$$ROYSTON PARK$$,#{state_id_sa},-34.890867,138.643967), - ($$5071$$,$$KENT TOWN$$,#{state_id_sa},-34.922911,138.623005), - ($$5071$$,$$KENT TOWN DC$$,#{state_id_sa},-34.922911,138.623005), - ($$5072$$,$$AULDANA$$,#{state_id_sa},-34.919717,138.684397), - ($$5072$$,$$MAGILL$$,#{state_id_sa},-34.919717,138.684397), - ($$5072$$,$$MAGILL NORTH$$,#{state_id_sa},-34.919717,138.684397), - ($$5072$$,$$MAGILL SOUTH$$,#{state_id_sa},-34.919717,138.684397), - ($$5072$$,$$ROSSLYN PARK$$,#{state_id_sa},-34.919717,138.684397), - ($$5072$$,$$SKYE$$,#{state_id_sa},-34.919717,138.684397), - ($$5072$$,$$TERINGIE$$,#{state_id_sa},-34.919717,138.684397), - ($$5072$$,$$WOODFORDE$$,#{state_id_sa},-34.919717,138.684397), - ($$5073$$,$$HECTORVILLE$$,#{state_id_sa},-34.890238,138.663636), - ($$5073$$,$$ROSTREVOR$$,#{state_id_sa},-34.890238,138.663636), - ($$5073$$,$$TRANMERE$$,#{state_id_sa},-34.890238,138.663636), - ($$5073$$,$$TRANMERE NORTH$$,#{state_id_sa},-34.890238,138.663636), - ($$5074$$,$$CAMPBELLTOWN$$,#{state_id_sa},-34.878302,138.663553), - ($$5074$$,$$NEWTON$$,#{state_id_sa},-34.878302,138.663553), - ($$5075$$,$$DERNANCOURT$$,#{state_id_sa},-34.861331,138.68319), - ($$5075$$,$$PARADISE$$,#{state_id_sa},-34.861331,138.68319), - ($$5076$$,$$ATHELSTONE$$,#{state_id_sa},-34.869798,138.702852), - ($$5076$$,$$CASTAMBUL$$,#{state_id_sa},-34.869798,138.702852), - ($$5081$$,$$COLLINSWOOD$$,#{state_id_sa},-34.884166,138.620121), - ($$5081$$,$$GILBERTON$$,#{state_id_sa},-34.884166,138.620121), - ($$5081$$,$$MEDINDIE$$,#{state_id_sa},-34.884166,138.620121), - ($$5081$$,$$MEDINDIE GARDENS$$,#{state_id_sa},-34.884166,138.620121), - ($$5081$$,$$VALE PARK$$,#{state_id_sa},-34.884166,138.620121), - ($$5081$$,$$WALKERVILLE$$,#{state_id_sa},-34.884166,138.620121), - ($$5082$$,$$FITZROY$$,#{state_id_sa},-34.89673,138.590248), - ($$5082$$,$$OVINGHAM$$,#{state_id_sa},-34.89673,138.590248), - ($$5082$$,$$PROSPECT$$,#{state_id_sa},-34.89673,138.590248), - ($$5082$$,$$PROSPECT EAST$$,#{state_id_sa},-34.89673,138.590248), - ($$5082$$,$$PROSPECT WEST$$,#{state_id_sa},-34.89673,138.590248), - ($$5082$$,$$THORNGATE$$,#{state_id_sa},-34.89673,138.590248), - ($$5083$$,$$BROADVIEW$$,#{state_id_sa},-34.873062,138.613932), - ($$5083$$,$$NAILSWORTH$$,#{state_id_sa},-34.873062,138.613932), - ($$5083$$,$$SEFTON PARK$$,#{state_id_sa},-34.873062,138.613932), - ($$5084$$,$$BLAIR ATHOL$$,#{state_id_sa},-34.858531,138.600915), - ($$5084$$,$$BLAIR ATHOL WEST$$,#{state_id_sa},-34.858531,138.600915), - ($$5084$$,$$KILBURN$$,#{state_id_sa},-34.858531,138.600915), - ($$5084$$,$$KILBURN NORTH$$,#{state_id_sa},-34.858531,138.600915), - ($$5085$$,$$CLEARVIEW$$,#{state_id_sa},-34.858477,138.616912), - ($$5085$$,$$ENFIELD$$,#{state_id_sa},-34.858477,138.616912), - ($$5085$$,$$ENFIELD PLAZA$$,#{state_id_sa},-34.858477,138.616912), - ($$5085$$,$$NORTHFIELD$$,#{state_id_sa},-34.858477,138.616912), - ($$5085$$,$$NORTHGATE$$,#{state_id_sa},-34.858477,138.616912), - ($$5086$$,$$GILLES PLAINS$$,#{state_id_sa},-34.855183,138.655892), - ($$5086$$,$$GREENACRES$$,#{state_id_sa},-34.855183,138.655892), - ($$5086$$,$$HAMPSTEAD GARDENS$$,#{state_id_sa},-34.855183,138.655892), - ($$5086$$,$$HILLCREST$$,#{state_id_sa},-34.855183,138.655892), - ($$5086$$,$$MANNINGHAM$$,#{state_id_sa},-34.855183,138.655892), - ($$5086$$,$$OAKDEN$$,#{state_id_sa},-34.855183,138.655892), - ($$5087$$,$$KLEMZIG$$,#{state_id_sa},-34.879636,138.635435), - ($$5087$$,$$WINDSOR GARDENS$$,#{state_id_sa},-34.879636,138.635435), - ($$5088$$,$$HOLDEN HILL$$,#{state_id_sa},-34.857212,138.655895), - ($$5089$$,$$HIGHBURY$$,#{state_id_sa},-34.854271,138.697275), - ($$5090$$,$$HOPE VALLEY$$,#{state_id_sa},0.0,0.0), - ($$5091$$,$$BANKSIA PARK$$,#{state_id_sa},-34.812061,138.724522), - ($$5091$$,$$TEA TREE GULLY$$,#{state_id_sa},-34.812061,138.724522), - ($$5091$$,$$VISTA$$,#{state_id_sa},-34.812061,138.724522), - ($$5092$$,$$MODBURY$$,#{state_id_sa},-34.832402,138.688), - ($$5092$$,$$MODBURY HEIGHTS$$,#{state_id_sa},-34.832402,138.688), - ($$5092$$,$$MODBURY NORTH$$,#{state_id_sa},-34.832402,138.688), - ($$5092$$,$$MODBURY NORTH DC$$,#{state_id_sa},-34.832402,138.688), - ($$5093$$,$$PARA VISTA$$,#{state_id_sa},-34.82944,138.665465), - ($$5093$$,$$VALLEY VIEW$$,#{state_id_sa},-34.82944,138.665465), - ($$5094$$,$$CAVAN$$,#{state_id_sa},-34.82798,138.59863), - ($$5094$$,$$DRY CREEK$$,#{state_id_sa},-34.82798,138.59863), - ($$5094$$,$$GEPPS CROSS$$,#{state_id_sa},-34.82798,138.59863), - ($$5095$$,$$MAWSON LAKES$$,#{state_id_sa},-34.817573,138.618743), - ($$5095$$,$$POORAKA$$,#{state_id_sa},-34.817573,138.618743), - ($$5096$$,$$GULFVIEW HEIGHTS$$,#{state_id_sa},-34.787609,138.674984), - ($$5096$$,$$PARA HILLS$$,#{state_id_sa},-34.787609,138.674984), - ($$5096$$,$$PARA HILLS WEST$$,#{state_id_sa},-34.787609,138.674984), - ($$5097$$,$$REDWOOD PARK$$,#{state_id_sa},-34.815932,138.699993), - ($$5097$$,$$RIDGEHAVEN$$,#{state_id_sa},-34.815932,138.699993), - ($$5097$$,$$ST AGNES$$,#{state_id_sa},-34.815932,138.699993), - ($$5098$$,$$INGLE FARM$$,#{state_id_sa},-34.82741,138.646376), - ($$5098$$,$$WALKLEY HEIGHTS$$,#{state_id_sa},-34.82741,138.646376), - ($$5106$$,$$PARAFIELD$$,#{state_id_sa},-34.789775,138.635024), - ($$5106$$,$$PARAFIELD AIRPORT$$,#{state_id_sa},-34.789775,138.635024), - ($$5106$$,$$SALISBURY SOUTH$$,#{state_id_sa},-34.789775,138.635024), - ($$5106$$,$$SALISBURY SOUTH BC$$,#{state_id_sa},-34.789775,138.635024), - ($$5106$$,$$SALISBURY SOUTH DC$$,#{state_id_sa},-34.789775,138.635024), - ($$5107$$,$$GREEN FIELDS$$,#{state_id_sa},-34.790623,138.600399), - ($$5107$$,$$PARAFIELD GARDENS$$,#{state_id_sa},-34.790623,138.600399), - ($$5108$$,$$PARALOWIE$$,#{state_id_sa},-34.764291,138.607991), - ($$5108$$,$$SALISBURY$$,#{state_id_sa},-34.764291,138.607991), - ($$5108$$,$$SALISBURY DOWNS$$,#{state_id_sa},-34.764291,138.607991), - ($$5108$$,$$SALISBURY NORTH$$,#{state_id_sa},-34.764291,138.607991), - ($$5108$$,$$SALISBURY NORTH WHITES ROAD$$,#{state_id_sa},-34.764291,138.607991), - ($$5109$$,$$BRAHMA LODGE$$,#{state_id_sa},-34.773664,138.65842), - ($$5109$$,$$SALISBURY EAST$$,#{state_id_sa},-34.773664,138.65842), - ($$5109$$,$$SALISBURY EAST NORTHBRI AVE$$,#{state_id_sa},-34.773664,138.65842), - ($$5109$$,$$SALISBURY HEIGHTS$$,#{state_id_sa},-34.773664,138.65842), - ($$5109$$,$$SALISBURY PARK$$,#{state_id_sa},-34.773664,138.65842), - ($$5109$$,$$SALISBURY PLAIN$$,#{state_id_sa},-34.773664,138.65842), - ($$5110$$,$$BOLIVAR$$,#{state_id_sa},-34.774479,138.588502), - ($$5110$$,$$BURTON$$,#{state_id_sa},-34.774479,138.588502), - ($$5110$$,$$DIREK$$,#{state_id_sa},-34.774479,138.588502), - ($$5110$$,$$GLOBE DERBY PARK$$,#{state_id_sa},-34.774479,138.588502), - ($$5110$$,$$ST KILDA$$,#{state_id_sa},-34.774479,138.588502), - ($$5110$$,$$WATERLOO CORNER$$,#{state_id_sa},-34.774479,138.588502), - ($$5111$$,$$EDINBURGH$$,#{state_id_sa},-34.711305,138.626077), - ($$5112$$,$$ELIZABETH$$,#{state_id_sa},-34.714535,138.669699), - ($$5112$$,$$ELIZABETH EAST$$,#{state_id_sa},-34.714535,138.669699), - ($$5112$$,$$ELIZABETH GROVE$$,#{state_id_sa},-34.714535,138.669699), - ($$5112$$,$$ELIZABETH SOUTH$$,#{state_id_sa},-34.714535,138.669699), - ($$5112$$,$$ELIZABETH VALE$$,#{state_id_sa},-34.714535,138.669699), - ($$5112$$,$$HILLBANK$$,#{state_id_sa},-34.714535,138.669699), - ($$5113$$,$$DAVOREN PARK$$,#{state_id_sa},-34.696707,138.666379), - ($$5113$$,$$DAVOREN PARK NORTH$$,#{state_id_sa},-34.696707,138.666379), - ($$5113$$,$$DAVOREN PARK SOUTH$$,#{state_id_sa},-34.696707,138.666379), - ($$5113$$,$$ELIZABETH DOWNS$$,#{state_id_sa},-34.696707,138.666379), - ($$5113$$,$$ELIZABETH NORTH$$,#{state_id_sa},-34.696707,138.666379), - ($$5113$$,$$ELIZABETH PARK$$,#{state_id_sa},-34.696707,138.666379), - ($$5113$$,$$ELIZABETH WEST$$,#{state_id_sa},-34.696707,138.666379), - ($$5113$$,$$ELIZABETH WEST DC$$,#{state_id_sa},-34.696707,138.666379), - ($$5114$$,$$ANDREWS FARM$$,#{state_id_sa},-34.671461,138.666778), - ($$5114$$,$$BLAKEVIEW$$,#{state_id_sa},-34.671461,138.666778), - ($$5114$$,$$CRAIGMORE$$,#{state_id_sa},-34.671461,138.666778), - ($$5114$$,$$GOULD CREEK$$,#{state_id_sa},-34.671461,138.666778), - ($$5114$$,$$HUMBUG SCRUB$$,#{state_id_sa},-34.671461,138.666778), - ($$5114$$,$$ONE TREE HILL$$,#{state_id_sa},-34.671461,138.666778), - ($$5114$$,$$SAMPSON FLAT$$,#{state_id_sa},-34.671461,138.666778), - ($$5114$$,$$SMITHFIELD$$,#{state_id_sa},-34.671461,138.666778), - ($$5114$$,$$SMITHFIELD PLAINS$$,#{state_id_sa},-34.671461,138.666778), - ($$5114$$,$$SMITHFIELD WEST$$,#{state_id_sa},-34.671461,138.666778), - ($$5114$$,$$ULEYBURY$$,#{state_id_sa},-34.671461,138.666778), - ($$5114$$,$$YATTALUNGA$$,#{state_id_sa},-34.671461,138.666778), - ($$5115$$,$$KUDLA$$,#{state_id_sa},-34.637249,138.701322), - ($$5115$$,$$MUNNO PARA$$,#{state_id_sa},-34.637249,138.701322), - ($$5115$$,$$MUNNO PARA DOWNS$$,#{state_id_sa},-34.637249,138.701322), - ($$5115$$,$$MUNNO PARA WEST$$,#{state_id_sa},-34.637249,138.701322), - ($$5116$$,$$EVANSTON$$,#{state_id_sa},-34.615268,138.728513), - ($$5116$$,$$EVANSTON GARDENS$$,#{state_id_sa},-34.615268,138.728513), - ($$5116$$,$$EVANSTON PARK$$,#{state_id_sa},-34.615268,138.728513), - ($$5116$$,$$EVANSTON SOUTH$$,#{state_id_sa},-34.615268,138.728513), - ($$5116$$,$$HILLIER$$,#{state_id_sa},-34.615268,138.728513), - ($$5117$$,$$ANGLE VALE$$,#{state_id_sa},-34.641224,138.643948), - ($$5118$$,$$BIBARINGA$$,#{state_id_sa},-34.641308,138.769002), - ($$5118$$,$$BUCHFELDE$$,#{state_id_sa},-34.641308,138.769002), - ($$5118$$,$$CONCORDIA$$,#{state_id_sa},-34.641308,138.769002), - ($$5118$$,$$GAWLER$$,#{state_id_sa},-34.641308,138.769002), - ($$5118$$,$$GAWLER BELT$$,#{state_id_sa},-34.641308,138.769002), - ($$5118$$,$$GAWLER EAST$$,#{state_id_sa},-34.641308,138.769002), - ($$5118$$,$$GAWLER RIVER$$,#{state_id_sa},-34.641308,138.769002), - ($$5118$$,$$GAWLER SOUTH$$,#{state_id_sa},-34.641308,138.769002), - ($$5118$$,$$GAWLER WEST$$,#{state_id_sa},-34.641308,138.769002), - ($$5118$$,$$HEWETT$$,#{state_id_sa},-34.641308,138.769002), - ($$5118$$,$$KALBEEBA$$,#{state_id_sa},-34.641308,138.769002), - ($$5118$$,$$KANGAROO FLAT$$,#{state_id_sa},-34.641308,138.769002), - ($$5118$$,$$KINGSFORD$$,#{state_id_sa},-34.641308,138.769002), - ($$5118$$,$$REID$$,#{state_id_sa},-34.641308,138.769002), - ($$5118$$,$$WARD BELT$$,#{state_id_sa},-34.641308,138.769002), - ($$5118$$,$$WILLASTON$$,#{state_id_sa},-34.641308,138.769002), - ($$5120$$,$$BUCKLAND PARK$$,#{state_id_sa},-34.662559,138.508995), - ($$5120$$,$$VIRGINIA$$,#{state_id_sa},-34.662559,138.508995), - ($$5121$$,$$MACDONALD PARK$$,#{state_id_sa},-34.663693,138.641206), - ($$5121$$,$$PENFIELD$$,#{state_id_sa},-34.663693,138.641206), - ($$5121$$,$$PENFIELD GARDENS$$,#{state_id_sa},-34.663693,138.641206), - ($$5125$$,$$GOLDEN GROVE$$,#{state_id_sa},-34.788994,138.696935), - ($$5125$$,$$GOLDEN GROVE VILLAGE$$,#{state_id_sa},-34.788994,138.696935), - ($$5125$$,$$GREENWITH$$,#{state_id_sa},-34.788994,138.696935), - ($$5126$$,$$FAIRVIEW PARK$$,#{state_id_sa},-34.802608,138.717348), - ($$5126$$,$$SURREY DOWNS$$,#{state_id_sa},-34.802608,138.717348), - ($$5126$$,$$YATALA VALE$$,#{state_id_sa},-34.802608,138.717348), - ($$5127$$,$$WYNN VALE$$,#{state_id_sa},-34.798817,138.685833), - ($$5131$$,$$HOUGHTON$$,#{state_id_sa},-34.825277,138.759507), - ($$5131$$,$$LOWER HERMITAGE$$,#{state_id_sa},-34.825277,138.759507), - ($$5131$$,$$UPPER HERMITAGE$$,#{state_id_sa},-34.825277,138.759507), - ($$5132$$,$$PARACOMBE$$,#{state_id_sa},-34.845602,138.768417), - ($$5133$$,$$INGLEWOOD$$,#{state_id_sa},-34.824005,138.774666), - ($$5134$$,$$CHERRYVILLE$$,#{state_id_sa},-34.912349,138.765697), - ($$5134$$,$$MONTACUTE$$,#{state_id_sa},-34.912349,138.765697), - ($$5136$$,$$NORTON SUMMIT$$,#{state_id_sa},-34.918308,138.720195), - ($$5137$$,$$ASHTON$$,#{state_id_sa},-34.939733,138.73739), - ($$5137$$,$$MARBLE HILL$$,#{state_id_sa},-34.939733,138.73739), - ($$5138$$,$$BASKET RANGE$$,#{state_id_sa},-34.947265,138.764194), - ($$5139$$,$$FOREST RANGE$$,#{state_id_sa},-34.931847,138.799104), - ($$5140$$,$$GREENHILL$$,#{state_id_sa},-34.953617,138.696352), - ($$5141$$,$$HORSNELL GULLY$$,#{state_id_sa},-34.942572,138.70612), - ($$5141$$,$$SUMMERTOWN$$,#{state_id_sa},-34.942572,138.70612), - ($$5142$$,$$URAIDLA$$,#{state_id_sa},-34.955523,138.743429), - ($$5144$$,$$CAREY GULLY$$,#{state_id_sa},-34.973365,138.754892), - ($$5150$$,$$EAGLE ON THE HILL$$,#{state_id_sa},-34.978915,138.668177), - ($$5150$$,$$LEAWOOD GARDENS$$,#{state_id_sa},-34.978915,138.668177), - ($$5151$$,$$PICCADILLY$$,#{state_id_sa},0.0,0.0), - ($$5152$$,$$CLELAND$$,#{state_id_sa},-34.967891,138.701301), - ($$5152$$,$$CRAFERS$$,#{state_id_sa},-34.967891,138.701301), - ($$5152$$,$$CRAFERS WEST$$,#{state_id_sa},-34.967891,138.701301), - ($$5152$$,$$MOUNT LOFTY$$,#{state_id_sa},-34.967891,138.701301), - ($$5152$$,$$STIRLING$$,#{state_id_sa},-34.967891,138.701301), - ($$5153$$,$$BIGGS FLAT$$,#{state_id_sa},-35.072031,138.782207), - ($$5153$$,$$BRADBURY$$,#{state_id_sa},-35.072031,138.782207), - ($$5153$$,$$CHAPEL HILL$$,#{state_id_sa},-35.072031,138.782207), - ($$5153$$,$$ECHUNGA$$,#{state_id_sa},-35.072031,138.782207), - ($$5153$$,$$FLAXLEY$$,#{state_id_sa},-35.072031,138.782207), - ($$5153$$,$$GREEN HILLS RANGE$$,#{state_id_sa},-35.072031,138.782207), - ($$5153$$,$$HEATHFIELD$$,#{state_id_sa},-35.072031,138.782207), - ($$5153$$,$$IRONBANK$$,#{state_id_sa},-35.072031,138.782207), - ($$5153$$,$$JUPITER CREEK$$,#{state_id_sa},-35.072031,138.782207), - ($$5153$$,$$LONGWOOD$$,#{state_id_sa},-35.072031,138.782207), - ($$5153$$,$$MACCLESFIELD$$,#{state_id_sa},-35.072031,138.782207), - ($$5153$$,$$MYLOR$$,#{state_id_sa},-35.072031,138.782207), - ($$5153$$,$$SCOTT CREEK$$,#{state_id_sa},-35.072031,138.782207), - ($$5154$$,$$ALDGATE$$,#{state_id_sa},-35.014141,138.733092), - ($$5155$$,$$BRIDGEWATER$$,#{state_id_sa},-35.009789,138.760157), - ($$5155$$,$$MOUNT GEORGE$$,#{state_id_sa},-35.009789,138.760157), - ($$5156$$,$$UPPER STURT$$,#{state_id_sa},-35.021763,138.679608), - ($$5157$$,$$ASHBOURNE$$,#{state_id_sa},-35.282825,138.770184), - ($$5157$$,$$BULL CREEK$$,#{state_id_sa},-35.282825,138.770184), - ($$5157$$,$$CHERRY GARDENS$$,#{state_id_sa},-35.282825,138.770184), - ($$5157$$,$$CLARENDON$$,#{state_id_sa},-35.282825,138.770184), - ($$5157$$,$$COROMANDEL EAST$$,#{state_id_sa},-35.282825,138.770184), - ($$5157$$,$$DORSET VALE$$,#{state_id_sa},-35.282825,138.770184), - ($$5157$$,$$KANGARILLA$$,#{state_id_sa},-35.282825,138.770184), - ($$5157$$,$$MCHARG CREEK$$,#{state_id_sa},-35.282825,138.770184), - ($$5158$$,$$HALLETT COVE$$,#{state_id_sa},-35.077039,138.518026), - ($$5158$$,$$O'HALLORAN HILL$$,#{state_id_sa},-35.077039,138.518026), - ($$5158$$,$$O'HALLORAN HILL DC$$,#{state_id_sa},-35.077039,138.518026), - ($$5158$$,$$SHEIDOW PARK$$,#{state_id_sa},-35.077039,138.518026), - ($$5158$$,$$TROTT PARK$$,#{state_id_sa},-35.077039,138.518026), - ($$5159$$,$$ABERFOYLE PARK$$,#{state_id_sa},-35.076282,138.593723), - ($$5159$$,$$CHANDLERS HILL$$,#{state_id_sa},-35.076282,138.593723), - ($$5159$$,$$FLAGSTAFF HILL$$,#{state_id_sa},-35.076282,138.593723), - ($$5159$$,$$HAPPY VALLEY$$,#{state_id_sa},-35.076282,138.593723), - ($$5160$$,$$LONSDALE$$,#{state_id_sa},-35.098851,138.498008), - ($$5160$$,$$LONSDALE DC$$,#{state_id_sa},-35.098851,138.498008), - ($$5160$$,$$PORT STANVAC$$,#{state_id_sa},-35.098851,138.498008), - ($$5161$$,$$OLD REYNELLA$$,#{state_id_sa},-35.093873,138.54149), - ($$5161$$,$$REYNELLA$$,#{state_id_sa},-35.093873,138.54149), - ($$5161$$,$$REYNELLA EAST$$,#{state_id_sa},-35.093873,138.54149), - ($$5162$$,$$MORPHETT VALE$$,#{state_id_sa},-35.121055,138.523205), - ($$5162$$,$$WOODCROFT$$,#{state_id_sa},-35.121055,138.523205), - ($$5163$$,$$HACKHAM$$,#{state_id_sa},-35.138794,138.532959), - ($$5163$$,$$HACKHAM WEST$$,#{state_id_sa},-35.138794,138.532959), - ($$5163$$,$$HUNTFIELD HEIGHTS$$,#{state_id_sa},-35.138794,138.532959), - ($$5163$$,$$ONKAPARINGA HILLS$$,#{state_id_sa},-35.138794,138.532959), - ($$5164$$,$$CHRISTIE DOWNS$$,#{state_id_sa},-35.129316,138.496257), - ($$5165$$,$$CHRISTIES BEACH$$,#{state_id_sa},-35.139309,138.474194), - ($$5165$$,$$CHRISTIES BEACH NORTH$$,#{state_id_sa},-35.139309,138.474194), - ($$5166$$,$$O'SULLIVAN BEACH$$,#{state_id_sa},-35.115305,138.478636), - ($$5167$$,$$PORT NOARLUNGA$$,#{state_id_sa},-35.148558,138.471519), - ($$5167$$,$$PORT NOARLUNGA SOUTH$$,#{state_id_sa},-35.148558,138.471519), - ($$5168$$,$$NOARLUNGA CENTRE$$,#{state_id_sa},-35.139646,138.493208), - ($$5168$$,$$NOARLUNGA DOWNS$$,#{state_id_sa},-35.139646,138.493208), - ($$5168$$,$$OLD NOARLUNGA$$,#{state_id_sa},-35.139646,138.493208), - ($$5169$$,$$MOANA$$,#{state_id_sa},-35.197777,138.473122), - ($$5169$$,$$SEAFORD$$,#{state_id_sa},-35.197777,138.473122), - ($$5169$$,$$SEAFORD HEIGHTS$$,#{state_id_sa},-35.197777,138.473122), - ($$5169$$,$$SEAFORD MEADOWS$$,#{state_id_sa},-35.197777,138.473122), - ($$5169$$,$$SEAFORD RISE$$,#{state_id_sa},-35.197777,138.473122), - ($$5170$$,$$MASLIN BEACH$$,#{state_id_sa},-35.222707,138.479904), - ($$5171$$,$$BLEWITT SPRINGS$$,#{state_id_sa},-35.163536,138.592337), - ($$5171$$,$$MCLAREN FLAT$$,#{state_id_sa},-35.163536,138.592337), - ($$5171$$,$$MCLAREN VALE$$,#{state_id_sa},-35.163536,138.592337), - ($$5171$$,$$PEDLER CREEK$$,#{state_id_sa},-35.163536,138.592337), - ($$5171$$,$$TATACHILLA$$,#{state_id_sa},-35.163536,138.592337), - ($$5172$$,$$DINGABLEDINGA$$,#{state_id_sa},-35.268849,138.630067), - ($$5172$$,$$HOPE FOREST$$,#{state_id_sa},-35.268849,138.630067), - ($$5172$$,$$KUITPO$$,#{state_id_sa},-35.268849,138.630067), - ($$5172$$,$$KUITPO COLONY$$,#{state_id_sa},-35.268849,138.630067), - ($$5172$$,$$KYEEMA$$,#{state_id_sa},-35.268849,138.630067), - ($$5172$$,$$MONTARRA$$,#{state_id_sa},-35.268849,138.630067), - ($$5172$$,$$PAGES FLAT$$,#{state_id_sa},-35.268849,138.630067), - ($$5172$$,$$THE RANGE$$,#{state_id_sa},-35.268849,138.630067), - ($$5172$$,$$WHITES VALLEY$$,#{state_id_sa},-35.268849,138.630067), - ($$5172$$,$$WILLUNGA$$,#{state_id_sa},-35.268849,138.630067), - ($$5172$$,$$WILLUNGA HILL$$,#{state_id_sa},-35.268849,138.630067), - ($$5172$$,$$WILLUNGA SOUTH$$,#{state_id_sa},-35.268849,138.630067), - ($$5172$$,$$YUNDI$$,#{state_id_sa},-35.268849,138.630067), - ($$5173$$,$$ALDINGA$$,#{state_id_sa},-35.267326,138.483136), - ($$5173$$,$$ALDINGA BEACH$$,#{state_id_sa},-35.267326,138.483136), - ($$5173$$,$$PORT WILLUNGA$$,#{state_id_sa},-35.267326,138.483136), - ($$5173$$,$$SILVER SANDS$$,#{state_id_sa},-35.267326,138.483136), - ($$5174$$,$$SELLICKS BEACH$$,#{state_id_sa},-35.329671,138.448146), - ($$5174$$,$$SELLICKS HILL$$,#{state_id_sa},-35.329671,138.448146), - ($$5201$$,$$BLACKFELLOWS CREEK$$,#{state_id_sa},-35.248592,138.707721), - ($$5201$$,$$MEADOWS$$,#{state_id_sa},-35.248592,138.707721), - ($$5201$$,$$PARIS CREEK$$,#{state_id_sa},-35.248592,138.707721), - ($$5201$$,$$PROSPECT HILL$$,#{state_id_sa},-35.248592,138.707721), - ($$5202$$,$$HINDMARSH TIERS$$,#{state_id_sa},-35.422515,138.570748), - ($$5202$$,$$MYPONGA$$,#{state_id_sa},-35.422515,138.570748), - ($$5202$$,$$MYPONGA BEACH$$,#{state_id_sa},-35.422515,138.570748), - ($$5203$$,$$BALD HILLS$$,#{state_id_sa},-35.495827,138.39325), - ($$5203$$,$$PARAWA$$,#{state_id_sa},-35.495827,138.39325), - ($$5203$$,$$TORRENS VALE$$,#{state_id_sa},-35.495827,138.39325), - ($$5203$$,$$TUNKALILLA$$,#{state_id_sa},-35.495827,138.39325), - ($$5203$$,$$WATTLE FLAT$$,#{state_id_sa},-35.495827,138.39325), - ($$5203$$,$$YANKALILLA$$,#{state_id_sa},-35.495827,138.39325), - ($$5204$$,$$CAPE JERVIS$$,#{state_id_sa},-35.603683,138.105284), - ($$5204$$,$$CARRICKALINGA$$,#{state_id_sa},-35.603683,138.105284), - ($$5204$$,$$DEEP CREEK$$,#{state_id_sa},-35.603683,138.105284), - ($$5204$$,$$DELAMERE$$,#{state_id_sa},-35.603683,138.105284), - ($$5204$$,$$HAY FLAT$$,#{state_id_sa},-35.603683,138.105284), - ($$5204$$,$$NORMANVILLE$$,#{state_id_sa},-35.603683,138.105284), - ($$5204$$,$$RAPID BAY$$,#{state_id_sa},-35.603683,138.105284), - ($$5204$$,$$SECOND VALLEY$$,#{state_id_sa},-35.603683,138.105284), - ($$5204$$,$$SILVERTON$$,#{state_id_sa},-35.603683,138.105284), - ($$5204$$,$$WIRRINA COVE$$,#{state_id_sa},-35.603683,138.105284), - ($$5210$$,$$MOUNT COMPASS$$,#{state_id_sa},-35.351091,138.621715), - ($$5210$$,$$MOUNT MAGNIFICENT$$,#{state_id_sa},-35.351091,138.621715), - ($$5210$$,$$NANGKITA$$,#{state_id_sa},-35.351091,138.621715), - ($$5211$$,$$BACK VALLEY$$,#{state_id_sa},-35.534413,138.529512), - ($$5211$$,$$ENCOUNTER BAY$$,#{state_id_sa},-35.534413,138.529512), - ($$5211$$,$$HAYBOROUGH$$,#{state_id_sa},-35.534413,138.529512), - ($$5211$$,$$HINDMARSH VALLEY$$,#{state_id_sa},-35.534413,138.529512), - ($$5211$$,$$INMAN VALLEY$$,#{state_id_sa},-35.534413,138.529512), - ($$5211$$,$$LOWER INMAN VALLEY$$,#{state_id_sa},-35.534413,138.529512), - ($$5211$$,$$MCCRACKEN$$,#{state_id_sa},-35.534413,138.529512), - ($$5211$$,$$MOUNT JAGGED$$,#{state_id_sa},-35.534413,138.529512), - ($$5211$$,$$VICTOR HARBOR$$,#{state_id_sa},-35.534413,138.529512), - ($$5211$$,$$WAITPINGA$$,#{state_id_sa},-35.534413,138.529512), - ($$5211$$,$$WILLOW CREEK$$,#{state_id_sa},-35.534413,138.529512), - ($$5211$$,$$YILKI$$,#{state_id_sa},-35.534413,138.529512), - ($$5212$$,$$PORT ELLIOT$$,#{state_id_sa},-35.531848,138.670529), - ($$5213$$,$$MIDDLETON$$,#{state_id_sa},-35.508951,138.707379), - ($$5214$$,$$CURRENCY CREEK$$,#{state_id_sa},-35.447992,138.766983), - ($$5214$$,$$GOOLWA$$,#{state_id_sa},-35.447992,138.766983), - ($$5214$$,$$GOOLWA BEACH$$,#{state_id_sa},-35.447992,138.766983), - ($$5214$$,$$GOOLWA NORTH$$,#{state_id_sa},-35.447992,138.766983), - ($$5214$$,$$GOOLWA SOUTH$$,#{state_id_sa},-35.447992,138.766983), - ($$5214$$,$$HINDMARSH ISLAND$$,#{state_id_sa},-35.447992,138.766983), - ($$5214$$,$$MOSQUITO HILL$$,#{state_id_sa},-35.447992,138.766983), - ($$5214$$,$$MUNDOO ISLAND$$,#{state_id_sa},-35.447992,138.766983), - ($$5220$$,$$PARNDANA$$,#{state_id_sa},0.0,0.0), - ($$5221$$,$$AMERICAN RIVER$$,#{state_id_sa},-35.773049,137.779956), - ($$5221$$,$$BALLAST HEAD$$,#{state_id_sa},-35.773049,137.779956), - ($$5221$$,$$MUSTON$$,#{state_id_sa},-35.773049,137.779956), - ($$5222$$,$$AMERICAN BEACH$$,#{state_id_sa},-35.776943,137.876499), - ($$5222$$,$$ANTECHAMBER BAY$$,#{state_id_sa},-35.776943,137.876499), - ($$5222$$,$$BAUDIN BEACH$$,#{state_id_sa},-35.776943,137.876499), - ($$5222$$,$$BROWNS BEACH$$,#{state_id_sa},-35.776943,137.876499), - ($$5222$$,$$CUTTLEFISH BAY$$,#{state_id_sa},-35.776943,137.876499), - ($$5222$$,$$DUDLEY EAST$$,#{state_id_sa},-35.776943,137.876499), - ($$5222$$,$$DUDLEY WEST$$,#{state_id_sa},-35.776943,137.876499), - ($$5222$$,$$HUNGERFORD$$,#{state_id_sa},-35.776943,137.876499), - ($$5222$$,$$IRONSTONE$$,#{state_id_sa},-35.776943,137.876499), - ($$5222$$,$$ISLAND BEACH$$,#{state_id_sa},-35.776943,137.876499), - ($$5222$$,$$KANGAROO HEAD$$,#{state_id_sa},-35.776943,137.876499), - ($$5222$$,$$PELICAN LAGOON$$,#{state_id_sa},-35.776943,137.876499), - ($$5222$$,$$PENNESHAW$$,#{state_id_sa},-35.776943,137.876499), - ($$5222$$,$$PORKY FLAT$$,#{state_id_sa},-35.776943,137.876499), - ($$5222$$,$$SAPPHIRETOWN$$,#{state_id_sa},-35.776943,137.876499), - ($$5222$$,$$WILLOUGHBY$$,#{state_id_sa},-35.776943,137.876499), - ($$5222$$,$$WILLSON RIVER$$,#{state_id_sa},-35.776943,137.876499), - ($$5223$$,$$BAY OF SHOALS$$,#{state_id_sa},-35.613861,137.572636), - ($$5223$$,$$BIRCHMORE$$,#{state_id_sa},-35.613861,137.572636), - ($$5223$$,$$BROWNLOW KI$$,#{state_id_sa},-35.613861,137.572636), - ($$5223$$,$$CAPE BORDA$$,#{state_id_sa},-35.613861,137.572636), - ($$5223$$,$$CASSINI$$,#{state_id_sa},-35.613861,137.572636), - ($$5223$$,$$CYGNET RIVER$$,#{state_id_sa},-35.613861,137.572636), - ($$5223$$,$$D'ESTREES BAY$$,#{state_id_sa},-35.613861,137.572636), - ($$5223$$,$$DE MOLE RIVER$$,#{state_id_sa},-35.613861,137.572636), - ($$5223$$,$$DUNCAN$$,#{state_id_sa},-35.613861,137.572636), - ($$5223$$,$$EMU BAY$$,#{state_id_sa},-35.613861,137.572636), - ($$5223$$,$$FLINDERS CHASE$$,#{state_id_sa},-35.613861,137.572636), - ($$5223$$,$$GOSSE$$,#{state_id_sa},-35.613861,137.572636), - ($$5223$$,$$HAINES$$,#{state_id_sa},-35.613861,137.572636), - ($$5223$$,$$HARRIET RIVER$$,#{state_id_sa},-35.613861,137.572636), - ($$5223$$,$$KARATTA$$,#{state_id_sa},-35.613861,137.572636), - ($$5223$$,$$KINGSCOTE$$,#{state_id_sa},-35.613861,137.572636), - ($$5223$$,$$KOHINOOR$$,#{state_id_sa},-35.613861,137.572636), - ($$5223$$,$$MACGILLIVRAY$$,#{state_id_sa},-35.613861,137.572636), - ($$5223$$,$$MENZIES$$,#{state_id_sa},-35.613861,137.572636), - ($$5223$$,$$MIDDLE RIVER$$,#{state_id_sa},-35.613861,137.572636), - ($$5223$$,$$NEPEAN BAY$$,#{state_id_sa},-35.613861,137.572636), - ($$5223$$,$$NEWLAND$$,#{state_id_sa},-35.613861,137.572636), - ($$5223$$,$$NORTH CAPE$$,#{state_id_sa},-35.613861,137.572636), - ($$5223$$,$$SEAL BAY$$,#{state_id_sa},-35.613861,137.572636), - ($$5223$$,$$SEDDON$$,#{state_id_sa},-35.613861,137.572636), - ($$5223$$,$$STOKES BAY$$,#{state_id_sa},-35.613861,137.572636), - ($$5223$$,$$STUN'SAIL BOOM$$,#{state_id_sa},-35.613861,137.572636), - ($$5223$$,$$VIVONNE BAY$$,#{state_id_sa},-35.613861,137.572636), - ($$5223$$,$$WESTERN RIVER$$,#{state_id_sa},-35.613861,137.572636), - ($$5223$$,$$WISANGER$$,#{state_id_sa},-35.613861,137.572636), - ($$5231$$,$$CHAIN OF PONDS$$,#{state_id_sa},-34.822343,138.832794), - ($$5231$$,$$KERSBROOK$$,#{state_id_sa},-34.822343,138.832794), - ($$5231$$,$$MILLBROOK$$,#{state_id_sa},-34.822343,138.832794), - ($$5232$$,$$CUDLEE CREEK$$,#{state_id_sa},-34.863338,138.855549), - ($$5233$$,$$FORRESTON$$,#{state_id_sa},-34.798003,138.898998), - ($$5233$$,$$GUMERACHA$$,#{state_id_sa},-34.798003,138.898998), - ($$5233$$,$$WARREN$$,#{state_id_sa},-34.798003,138.898998), - ($$5234$$,$$BIRDWOOD$$,#{state_id_sa},-34.953301,138.559527), - ($$5235$$,$$CROMER$$,#{state_id_sa},-34.785799,138.970244), - ($$5235$$,$$EDEN VALLEY$$,#{state_id_sa},-34.785799,138.970244), - ($$5235$$,$$FLAXMAN VALLEY$$,#{state_id_sa},-34.785799,138.970244), - ($$5235$$,$$MOUNT PLEASANT$$,#{state_id_sa},-34.785799,138.970244), - ($$5235$$,$$SPRINGTON$$,#{state_id_sa},-34.785799,138.970244), - ($$5235$$,$$TAUNTON$$,#{state_id_sa},-34.785799,138.970244), - ($$5236$$,$$TUNGKILLO$$,#{state_id_sa},-34.866175,139.064821), - ($$5237$$,$$APAMURRA$$,#{state_id_sa},-34.855242,139.19677), - ($$5237$$,$$MILENDELLA$$,#{state_id_sa},-34.855242,139.19677), - ($$5237$$,$$PALMER$$,#{state_id_sa},-34.855242,139.19677), - ($$5237$$,$$SANDERSTON$$,#{state_id_sa},-34.855242,139.19677), - ($$5238$$,$$ANGAS VALLEY$$,#{state_id_sa},-34.755133,139.306156), - ($$5238$$,$$BIG BEND$$,#{state_id_sa},-34.755133,139.306156), - ($$5238$$,$$BOLTO$$,#{state_id_sa},-34.755133,139.306156), - ($$5238$$,$$BOWHILL$$,#{state_id_sa},-34.755133,139.306156), - ($$5238$$,$$CAURNAMONT$$,#{state_id_sa},-34.755133,139.306156), - ($$5238$$,$$CLAYPANS$$,#{state_id_sa},-34.755133,139.306156), - ($$5238$$,$$COWIRRA$$,#{state_id_sa},-34.755133,139.306156), - ($$5238$$,$$FIVE MILES$$,#{state_id_sa},-34.755133,139.306156), - ($$5238$$,$$FORSTER$$,#{state_id_sa},-34.755133,139.306156), - ($$5238$$,$$FRAHNS$$,#{state_id_sa},-34.755133,139.306156), - ($$5238$$,$$FRAYVILLE$$,#{state_id_sa},-34.755133,139.306156), - ($$5238$$,$$JULANKA HOLDINGS$$,#{state_id_sa},-34.755133,139.306156), - ($$5238$$,$$LAKE CARLET$$,#{state_id_sa},-34.755133,139.306156), - ($$5238$$,$$MANNUM$$,#{state_id_sa},-34.755133,139.306156), - ($$5238$$,$$NILDOTTIE$$,#{state_id_sa},-34.755133,139.306156), - ($$5238$$,$$OLD TEAL FLAT$$,#{state_id_sa},-34.755133,139.306156), - ($$5238$$,$$PELLARING FLAT$$,#{state_id_sa},-34.755133,139.306156), - ($$5238$$,$$POMPOOTA$$,#{state_id_sa},-34.755133,139.306156), - ($$5238$$,$$PONDE$$,#{state_id_sa},-34.755133,139.306156), - ($$5238$$,$$PORT MANNUM$$,#{state_id_sa},-34.755133,139.306156), - ($$5238$$,$$PUNTHARI$$,#{state_id_sa},-34.755133,139.306156), - ($$5238$$,$$PURNONG$$,#{state_id_sa},-34.755133,139.306156), - ($$5238$$,$$PURNONG LANDING$$,#{state_id_sa},-34.755133,139.306156), - ($$5238$$,$$ROCKY POINT$$,#{state_id_sa},-34.755133,139.306156), - ($$5238$$,$$TEAL FLAT$$,#{state_id_sa},-34.755133,139.306156), - ($$5238$$,$$WALKER FLAT$$,#{state_id_sa},-34.755133,139.306156), - ($$5238$$,$$WALL FLAT$$,#{state_id_sa},-34.755133,139.306156), - ($$5238$$,$$WONGULLA$$,#{state_id_sa},-34.755133,139.306156), - ($$5238$$,$$WOODLANE$$,#{state_id_sa},-34.755133,139.306156), - ($$5238$$,$$YOUNGHUSBAND$$,#{state_id_sa},-34.755133,139.306156), - ($$5238$$,$$YOUNGHUSBAND HOLDINGS$$,#{state_id_sa},-34.755133,139.306156), - ($$5240$$,$$LENSWOOD$$,#{state_id_sa},-34.920014,138.828222), - ($$5241$$,$$LOBETHAL$$,#{state_id_sa},-34.911789,138.852975), - ($$5242$$,$$BALHANNAH$$,#{state_id_sa},-34.990072,138.827913), - ($$5243$$,$$OAKBANK$$,#{state_id_sa},-34.988216,138.838559), - ($$5244$$,$$CHARLESTON$$,#{state_id_sa},-34.918182,138.900501), - ($$5244$$,$$HARROGATE$$,#{state_id_sa},-34.918182,138.900501), - ($$5244$$,$$INVERBRACKIE$$,#{state_id_sa},-34.918182,138.900501), - ($$5244$$,$$MOUNT TORRENS$$,#{state_id_sa},-34.918182,138.900501), - ($$5244$$,$$WOODSIDE$$,#{state_id_sa},-34.918182,138.900501), - ($$5245$$,$$HAHNDORF$$,#{state_id_sa},-35.029715,138.81018), - ($$5245$$,$$PAECHTOWN$$,#{state_id_sa},-35.029715,138.81018), - ($$5245$$,$$VERDUN$$,#{state_id_sa},-35.029715,138.81018), - ($$5246$$,$$WOODHOUSE$$,#{state_id_sa},-34.927497,138.681881), - ($$5250$$,$$BLAKISTON$$,#{state_id_sa},-35.039607,138.884134), - ($$5250$$,$$LITTLEHAMPTON$$,#{state_id_sa},-35.039607,138.884134), - ($$5250$$,$$TOTNESS$$,#{state_id_sa},-35.039607,138.884134), - ($$5251$$,$$BUGLE RANGES$$,#{state_id_sa},-35.134951,138.887568), - ($$5251$$,$$MOUNT BARKER$$,#{state_id_sa},-35.134951,138.887568), - ($$5251$$,$$MOUNT BARKER JUNCTION$$,#{state_id_sa},-35.134951,138.887568), - ($$5251$$,$$MOUNT BARKER SPRINGS$$,#{state_id_sa},-35.134951,138.887568), - ($$5251$$,$$MOUNT BARKER SUMMIT$$,#{state_id_sa},-35.134951,138.887568), - ($$5251$$,$$WISTOW$$,#{state_id_sa},-35.134951,138.887568), - ($$5252$$,$$BRUKUNGA$$,#{state_id_sa},-35.003664,138.94171), - ($$5252$$,$$DAWESLEY$$,#{state_id_sa},-35.003664,138.94171), - ($$5252$$,$$HAY VALLEY$$,#{state_id_sa},-35.003664,138.94171), - ($$5252$$,$$KANMANTOO$$,#{state_id_sa},-35.003664,138.94171), - ($$5252$$,$$NAIRNE$$,#{state_id_sa},-35.003664,138.94171), - ($$5252$$,$$ST IVES$$,#{state_id_sa},-35.003664,138.94171), - ($$5253$$,$$AVOCA DELL$$,#{state_id_sa},-35.088554,139.308967), - ($$5253$$,$$BRINKLEY$$,#{state_id_sa},-35.088554,139.308967), - ($$5253$$,$$BURDETT$$,#{state_id_sa},-35.088554,139.308967), - ($$5253$$,$$CHAPMAN BORE$$,#{state_id_sa},-35.088554,139.308967), - ($$5253$$,$$ETTRICK$$,#{state_id_sa},-35.088554,139.308967), - ($$5253$$,$$GIFFORD HILL$$,#{state_id_sa},-35.088554,139.308967), - ($$5253$$,$$GREENBANKS$$,#{state_id_sa},-35.088554,139.308967), - ($$5253$$,$$LONG FLAT$$,#{state_id_sa},-35.088554,139.308967), - ($$5253$$,$$MOBILONG$$,#{state_id_sa},-35.088554,139.308967), - ($$5253$$,$$MURRAWONG$$,#{state_id_sa},-35.088554,139.308967), - ($$5253$$,$$MURRAY BRIDGE$$,#{state_id_sa},-35.088554,139.308967), - ($$5253$$,$$MURRAY BRIDGE EAST$$,#{state_id_sa},-35.088554,139.308967), - ($$5253$$,$$MURRAY BRIDGE NORTH$$,#{state_id_sa},-35.088554,139.308967), - ($$5253$$,$$MURRAY BRIDGE SOUTH$$,#{state_id_sa},-35.088554,139.308967), - ($$5253$$,$$NORTHERN HEIGHTS$$,#{state_id_sa},-35.088554,139.308967), - ($$5253$$,$$RIVERGLADES$$,#{state_id_sa},-35.088554,139.308967), - ($$5253$$,$$RIVERGLEN$$,#{state_id_sa},-35.088554,139.308967), - ($$5253$$,$$ROCKY GULLY$$,#{state_id_sa},-35.088554,139.308967), - ($$5253$$,$$SUNNYSIDE$$,#{state_id_sa},-35.088554,139.308967), - ($$5253$$,$$SWANPORT$$,#{state_id_sa},-35.088554,139.308967), - ($$5253$$,$$TOORA$$,#{state_id_sa},-35.088554,139.308967), - ($$5253$$,$$WHITE HILL$$,#{state_id_sa},-35.088554,139.308967), - ($$5253$$,$$WHITE SANDS$$,#{state_id_sa},-35.088554,139.308967), - ($$5253$$,$$WILLOW BANKS$$,#{state_id_sa},-35.088554,139.308967), - ($$5253$$,$$WOODS POINT$$,#{state_id_sa},-35.088554,139.308967), - ($$5254$$,$$CALLINGTON$$,#{state_id_sa},-35.333047,139.875715), - ($$5254$$,$$CALOOTE$$,#{state_id_sa},-35.333047,139.875715), - ($$5254$$,$$MONARTO$$,#{state_id_sa},-35.333047,139.875715), - ($$5254$$,$$MONARTO SOUTH$$,#{state_id_sa},-35.333047,139.875715), - ($$5254$$,$$MONTEITH$$,#{state_id_sa},-35.333047,139.875715), - ($$5254$$,$$MURRAY BRIDGE$$,#{state_id_sa},-35.333047,139.875715), - ($$5254$$,$$MYPOLONGA$$,#{state_id_sa},-35.333047,139.875715), - ($$5254$$,$$PALLAMANA$$,#{state_id_sa},-35.333047,139.875715), - ($$5254$$,$$PETWOOD$$,#{state_id_sa},-35.333047,139.875715), - ($$5254$$,$$ROCKLEIGH$$,#{state_id_sa},-35.333047,139.875715), - ($$5254$$,$$TEPKO$$,#{state_id_sa},-35.333047,139.875715), - ($$5254$$,$$ZADOWS LANDING$$,#{state_id_sa},-35.333047,139.875715), - ($$5255$$,$$ANGAS PLAINS$$,#{state_id_sa},-35.317169,138.997514), - ($$5255$$,$$BELVIDERE$$,#{state_id_sa},-35.317169,138.997514), - ($$5255$$,$$BLETCHLEY$$,#{state_id_sa},-35.317169,138.997514), - ($$5255$$,$$FINNISS$$,#{state_id_sa},-35.317169,138.997514), - ($$5255$$,$$GEMMELLS$$,#{state_id_sa},-35.317169,138.997514), - ($$5255$$,$$HARTLEY$$,#{state_id_sa},-35.317169,138.997514), - ($$5255$$,$$HIGHLAND VALLEY$$,#{state_id_sa},-35.317169,138.997514), - ($$5255$$,$$LAKE PLAINS$$,#{state_id_sa},-35.317169,138.997514), - ($$5255$$,$$LANGHORNE CREEK$$,#{state_id_sa},-35.317169,138.997514), - ($$5255$$,$$MOUNT OBSERVATION$$,#{state_id_sa},-35.317169,138.997514), - ($$5255$$,$$MULGUNDAWA$$,#{state_id_sa},-35.317169,138.997514), - ($$5255$$,$$NALPA$$,#{state_id_sa},-35.317169,138.997514), - ($$5255$$,$$RED CREEK$$,#{state_id_sa},-35.317169,138.997514), - ($$5255$$,$$SALEM$$,#{state_id_sa},-35.317169,138.997514), - ($$5255$$,$$SANDERGROVE$$,#{state_id_sa},-35.317169,138.997514), - ($$5255$$,$$STRATHALBYN$$,#{state_id_sa},-35.317169,138.997514), - ($$5255$$,$$TOOPERANG$$,#{state_id_sa},-35.317169,138.997514), - ($$5255$$,$$WILLYAROO$$,#{state_id_sa},-35.317169,138.997514), - ($$5255$$,$$WOODCHESTER$$,#{state_id_sa},-35.317169,138.997514), - ($$5256$$,$$CLAYTON BAY$$,#{state_id_sa},-35.497238,138.928769), - ($$5256$$,$$MILANG$$,#{state_id_sa},-35.497238,138.928769), - ($$5256$$,$$NURRAGI$$,#{state_id_sa},-35.497238,138.928769), - ($$5256$$,$$POINT STURT$$,#{state_id_sa},-35.497238,138.928769), - ($$5256$$,$$TOLDEROL$$,#{state_id_sa},-35.497238,138.928769), - ($$5259$$,$$ASHVILLE$$,#{state_id_sa},-35.510451,139.365759), - ($$5259$$,$$JERVOIS$$,#{state_id_sa},-35.510451,139.365759), - ($$5259$$,$$KEPA$$,#{state_id_sa},-35.510451,139.365759), - ($$5259$$,$$MALINONG$$,#{state_id_sa},-35.510451,139.365759), - ($$5259$$,$$NARRUNG$$,#{state_id_sa},-35.510451,139.365759), - ($$5259$$,$$NATURI$$,#{state_id_sa},-35.510451,139.365759), - ($$5259$$,$$POINT MCLEAY$$,#{state_id_sa},-35.510451,139.365759), - ($$5259$$,$$POLTALLOCH$$,#{state_id_sa},-35.510451,139.365759), - ($$5259$$,$$RAUKKAN$$,#{state_id_sa},-35.510451,139.365759), - ($$5259$$,$$TAILEM BEND$$,#{state_id_sa},-35.510451,139.365759), - ($$5259$$,$$WELLINGTON$$,#{state_id_sa},-35.510451,139.365759), - ($$5259$$,$$WELLINGTON EAST$$,#{state_id_sa},-35.510451,139.365759), - ($$5260$$,$$ELWOMPLE$$,#{state_id_sa},-34.605515,140.296746), - ($$5260$$,$$TAILEM BEND$$,#{state_id_sa},-34.605515,140.296746), - ($$5261$$,$$COOKE PLAINS$$,#{state_id_sa},-35.379387,139.562175), - ($$5261$$,$$COOMANDOOK$$,#{state_id_sa},-35.379387,139.562175), - ($$5261$$,$$CULBURRA$$,#{state_id_sa},-35.379387,139.562175), - ($$5261$$,$$KI KI$$,#{state_id_sa},-35.379387,139.562175), - ($$5261$$,$$YUMALI$$,#{state_id_sa},-35.379387,139.562175), - ($$5262$$,$$BINNUM$$,#{state_id_sa},-36.795548,140.929888), - ($$5262$$,$$FRANCES$$,#{state_id_sa},-36.795548,140.929888), - ($$5262$$,$$HYNAM$$,#{state_id_sa},-36.795548,140.929888), - ($$5262$$,$$KYBYBOLITE$$,#{state_id_sa},-36.795548,140.929888), - ($$5263$$,$$COONAWARRA$$,#{state_id_sa},-37.292105,140.839049), - ($$5264$$,$$COORONG$$,#{state_id_sa},-35.57145,138.979395), - ($$5264$$,$$MENINGIE$$,#{state_id_sa},-35.57145,138.979395), - ($$5264$$,$$MENINGIE EAST$$,#{state_id_sa},-35.57145,138.979395), - ($$5264$$,$$MENINGIE WEST$$,#{state_id_sa},-35.57145,138.979395), - ($$5264$$,$$POLICEMAN POINT$$,#{state_id_sa},-35.57145,138.979395), - ($$5264$$,$$SALT CREEK$$,#{state_id_sa},-35.57145,138.979395), - ($$5264$$,$$WALTOWA$$,#{state_id_sa},-35.57145,138.979395), - ($$5265$$,$$COONALPYN$$,#{state_id_sa},-35.696027,139.856924), - ($$5265$$,$$FIELD$$,#{state_id_sa},-35.696027,139.856924), - ($$5266$$,$$BUNBURY$$,#{state_id_sa},-36.237582,139.97306), - ($$5266$$,$$COLEBATCH$$,#{state_id_sa},-36.237582,139.97306), - ($$5266$$,$$DEEPWATER$$,#{state_id_sa},-36.237582,139.97306), - ($$5266$$,$$TINTINARA$$,#{state_id_sa},-36.237582,139.97306), - ($$5267$$,$$BRIMBAGO$$,#{state_id_sa},-36.166689,140.46619), - ($$5267$$,$$COOMBE$$,#{state_id_sa},-36.166689,140.46619), - ($$5267$$,$$KEITH$$,#{state_id_sa},-36.166689,140.46619), - ($$5267$$,$$LAFFER$$,#{state_id_sa},-36.166689,140.46619), - ($$5267$$,$$MAKIN$$,#{state_id_sa},-36.166689,140.46619), - ($$5267$$,$$MCCALLUM$$,#{state_id_sa},-36.166689,140.46619), - ($$5267$$,$$MOUNT CHARLES$$,#{state_id_sa},-36.166689,140.46619), - ($$5267$$,$$PETHERICK$$,#{state_id_sa},-36.166689,140.46619), - ($$5267$$,$$SHAUGH$$,#{state_id_sa},-36.166689,140.46619), - ($$5267$$,$$SHERWOOD$$,#{state_id_sa},-36.166689,140.46619), - ($$5267$$,$$WILLALOOKA$$,#{state_id_sa},-36.166689,140.46619), - ($$5267$$,$$WIRREGA$$,#{state_id_sa},-36.166689,140.46619), - ($$5268$$,$$BANGHAM$$,#{state_id_sa},-36.566777,140.940619), - ($$5268$$,$$BORDERTOWN$$,#{state_id_sa},-36.566777,140.940619), - ($$5268$$,$$BORDERTOWN SOUTH$$,#{state_id_sa},-36.566777,140.940619), - ($$5268$$,$$CANNAWIGARA$$,#{state_id_sa},-36.566777,140.940619), - ($$5268$$,$$LOWAN VALE$$,#{state_id_sa},-36.566777,140.940619), - ($$5268$$,$$POOGINAGORIC$$,#{state_id_sa},-36.566777,140.940619), - ($$5268$$,$$SENIOR$$,#{state_id_sa},-36.566777,140.940619), - ($$5268$$,$$WESTERN FLAT$$,#{state_id_sa},-36.566777,140.940619), - ($$5269$$,$$CUSTON$$,#{state_id_sa},-36.276267,140.911889), - ($$5269$$,$$PINE HILL$$,#{state_id_sa},-36.276267,140.911889), - ($$5269$$,$$WOLSELEY$$,#{state_id_sa},-36.276267,140.911889), - ($$5270$$,$$BUCKINGHAM$$,#{state_id_sa},-36.341305,140.554067), - ($$5270$$,$$CAREW$$,#{state_id_sa},-36.341305,140.554067), - ($$5270$$,$$KONGAL$$,#{state_id_sa},-36.341305,140.554067), - ($$5270$$,$$MUNDULLA$$,#{state_id_sa},-36.341305,140.554067), - ($$5270$$,$$MUNDULLA WEST$$,#{state_id_sa},-36.341305,140.554067), - ($$5270$$,$$SWEDE FLAT$$,#{state_id_sa},-36.341305,140.554067), - ($$5271$$,$$BOOL LAGOON$$,#{state_id_sa},-37.143673,140.71502), - ($$5271$$,$$CADGEE$$,#{state_id_sa},-37.143673,140.71502), - ($$5271$$,$$JOANNA$$,#{state_id_sa},-37.143673,140.71502), - ($$5271$$,$$KEPPOCH$$,#{state_id_sa},-37.143673,140.71502), - ($$5271$$,$$KOPPAMURRA$$,#{state_id_sa},-37.143673,140.71502), - ($$5271$$,$$LAURIE PARK$$,#{state_id_sa},-37.143673,140.71502), - ($$5271$$,$$LOCHABER$$,#{state_id_sa},-37.143673,140.71502), - ($$5271$$,$$MARCOLLAT$$,#{state_id_sa},-37.143673,140.71502), - ($$5271$$,$$MOUNT LIGHT$$,#{state_id_sa},-37.143673,140.71502), - ($$5271$$,$$MOYHALL$$,#{state_id_sa},-37.143673,140.71502), - ($$5271$$,$$NARACOORTE$$,#{state_id_sa},-37.143673,140.71502), - ($$5271$$,$$PADTHAWAY$$,#{state_id_sa},-37.143673,140.71502), - ($$5271$$,$$SPENCE$$,#{state_id_sa},-37.143673,140.71502), - ($$5271$$,$$STEWART RANGE$$,#{state_id_sa},-37.143673,140.71502), - ($$5271$$,$$STRUAN$$,#{state_id_sa},-37.143673,140.71502), - ($$5271$$,$$THE GAP$$,#{state_id_sa},-37.143673,140.71502), - ($$5271$$,$$WILD DOG VALLEY$$,#{state_id_sa},-37.143673,140.71502), - ($$5271$$,$$WRATTONBULLY$$,#{state_id_sa},-37.143673,140.71502), - ($$5272$$,$$COLES$$,#{state_id_sa},-37.256253,140.611845), - ($$5272$$,$$CONMURRA$$,#{state_id_sa},-37.256253,140.611845), - ($$5272$$,$$FOX$$,#{state_id_sa},-37.256253,140.611845), - ($$5272$$,$$GREENWAYS$$,#{state_id_sa},-37.256253,140.611845), - ($$5272$$,$$LUCINDALE$$,#{state_id_sa},-37.256253,140.611845), - ($$5272$$,$$WOOLUMBOOL$$,#{state_id_sa},-37.256253,140.611845), - ($$5273$$,$$AVENUE RANGE$$,#{state_id_sa},-34.803467,138.755016), - ($$5275$$,$$BLACKFORD$$,#{state_id_sa},-36.784423,140.020981), - ($$5275$$,$$BOATSWAIN POINT$$,#{state_id_sa},-36.784423,140.020981), - ($$5275$$,$$CAPE JAFFA$$,#{state_id_sa},-36.784423,140.020981), - ($$5275$$,$$KEILIRA$$,#{state_id_sa},-36.784423,140.020981), - ($$5275$$,$$KINGSTON SE$$,#{state_id_sa},-36.784423,140.020981), - ($$5275$$,$$MOUNT BENSON$$,#{state_id_sa},-36.784423,140.020981), - ($$5275$$,$$PINKS BEACH$$,#{state_id_sa},-36.784423,140.020981), - ($$5275$$,$$REEDY CREEK$$,#{state_id_sa},-36.784423,140.020981), - ($$5275$$,$$ROSETOWN$$,#{state_id_sa},-36.784423,140.020981), - ($$5275$$,$$SANDY GROVE$$,#{state_id_sa},-36.784423,140.020981), - ($$5275$$,$$TARATAP$$,#{state_id_sa},-36.784423,140.020981), - ($$5275$$,$$TILLEY SWAMP$$,#{state_id_sa},-36.784423,140.020981), - ($$5275$$,$$WANGOLINA$$,#{state_id_sa},-36.784423,140.020981), - ($$5275$$,$$WEST RANGE$$,#{state_id_sa},-36.784423,140.020981), - ($$5275$$,$$WYOMI$$,#{state_id_sa},-36.784423,140.020981), - ($$5276$$,$$BRAY$$,#{state_id_sa},-37.269544,139.954379), - ($$5276$$,$$NORA CREINA$$,#{state_id_sa},-37.269544,139.954379), - ($$5276$$,$$ROBE$$,#{state_id_sa},-37.269544,139.954379), - ($$5277$$,$$COMAUM$$,#{state_id_sa},-37.231471,140.934145), - ($$5277$$,$$GLENROY$$,#{state_id_sa},-37.231471,140.934145), - ($$5277$$,$$MAAOUPE$$,#{state_id_sa},-37.231471,140.934145), - ($$5277$$,$$MONBULLA$$,#{state_id_sa},-37.231471,140.934145), - ($$5277$$,$$NANGWARRY$$,#{state_id_sa},-37.231471,140.934145), - ($$5277$$,$$PENOLA$$,#{state_id_sa},-37.231471,140.934145), - ($$5277$$,$$PLEASANT PARK$$,#{state_id_sa},-37.231471,140.934145), - ($$5277$$,$$TARPEENA$$,#{state_id_sa},-37.231471,140.934145), - ($$5278$$,$$KALANGADOO$$,#{state_id_sa},-37.561697,140.701318), - ($$5278$$,$$KRONGART$$,#{state_id_sa},-37.561697,140.701318), - ($$5278$$,$$MOERLONG$$,#{state_id_sa},-37.561697,140.701318), - ($$5278$$,$$WEPAR$$,#{state_id_sa},-37.561697,140.701318), - ($$5279$$,$$KOORINE$$,#{state_id_sa},-37.624772,140.654593), - ($$5279$$,$$MOUNT BURR$$,#{state_id_sa},-37.624772,140.654593), - ($$5279$$,$$MOUNT MCINTYRE$$,#{state_id_sa},-37.624772,140.654593), - ($$5279$$,$$SHORT$$,#{state_id_sa},-37.624772,140.654593), - ($$5279$$,$$TRIHI$$,#{state_id_sa},-37.624772,140.654593), - ($$5279$$,$$WATTLE RANGE EAST$$,#{state_id_sa},-37.624772,140.654593), - ($$5280$$,$$BEACHPORT$$,#{state_id_sa},-37.480978,140.01304), - ($$5280$$,$$CLAY WELLS$$,#{state_id_sa},-37.480978,140.01304), - ($$5280$$,$$FURNER$$,#{state_id_sa},-37.480978,140.01304), - ($$5280$$,$$GERMAN CREEK$$,#{state_id_sa},-37.480978,140.01304), - ($$5280$$,$$GERMAN FLAT$$,#{state_id_sa},-37.480978,140.01304), - ($$5280$$,$$HATHERLEIGH$$,#{state_id_sa},-37.480978,140.01304), - ($$5280$$,$$KANGAROO INN$$,#{state_id_sa},-37.480978,140.01304), - ($$5280$$,$$MAGAREY$$,#{state_id_sa},-37.480978,140.01304), - ($$5280$$,$$MILLICENT$$,#{state_id_sa},-37.480978,140.01304), - ($$5280$$,$$RENDELSHAM$$,#{state_id_sa},-37.480978,140.01304), - ($$5280$$,$$ROCKY CAMP$$,#{state_id_sa},-37.480978,140.01304), - ($$5280$$,$$SEBASTOPOL$$,#{state_id_sa},-37.480978,140.01304), - ($$5280$$,$$SOUTHEND$$,#{state_id_sa},-37.480978,140.01304), - ($$5280$$,$$TANTANOOLA$$,#{state_id_sa},-37.480978,140.01304), - ($$5280$$,$$THORNLEA$$,#{state_id_sa},-37.480978,140.01304), - ($$5280$$,$$WATTLE RANGE$$,#{state_id_sa},-37.480978,140.01304), - ($$5290$$,$$MOUNT GAMBIER$$,#{state_id_sa},-37.826321,140.783303), - ($$5290$$,$$MOUNT GAMBIER DC$$,#{state_id_sa},-37.826321,140.783303), - ($$5291$$,$$ALLENDALE EAST$$,#{state_id_sa},-38.003369,140.70894), - ($$5291$$,$$BLACKFELLOWS CAVES$$,#{state_id_sa},-38.003369,140.70894), - ($$5291$$,$$BURRUNGULE$$,#{state_id_sa},-38.003369,140.70894), - ($$5291$$,$$CANUNDA$$,#{state_id_sa},-38.003369,140.70894), - ($$5291$$,$$CAPE DOUGLAS$$,#{state_id_sa},-38.003369,140.70894), - ($$5291$$,$$CAROLINE$$,#{state_id_sa},-38.003369,140.70894), - ($$5291$$,$$CARPENTER ROCKS$$,#{state_id_sa},-38.003369,140.70894), - ($$5291$$,$$CAVETON$$,#{state_id_sa},-38.003369,140.70894), - ($$5291$$,$$COMPTON$$,#{state_id_sa},-38.003369,140.70894), - ($$5291$$,$$DISMAL SWAMP$$,#{state_id_sa},-38.003369,140.70894), - ($$5291$$,$$DONOVANS$$,#{state_id_sa},-38.003369,140.70894), - ($$5291$$,$$EIGHT MILE CREEK$$,#{state_id_sa},-38.003369,140.70894), - ($$5291$$,$$GLENBURNIE$$,#{state_id_sa},-38.003369,140.70894), - ($$5291$$,$$GLENCOE$$,#{state_id_sa},-38.003369,140.70894), - ($$5291$$,$$GLENCOE WEST$$,#{state_id_sa},-38.003369,140.70894), - ($$5291$$,$$KONGORONG$$,#{state_id_sa},-38.003369,140.70894), - ($$5291$$,$$MIL LEL$$,#{state_id_sa},-38.003369,140.70894), - ($$5291$$,$$MINGBOOL$$,#{state_id_sa},-38.003369,140.70894), - ($$5291$$,$$MOORAK$$,#{state_id_sa},-38.003369,140.70894), - ($$5291$$,$$MOUNT GAMBIER$$,#{state_id_sa},-38.003369,140.70894), - ($$5291$$,$$MOUNT GAMBIER EAST$$,#{state_id_sa},-38.003369,140.70894), - ($$5291$$,$$MOUNT GAMBIER WEST$$,#{state_id_sa},-38.003369,140.70894), - ($$5291$$,$$MOUNT SCHANK$$,#{state_id_sa},-38.003369,140.70894), - ($$5291$$,$$NENE VALLEY$$,#{state_id_sa},-38.003369,140.70894), - ($$5291$$,$$OB FLAT$$,#{state_id_sa},-38.003369,140.70894), - ($$5291$$,$$PELICAN POINT$$,#{state_id_sa},-38.003369,140.70894), - ($$5291$$,$$PORT MACDONNELL$$,#{state_id_sa},-38.003369,140.70894), - ($$5291$$,$$RACECOURSE BAY$$,#{state_id_sa},-38.003369,140.70894), - ($$5291$$,$$SQUARE MILE$$,#{state_id_sa},-38.003369,140.70894), - ($$5291$$,$$SUTTONTOWN$$,#{state_id_sa},-38.003369,140.70894), - ($$5291$$,$$WANDILO$$,#{state_id_sa},-38.003369,140.70894), - ($$5291$$,$$WORROLONG$$,#{state_id_sa},-38.003369,140.70894), - ($$5291$$,$$WYE$$,#{state_id_sa},-38.003369,140.70894), - ($$5291$$,$$YAHL$$,#{state_id_sa},-38.003369,140.70894), - ($$5301$$,$$CARCUMA$$,#{state_id_sa},-35.575453,140.043325), - ($$5301$$,$$GERANIUM$$,#{state_id_sa},-35.575453,140.043325), - ($$5301$$,$$JABUK$$,#{state_id_sa},-35.575453,140.043325), - ($$5301$$,$$MOORLANDS$$,#{state_id_sa},-35.575453,140.043325), - ($$5301$$,$$NETHERTON$$,#{state_id_sa},-35.575453,140.043325), - ($$5301$$,$$PARRAKIE$$,#{state_id_sa},-35.575453,140.043325), - ($$5301$$,$$PEAKE$$,#{state_id_sa},-35.575453,140.043325), - ($$5301$$,$$SHERLOCK$$,#{state_id_sa},-35.575453,140.043325), - ($$5301$$,$$WILKAWATT$$,#{state_id_sa},-35.575453,140.043325), - ($$5302$$,$$LAMEROO$$,#{state_id_sa},-35.329274,140.517994), - ($$5302$$,$$NGARKAT$$,#{state_id_sa},-35.329274,140.517994), - ($$5303$$,$$PARILLA$$,#{state_id_sa},-35.420272,140.689555), - ($$5304$$,$$KRINGIN$$,#{state_id_sa},-34.986253,140.784302), - ($$5304$$,$$PEEBINGA$$,#{state_id_sa},-34.986253,140.784302), - ($$5304$$,$$PINNAROO$$,#{state_id_sa},-34.986253,140.784302), - ($$5306$$,$$WYNARKA$$,#{state_id_sa},-35.131226,139.731411), - ($$5307$$,$$KAROONDA$$,#{state_id_sa},-35.095535,139.893113), - ($$5307$$,$$KARTE$$,#{state_id_sa},-35.095535,139.893113), - ($$5307$$,$$KULKAMI$$,#{state_id_sa},-35.095535,139.893113), - ($$5307$$,$$MARAMA$$,#{state_id_sa},-35.095535,139.893113), - ($$5307$$,$$MOOTATUNGA$$,#{state_id_sa},-35.095535,139.893113), - ($$5308$$,$$COPEVILLE$$,#{state_id_sa},-34.794332,139.848689), - ($$5308$$,$$GALGA$$,#{state_id_sa},-34.794332,139.848689), - ($$5308$$,$$KALYAN$$,#{state_id_sa},-34.794332,139.848689), - ($$5308$$,$$MANTUNG$$,#{state_id_sa},-34.794332,139.848689), - ($$5308$$,$$MERCUNDA$$,#{state_id_sa},-34.794332,139.848689), - ($$5308$$,$$PERPONDA$$,#{state_id_sa},-34.794332,139.848689), - ($$5309$$,$$BORRIKA$$,#{state_id_sa},-35.023488,140.046185), - ($$5309$$,$$HALIDON$$,#{state_id_sa},-35.023488,140.046185), - ($$5309$$,$$MINDARIE$$,#{state_id_sa},-35.023488,140.046185), - ($$5309$$,$$SANDALWOOD$$,#{state_id_sa},-35.023488,140.046185), - ($$5310$$,$$CALIPH$$,#{state_id_sa},-34.65471,140.282598), - ($$5310$$,$$WANBI$$,#{state_id_sa},-34.65471,140.282598), - ($$5311$$,$$ALAWOONA$$,#{state_id_sa},-34.735891,140.508785), - ($$5311$$,$$BILLIATT$$,#{state_id_sa},-34.735891,140.508785), - ($$5311$$,$$BUGLE HUT$$,#{state_id_sa},-34.735891,140.508785), - ($$5311$$,$$MAGGEA$$,#{state_id_sa},-34.735891,140.508785), - ($$5311$$,$$MALPAS$$,#{state_id_sa},-34.735891,140.508785), - ($$5311$$,$$MERIBAH$$,#{state_id_sa},-34.735891,140.508785), - ($$5311$$,$$PARUNA$$,#{state_id_sa},-34.735891,140.508785), - ($$5311$$,$$SCHELL WELL$$,#{state_id_sa},-34.735891,140.508785), - ($$5311$$,$$TALDRA$$,#{state_id_sa},-34.735891,140.508785), - ($$5311$$,$$TAPLAN$$,#{state_id_sa},-34.735891,140.508785), - ($$5311$$,$$VEITCH$$,#{state_id_sa},-34.735891,140.508785), - ($$5311$$,$$WOODLEIGH$$,#{state_id_sa},-34.735891,140.508785), - ($$5311$$,$$WUNKAR$$,#{state_id_sa},-34.735891,140.508785), - ($$5320$$,$$BEATTY$$,#{state_id_sa},-34.005379,139.435986), - ($$5320$$,$$BEAUMONTS$$,#{state_id_sa},-34.005379,139.435986), - ($$5320$$,$$BRENDA PARK$$,#{state_id_sa},-34.005379,139.435986), - ($$5320$$,$$BUNDEY$$,#{state_id_sa},-34.005379,139.435986), - ($$5320$$,$$EBA$$,#{state_id_sa},-34.005379,139.435986), - ($$5320$$,$$LINDLEY$$,#{state_id_sa},-34.005379,139.435986), - ($$5320$$,$$MAUDE$$,#{state_id_sa},-34.005379,139.435986), - ($$5320$$,$$MORGAN$$,#{state_id_sa},-34.005379,139.435986), - ($$5320$$,$$MORPHETTS FLAT$$,#{state_id_sa},-34.005379,139.435986), - ($$5320$$,$$MURBKO$$,#{state_id_sa},-34.005379,139.435986), - ($$5320$$,$$NORTH WEST BEND$$,#{state_id_sa},-34.005379,139.435986), - ($$5320$$,$$STUART$$,#{state_id_sa},-34.005379,139.435986), - ($$5320$$,$$WOMBATS REST$$,#{state_id_sa},-34.005379,139.435986), - ($$5321$$,$$CADELL$$,#{state_id_sa},-34.089801,139.766386), - ($$5321$$,$$CADELL LAGOON$$,#{state_id_sa},-34.089801,139.766386), - ($$5322$$,$$GOLDEN HEIGHTS$$,#{state_id_sa},-34.20301,139.937836), - ($$5322$$,$$QUALCO$$,#{state_id_sa},-34.20301,139.937836), - ($$5322$$,$$RAMCO$$,#{state_id_sa},-34.20301,139.937836), - ($$5322$$,$$RAMCO HEIGHTS$$,#{state_id_sa},-34.20301,139.937836), - ($$5322$$,$$SUNLANDS$$,#{state_id_sa},-34.20301,139.937836), - ($$5330$$,$$BOOLGUN$$,#{state_id_sa},-34.417938,140.035181), - ($$5330$$,$$DEVLINS POUND$$,#{state_id_sa},-34.417938,140.035181), - ($$5330$$,$$GOOD HOPE LANDING$$,#{state_id_sa},-34.417938,140.035181), - ($$5330$$,$$HOLDER$$,#{state_id_sa},-34.417938,140.035181), - ($$5330$$,$$HOLDER SIDING$$,#{state_id_sa},-34.417938,140.035181), - ($$5330$$,$$KANNI$$,#{state_id_sa},-34.417938,140.035181), - ($$5330$$,$$LOWBANK$$,#{state_id_sa},-34.417938,140.035181), - ($$5330$$,$$MARKARANKA$$,#{state_id_sa},-34.417938,140.035181), - ($$5330$$,$$OVERLAND CORNER$$,#{state_id_sa},-34.417938,140.035181), - ($$5330$$,$$POOGINOOK$$,#{state_id_sa},-34.417938,140.035181), - ($$5330$$,$$STOCKYARD PLAIN$$,#{state_id_sa},-34.417938,140.035181), - ($$5330$$,$$TAYLORVILLE$$,#{state_id_sa},-34.417938,140.035181), - ($$5330$$,$$WAIKERIE$$,#{state_id_sa},-34.417938,140.035181), - ($$5330$$,$$WOOLPUNDA$$,#{state_id_sa},-34.417938,140.035181), - ($$5331$$,$$KINGSTON ON MURRAY$$,#{state_id_sa},-34.221812,140.347914), - ($$5332$$,$$MOOROOK$$,#{state_id_sa},-34.290548,140.364606), - ($$5332$$,$$MOOROOK SOUTH$$,#{state_id_sa},-34.290548,140.364606), - ($$5332$$,$$WAPPILKA$$,#{state_id_sa},-34.290548,140.364606), - ($$5332$$,$$YINKANIE$$,#{state_id_sa},-34.290548,140.364606), - ($$5333$$,$$BOOKPURNONG$$,#{state_id_sa},-34.345379,140.604139), - ($$5333$$,$$LOXTON$$,#{state_id_sa},-34.345379,140.604139), - ($$5333$$,$$LOXTON NORTH$$,#{state_id_sa},-34.345379,140.604139), - ($$5333$$,$$NEW RESIDENCE$$,#{state_id_sa},-34.345379,140.604139), - ($$5333$$,$$PATA$$,#{state_id_sa},-34.345379,140.604139), - ($$5333$$,$$PYAP$$,#{state_id_sa},-34.345379,140.604139), - ($$5333$$,$$PYAP WEST$$,#{state_id_sa},-34.345379,140.604139), - ($$5340$$,$$MUNDIC CREEK$$,#{state_id_sa},-34.215647,140.8109), - ($$5340$$,$$MURTHO$$,#{state_id_sa},-34.215647,140.8109), - ($$5340$$,$$PARINGA$$,#{state_id_sa},-34.215647,140.8109), - ($$5340$$,$$PIKE RIVER$$,#{state_id_sa},-34.215647,140.8109), - ($$5340$$,$$WONUARRA$$,#{state_id_sa},-34.215647,140.8109), - ($$5340$$,$$YAMBA$$,#{state_id_sa},-34.215647,140.8109), - ($$5341$$,$$CHAFFEY$$,#{state_id_sa},-34.205781,140.689287), - ($$5341$$,$$COOLTONG$$,#{state_id_sa},-34.205781,140.689287), - ($$5341$$,$$CRESCENT$$,#{state_id_sa},-34.205781,140.689287), - ($$5341$$,$$OLD CALPERUM$$,#{state_id_sa},-34.205781,140.689287), - ($$5341$$,$$RENMARK$$,#{state_id_sa},-34.205781,140.689287), - ($$5341$$,$$RENMARK NORTH$$,#{state_id_sa},-34.205781,140.689287), - ($$5341$$,$$RENMARK SOUTH$$,#{state_id_sa},-34.205781,140.689287), - ($$5341$$,$$RENMARK WEST$$,#{state_id_sa},-34.205781,140.689287), - ($$5342$$,$$MONASH$$,#{state_id_sa},-34.238211,140.557623), - ($$5343$$,$$BERRI$$,#{state_id_sa},-34.285487,140.601715), - ($$5343$$,$$GERARD$$,#{state_id_sa},-34.285487,140.601715), - ($$5343$$,$$GURRA GURRA$$,#{state_id_sa},-34.285487,140.601715), - ($$5343$$,$$KATARAPKO$$,#{state_id_sa},-34.285487,140.601715), - ($$5343$$,$$LYRUP$$,#{state_id_sa},-34.285487,140.601715), - ($$5343$$,$$WINKIE$$,#{state_id_sa},-34.285487,140.601715), - ($$5344$$,$$GLOSSOP$$,#{state_id_sa},-34.2692,140.528371), - ($$5345$$,$$BARMERA$$,#{state_id_sa},-34.252037,140.466708), - ($$5345$$,$$LOVEDAY$$,#{state_id_sa},-34.252037,140.466708), - ($$5345$$,$$SPECTACLE LAKE$$,#{state_id_sa},-34.252037,140.466708), - ($$5346$$,$$COBDOGLA$$,#{state_id_sa},-34.241451,140.408119), - ($$5350$$,$$ROSEDALE$$,#{state_id_sa},-34.550593,138.84665), - ($$5350$$,$$SANDY CREEK$$,#{state_id_sa},-34.550593,138.84665), - ($$5351$$,$$ALTONA$$,#{state_id_sa},-34.597167,138.91362), - ($$5351$$,$$BAROSSA GOLDFIELDS$$,#{state_id_sa},-34.597167,138.91362), - ($$5351$$,$$COCKATOO VALLEY$$,#{state_id_sa},-34.597167,138.91362), - ($$5351$$,$$LYNDOCH$$,#{state_id_sa},-34.597167,138.91362), - ($$5351$$,$$MOUNT CRAWFORD$$,#{state_id_sa},-34.597167,138.91362), - ($$5351$$,$$PEWSEY VALE$$,#{state_id_sa},-34.597167,138.91362), - ($$5351$$,$$WILLIAMSTOWN$$,#{state_id_sa},-34.597167,138.91362), - ($$5352$$,$$BETHANY$$,#{state_id_sa},-34.541083,138.979618), - ($$5352$$,$$GOMERSAL$$,#{state_id_sa},-34.541083,138.979618), - ($$5352$$,$$KRONDORF$$,#{state_id_sa},-34.541083,138.979618), - ($$5352$$,$$ROWLAND FLAT$$,#{state_id_sa},-34.541083,138.979618), - ($$5352$$,$$STONE WELL$$,#{state_id_sa},-34.541083,138.979618), - ($$5352$$,$$TANUNDA$$,#{state_id_sa},-34.541083,138.979618), - ($$5352$$,$$VINE VALE$$,#{state_id_sa},-34.541083,138.979618), - ($$5353$$,$$ANGASTON$$,#{state_id_sa},-34.501149,139.046829), - ($$5353$$,$$BLACK HILL$$,#{state_id_sa},-34.501149,139.046829), - ($$5353$$,$$CAMBRAI$$,#{state_id_sa},-34.501149,139.046829), - ($$5353$$,$$FLAXMAN VALLEY$$,#{state_id_sa},-34.501149,139.046829), - ($$5353$$,$$KEYNETON$$,#{state_id_sa},-34.501149,139.046829), - ($$5353$$,$$KONGOLIA$$,#{state_id_sa},-34.501149,139.046829), - ($$5353$$,$$MOCULTA$$,#{state_id_sa},-34.501149,139.046829), - ($$5353$$,$$MOUNT MCKENZIE$$,#{state_id_sa},-34.501149,139.046829), - ($$5353$$,$$PENRICE$$,#{state_id_sa},-34.501149,139.046829), - ($$5353$$,$$SEDAN$$,#{state_id_sa},-34.501149,139.046829), - ($$5353$$,$$TOWITTA$$,#{state_id_sa},-34.501149,139.046829), - ($$5354$$,$$BAKARA$$,#{state_id_sa},-34.65657,139.779968), - ($$5354$$,$$BAKARA WELL$$,#{state_id_sa},-34.65657,139.779968), - ($$5354$$,$$FISHER$$,#{state_id_sa},-34.65657,139.779968), - ($$5354$$,$$GREENWAYS LANDING$$,#{state_id_sa},-34.65657,139.779968), - ($$5354$$,$$LANGS LANDING$$,#{state_id_sa},-34.65657,139.779968), - ($$5354$$,$$MARKS LANDING$$,#{state_id_sa},-34.65657,139.779968), - ($$5354$$,$$NAIDIA$$,#{state_id_sa},-34.65657,139.779968), - ($$5354$$,$$PUNYELROO$$,#{state_id_sa},-34.65657,139.779968), - ($$5354$$,$$SUNNYDALE$$,#{state_id_sa},-34.65657,139.779968), - ($$5354$$,$$SWAN REACH$$,#{state_id_sa},-34.65657,139.779968), - ($$5355$$,$$DAVEYSTON$$,#{state_id_sa},-34.471194,138.880335), - ($$5355$$,$$EBENEZER$$,#{state_id_sa},-34.471194,138.880335), - ($$5355$$,$$LIGHT PASS$$,#{state_id_sa},-34.471194,138.880335), - ($$5355$$,$$MARANANGA$$,#{state_id_sa},-34.471194,138.880335), - ($$5355$$,$$MOPPA$$,#{state_id_sa},-34.471194,138.880335), - ($$5355$$,$$NURIOOTPA$$,#{state_id_sa},-34.471194,138.880335), - ($$5355$$,$$SEPPELTSFIELD$$,#{state_id_sa},-34.471194,138.880335), - ($$5355$$,$$STOCKWELL$$,#{state_id_sa},-34.471194,138.880335), - ($$5356$$,$$ANNADALE$$,#{state_id_sa},-34.389315,139.388418), - ($$5356$$,$$DUTTON$$,#{state_id_sa},-34.389315,139.388418), - ($$5356$$,$$DUTTON EAST$$,#{state_id_sa},-34.389315,139.388418), - ($$5356$$,$$SANDLETON$$,#{state_id_sa},-34.389315,139.388418), - ($$5356$$,$$ST KITTS$$,#{state_id_sa},-34.389315,139.388418), - ($$5356$$,$$STEINFELD$$,#{state_id_sa},-34.389315,139.388418), - ($$5356$$,$$TRURO$$,#{state_id_sa},-34.389315,139.388418), - ($$5357$$,$$BLANCHETOWN$$,#{state_id_sa},-34.351995,139.614036), - ($$5357$$,$$MCBEAN POUND$$,#{state_id_sa},-34.351995,139.614036), - ($$5357$$,$$NEW WELL$$,#{state_id_sa},-34.351995,139.614036), - ($$5357$$,$$NOTTS WELL$$,#{state_id_sa},-34.351995,139.614036), - ($$5357$$,$$PAISLEY$$,#{state_id_sa},-34.351995,139.614036), - ($$5357$$,$$WIGLEY FLAT$$,#{state_id_sa},-34.351995,139.614036), - ($$5360$$,$$GREENOCK$$,#{state_id_sa},-34.458936,138.932244), - ($$5360$$,$$NAIN$$,#{state_id_sa},-34.458936,138.932244), - ($$5371$$,$$MORN HILL$$,#{state_id_sa},-34.41138,138.751227), - ($$5371$$,$$ROSEWORTHY$$,#{state_id_sa},-34.41138,138.751227), - ($$5371$$,$$SHEA-OAK LOG$$,#{state_id_sa},-34.41138,138.751227), - ($$5371$$,$$TEMPLERS$$,#{state_id_sa},-34.41138,138.751227), - ($$5372$$,$$FREELING$$,#{state_id_sa},-34.454289,138.813242), - ($$5373$$,$$ALLENDALE NORTH$$,#{state_id_sa},-34.303983,138.917658), - ($$5373$$,$$BAGOT WELL$$,#{state_id_sa},-34.303983,138.917658), - ($$5373$$,$$BETHEL$$,#{state_id_sa},-34.303983,138.917658), - ($$5373$$,$$FORDS$$,#{state_id_sa},-34.303983,138.917658), - ($$5373$$,$$HAMILTON$$,#{state_id_sa},-34.303983,138.917658), - ($$5373$$,$$KAPUNDA$$,#{state_id_sa},-34.303983,138.917658), - ($$5373$$,$$KOONUNGA$$,#{state_id_sa},-34.303983,138.917658), - ($$5373$$,$$ST JOHNS$$,#{state_id_sa},-34.303983,138.917658), - ($$5374$$,$$AUSTRALIA PLAINS$$,#{state_id_sa},-34.093799,139.169087), - ($$5374$$,$$BOWER$$,#{state_id_sa},-34.093799,139.169087), - ($$5374$$,$$BROWNLOW$$,#{state_id_sa},-34.093799,139.169087), - ($$5374$$,$$BUCHANAN$$,#{state_id_sa},-34.093799,139.169087), - ($$5374$$,$$EUDUNDA$$,#{state_id_sa},-34.093799,139.169087), - ($$5374$$,$$FRANKTON$$,#{state_id_sa},-34.093799,139.169087), - ($$5374$$,$$HAMPDEN$$,#{state_id_sa},-34.093799,139.169087), - ($$5374$$,$$HANSBOROUGH$$,#{state_id_sa},-34.093799,139.169087), - ($$5374$$,$$JULIA$$,#{state_id_sa},-34.093799,139.169087), - ($$5374$$,$$MOUNT MARY$$,#{state_id_sa},-34.093799,139.169087), - ($$5374$$,$$NEALES FLAT$$,#{state_id_sa},-34.093799,139.169087), - ($$5374$$,$$NGAPALA$$,#{state_id_sa},-34.093799,139.169087), - ($$5374$$,$$PEEP HILL$$,#{state_id_sa},-34.093799,139.169087), - ($$5374$$,$$POINT PASS$$,#{state_id_sa},-34.093799,139.169087), - ($$5374$$,$$SUTHERLANDS$$,#{state_id_sa},-34.093799,139.169087), - ($$5381$$,$$BRADY CREEK$$,#{state_id_sa},-33.968264,139.00005), - ($$5381$$,$$BRIGHT$$,#{state_id_sa},-33.968264,139.00005), - ($$5381$$,$$EMU DOWNS$$,#{state_id_sa},-33.968264,139.00005), - ($$5381$$,$$GERANIUM PLAINS$$,#{state_id_sa},-33.968264,139.00005), - ($$5381$$,$$HALLELUJAH HILLS$$,#{state_id_sa},-33.968264,139.00005), - ($$5381$$,$$ROBERTSTOWN$$,#{state_id_sa},-33.968264,139.00005), - ($$5381$$,$$ROCKY PLAIN$$,#{state_id_sa},-33.968264,139.00005), - ($$5381$$,$$WORLDS END$$,#{state_id_sa},-33.968264,139.00005), - ($$5381$$,$$WORLDS END CREEK$$,#{state_id_sa},-33.968264,139.00005), - ($$5400$$,$$MAGDALLA$$,#{state_id_sa},-34.425354,138.700852), - ($$5400$$,$$PINKERTON PLAINS$$,#{state_id_sa},-34.425354,138.700852), - ($$5400$$,$$WASLEYS$$,#{state_id_sa},-34.425354,138.700852), - ($$5400$$,$$WOOLSHEDS$$,#{state_id_sa},-34.425354,138.700852), - ($$5401$$,$$ALMA$$,#{state_id_sa},-34.266619,138.630864), - ($$5401$$,$$HAMLEY BRIDGE$$,#{state_id_sa},-34.266619,138.630864), - ($$5401$$,$$SALTER SPRINGS$$,#{state_id_sa},-34.266619,138.630864), - ($$5410$$,$$LINWOOD$$,#{state_id_sa},-34.360716,138.766286), - ($$5410$$,$$STOCKPORT$$,#{state_id_sa},-34.360716,138.766286), - ($$5411$$,$$GILES CORNER$$,#{state_id_sa},-34.222937,138.728866), - ($$5411$$,$$TARLEE$$,#{state_id_sa},-34.222937,138.728866), - ($$5412$$,$$NAVAN$$,#{state_id_sa},-34.217989,138.740043), - ($$5412$$,$$RHYNIE$$,#{state_id_sa},-34.217989,138.740043), - ($$5412$$,$$RIVERTON$$,#{state_id_sa},-34.217989,138.740043), - ($$5412$$,$$WOOLSHED FLAT$$,#{state_id_sa},-34.217989,138.740043), - ($$5413$$,$$APOINGA$$,#{state_id_sa},-33.928076,138.937581), - ($$5413$$,$$BLACK SPRINGS$$,#{state_id_sa},-33.928076,138.937581), - ($$5413$$,$$MARRABEL$$,#{state_id_sa},-33.928076,138.937581), - ($$5413$$,$$SADDLEWORTH$$,#{state_id_sa},-33.928076,138.937581), - ($$5413$$,$$STEELTON$$,#{state_id_sa},-33.928076,138.937581), - ($$5413$$,$$TARNMA$$,#{state_id_sa},-33.928076,138.937581), - ($$5413$$,$$TOTHILL BELT$$,#{state_id_sa},-33.928076,138.937581), - ($$5413$$,$$TOTHILL CREEK$$,#{state_id_sa},-33.928076,138.937581), - ($$5413$$,$$WATERLOO$$,#{state_id_sa},-33.928076,138.937581), - ($$5414$$,$$MANOORA$$,#{state_id_sa},-33.977601,138.785706), - ($$5415$$,$$MINTARO$$,#{state_id_sa},-33.915727,138.72407), - ($$5415$$,$$STANLEY$$,#{state_id_sa},-33.915727,138.72407), - ($$5416$$,$$FARRELL FLAT$$,#{state_id_sa},-33.829542,138.792684), - ($$5416$$,$$PORTER LAGOON$$,#{state_id_sa},-33.829542,138.792684), - ($$5417$$,$$BALDINA$$,#{state_id_sa},-33.654798,139.05502), - ($$5417$$,$$BOOBOROWIE$$,#{state_id_sa},-33.654798,139.05502), - ($$5417$$,$$BURRA$$,#{state_id_sa},-33.654798,139.05502), - ($$5417$$,$$BURRA EASTERN DISTRICTS$$,#{state_id_sa},-33.654798,139.05502), - ($$5417$$,$$GUM CREEK$$,#{state_id_sa},-33.654798,139.05502), - ($$5417$$,$$HANSON$$,#{state_id_sa},-33.654798,139.05502), - ($$5417$$,$$KOONOONA$$,#{state_id_sa},-33.654798,139.05502), - ($$5417$$,$$LEIGHTON$$,#{state_id_sa},-33.654798,139.05502), - ($$5417$$,$$MONGOLATA$$,#{state_id_sa},-33.654798,139.05502), - ($$5417$$,$$NORTH BOOBOROWIE$$,#{state_id_sa},-33.654798,139.05502), - ($$5418$$,$$COLLINSVILLE$$,#{state_id_sa},-33.35168,139.105314), - ($$5418$$,$$MOUNT BRYAN$$,#{state_id_sa},-33.35168,139.105314), - ($$5419$$,$$CANOWIE$$,#{state_id_sa},-33.388018,138.759817), - ($$5419$$,$$HALLETT$$,#{state_id_sa},-33.388018,138.759817), - ($$5419$$,$$MOUNT BRYAN EAST$$,#{state_id_sa},-33.388018,138.759817), - ($$5419$$,$$PINE CREEK$$,#{state_id_sa},-33.388018,138.759817), - ($$5419$$,$$ULOOLOO$$,#{state_id_sa},-33.388018,138.759817), - ($$5419$$,$$WILLALO$$,#{state_id_sa},-33.388018,138.759817), - ($$5419$$,$$WONNA$$,#{state_id_sa},-33.388018,138.759817), - ($$5420$$,$$CANOWIE BELT$$,#{state_id_sa},-33.181747,138.758352), - ($$5420$$,$$WHYTE YARCOWIE$$,#{state_id_sa},-33.181747,138.758352), - ($$5421$$,$$FRANKLYN$$,#{state_id_sa},-33.132679,139.076986), - ($$5421$$,$$TEROWIE$$,#{state_id_sa},-33.132679,139.076986), - ($$5422$$,$$CAVENAGH$$,#{state_id_sa},-32.729333,139.000159), - ($$5422$$,$$DAWSON$$,#{state_id_sa},-32.729333,139.000159), - ($$5422$$,$$ERSKINE$$,#{state_id_sa},-32.729333,139.000159), - ($$5422$$,$$HARDY$$,#{state_id_sa},-32.729333,139.000159), - ($$5422$$,$$MANNANARIE$$,#{state_id_sa},-32.729333,139.000159), - ($$5422$$,$$MINVALARA$$,#{state_id_sa},-32.729333,139.000159), - ($$5422$$,$$OODLA WIRRA$$,#{state_id_sa},-32.729333,139.000159), - ($$5422$$,$$PARATOO$$,#{state_id_sa},-32.729333,139.000159), - ($$5422$$,$$PARNAROO$$,#{state_id_sa},-32.729333,139.000159), - ($$5422$$,$$PETERBOROUGH$$,#{state_id_sa},-32.729333,139.000159), - ($$5422$$,$$SUNNYBRAE$$,#{state_id_sa},-32.729333,139.000159), - ($$5422$$,$$UCOLTA$$,#{state_id_sa},-32.729333,139.000159), - ($$5422$$,$$YATINA$$,#{state_id_sa},-32.729333,139.000159), - ($$5431$$,$$AMYTON$$,#{state_id_sa},-32.603434,138.327394), - ($$5431$$,$$BLACK ROCK$$,#{state_id_sa},-32.603434,138.327394), - ($$5431$$,$$COOMOOROO$$,#{state_id_sa},-32.603434,138.327394), - ($$5431$$,$$EURELIA$$,#{state_id_sa},-32.603434,138.327394), - ($$5431$$,$$HAMMOND$$,#{state_id_sa},-32.603434,138.327394), - ($$5431$$,$$JOHNBURGH$$,#{state_id_sa},-32.603434,138.327394), - ($$5431$$,$$MINBURRA$$,#{state_id_sa},-32.603434,138.327394), - ($$5431$$,$$MORCHARD$$,#{state_id_sa},-32.603434,138.327394), - ($$5431$$,$$ORROROO$$,#{state_id_sa},-32.603434,138.327394), - ($$5431$$,$$PEKINA$$,#{state_id_sa},-32.603434,138.327394), - ($$5431$$,$$TARCOWIE$$,#{state_id_sa},-32.603434,138.327394), - ($$5431$$,$$WALLOWAY$$,#{state_id_sa},-32.603434,138.327394), - ($$5431$$,$$WILLOWIE$$,#{state_id_sa},-32.603434,138.327394), - ($$5431$$,$$YALPARA$$,#{state_id_sa},-32.603434,138.327394), - ($$5432$$,$$BELTON$$,#{state_id_sa},-32.2264,138.709657), - ($$5432$$,$$CARRIETON$$,#{state_id_sa},-32.2264,138.709657), - ($$5432$$,$$CRADOCK$$,#{state_id_sa},-32.2264,138.709657), - ($$5432$$,$$MOOCKRA$$,#{state_id_sa},-32.2264,138.709657), - ($$5432$$,$$YANYARRIE$$,#{state_id_sa},-32.2264,138.709657), - ($$5433$$,$$BRUCE$$,#{state_id_sa},-32.447436,138.201775), - ($$5433$$,$$QUORN$$,#{state_id_sa},-32.447436,138.201775), - ($$5433$$,$$SALTIA$$,#{state_id_sa},-32.447436,138.201775), - ($$5433$$,$$STEPHENSTON$$,#{state_id_sa},-32.447436,138.201775), - ($$5433$$,$$WILLOCHRA$$,#{state_id_sa},-32.447436,138.201775), - ($$5433$$,$$YARRAH$$,#{state_id_sa},-32.447436,138.201775), - ($$5434$$,$$BARNDIOOTA$$,#{state_id_sa},-31.795825,138.357939), - ($$5434$$,$$HAWKER$$,#{state_id_sa},-31.795825,138.357939), - ($$5434$$,$$KANYAKA$$,#{state_id_sa},-31.795825,138.357939), - ($$5440$$,$$COCKBURN$$,#{state_id_sa},-32.078008,140.997812), - ($$5440$$,$$MANNA HILL$$,#{state_id_sa},-32.078008,140.997812), - ($$5440$$,$$MINGARY$$,#{state_id_sa},-32.078008,140.997812), - ($$5440$$,$$NACKARA$$,#{state_id_sa},-32.078008,140.997812), - ($$5440$$,$$OLARY$$,#{state_id_sa},-32.078008,140.997812), - ($$5440$$,$$WAUKARINGA$$,#{state_id_sa},-32.078008,140.997812), - ($$5440$$,$$YUNTA$$,#{state_id_sa},-32.078008,140.997812), - ($$5451$$,$$AUBURN$$,#{state_id_sa},-34.028001,138.681644), - ($$5451$$,$$UNDALYA$$,#{state_id_sa},-34.028001,138.681644), - ($$5452$$,$$LEASINGHAM$$,#{state_id_sa},-33.982193,138.651072), - ($$5452$$,$$WATERVALE$$,#{state_id_sa},-33.982193,138.651072), - ($$5453$$,$$ARMAGH$$,#{state_id_sa},-33.836794,138.57943), - ($$5453$$,$$BARINIA$$,#{state_id_sa},-33.836794,138.57943), - ($$5453$$,$$BENBOURNIE$$,#{state_id_sa},-33.836794,138.57943), - ($$5453$$,$$BOCONNOC PARK$$,#{state_id_sa},-33.836794,138.57943), - ($$5453$$,$$CLARE$$,#{state_id_sa},-33.836794,138.57943), - ($$5453$$,$$EMU FLAT$$,#{state_id_sa},-33.836794,138.57943), - ($$5453$$,$$GILLENTOWN$$,#{state_id_sa},-33.836794,138.57943), - ($$5453$$,$$HILL RIVER$$,#{state_id_sa},-33.836794,138.57943), - ($$5453$$,$$HOYLETON$$,#{state_id_sa},-33.836794,138.57943), - ($$5453$$,$$KYBUNGA$$,#{state_id_sa},-33.836794,138.57943), - ($$5453$$,$$PENWORTHAM$$,#{state_id_sa},-33.836794,138.57943), - ($$5453$$,$$POLISH HILL RIVER$$,#{state_id_sa},-33.836794,138.57943), - ($$5453$$,$$SEVENHILL$$,#{state_id_sa},-33.836794,138.57943), - ($$5453$$,$$SPRING FARM$$,#{state_id_sa},-33.836794,138.57943), - ($$5453$$,$$SPRING GULLY$$,#{state_id_sa},-33.836794,138.57943), - ($$5453$$,$$STANLEY FLAT$$,#{state_id_sa},-33.836794,138.57943), - ($$5454$$,$$ANDREWS$$,#{state_id_sa},-33.598997,138.631237), - ($$5454$$,$$BROUGHTON RIVER VALLEY$$,#{state_id_sa},-33.598997,138.631237), - ($$5454$$,$$EUROMINA$$,#{state_id_sa},-33.598997,138.631237), - ($$5454$$,$$HACKLINS CORNER$$,#{state_id_sa},-33.598997,138.631237), - ($$5454$$,$$MAYFIELD$$,#{state_id_sa},-33.598997,138.631237), - ($$5454$$,$$SPALDING$$,#{state_id_sa},-33.598997,138.631237), - ($$5454$$,$$WASHPOOL$$,#{state_id_sa},-33.598997,138.631237), - ($$5455$$,$$HILLTOWN$$,#{state_id_sa},-33.712061,138.647769), - ($$5460$$,$$BARABBA$$,#{state_id_sa},-34.345014,138.589904), - ($$5460$$,$$OWEN$$,#{state_id_sa},-34.345014,138.589904), - ($$5460$$,$$PINERY$$,#{state_id_sa},-34.345014,138.589904), - ($$5460$$,$$STOCKYARD CREEK$$,#{state_id_sa},-34.345014,138.589904), - ($$5461$$,$$BALAKLAVA$$,#{state_id_sa},-34.147422,138.415626), - ($$5461$$,$$BOWILLIA$$,#{state_id_sa},-34.147422,138.415626), - ($$5461$$,$$DALKEY$$,#{state_id_sa},-34.147422,138.415626), - ($$5461$$,$$ERITH$$,#{state_id_sa},-34.147422,138.415626), - ($$5461$$,$$EVERARD CENTRAL$$,#{state_id_sa},-34.147422,138.415626), - ($$5461$$,$$GOYDER$$,#{state_id_sa},-34.147422,138.415626), - ($$5461$$,$$HALBURY$$,#{state_id_sa},-34.147422,138.415626), - ($$5461$$,$$HOSKIN CORNER$$,#{state_id_sa},-34.147422,138.415626), - ($$5461$$,$$MOUNT TEMPLETON$$,#{state_id_sa},-34.147422,138.415626), - ($$5461$$,$$SAINTS$$,#{state_id_sa},-34.147422,138.415626), - ($$5461$$,$$STOW$$,#{state_id_sa},-34.147422,138.415626), - ($$5461$$,$$WATCHMAN$$,#{state_id_sa},-34.147422,138.415626), - ($$5461$$,$$WHITWARTA$$,#{state_id_sa},-34.147422,138.415626), - ($$5462$$,$$BLYTH$$,#{state_id_sa},-33.845511,138.490294), - ($$5464$$,$$ANAMA$$,#{state_id_sa},-33.717096,138.553465), - ($$5464$$,$$BRINKWORTH$$,#{state_id_sa},-33.717096,138.553465), - ($$5464$$,$$BUNGAREE$$,#{state_id_sa},-33.717096,138.553465), - ($$5464$$,$$CONDOWIE$$,#{state_id_sa},-33.717096,138.553465), - ($$5464$$,$$HART$$,#{state_id_sa},-33.717096,138.553465), - ($$5464$$,$$KOOLUNGA$$,#{state_id_sa},-33.717096,138.553465), - ($$5464$$,$$MAROLA$$,#{state_id_sa},-33.717096,138.553465), - ($$5464$$,$$ROCHESTER$$,#{state_id_sa},-33.717096,138.553465), - ($$5470$$,$$YACKA$$,#{state_id_sa},-33.573999,138.408049), - ($$5471$$,$$GULNARE$$,#{state_id_sa},-33.465159,138.482149), - ($$5472$$,$$GEORGETOWN$$,#{state_id_sa},-33.374062,138.425703), - ($$5473$$,$$GLADSTONE$$,#{state_id_sa},-33.282514,138.354703), - ($$5480$$,$$APPILA$$,#{state_id_sa},-33.050537,138.42808), - ($$5480$$,$$LAURA$$,#{state_id_sa},-33.050537,138.42808), - ($$5480$$,$$STONE HUT$$,#{state_id_sa},-33.050537,138.42808), - ($$5481$$,$$BANGOR$$,#{state_id_sa},-32.952889,138.157372), - ($$5481$$,$$MURRAY TOWN$$,#{state_id_sa},-32.952889,138.157372), - ($$5481$$,$$WIRRABARA$$,#{state_id_sa},-32.952889,138.157372), - ($$5481$$,$$WONGYARRA$$,#{state_id_sa},-32.952889,138.157372), - ($$5482$$,$$BOOLEROO CENTRE$$,#{state_id_sa},-32.880512,138.351993), - ($$5482$$,$$WEPOWIE$$,#{state_id_sa},-32.880512,138.351993), - ($$5483$$,$$MELROSE$$,#{state_id_sa},-32.825064,138.187939), - ($$5485$$,$$WILMINGTON$$,#{state_id_sa},-32.649055,138.097284), - ($$5490$$,$$CALTOWIE$$,#{state_id_sa},-33.181404,138.48152), - ($$5490$$,$$CALTOWIE NORTH$$,#{state_id_sa},-33.181404,138.48152), - ($$5490$$,$$CALTOWIE WEST$$,#{state_id_sa},-33.181404,138.48152), - ($$5491$$,$$BELALIE EAST$$,#{state_id_sa},-33.269532,138.654934), - ($$5491$$,$$BELALIE NORTH$$,#{state_id_sa},-33.269532,138.654934), - ($$5491$$,$$BUNDALEER GARDENS$$,#{state_id_sa},-33.269532,138.654934), - ($$5491$$,$$BUNDALEER NORTH$$,#{state_id_sa},-33.269532,138.654934), - ($$5491$$,$$HORNSDALE$$,#{state_id_sa},-33.269532,138.654934), - ($$5491$$,$$JAMESTOWN$$,#{state_id_sa},-33.269532,138.654934), - ($$5491$$,$$WEST BUNDALEER$$,#{state_id_sa},-33.269532,138.654934), - ($$5493$$,$$YONGALA$$,#{state_id_sa},-33.02799,138.74886), - ($$5495$$,$$BAROOTA$$,#{state_id_sa},-32.924116,137.982971), - ($$5495$$,$$GERMEIN BAY$$,#{state_id_sa},-32.924116,137.982971), - ($$5495$$,$$MAMBRAY CREEK$$,#{state_id_sa},-32.924116,137.982971), - ($$5495$$,$$NECTAR BROOK$$,#{state_id_sa},-32.924116,137.982971), - ($$5495$$,$$PORT FLINDERS$$,#{state_id_sa},-32.924116,137.982971), - ($$5495$$,$$PORT GERMEIN$$,#{state_id_sa},-32.924116,137.982971), - ($$5501$$,$$AVON$$,#{state_id_sa},-34.281161,138.338749), - ($$5501$$,$$CALOMBA$$,#{state_id_sa},-34.281161,138.338749), - ($$5501$$,$$DUBLIN$$,#{state_id_sa},-34.281161,138.338749), - ($$5501$$,$$LEWISTON$$,#{state_id_sa},-34.281161,138.338749), - ($$5501$$,$$LONG PLAINS$$,#{state_id_sa},-34.281161,138.338749), - ($$5501$$,$$LOWER LIGHT$$,#{state_id_sa},-34.281161,138.338749), - ($$5501$$,$$MIDDLE BEACH$$,#{state_id_sa},-34.281161,138.338749), - ($$5501$$,$$PARHAM$$,#{state_id_sa},-34.281161,138.338749), - ($$5501$$,$$PORT GAWLER$$,#{state_id_sa},-34.281161,138.338749), - ($$5501$$,$$THOMPSON BEACH$$,#{state_id_sa},-34.281161,138.338749), - ($$5501$$,$$TWO WELLS$$,#{state_id_sa},-34.281161,138.338749), - ($$5501$$,$$WEBB BEACH$$,#{state_id_sa},-34.281161,138.338749), - ($$5501$$,$$WILD HORSE PLAINS$$,#{state_id_sa},-34.281161,138.338749), - ($$5501$$,$$WINDSOR$$,#{state_id_sa},-34.281161,138.338749), - ($$5502$$,$$FISCHER$$,#{state_id_sa},-34.494033,138.613197), - ($$5502$$,$$GRACE PLAINS$$,#{state_id_sa},-34.494033,138.613197), - ($$5502$$,$$KORUNYE$$,#{state_id_sa},-34.494033,138.613197), - ($$5502$$,$$MALLALA$$,#{state_id_sa},-34.494033,138.613197), - ($$5502$$,$$REDBANKS$$,#{state_id_sa},-34.494033,138.613197), - ($$5502$$,$$REEVES PLAINS$$,#{state_id_sa},-34.494033,138.613197), - ($$5510$$,$$LOCHIEL$$,#{state_id_sa},-33.931863,138.171497), - ($$5520$$,$$BARUNGA GAP$$,#{state_id_sa},-33.828679,138.124671), - ($$5520$$,$$BUMBUNGA$$,#{state_id_sa},-33.828679,138.124671), - ($$5520$$,$$BURNSFIELD$$,#{state_id_sa},-33.828679,138.124671), - ($$5520$$,$$GLEESON HILL$$,#{state_id_sa},-33.828679,138.124671), - ($$5520$$,$$SNOWTOWN$$,#{state_id_sa},-33.828679,138.124671), - ($$5520$$,$$WOKURNA$$,#{state_id_sa},-33.828679,138.124671), - ($$5521$$,$$REDHILL$$,#{state_id_sa},-33.537636,138.22494), - ($$5522$$,$$FISHERMAN BAY$$,#{state_id_sa},-33.553743,137.938071), - ($$5522$$,$$LOWER BROUGHTON$$,#{state_id_sa},-33.553743,137.938071), - ($$5522$$,$$PORT BROUGHTON$$,#{state_id_sa},-33.553743,137.938071), - ($$5522$$,$$WARD HILL$$,#{state_id_sa},-33.553743,137.938071), - ($$5523$$,$$BEETALOO$$,#{state_id_sa},-33.258052,138.25167), - ($$5523$$,$$CLEMENTS GAP$$,#{state_id_sa},-33.258052,138.25167), - ($$5523$$,$$CRYSTAL BROOK$$,#{state_id_sa},-33.258052,138.25167), - ($$5523$$,$$HUDDLESTON$$,#{state_id_sa},-33.258052,138.25167), - ($$5523$$,$$MERRITON$$,#{state_id_sa},-33.258052,138.25167), - ($$5523$$,$$NARRIDY$$,#{state_id_sa},-33.258052,138.25167), - ($$5523$$,$$NUROM$$,#{state_id_sa},-33.258052,138.25167), - ($$5523$$,$$WANDEARAH$$,#{state_id_sa},-33.258052,138.25167), - ($$5523$$,$$WANDEARAH EAST$$,#{state_id_sa},-33.258052,138.25167), - ($$5523$$,$$WANDEARAH WEST$$,#{state_id_sa},-33.258052,138.25167), - ($$5540$$,$$BUNGAMA$$,#{state_id_sa},-33.196523,138.068265), - ($$5540$$,$$COONAMIA$$,#{state_id_sa},-33.196523,138.068265), - ($$5540$$,$$NAPPERBY$$,#{state_id_sa},-33.196523,138.068265), - ($$5540$$,$$NELSHABY$$,#{state_id_sa},-33.196523,138.068265), - ($$5540$$,$$PIRIE EAST$$,#{state_id_sa},-33.196523,138.068265), - ($$5540$$,$$PORT DAVIS$$,#{state_id_sa},-33.196523,138.068265), - ($$5540$$,$$PORT PIRIE$$,#{state_id_sa},-33.196523,138.068265), - ($$5540$$,$$PORT PIRIE SOUTH$$,#{state_id_sa},-33.196523,138.068265), - ($$5540$$,$$PORT PIRIE WEST$$,#{state_id_sa},-33.196523,138.068265), - ($$5540$$,$$RISDON PARK$$,#{state_id_sa},-33.196523,138.068265), - ($$5540$$,$$RISDON PARK SOUTH$$,#{state_id_sa},-33.196523,138.068265), - ($$5540$$,$$SOLOMONTOWN$$,#{state_id_sa},-33.196523,138.068265), - ($$5540$$,$$TELOWIE$$,#{state_id_sa},-33.196523,138.068265), - ($$5540$$,$$WARNERTOWN$$,#{state_id_sa},-33.196523,138.068265), - ($$5550$$,$$BEAUFORT$$,#{state_id_sa},-34.070596,138.227272), - ($$5550$$,$$BOWMANS$$,#{state_id_sa},-34.070596,138.227272), - ($$5550$$,$$INKERMAN$$,#{state_id_sa},-34.070596,138.227272), - ($$5550$$,$$KALLORA$$,#{state_id_sa},-34.070596,138.227272), - ($$5550$$,$$NANTAWARRA$$,#{state_id_sa},-34.070596,138.227272), - ($$5550$$,$$PORT WAKEFIELD$$,#{state_id_sa},-34.070596,138.227272), - ($$5550$$,$$PROOF RANGE$$,#{state_id_sa},-34.070596,138.227272), - ($$5550$$,$$SOUTH HUMMOCKS$$,#{state_id_sa},-34.070596,138.227272), - ($$5552$$,$$KAINTON$$,#{state_id_sa},-34.148316,137.941585), - ($$5552$$,$$PASKEVILLE$$,#{state_id_sa},-34.148316,137.941585), - ($$5552$$,$$SUNNYVALE$$,#{state_id_sa},-34.148316,137.941585), - ($$5552$$,$$THRINGTON$$,#{state_id_sa},-34.148316,137.941585), - ($$5554$$,$$BOORS PLAIN$$,#{state_id_sa},-34.025225,137.666528), - ($$5554$$,$$CUNLIFFE$$,#{state_id_sa},-34.025225,137.666528), - ($$5554$$,$$JERICHO$$,#{state_id_sa},-34.025225,137.666528), - ($$5554$$,$$JERUSALEM$$,#{state_id_sa},-34.025225,137.666528), - ($$5554$$,$$KADINA$$,#{state_id_sa},-34.025225,137.666528), - ($$5554$$,$$MATTA FLAT$$,#{state_id_sa},-34.025225,137.666528), - ($$5554$$,$$NEW TOWN$$,#{state_id_sa},-34.025225,137.666528), - ($$5554$$,$$THOMAS PLAIN$$,#{state_id_sa},-34.025225,137.666528), - ($$5554$$,$$WALLAROO MINES$$,#{state_id_sa},-34.025225,137.666528), - ($$5554$$,$$WILLAMULKA$$,#{state_id_sa},-34.025225,137.666528), - ($$5555$$,$$ALFORD$$,#{state_id_sa},-33.81518,137.821268), - ($$5555$$,$$COLLINSFIELD$$,#{state_id_sa},-33.81518,137.821268), - ($$5555$$,$$DOWLING$$,#{state_id_sa},-33.81518,137.821268), - ($$5555$$,$$DOWLINGVILLE$$,#{state_id_sa},-33.81518,137.821268), - ($$5555$$,$$HOPE GAP$$,#{state_id_sa},-33.81518,137.821268), - ($$5555$$,$$KULPARA$$,#{state_id_sa},-33.81518,137.821268), - ($$5555$$,$$LAKE VIEW$$,#{state_id_sa},-33.81518,137.821268), - ($$5555$$,$$MELTON$$,#{state_id_sa},-33.81518,137.821268), - ($$5555$$,$$MUNDOORA$$,#{state_id_sa},-33.81518,137.821268), - ($$5555$$,$$TICKERA$$,#{state_id_sa},-33.81518,137.821268), - ($$5555$$,$$WINULTA$$,#{state_id_sa},-33.81518,137.821268), - ($$5556$$,$$NORTH BEACH$$,#{state_id_sa},-33.900295,137.630764), - ($$5556$$,$$WALLAROO$$,#{state_id_sa},-33.900295,137.630764), - ($$5556$$,$$WALLAROO PLAIN$$,#{state_id_sa},-33.900295,137.630764), - ($$5556$$,$$WARBURTO$$,#{state_id_sa},-33.900295,137.630764), - ($$5558$$,$$AGERY$$,#{state_id_sa},-34.156899,137.742805), - ($$5558$$,$$CROSS ROADS$$,#{state_id_sa},-34.156899,137.742805), - ($$5558$$,$$EAST MOONTA$$,#{state_id_sa},-34.156899,137.742805), - ($$5558$$,$$HAMLEY$$,#{state_id_sa},-34.156899,137.742805), - ($$5558$$,$$KOOROONA$$,#{state_id_sa},-34.156899,137.742805), - ($$5558$$,$$MOONTA$$,#{state_id_sa},-34.156899,137.742805), - ($$5558$$,$$MOONTA BAY$$,#{state_id_sa},-34.156899,137.742805), - ($$5558$$,$$MOONTA MINES$$,#{state_id_sa},-34.156899,137.742805), - ($$5558$$,$$NALYAPPA$$,#{state_id_sa},-34.156899,137.742805), - ($$5558$$,$$NORTH MOONTA$$,#{state_id_sa},-34.156899,137.742805), - ($$5558$$,$$NORTH YELTA$$,#{state_id_sa},-34.156899,137.742805), - ($$5558$$,$$PARAMATTA$$,#{state_id_sa},-34.156899,137.742805), - ($$5558$$,$$PORT HUGHES$$,#{state_id_sa},-34.156899,137.742805), - ($$5558$$,$$YELTA$$,#{state_id_sa},-34.156899,137.742805), - ($$5560$$,$$BUTE$$,#{state_id_sa},-33.863962,138.007629), - ($$5560$$,$$NINNES$$,#{state_id_sa},-33.863962,138.007629), - ($$5570$$,$$CLINTON$$,#{state_id_sa},-34.178371,137.978866), - ($$5570$$,$$CLINTON CENTRE$$,#{state_id_sa},-34.178371,137.978866), - ($$5570$$,$$PORT CLINTON$$,#{state_id_sa},-34.178371,137.978866), - ($$5570$$,$$PRICE$$,#{state_id_sa},-34.178371,137.978866), - ($$5571$$,$$ARDROSSAN$$,#{state_id_sa},-34.422953,137.916805), - ($$5571$$,$$BLACK POINT$$,#{state_id_sa},-34.422953,137.916805), - ($$5571$$,$$CUNNINGHAM$$,#{state_id_sa},-34.422953,137.916805), - ($$5571$$,$$JAMES WELL$$,#{state_id_sa},-34.422953,137.916805), - ($$5571$$,$$PETERSVILLE$$,#{state_id_sa},-34.422953,137.916805), - ($$5571$$,$$PINE POINT$$,#{state_id_sa},-34.422953,137.916805), - ($$5571$$,$$ROGUES POINT$$,#{state_id_sa},-34.422953,137.916805), - ($$5571$$,$$SANDILANDS$$,#{state_id_sa},-34.422953,137.916805), - ($$5571$$,$$TIDDY WIDDY BEACH$$,#{state_id_sa},-34.422953,137.916805), - ($$5572$$,$$ARTHURTON$$,#{state_id_sa},-34.257529,137.7564), - ($$5572$$,$$PORT ARTHUR$$,#{state_id_sa},-34.257529,137.7564), - ($$5573$$,$$BALGOWAN$$,#{state_id_sa},-34.324332,137.494924), - ($$5573$$,$$CHINAMAN WELLS$$,#{state_id_sa},-34.324332,137.494924), - ($$5573$$,$$MAITLAND$$,#{state_id_sa},-34.324332,137.494924), - ($$5573$$,$$POINT PEARCE$$,#{state_id_sa},-34.324332,137.494924), - ($$5573$$,$$PORT VICTORIA$$,#{state_id_sa},-34.324332,137.494924), - ($$5573$$,$$SOUTH KILKERRAN$$,#{state_id_sa},-34.324332,137.494924), - ($$5573$$,$$URANIA$$,#{state_id_sa},-34.324332,137.494924), - ($$5573$$,$$WAURALTEE$$,#{state_id_sa},-34.324332,137.494924), - ($$5573$$,$$WEETULTA$$,#{state_id_sa},-34.324332,137.494924), - ($$5573$$,$$YORKE VALLEY$$,#{state_id_sa},-34.324332,137.494924), - ($$5575$$,$$BLUFF BEACH$$,#{state_id_sa},-34.732992,137.480834), - ($$5575$$,$$BRENTWOOD$$,#{state_id_sa},-34.732992,137.480834), - ($$5575$$,$$CORNY POINT$$,#{state_id_sa},-34.732992,137.480834), - ($$5575$$,$$COUCH BEACH$$,#{state_id_sa},-34.732992,137.480834), - ($$5575$$,$$HARDWICKE BAY$$,#{state_id_sa},-34.732992,137.480834), - ($$5575$$,$$KOOLYWURTIE$$,#{state_id_sa},-34.732992,137.480834), - ($$5575$$,$$MARION BAY$$,#{state_id_sa},-34.732992,137.480834), - ($$5575$$,$$MINLATON$$,#{state_id_sa},-34.732992,137.480834), - ($$5575$$,$$POINT SOUTTAR$$,#{state_id_sa},-34.732992,137.480834), - ($$5575$$,$$POINT TURTON$$,#{state_id_sa},-34.732992,137.480834), - ($$5575$$,$$PORT JULIA$$,#{state_id_sa},-34.732992,137.480834), - ($$5575$$,$$PORT RICKABY$$,#{state_id_sa},-34.732992,137.480834), - ($$5575$$,$$RAMSAY$$,#{state_id_sa},-34.732992,137.480834), - ($$5575$$,$$STENHOUSE BAY$$,#{state_id_sa},-34.732992,137.480834), - ($$5575$$,$$THE PINES$$,#{state_id_sa},-34.732992,137.480834), - ($$5575$$,$$WHITE HUT$$,#{state_id_sa},-34.732992,137.480834), - ($$5575$$,$$WOOL BAY$$,#{state_id_sa},-34.732992,137.480834), - ($$5576$$,$$HONITON$$,#{state_id_sa},-35.101808,137.648499), - ($$5576$$,$$PORT MOOROWIE$$,#{state_id_sa},-35.101808,137.648499), - ($$5576$$,$$YORKETOWN$$,#{state_id_sa},-35.101808,137.648499), - ($$5577$$,$$FOUL BAY$$,#{state_id_sa},-35.179011,137.210072), - ($$5577$$,$$INNESTON$$,#{state_id_sa},-35.179011,137.210072), - ($$5577$$,$$WAROOKA$$,#{state_id_sa},-35.179011,137.210072), - ($$5580$$,$$CURRAMULKA$$,#{state_id_sa},-34.667404,137.75615), - ($$5581$$,$$PORT VINCENT$$,#{state_id_sa},-34.779593,137.859065), - ($$5581$$,$$SHEAOAK FLAT$$,#{state_id_sa},-34.779593,137.859065), - ($$5582$$,$$PORT GILES$$,#{state_id_sa},-35.02483,137.760696), - ($$5582$$,$$STANSBURY$$,#{state_id_sa},-35.02483,137.760696), - ($$5583$$,$$COOBOWIE$$,#{state_id_sa},-35.042188,137.733933), - ($$5583$$,$$EDITHBURGH$$,#{state_id_sa},-35.042188,137.733933), - ($$5600$$,$$BLACKY POINT$$,#{state_id_sa},-32.936701,137.57605), - ($$5600$$,$$CULTANA$$,#{state_id_sa},-32.936701,137.57605), - ($$5600$$,$$DOUGLAS POINT$$,#{state_id_sa},-32.936701,137.57605), - ($$5600$$,$$DOUGLAS POINT SOUTH$$,#{state_id_sa},-32.936701,137.57605), - ($$5600$$,$$FALSE BAY$$,#{state_id_sa},-32.936701,137.57605), - ($$5600$$,$$FITZGERALD BAY$$,#{state_id_sa},-32.936701,137.57605), - ($$5600$$,$$IRON BARON$$,#{state_id_sa},-32.936701,137.57605), - ($$5600$$,$$MULLAQUANA$$,#{state_id_sa},-32.936701,137.57605), - ($$5600$$,$$POINT LOWLY$$,#{state_id_sa},-32.936701,137.57605), - ($$5600$$,$$POINT LOWLY NORTH$$,#{state_id_sa},-32.936701,137.57605), - ($$5600$$,$$PORT BONYTHON$$,#{state_id_sa},-32.936701,137.57605), - ($$5600$$,$$WHYALLA$$,#{state_id_sa},-32.936701,137.57605), - ($$5600$$,$$WHYALLA DC$$,#{state_id_sa},-32.936701,137.57605), - ($$5600$$,$$WHYALLA PLAYFORD$$,#{state_id_sa},-32.936701,137.57605), - ($$5601$$,$$IRON KNOB$$,#{state_id_sa},-32.75339,137.165748), - ($$5602$$,$$COWELL$$,#{state_id_sa},-33.684835,136.926709), - ($$5602$$,$$LUCKY BAY$$,#{state_id_sa},-33.684835,136.926709), - ($$5602$$,$$MANGALO$$,#{state_id_sa},-33.684835,136.926709), - ($$5602$$,$$MIDGEE$$,#{state_id_sa},-33.684835,136.926709), - ($$5602$$,$$MILTALIE$$,#{state_id_sa},-33.684835,136.926709), - ($$5602$$,$$MINBRIE$$,#{state_id_sa},-33.684835,136.926709), - ($$5602$$,$$MITCHELLVILLE$$,#{state_id_sa},-33.684835,136.926709), - ($$5602$$,$$PORT GIBBON$$,#{state_id_sa},-33.684835,136.926709), - ($$5603$$,$$ARNO BAY$$,#{state_id_sa},-33.911901,136.568345), - ($$5603$$,$$HINCKS$$,#{state_id_sa},-33.911901,136.568345), - ($$5603$$,$$VERRAN$$,#{state_id_sa},-33.911901,136.568345), - ($$5603$$,$$WHARMINDA$$,#{state_id_sa},-33.911901,136.568345), - ($$5604$$,$$PORT NEILL$$,#{state_id_sa},-34.111906,136.329985), - ($$5605$$,$$BUTLER$$,#{state_id_sa},-34.105939,136.169528), - ($$5605$$,$$TUMBY BAY$$,#{state_id_sa},-34.105939,136.169528), - ($$5606$$,$$KIRTON POINT$$,#{state_id_sa},-34.726235,135.874457), - ($$5606$$,$$PORT LINCOLN$$,#{state_id_sa},-34.726235,135.874457), - ($$5607$$,$$BOSTON$$,#{state_id_sa},-34.655349,135.856036), - ($$5607$$,$$BROOKER$$,#{state_id_sa},-34.655349,135.856036), - ($$5607$$,$$CHARLTON GULLY$$,#{state_id_sa},-34.655349,135.856036), - ($$5607$$,$$COFFIN BAY$$,#{state_id_sa},-34.655349,135.856036), - ($$5607$$,$$COOMUNGA$$,#{state_id_sa},-34.655349,135.856036), - ($$5607$$,$$COULTA$$,#{state_id_sa},-34.655349,135.856036), - ($$5607$$,$$DUCK PONDS$$,#{state_id_sa},-34.655349,135.856036), - ($$5607$$,$$FOUNTAIN$$,#{state_id_sa},-34.655349,135.856036), - ($$5607$$,$$GREEN PATCH$$,#{state_id_sa},-34.655349,135.856036), - ($$5607$$,$$HAWSON$$,#{state_id_sa},-34.655349,135.856036), - ($$5607$$,$$HORSE PENINSULA$$,#{state_id_sa},-34.655349,135.856036), - ($$5607$$,$$KARKOO$$,#{state_id_sa},-34.655349,135.856036), - ($$5607$$,$$KELLIDIE BAY$$,#{state_id_sa},-34.655349,135.856036), - ($$5607$$,$$KIANA$$,#{state_id_sa},-34.655349,135.856036), - ($$5607$$,$$KOPPIO$$,#{state_id_sa},-34.655349,135.856036), - ($$5607$$,$$LINCOLN NATIONAL PARK$$,#{state_id_sa},-34.655349,135.856036), - ($$5607$$,$$LIPSON$$,#{state_id_sa},-34.655349,135.856036), - ($$5607$$,$$LOUTH BAY$$,#{state_id_sa},-34.655349,135.856036), - ($$5607$$,$$MITCHELL$$,#{state_id_sa},-34.655349,135.856036), - ($$5607$$,$$MOODY$$,#{state_id_sa},-34.655349,135.856036), - ($$5607$$,$$MOUNT DRUMMOND$$,#{state_id_sa},-34.655349,135.856036), - ($$5607$$,$$MOUNT DUTTON BAY$$,#{state_id_sa},-34.655349,135.856036), - ($$5607$$,$$MOUNT HOPE$$,#{state_id_sa},-34.655349,135.856036), - ($$5607$$,$$MURDINGA$$,#{state_id_sa},-34.655349,135.856036), - ($$5607$$,$$NORTH SHIELDS$$,#{state_id_sa},-34.655349,135.856036), - ($$5607$$,$$PEACHNA$$,#{state_id_sa},-34.655349,135.856036), - ($$5607$$,$$PEARLAH$$,#{state_id_sa},-34.655349,135.856036), - ($$5607$$,$$POONINDIE$$,#{state_id_sa},-34.655349,135.856036), - ($$5607$$,$$PORT LINCOLN$$,#{state_id_sa},-34.655349,135.856036), - ($$5607$$,$$SHERINGA$$,#{state_id_sa},-34.655349,135.856036), - ($$5607$$,$$SLEAFORD$$,#{state_id_sa},-34.655349,135.856036), - ($$5607$$,$$SULLIVAN$$,#{state_id_sa},-34.655349,135.856036), - ($$5607$$,$$TOOLIGIE$$,#{state_id_sa},-34.655349,135.856036), - ($$5607$$,$$TOOTENILLA$$,#{state_id_sa},-34.655349,135.856036), - ($$5607$$,$$TULKA$$,#{state_id_sa},-34.655349,135.856036), - ($$5607$$,$$TULKA NORTH$$,#{state_id_sa},-34.655349,135.856036), - ($$5607$$,$$ULEY$$,#{state_id_sa},-34.655349,135.856036), - ($$5607$$,$$UNGARRA$$,#{state_id_sa},-34.655349,135.856036), - ($$5607$$,$$VENUS BAY$$,#{state_id_sa},-34.655349,135.856036), - ($$5607$$,$$WANGARY$$,#{state_id_sa},-34.655349,135.856036), - ($$5607$$,$$WANILLA$$,#{state_id_sa},-34.655349,135.856036), - ($$5607$$,$$WARRACHIE$$,#{state_id_sa},-34.655349,135.856036), - ($$5607$$,$$WARROW$$,#{state_id_sa},-34.655349,135.856036), - ($$5607$$,$$WARUNDA$$,#{state_id_sa},-34.655349,135.856036), - ($$5607$$,$$WHITES FLAT$$,#{state_id_sa},-34.655349,135.856036), - ($$5607$$,$$WHITES RIVER$$,#{state_id_sa},-34.655349,135.856036), - ($$5607$$,$$YALLUNDA FLAT$$,#{state_id_sa},-34.655349,135.856036), - ($$5608$$,$$WHYALLA NORRIE$$,#{state_id_sa},-33.030705,137.534279), - ($$5608$$,$$WHYALLA NORRIE EAST$$,#{state_id_sa},-33.030705,137.534279), - ($$5608$$,$$WHYALLA NORRIE NORTH$$,#{state_id_sa},-33.030705,137.534279), - ($$5608$$,$$WHYALLA STUART$$,#{state_id_sa},-33.030705,137.534279), - ($$5609$$,$$WHYALLA JENKINS$$,#{state_id_sa},-33.030015,137.546266), - ($$5630$$,$$EDILLILIE$$,#{state_id_sa},-34.409842,135.708499), - ($$5631$$,$$COCKALEECHIE$$,#{state_id_sa},-34.205631,135.844197), - ($$5631$$,$$CUMMINS$$,#{state_id_sa},-34.205631,135.844197), - ($$5632$$,$$KAPINNIE$$,#{state_id_sa},-34.149121,135.497696), - ($$5632$$,$$YEELANNA$$,#{state_id_sa},-34.149121,135.497696), - ($$5633$$,$$BOONERDO$$,#{state_id_sa},-33.562483,135.976076), - ($$5633$$,$$LOCK$$,#{state_id_sa},-33.562483,135.976076), - ($$5640$$,$$CAMPOONA$$,#{state_id_sa},-33.502394,136.446932), - ($$5640$$,$$CLEVE$$,#{state_id_sa},-33.502394,136.446932), - ($$5640$$,$$JAMIESON$$,#{state_id_sa},-33.502394,136.446932), - ($$5640$$,$$WADDIKEE$$,#{state_id_sa},-33.502394,136.446932), - ($$5641$$,$$BARNA$$,#{state_id_sa},-33.183815,136.619341), - ($$5641$$,$$BUCKLEBOO$$,#{state_id_sa},-33.183815,136.619341), - ($$5641$$,$$CARALUE$$,#{state_id_sa},-33.183815,136.619341), - ($$5641$$,$$CORTLINYE$$,#{state_id_sa},-33.183815,136.619341), - ($$5641$$,$$CUNYARIE$$,#{state_id_sa},-33.183815,136.619341), - ($$5641$$,$$KELLY$$,#{state_id_sa},-33.183815,136.619341), - ($$5641$$,$$KIMBA$$,#{state_id_sa},-33.183815,136.619341), - ($$5641$$,$$MOSELEY$$,#{state_id_sa},-33.183815,136.619341), - ($$5641$$,$$PANITYA$$,#{state_id_sa},-33.183815,136.619341), - ($$5641$$,$$PINKAWILLINIE$$,#{state_id_sa},-33.183815,136.619341), - ($$5641$$,$$SOLOMON$$,#{state_id_sa},-33.183815,136.619341), - ($$5641$$,$$WILCHERRY$$,#{state_id_sa},-33.183815,136.619341), - ($$5641$$,$$YALANDA$$,#{state_id_sa},-33.183815,136.619341), - ($$5642$$,$$DARKE PEAK$$,#{state_id_sa},-33.469064,136.199199), - ($$5642$$,$$HAMBIDGE$$,#{state_id_sa},-33.469064,136.199199), - ($$5642$$,$$KIELPA$$,#{state_id_sa},-33.469064,136.199199), - ($$5642$$,$$MURLONG$$,#{state_id_sa},-33.469064,136.199199), - ($$5642$$,$$RUDALL$$,#{state_id_sa},-33.469064,136.199199), - ($$5650$$,$$COOTRA$$,#{state_id_sa},-33.209405,135.955463), - ($$5650$$,$$KOONGAWA$$,#{state_id_sa},-33.209405,135.955463), - ($$5650$$,$$WARRAMBOO$$,#{state_id_sa},-33.209405,135.955463), - ($$5651$$,$$KYANCUTTA$$,#{state_id_sa},-33.134498,135.555276), - ($$5652$$,$$PANEY$$,#{state_id_sa},-32.789341,135.378835), - ($$5652$$,$$WUDINNA$$,#{state_id_sa},-32.789341,135.378835), - ($$5653$$,$$YANINEE$$,#{state_id_sa},0.0,0.0), - ($$5654$$,$$COCATA$$,#{state_id_sa},-33.20121,135.147729), - ($$5654$$,$$KARCULTABY$$,#{state_id_sa},-33.20121,135.147729), - ($$5654$$,$$MINNIPA$$,#{state_id_sa},-33.20121,135.147729), - ($$5654$$,$$MOUNT DAMPER$$,#{state_id_sa},-33.20121,135.147729), - ($$5655$$,$$BOCKELBERG$$,#{state_id_sa},-32.58066,135.054861), - ($$5655$$,$$KALDOONERA$$,#{state_id_sa},-32.58066,135.054861), - ($$5655$$,$$POOCHERA$$,#{state_id_sa},-32.58066,135.054861), - ($$5655$$,$$PYGERY$$,#{state_id_sa},-32.58066,135.054861), - ($$5660$$,$$CHILPENUNDA$$,#{state_id_sa},-32.610433,134.499729), - ($$5660$$,$$CUNGENA$$,#{state_id_sa},-32.610433,134.499729), - ($$5661$$,$$KOOLGERA$$,#{state_id_sa},-32.360913,134.735545), - ($$5661$$,$$PIMBAACLA$$,#{state_id_sa},-32.360913,134.735545), - ($$5661$$,$$WALLALA$$,#{state_id_sa},-32.360913,134.735545), - ($$5661$$,$$WIRRULLA$$,#{state_id_sa},-32.360913,134.735545), - ($$5661$$,$$YANTANABIE$$,#{state_id_sa},-32.360913,134.735545), - ($$5670$$,$$BRAMFIELD$$,#{state_id_sa},-33.621171,134.994039), - ($$5670$$,$$COLTON$$,#{state_id_sa},-33.621171,134.994039), - ($$5670$$,$$COOLILLIE$$,#{state_id_sa},-33.621171,134.994039), - ($$5670$$,$$ELLISTON$$,#{state_id_sa},-33.621171,134.994039), - ($$5670$$,$$KAPPAWANTA$$,#{state_id_sa},-33.621171,134.994039), - ($$5670$$,$$MOUNT JOY$$,#{state_id_sa},-33.621171,134.994039), - ($$5670$$,$$MOUNT WEDGE$$,#{state_id_sa},-33.621171,134.994039), - ($$5670$$,$$PALKAGEE$$,#{state_id_sa},-33.621171,134.994039), - ($$5670$$,$$POLDA$$,#{state_id_sa},-33.621171,134.994039), - ($$5670$$,$$TALIA$$,#{state_id_sa},-33.621171,134.994039), - ($$5670$$,$$ULYERRA$$,#{state_id_sa},-33.621171,134.994039), - ($$5671$$,$$BAIRD BAY$$,#{state_id_sa},-33.142057,134.362438), - ($$5671$$,$$CALCA$$,#{state_id_sa},-33.142057,134.362438), - ($$5671$$,$$COLLEY$$,#{state_id_sa},-33.142057,134.362438), - ($$5671$$,$$MORTANA$$,#{state_id_sa},-33.142057,134.362438), - ($$5671$$,$$MOUNT COOPER$$,#{state_id_sa},-33.142057,134.362438), - ($$5671$$,$$PORT KENNY$$,#{state_id_sa},-33.142057,134.362438), - ($$5671$$,$$TYRINGA$$,#{state_id_sa},-33.142057,134.362438), - ($$5671$$,$$WITERA$$,#{state_id_sa},-33.142057,134.362438), - ($$5680$$,$$CARAWA$$,#{state_id_sa},-32.364553,134.235027), - ($$5680$$,$$CHANDADA$$,#{state_id_sa},-32.364553,134.235027), - ($$5680$$,$$CHINBINGINA$$,#{state_id_sa},-32.364553,134.235027), - ($$5680$$,$$EBA ANCHORAGE$$,#{state_id_sa},-32.364553,134.235027), - ($$5680$$,$$HASLAM$$,#{state_id_sa},-32.364553,134.235027), - ($$5680$$,$$INKSTER$$,#{state_id_sa},-32.364553,134.235027), - ($$5680$$,$$LAURA BAY$$,#{state_id_sa},-32.364553,134.235027), - ($$5680$$,$$MARYVALE$$,#{state_id_sa},-32.364553,134.235027), - ($$5680$$,$$MUDAMUCKLA$$,#{state_id_sa},-32.364553,134.235027), - ($$5680$$,$$NUNJIKOMPITA$$,#{state_id_sa},-32.364553,134.235027), - ($$5680$$,$$PERLUBIE$$,#{state_id_sa},-32.364553,134.235027), - ($$5680$$,$$PETINA$$,#{state_id_sa},-32.364553,134.235027), - ($$5680$$,$$PIEDNIPPIE$$,#{state_id_sa},-32.364553,134.235027), - ($$5680$$,$$PUNTABIE$$,#{state_id_sa},-32.364553,134.235027), - ($$5680$$,$$PUREBA$$,#{state_id_sa},-32.364553,134.235027), - ($$5680$$,$$SCEALE BAY$$,#{state_id_sa},-32.364553,134.235027), - ($$5680$$,$$SMOKY BAY$$,#{state_id_sa},-32.364553,134.235027), - ($$5680$$,$$STREAKY BAY$$,#{state_id_sa},-32.364553,134.235027), - ($$5680$$,$$WESTALL$$,#{state_id_sa},-32.364553,134.235027), - ($$5680$$,$$YANERBIE$$,#{state_id_sa},-32.364553,134.235027), - ($$5690$$,$$BOOKABIE$$,#{state_id_sa},-31.839911,132.6767), - ($$5690$$,$$CEDUNA$$,#{state_id_sa},-31.839911,132.6767), - ($$5690$$,$$CHARRA$$,#{state_id_sa},-31.839911,132.6767), - ($$5690$$,$$COORABIE$$,#{state_id_sa},-31.839911,132.6767), - ($$5690$$,$$DENIAL BAY$$,#{state_id_sa},-31.839911,132.6767), - ($$5690$$,$$FOWLERS BAY$$,#{state_id_sa},-31.839911,132.6767), - ($$5690$$,$$KALANBI$$,#{state_id_sa},-31.839911,132.6767), - ($$5690$$,$$KOONIBBA$$,#{state_id_sa},-31.839911,132.6767), - ($$5690$$,$$MALTEE$$,#{state_id_sa},-31.839911,132.6767), - ($$5690$$,$$MERGHINY$$,#{state_id_sa},-31.839911,132.6767), - ($$5690$$,$$NADIA$$,#{state_id_sa},-31.839911,132.6767), - ($$5690$$,$$NUNDROO$$,#{state_id_sa},-31.839911,132.6767), - ($$5690$$,$$PENONG$$,#{state_id_sa},-31.839911,132.6767), - ($$5690$$,$$THEVENARD$$,#{state_id_sa},-31.839911,132.6767), - ($$5690$$,$$UWORRA$$,#{state_id_sa},-31.839911,132.6767), - ($$5690$$,$$WANDANA$$,#{state_id_sa},-31.839911,132.6767), - ($$5690$$,$$WATRABA$$,#{state_id_sa},-31.839911,132.6767), - ($$5690$$,$$WHITE WELL CORNER$$,#{state_id_sa},-31.839911,132.6767), - ($$5690$$,$$YALATA$$,#{state_id_sa},-31.839911,132.6767), - ($$5700$$,$$BLANCHE HARBOR$$,#{state_id_sa},-32.732753,137.924519), - ($$5700$$,$$MIRANDA$$,#{state_id_sa},-32.732753,137.924519), - ($$5700$$,$$MUNDALLIO$$,#{state_id_sa},-32.732753,137.924519), - ($$5700$$,$$PORT AUGUSTA$$,#{state_id_sa},-32.732753,137.924519), - ($$5700$$,$$PORT AUGUSTA NORTH$$,#{state_id_sa},-32.732753,137.924519), - ($$5700$$,$$PORT AUGUSTA WEST$$,#{state_id_sa},-32.732753,137.924519), - ($$5700$$,$$PORT PATERSON$$,#{state_id_sa},-32.732753,137.924519), - ($$5700$$,$$WAMI KATA$$,#{state_id_sa},-32.732753,137.924519), - ($$5710$$,$$ARKAROOLA VILLAGE$$,#{state_id_sa},-32.544495,137.744019), - ($$5710$$,$$COMMISSARIAT POINT$$,#{state_id_sa},-32.544495,137.744019), - ($$5710$$,$$COOK$$,#{state_id_sa},-32.544495,137.744019), - ($$5710$$,$$GLENDAMBO$$,#{state_id_sa},-32.544495,137.744019), - ($$5710$$,$$KINGOONYA$$,#{state_id_sa},-32.544495,137.744019), - ($$5710$$,$$NONNING$$,#{state_id_sa},-32.544495,137.744019), - ($$5710$$,$$PORT AUGUSTA$$,#{state_id_sa},-32.544495,137.744019), - ($$5710$$,$$STIRLING NORTH$$,#{state_id_sa},-32.544495,137.744019), - ($$5710$$,$$TARCOOLA$$,#{state_id_sa},-32.544495,137.744019), - ($$5710$$,$$WINNINOWIE$$,#{state_id_sa},-32.544495,137.744019), - ($$5710$$,$$WOOLUNDUNGA$$,#{state_id_sa},-32.544495,137.744019), - ($$5720$$,$$PIMBA$$,#{state_id_sa},-31.259011,136.804586), - ($$5720$$,$$WOOMERA$$,#{state_id_sa},-31.259011,136.804586), - ($$5722$$,$$ANDAMOOKA$$,#{state_id_sa},-30.449401,137.163989), - ($$5723$$,$$COOBER PEDY$$,#{state_id_sa},-29.037845,134.723814), - ($$5724$$,$$MARLA$$,#{state_id_sa},-27.306573,133.623499), - ($$5724$$,$$MINTABIE$$,#{state_id_sa},-27.306573,133.623499), - ($$5725$$,$$OLYMPIC DAM$$,#{state_id_sa},-30.462627,136.879824), - ($$5725$$,$$ROXBY DOWNS$$,#{state_id_sa},-30.462627,136.879824), - ($$5730$$,$$BELTANA$$,#{state_id_sa},-30.814875,138.40378), - ($$5730$$,$$BLINMAN$$,#{state_id_sa},-30.814875,138.40378), - ($$5730$$,$$PARACHILNA$$,#{state_id_sa},-30.814875,138.40378), - ($$5731$$,$$COOPERS CREEK$$,#{state_id_sa},-27.800455,140.619083), - ($$5731$$,$$CORDILLO DOWNS$$,#{state_id_sa},-27.800455,140.619083), - ($$5731$$,$$DURHAM DOWNS$$,#{state_id_sa},-27.800455,140.619083), - ($$5731$$,$$INNAMINCKA$$,#{state_id_sa},-27.800455,140.619083), - ($$5731$$,$$LEIGH CREEK$$,#{state_id_sa},-27.800455,140.619083), - ($$5731$$,$$LYNDHURST$$,#{state_id_sa},-27.800455,140.619083), - ($$5731$$,$$MERTY MERTY$$,#{state_id_sa},-27.800455,140.619083), - ($$5731$$,$$MOOLAWATANA$$,#{state_id_sa},-27.800455,140.619083), - ($$5731$$,$$WITCHELINA$$,#{state_id_sa},-27.800455,140.619083), - ($$5732$$,$$COPLEY$$,#{state_id_sa},-30.552387,138.42385), - ($$5732$$,$$NEPABUNNA$$,#{state_id_sa},-30.552387,138.42385), - ($$5733$$,$$FARINA$$,#{state_id_sa},-30.067057,138.27706), - ($$5733$$,$$MARREE$$,#{state_id_sa},-30.067057,138.27706), - ($$5734$$,$$OODNADATTA$$,#{state_id_sa},-27.546529,135.447026), - ($$5800$$,$$ADELAIDE$$,#{state_id_sa},-35.120097,139.273782), - ($$5810$$,$$ADELAIDE$$,#{state_id_sa},-35.120097,139.273782), - ($$5839$$,$$ADELAIDE$$,#{state_id_sa},-35.120097,139.273782), - ($$5860$$,$$ADELAIDE MAIL CENTRE$$,#{state_id_sa},0.0,0.0), - ($$5861$$,$$ADELAIDE MAIL CENTRE$$,#{state_id_sa},0.0,0.0), - ($$5862$$,$$ADELAIDE MAIL CENTRE$$,#{state_id_sa},0.0,0.0), - ($$5863$$,$$ADELAIDE MAIL CENTRE$$,#{state_id_sa},0.0,0.0), - ($$5864$$,$$ADELAIDE MAIL CENTRE$$,#{state_id_sa},0.0,0.0), - ($$5865$$,$$ADELAIDE MAIL CENTRE$$,#{state_id_sa},0.0,0.0), - ($$5866$$,$$ADELAIDE MAIL CENTRE$$,#{state_id_sa},0.0,0.0), - ($$5867$$,$$ADELAIDE MAIL CENTRE$$,#{state_id_sa},0.0,0.0), - ($$5868$$,$$ADELAIDE MAIL CENTRE$$,#{state_id_sa},0.0,0.0), - ($$5869$$,$$ADELAIDE MAIL CENTRE$$,#{state_id_sa},0.0,0.0), - ($$5870$$,$$ADELAIDE MAIL CENTRE$$,#{state_id_sa},0.0,0.0), - ($$5871$$,$$ADELAIDE MAIL CENTRE$$,#{state_id_sa},0.0,0.0), - ($$5872$$,$$ADELAIDE MAIL CENTRE$$,#{state_id_sa},0.0,0.0), - ($$5873$$,$$ADELAIDE MAIL CENTRE$$,#{state_id_sa},0.0,0.0), - ($$5874$$,$$ADELAIDE MAIL CENTRE$$,#{state_id_sa},0.0,0.0), - ($$5875$$,$$ADELAIDE MAIL CENTRE$$,#{state_id_sa},0.0,0.0), - ($$5876$$,$$ADELAIDE MAIL CENTRE$$,#{state_id_sa},0.0,0.0), - ($$5877$$,$$ADELAIDE MAIL CENTRE$$,#{state_id_sa},0.0,0.0), - ($$5878$$,$$ADELAIDE MAIL CENTRE$$,#{state_id_sa},0.0,0.0), - ($$5879$$,$$ADELAIDE MAIL CENTRE$$,#{state_id_sa},0.0,0.0), - ($$5880$$,$$ADELAIDE MAIL CENTRE$$,#{state_id_sa},0.0,0.0), - ($$5881$$,$$ADELAIDE MAIL CENTRE$$,#{state_id_sa},0.0,0.0), - ($$5882$$,$$ADELAIDE MAIL CENTRE$$,#{state_id_sa},0.0,0.0), - ($$5883$$,$$ADELAIDE MAIL CENTRE$$,#{state_id_sa},0.0,0.0), - ($$5884$$,$$ADELAIDE MAIL CENTRE$$,#{state_id_sa},0.0,0.0), - ($$5885$$,$$ADELAIDE MAIL CENTRE$$,#{state_id_sa},0.0,0.0), - ($$5886$$,$$ADELAIDE MAIL CENTRE$$,#{state_id_sa},0.0,0.0), - ($$5887$$,$$ADELAIDE MAIL CENTRE$$,#{state_id_sa},0.0,0.0), - ($$5888$$,$$ADELAIDE MAIL CENTRE$$,#{state_id_sa},0.0,0.0), - ($$5889$$,$$ADELAIDE MAIL CENTRE$$,#{state_id_sa},0.0,0.0), - ($$5890$$,$$ADELAIDE MAIL CENTRE$$,#{state_id_sa},0.0,0.0), - ($$5891$$,$$ADELAIDE MAIL CENTRE$$,#{state_id_sa},0.0,0.0), - ($$5892$$,$$ADELAIDE MAIL CENTRE$$,#{state_id_sa},0.0,0.0), - ($$5893$$,$$ADELAIDE MAIL CENTRE$$,#{state_id_sa},0.0,0.0), - ($$5894$$,$$ADELAIDE MAIL CENTRE$$,#{state_id_sa},0.0,0.0), - ($$5895$$,$$ADELAIDE MAIL CENTRE$$,#{state_id_sa},0.0,0.0), - ($$5896$$,$$ADELAIDE MAIL CENTRE$$,#{state_id_sa},0.0,0.0), - ($$5897$$,$$ADELAIDE MAIL CENTRE$$,#{state_id_sa},0.0,0.0), - ($$5898$$,$$ADELAIDE MAIL CENTRE$$,#{state_id_sa},0.0,0.0), - ($$5899$$,$$ADELAIDE MAIL CENTRE$$,#{state_id_sa},0.0,0.0), - ($$5900$$,$$ADELAIDE MAIL CENTRE$$,#{state_id_sa},0.0,0.0), - ($$5901$$,$$ADELAIDE MAIL CENTRE$$,#{state_id_sa},0.0,0.0), - ($$5902$$,$$ADELAIDE MAIL CENTRE$$,#{state_id_sa},0.0,0.0), - ($$5903$$,$$ADELAIDE MAIL CENTRE$$,#{state_id_sa},0.0,0.0), - ($$5904$$,$$ADELAIDE MAIL CENTRE$$,#{state_id_sa},0.0,0.0), - ($$5920$$,$$ADELAIDE MAIL CENTRE$$,#{state_id_sa},0.0,0.0), - ($$5942$$,$$REGENCY PARK$$,#{state_id_sa},-34.860017,138.565906), - ($$5950$$,$$ADELAIDE AIRPORT$$,#{state_id_sa},-34.947669,138.531617), - ($$5950$$,$$EXPORT PARK$$,#{state_id_sa},-34.947669,138.531617), - ($$5960$$,$$GARDEN ISLAND$$,#{state_id_sa},0.0,0.0), - ($$5960$$,$$TORRENS ISLAND$$,#{state_id_sa},0.0,0.0), - ($$6000$$,$$CITY DELIVERY CENTRE$$,#{state_id_wa},-31.924074,115.91223), - ($$6000$$,$$PERTH$$,#{state_id_wa},-31.924074,115.91223), - ($$6000$$,$$PERTH GPO$$,#{state_id_wa},-31.924074,115.91223), - ($$6001$$,$$PERTH$$,#{state_id_wa},-31.99212,115.763228), - ($$6003$$,$$HIGHGATE$$,#{state_id_wa},-31.939272,115.869136), - ($$6003$$,$$NORTHBRIDGE$$,#{state_id_wa},-31.939272,115.869136), - ($$6004$$,$$EAST PERTH$$,#{state_id_wa},-31.943109,115.877401), - ($$6005$$,$$KINGS PARK$$,#{state_id_wa},-31.967791,115.836209), - ($$6005$$,$$WEST PERTH$$,#{state_id_wa},-31.967791,115.836209), - ($$6006$$,$$NORTH PERTH$$,#{state_id_wa},-31.982001,115.760961), - ($$6007$$,$$LEEDERVILLE$$,#{state_id_wa},-31.93648,115.841231), - ($$6007$$,$$WEST LEEDERVILLE$$,#{state_id_wa},-31.93648,115.841231), - ($$6008$$,$$DAGLISH$$,#{state_id_wa},-31.954097,115.809407), - ($$6008$$,$$SHENTON PARK$$,#{state_id_wa},-31.954097,115.809407), - ($$6008$$,$$SUBIACO$$,#{state_id_wa},-31.954097,115.809407), - ($$6008$$,$$SUBIACO EAST$$,#{state_id_wa},-31.954097,115.809407), - ($$6009$$,$$BROADWAY NEDLANDS$$,#{state_id_wa},-31.984059,115.814535), - ($$6009$$,$$CRAWLEY$$,#{state_id_wa},-31.984059,115.814535), - ($$6009$$,$$DALKEITH$$,#{state_id_wa},-31.984059,115.814535), - ($$6009$$,$$NEDLANDS$$,#{state_id_wa},-31.984059,115.814535), - ($$6009$$,$$NEDLANDS DC$$,#{state_id_wa},-31.984059,115.814535), - ($$6010$$,$$CLAREMONT$$,#{state_id_wa},-31.981145,115.781247), - ($$6010$$,$$CLAREMONT NORTH$$,#{state_id_wa},-31.981145,115.781247), - ($$6010$$,$$KARRAKATTA$$,#{state_id_wa},-31.981145,115.781247), - ($$6010$$,$$MOUNT CLAREMONT$$,#{state_id_wa},-31.981145,115.781247), - ($$6010$$,$$SWANBOURNE$$,#{state_id_wa},-31.981145,115.781247), - ($$6011$$,$$COTTESLOE$$,#{state_id_wa},-31.997976,115.762877), - ($$6011$$,$$PEPPERMINT GROVE$$,#{state_id_wa},-31.997976,115.762877), - ($$6012$$,$$MOSMAN PARK$$,#{state_id_wa},-32.006407,115.757689), - ($$6014$$,$$FLOREAT$$,#{state_id_wa},-31.937648,115.792209), - ($$6014$$,$$FLOREAT FORUM$$,#{state_id_wa},-31.937648,115.792209), - ($$6014$$,$$JOLIMONT$$,#{state_id_wa},-31.937648,115.792209), - ($$6014$$,$$WEMBLEY$$,#{state_id_wa},-31.937648,115.792209), - ($$6015$$,$$CITY BEACH$$,#{state_id_wa},-31.938276,115.754198), - ($$6016$$,$$GLENDALOUGH$$,#{state_id_wa},-31.920761,115.814243), - ($$6016$$,$$MOUNT HAWTHORN$$,#{state_id_wa},-31.920761,115.814243), - ($$6017$$,$$HERDSMAN$$,#{state_id_wa},-31.91279,115.811165), - ($$6017$$,$$OSBORNE PARK$$,#{state_id_wa},-31.91279,115.811165), - ($$6017$$,$$OSBORNE PARK DC$$,#{state_id_wa},-31.91279,115.811165), - ($$6018$$,$$CHURCHLANDS$$,#{state_id_wa},-31.920203,115.787679), - ($$6018$$,$$DOUBLEVIEW$$,#{state_id_wa},-31.920203,115.787679), - ($$6018$$,$$GWELUP$$,#{state_id_wa},-31.920203,115.787679), - ($$6018$$,$$GWELUP DC$$,#{state_id_wa},-31.920203,115.787679), - ($$6018$$,$$INNALOO$$,#{state_id_wa},-31.920203,115.787679), - ($$6018$$,$$KARRINYUP$$,#{state_id_wa},-31.920203,115.787679), - ($$6018$$,$$WOODLANDS$$,#{state_id_wa},-31.920203,115.787679), - ($$6019$$,$$SCARBOROUGH$$,#{state_id_wa},-31.894358,115.760162), - ($$6019$$,$$WEMBLEY DOWNS$$,#{state_id_wa},-31.894358,115.760162), - ($$6020$$,$$CARINE$$,#{state_id_wa},-31.850982,115.778122), - ($$6020$$,$$MARMION$$,#{state_id_wa},-31.850982,115.778122), - ($$6020$$,$$NORTH BEACH$$,#{state_id_wa},-31.850982,115.778122), - ($$6020$$,$$SORRENTO$$,#{state_id_wa},-31.850982,115.778122), - ($$6020$$,$$WATERMANS BAY$$,#{state_id_wa},-31.850982,115.778122), - ($$6021$$,$$BALCATTA$$,#{state_id_wa},-31.863835,115.817189), - ($$6021$$,$$STIRLING$$,#{state_id_wa},-31.863835,115.817189), - ($$6022$$,$$HAMERSLEY$$,#{state_id_wa},-31.849089,115.804841), - ($$6023$$,$$DUNCRAIG$$,#{state_id_wa},-31.833057,115.766122), - ($$6024$$,$$GREENWOOD$$,#{state_id_wa},-31.83105,115.797574), - ($$6024$$,$$WARWICK$$,#{state_id_wa},-31.83105,115.797574), - ($$6025$$,$$CRAIGIE$$,#{state_id_wa},-31.784792,115.767788), - ($$6025$$,$$HILLARYS$$,#{state_id_wa},-31.784792,115.767788), - ($$6025$$,$$KALLAROO$$,#{state_id_wa},-31.784792,115.767788), - ($$6025$$,$$PADBURY$$,#{state_id_wa},-31.784792,115.767788), - ($$6026$$,$$KINGSLEY$$,#{state_id_wa},-31.809099,115.788898), - ($$6026$$,$$WOODVALE$$,#{state_id_wa},-31.809099,115.788898), - ($$6027$$,$$BELDON$$,#{state_id_wa},-31.777325,115.75682), - ($$6027$$,$$CONNOLLY$$,#{state_id_wa},-31.777325,115.75682), - ($$6027$$,$$EDGEWATER$$,#{state_id_wa},-31.777325,115.75682), - ($$6027$$,$$HEATHRIDGE$$,#{state_id_wa},-31.777325,115.75682), - ($$6027$$,$$JOONDALUP$$,#{state_id_wa},-31.777325,115.75682), - ($$6027$$,$$JOONDALUP DC$$,#{state_id_wa},-31.777325,115.75682), - ($$6027$$,$$MULLALOO$$,#{state_id_wa},-31.777325,115.75682), - ($$6027$$,$$OCEAN REEF$$,#{state_id_wa},-31.777325,115.75682), - ($$6028$$,$$BURNS BEACH$$,#{state_id_wa},-31.7292,115.717254), - ($$6028$$,$$CURRAMBINE$$,#{state_id_wa},-31.7292,115.717254), - ($$6028$$,$$ILUKA$$,#{state_id_wa},-31.7292,115.717254), - ($$6028$$,$$KINROSS$$,#{state_id_wa},-31.7292,115.717254), - ($$6029$$,$$TRIGG$$,#{state_id_wa},-31.876031,115.752936), - ($$6030$$,$$CLARKSON$$,#{state_id_wa},-31.677452,115.721326), - ($$6030$$,$$MERRIWA$$,#{state_id_wa},-31.677452,115.721326), - ($$6030$$,$$MINDARIE$$,#{state_id_wa},-31.677452,115.721326), - ($$6030$$,$$QUINNS ROCKS$$,#{state_id_wa},-31.677452,115.721326), - ($$6030$$,$$RIDGEWOOD$$,#{state_id_wa},-31.677452,115.721326), - ($$6030$$,$$TAMALA PARK$$,#{state_id_wa},-31.677452,115.721326), - ($$6031$$,$$BANKSIA GROVE$$,#{state_id_wa},-31.697991,115.814615), - ($$6031$$,$$CARRAMAR$$,#{state_id_wa},-31.697991,115.814615), - ($$6031$$,$$NEERABUP$$,#{state_id_wa},-31.697991,115.814615), - ($$6032$$,$$NOWERGUP$$,#{state_id_wa},-31.642666,115.735647), - ($$6033$$,$$CARABOODA$$,#{state_id_wa},-31.60348,115.746096), - ($$6034$$,$$EGLINTON$$,#{state_id_wa},-31.85291,115.80596), - ($$6035$$,$$YANCHEP$$,#{state_id_wa},-31.54667,115.632045), - ($$6036$$,$$BUTLER$$,#{state_id_wa},-31.649385,115.70986), - ($$6036$$,$$JINDALEE$$,#{state_id_wa},-31.649385,115.70986), - ($$6037$$,$$TWO ROCKS$$,#{state_id_wa},-31.511727,115.604615), - ($$6038$$,$$ALKIMOS$$,#{state_id_wa},-32.289149,115.723047), - ($$6041$$,$$CARABAN$$,#{state_id_wa},-31.353571,115.526545), - ($$6041$$,$$GABBADAH$$,#{state_id_wa},-31.353571,115.526545), - ($$6041$$,$$GUILDERTON$$,#{state_id_wa},-31.353571,115.526545), - ($$6041$$,$$WILBINGA$$,#{state_id_wa},-31.353571,115.526545), - ($$6041$$,$$WOODRIDGE$$,#{state_id_wa},-31.353571,115.526545), - ($$6042$$,$$SEABIRD$$,#{state_id_wa},-31.254039,115.452067), - ($$6043$$,$$BRETON BAY$$,#{state_id_wa},-31.199838,115.420418), - ($$6043$$,$$LEDGE POINT$$,#{state_id_wa},-31.199838,115.420418), - ($$6044$$,$$KARAKIN$$,#{state_id_wa},-31.045751,115.421678), - ($$6044$$,$$LANCELIN$$,#{state_id_wa},-31.045751,115.421678), - ($$6044$$,$$NILGEN$$,#{state_id_wa},-31.045751,115.421678), - ($$6044$$,$$WEDGE ISLAND$$,#{state_id_wa},-31.045751,115.421678), - ($$6050$$,$$COOLBINIA$$,#{state_id_wa},-31.913458,115.857753), - ($$6050$$,$$MENORA$$,#{state_id_wa},-31.913458,115.857753), - ($$6050$$,$$MOUNT LAWLEY$$,#{state_id_wa},-31.913458,115.857753), - ($$6051$$,$$MAYLANDS$$,#{state_id_wa},-31.928146,115.892018), - ($$6052$$,$$BEDFORD$$,#{state_id_wa},-31.911949,115.893304), - ($$6052$$,$$INGLEWOOD$$,#{state_id_wa},-31.911949,115.893304), - ($$6053$$,$$BAYSWATER$$,#{state_id_wa},-31.91783,115.913379), - ($$6054$$,$$ASHFIELD$$,#{state_id_wa},-31.915202,115.937635), - ($$6054$$,$$BASSENDEAN$$,#{state_id_wa},-31.915202,115.937635), - ($$6054$$,$$BASSENDEAN DC$$,#{state_id_wa},-31.915202,115.937635), - ($$6054$$,$$EDEN HILL$$,#{state_id_wa},-31.915202,115.937635), - ($$6054$$,$$KIARA$$,#{state_id_wa},-31.915202,115.937635), - ($$6054$$,$$LOCKRIDGE$$,#{state_id_wa},-31.915202,115.937635), - ($$6055$$,$$CAVERSHAM$$,#{state_id_wa},-31.873677,115.977483), - ($$6055$$,$$GUILDFORD$$,#{state_id_wa},-31.873677,115.977483), - ($$6055$$,$$HAZELMERE$$,#{state_id_wa},-31.873677,115.977483), - ($$6055$$,$$HENLEY BROOK$$,#{state_id_wa},-31.873677,115.977483), - ($$6055$$,$$SOUTH GUILDFORD$$,#{state_id_wa},-31.873677,115.977483), - ($$6055$$,$$WEST SWAN$$,#{state_id_wa},-31.873677,115.977483), - ($$6056$$,$$BASKERVILLE$$,#{state_id_wa},-31.797228,116.02816), - ($$6056$$,$$BELLEVUE$$,#{state_id_wa},-31.797228,116.02816), - ($$6056$$,$$BOYA$$,#{state_id_wa},-31.797228,116.02816), - ($$6056$$,$$GREENMOUNT$$,#{state_id_wa},-31.797228,116.02816), - ($$6056$$,$$HELENA VALLEY$$,#{state_id_wa},-31.797228,116.02816), - ($$6056$$,$$HERNE HILL$$,#{state_id_wa},-31.797228,116.02816), - ($$6056$$,$$JANE BROOK$$,#{state_id_wa},-31.797228,116.02816), - ($$6056$$,$$KOONGAMIA$$,#{state_id_wa},-31.797228,116.02816), - ($$6056$$,$$MIDDLE SWAN$$,#{state_id_wa},-31.797228,116.02816), - ($$6056$$,$$MIDLAND$$,#{state_id_wa},-31.797228,116.02816), - ($$6056$$,$$MIDVALE$$,#{state_id_wa},-31.797228,116.02816), - ($$6056$$,$$MILLENDON$$,#{state_id_wa},-31.797228,116.02816), - ($$6056$$,$$RED HILL$$,#{state_id_wa},-31.797228,116.02816), - ($$6056$$,$$STRATTON$$,#{state_id_wa},-31.797228,116.02816), - ($$6056$$,$$SWAN VIEW$$,#{state_id_wa},-31.797228,116.02816), - ($$6056$$,$$VIVEASH$$,#{state_id_wa},-31.797228,116.02816), - ($$6056$$,$$WOODBRIDGE$$,#{state_id_wa},-31.797228,116.02816), - ($$6057$$,$$HIGH WYCOMBE$$,#{state_id_wa},-31.939411,116.013318), - ($$6057$$,$$MAIDA VALE$$,#{state_id_wa},-31.939411,116.013318), - ($$6058$$,$$FORRESTFIELD$$,#{state_id_wa},-31.986574,116.010649), - ($$6059$$,$$DIANELLA$$,#{state_id_wa},-31.882862,115.861726), - ($$6060$$,$$DOG SWAMP$$,#{state_id_wa},-31.909538,115.84596), - ($$6060$$,$$JOONDANNA$$,#{state_id_wa},-31.909538,115.84596), - ($$6060$$,$$TUART HILL$$,#{state_id_wa},-31.909538,115.84596), - ($$6060$$,$$YOKINE$$,#{state_id_wa},-31.909538,115.84596), - ($$6061$$,$$BALGA$$,#{state_id_wa},-31.861285,115.84319), - ($$6061$$,$$MIRRABOOKA$$,#{state_id_wa},-31.861285,115.84319), - ($$6061$$,$$NOLLAMARA$$,#{state_id_wa},-31.861285,115.84319), - ($$6061$$,$$WESTMINSTER$$,#{state_id_wa},-31.861285,115.84319), - ($$6062$$,$$EMBLETON$$,#{state_id_wa},-31.896813,115.91656), - ($$6062$$,$$MORLEY$$,#{state_id_wa},-31.896813,115.91656), - ($$6062$$,$$NORANDA$$,#{state_id_wa},-31.896813,115.91656), - ($$6063$$,$$BEECHBORO$$,#{state_id_wa},-31.867238,115.924904), - ($$6064$$,$$ALEXANDER HEIGHTS$$,#{state_id_wa},-31.82761,115.867134), - ($$6064$$,$$GIRRAWHEEN$$,#{state_id_wa},-31.82761,115.867134), - ($$6064$$,$$KOONDOOLA$$,#{state_id_wa},-31.82761,115.867134), - ($$6064$$,$$MARANGAROO$$,#{state_id_wa},-31.82761,115.867134), - ($$6065$$,$$ASHBY$$,#{state_id_wa},-31.734209,115.792445), - ($$6065$$,$$DARCH$$,#{state_id_wa},-31.734209,115.792445), - ($$6065$$,$$HOCKING$$,#{state_id_wa},-31.734209,115.792445), - ($$6065$$,$$KINGSWAY$$,#{state_id_wa},-31.734209,115.792445), - ($$6065$$,$$LANDSDALE$$,#{state_id_wa},-31.734209,115.792445), - ($$6065$$,$$MADELEY$$,#{state_id_wa},-31.734209,115.792445), - ($$6065$$,$$PEARSALL$$,#{state_id_wa},-31.734209,115.792445), - ($$6065$$,$$SINAGRA$$,#{state_id_wa},-31.734209,115.792445), - ($$6065$$,$$TAPPING$$,#{state_id_wa},-31.734209,115.792445), - ($$6065$$,$$WANGARA$$,#{state_id_wa},-31.734209,115.792445), - ($$6065$$,$$WANGARA DC$$,#{state_id_wa},-31.734209,115.792445), - ($$6065$$,$$WANNEROO$$,#{state_id_wa},-31.734209,115.792445), - ($$6066$$,$$BALLAJURA$$,#{state_id_wa},-31.83574,115.891911), - ($$6067$$,$$CULLACABARDEE$$,#{state_id_wa},-31.821166,115.912948), - ($$6068$$,$$WHITEMAN$$,#{state_id_wa},-31.834488,115.943632), - ($$6069$$,$$AVELEY$$,#{state_id_wa},-31.771612,116.007663), - ($$6069$$,$$BELHUS$$,#{state_id_wa},-31.771612,116.007663), - ($$6069$$,$$BRIGADOON$$,#{state_id_wa},-31.771612,116.007663), - ($$6069$$,$$ELLENBROOK$$,#{state_id_wa},-31.771612,116.007663), - ($$6069$$,$$ELLENBROOK EAST$$,#{state_id_wa},-31.771612,116.007663), - ($$6069$$,$$THE VINES$$,#{state_id_wa},-31.771612,116.007663), - ($$6069$$,$$UPPER SWAN$$,#{state_id_wa},-31.771612,116.007663), - ($$6070$$,$$DARLINGTON$$,#{state_id_wa},-31.918978,116.078749), - ($$6071$$,$$GLEN FORREST$$,#{state_id_wa},-31.909289,116.100209), - ($$6071$$,$$HOVEA$$,#{state_id_wa},-31.909289,116.100209), - ($$6072$$,$$MAHOGANY CREEK$$,#{state_id_wa},-31.868885,116.177751), - ($$6073$$,$$MUNDARING$$,#{state_id_wa},-31.902029,116.168205), - ($$6073$$,$$MUNDARING DC$$,#{state_id_wa},-31.902029,116.168205), - ($$6074$$,$$SAWYERS VALLEY$$,#{state_id_wa},-31.902142,116.201914), - ($$6076$$,$$BICKLEY$$,#{state_id_wa},-32.005284,116.090007), - ($$6076$$,$$CARMEL$$,#{state_id_wa},-32.005284,116.090007), - ($$6076$$,$$GOOSEBERRY HILL$$,#{state_id_wa},-32.005284,116.090007), - ($$6076$$,$$HACKETTS GULLY$$,#{state_id_wa},-32.005284,116.090007), - ($$6076$$,$$KALAMUNDA$$,#{state_id_wa},-32.005284,116.090007), - ($$6076$$,$$LESMURDIE$$,#{state_id_wa},-32.005284,116.090007), - ($$6076$$,$$PAULLS VALLEY$$,#{state_id_wa},-32.005284,116.090007), - ($$6076$$,$$PICKERING BROOK$$,#{state_id_wa},-32.005284,116.090007), - ($$6076$$,$$PIESSE BROOK$$,#{state_id_wa},-32.005284,116.090007), - ($$6076$$,$$RESERVOIR$$,#{state_id_wa},-32.005284,116.090007), - ($$6076$$,$$WALLISTON$$,#{state_id_wa},-32.005284,116.090007), - ($$6076$$,$$WALLISTON DC$$,#{state_id_wa},-32.005284,116.090007), - ($$6077$$,$$GNANGARA$$,#{state_id_wa},0.0,0.0), - ($$6077$$,$$JANDABUP$$,#{state_id_wa},0.0,0.0), - ($$6078$$,$$MARIGINIUP$$,#{state_id_wa},0.0,0.0), - ($$6078$$,$$PINJAR$$,#{state_id_wa},0.0,0.0), - ($$6079$$,$$LEXIA$$,#{state_id_wa},0.0,0.0), - ($$6079$$,$$MELALEUCA$$,#{state_id_wa},0.0,0.0), - ($$6081$$,$$PARKERVILLE$$,#{state_id_wa},-31.876046,116.146074), - ($$6081$$,$$STONEVILLE$$,#{state_id_wa},-31.876046,116.146074), - ($$6082$$,$$BAILUP$$,#{state_id_wa},-31.738368,116.295706), - ($$6082$$,$$MOUNT HELENA$$,#{state_id_wa},-31.738368,116.295706), - ($$6083$$,$$GIDGEGANNUP$$,#{state_id_wa},-31.793051,116.196639), - ($$6083$$,$$MORANGUP$$,#{state_id_wa},-31.793051,116.196639), - ($$6084$$,$$AVON VALLEY NATIONAL PARK$$,#{state_id_wa},-31.622485,116.207151), - ($$6084$$,$$BULLSBROOK$$,#{state_id_wa},-31.622485,116.207151), - ($$6084$$,$$CHITTERING$$,#{state_id_wa},-31.622485,116.207151), - ($$6084$$,$$LOWER CHITTERING$$,#{state_id_wa},-31.622485,116.207151), - ($$6084$$,$$WALYUNGA NATIONAL PARK$$,#{state_id_wa},-31.622485,116.207151), - ($$6090$$,$$MALAGA$$,#{state_id_wa},-31.862589,115.894254), - ($$6100$$,$$BURSWOOD$$,#{state_id_wa},-31.959952,115.902637), - ($$6100$$,$$LATHLAIN$$,#{state_id_wa},-31.959952,115.902637), - ($$6100$$,$$VICTORIA PARK$$,#{state_id_wa},-31.959952,115.902637), - ($$6101$$,$$CARLISLE$$,#{state_id_wa},-31.975068,115.91493), - ($$6101$$,$$CARLISLE NORTH$$,#{state_id_wa},-31.975068,115.91493), - ($$6101$$,$$CARLISLE SOUTH$$,#{state_id_wa},-31.975068,115.91493), - ($$6101$$,$$EAST VICTORIA PARK$$,#{state_id_wa},-31.975068,115.91493), - ($$6102$$,$$BENTLEY$$,#{state_id_wa},-32.001778,115.918975), - ($$6102$$,$$BENTLEY DC$$,#{state_id_wa},-32.001778,115.918975), - ($$6102$$,$$BENTLEY SOUTH$$,#{state_id_wa},-32.001778,115.918975), - ($$6102$$,$$ST JAMES$$,#{state_id_wa},-32.001778,115.918975), - ($$6103$$,$$RIVERVALE$$,#{state_id_wa},-31.955979,115.905144), - ($$6104$$,$$ASCOT$$,#{state_id_wa},-31.940682,115.923003), - ($$6104$$,$$BELMONT$$,#{state_id_wa},-31.940682,115.923003), - ($$6104$$,$$REDCLIFFE$$,#{state_id_wa},-31.940682,115.923003), - ($$6105$$,$$CLOVERDALE$$,#{state_id_wa},-31.963644,115.934344), - ($$6105$$,$$KEWDALE$$,#{state_id_wa},-31.963644,115.934344), - ($$6105$$,$$PERTH AIRPORT$$,#{state_id_wa},-31.963644,115.934344), - ($$6106$$,$$WELSHPOOL$$,#{state_id_wa},-31.988232,115.945389), - ($$6106$$,$$WELSHPOOL DC$$,#{state_id_wa},-31.988232,115.945389), - ($$6107$$,$$BECKENHAM$$,#{state_id_wa},-32.014104,115.964808), - ($$6107$$,$$CANNINGTON$$,#{state_id_wa},-32.014104,115.964808), - ($$6107$$,$$EAST CANNINGTON$$,#{state_id_wa},-32.014104,115.964808), - ($$6107$$,$$KENWICK$$,#{state_id_wa},-32.014104,115.964808), - ($$6107$$,$$QUEENS PARK$$,#{state_id_wa},-32.014104,115.964808), - ($$6107$$,$$WATTLE GROVE$$,#{state_id_wa},-32.014104,115.964808), - ($$6107$$,$$WILSON$$,#{state_id_wa},-32.014104,115.964808), - ($$6108$$,$$THORNLIE$$,#{state_id_wa},-32.050009,115.964764), - ($$6109$$,$$MADDINGTON$$,#{state_id_wa},-32.050158,115.976376), - ($$6109$$,$$ORANGE GROVE$$,#{state_id_wa},-32.050158,115.976376), - ($$6110$$,$$GOSNELLS$$,#{state_id_wa},-32.072009,116.002362), - ($$6110$$,$$HUNTINGDALE$$,#{state_id_wa},-32.072009,116.002362), - ($$6110$$,$$MARTIN$$,#{state_id_wa},-32.072009,116.002362), - ($$6110$$,$$SOUTHERN RIVER$$,#{state_id_wa},-32.072009,116.002362), - ($$6111$$,$$ASHENDON$$,#{state_id_wa},-32.10398,116.192636), - ($$6111$$,$$CAMILLO$$,#{state_id_wa},-32.10398,116.192636), - ($$6111$$,$$CANNING MILLS$$,#{state_id_wa},-32.10398,116.192636), - ($$6111$$,$$CHAMPION LAKES$$,#{state_id_wa},-32.10398,116.192636), - ($$6111$$,$$KARRAGULLEN$$,#{state_id_wa},-32.10398,116.192636), - ($$6111$$,$$KELMSCOTT$$,#{state_id_wa},-32.10398,116.192636), - ($$6111$$,$$KELMSCOTT DC$$,#{state_id_wa},-32.10398,116.192636), - ($$6111$$,$$LESLEY$$,#{state_id_wa},-32.10398,116.192636), - ($$6111$$,$$ROLEYSTONE$$,#{state_id_wa},-32.10398,116.192636), - ($$6112$$,$$ARMADALE$$,#{state_id_wa},-32.152386,116.008274), - ($$6112$$,$$BEDFORDALE$$,#{state_id_wa},-32.152386,116.008274), - ($$6112$$,$$BROOKDALE$$,#{state_id_wa},-32.152386,116.008274), - ($$6112$$,$$FORRESTDALE$$,#{state_id_wa},-32.152386,116.008274), - ($$6112$$,$$HARRISDALE$$,#{state_id_wa},-32.152386,116.008274), - ($$6112$$,$$HAYNES$$,#{state_id_wa},-32.152386,116.008274), - ($$6112$$,$$HILBERT$$,#{state_id_wa},-32.152386,116.008274), - ($$6112$$,$$MOUNT NASURA$$,#{state_id_wa},-32.152386,116.008274), - ($$6112$$,$$MOUNT RICHON$$,#{state_id_wa},-32.152386,116.008274), - ($$6112$$,$$PIARA WATERS$$,#{state_id_wa},-32.152386,116.008274), - ($$6112$$,$$SEVILLE GROVE$$,#{state_id_wa},-32.152386,116.008274), - ($$6112$$,$$WUNGONG$$,#{state_id_wa},-32.152386,116.008274), - ($$6121$$,$$OAKFORD$$,#{state_id_wa},-32.208934,115.916459), - ($$6121$$,$$OLDBURY$$,#{state_id_wa},-32.208934,115.916459), - ($$6122$$,$$BYFORD$$,#{state_id_wa},-32.221725,116.0072), - ($$6122$$,$$CARDUP$$,#{state_id_wa},-32.221725,116.0072), - ($$6122$$,$$DARLING DOWNS$$,#{state_id_wa},-32.221725,116.0072), - ($$6122$$,$$KARRAKUP$$,#{state_id_wa},-32.221725,116.0072), - ($$6123$$,$$MUNDIJONG$$,#{state_id_wa},-32.295176,115.98589), - ($$6123$$,$$WHITBY$$,#{state_id_wa},-32.295176,115.98589), - ($$6124$$,$$JARRAHDALE$$,#{state_id_wa},-32.338316,116.072494), - ($$6125$$,$$HOPELAND$$,#{state_id_wa},-32.364427,115.901277), - ($$6125$$,$$MARDELLA$$,#{state_id_wa},-32.364427,115.901277), - ($$6125$$,$$SERPENTINE$$,#{state_id_wa},-32.364427,115.901277), - ($$6126$$,$$KEYSBROOK$$,#{state_id_wa},-32.440033,115.977043), - ($$6147$$,$$LANGFORD$$,#{state_id_wa},-32.043771,115.941353), - ($$6147$$,$$LYNWOOD$$,#{state_id_wa},-32.043771,115.941353), - ($$6147$$,$$PARKWOOD$$,#{state_id_wa},-32.043771,115.941353), - ($$6148$$,$$FERNDALE$$,#{state_id_wa},-32.030183,115.924691), - ($$6148$$,$$RIVERTON$$,#{state_id_wa},-32.030183,115.924691), - ($$6148$$,$$ROSSMOYNE$$,#{state_id_wa},-32.030183,115.924691), - ($$6148$$,$$SHELLEY$$,#{state_id_wa},-32.030183,115.924691), - ($$6149$$,$$BULL CREEK$$,#{state_id_wa},-32.064941,115.858679), - ($$6149$$,$$LEEMING$$,#{state_id_wa},-32.064941,115.858679), - ($$6150$$,$$BATEMAN$$,#{state_id_wa},-32.059615,115.844417), - ($$6150$$,$$MURDOCH$$,#{state_id_wa},-32.059615,115.844417), - ($$6150$$,$$WINTHROP$$,#{state_id_wa},-32.059615,115.844417), - ($$6151$$,$$KENSINGTON$$,#{state_id_wa},-31.987458,115.882958), - ($$6151$$,$$SOUTH PERTH$$,#{state_id_wa},-31.987458,115.882958), - ($$6151$$,$$SOUTH PERTH ANGELO ST$$,#{state_id_wa},-31.987458,115.882958), - ($$6152$$,$$COMO$$,#{state_id_wa},-31.989651,115.870024), - ($$6152$$,$$KARAWARA$$,#{state_id_wa},-31.989651,115.870024), - ($$6152$$,$$MANNING$$,#{state_id_wa},-31.989651,115.870024), - ($$6152$$,$$SALTER POINT$$,#{state_id_wa},-31.989651,115.870024), - ($$6152$$,$$WATERFORD$$,#{state_id_wa},-31.989651,115.870024), - ($$6153$$,$$APPLECROSS$$,#{state_id_wa},-32.0194,115.833223), - ($$6153$$,$$APPLECROSS NORTH$$,#{state_id_wa},-32.0194,115.833223), - ($$6153$$,$$ARDROSS$$,#{state_id_wa},-32.0194,115.833223), - ($$6153$$,$$BRENTWOOD$$,#{state_id_wa},-32.0194,115.833223), - ($$6153$$,$$CANNING BRIDGE APPLECROSS$$,#{state_id_wa},-32.0194,115.833223), - ($$6153$$,$$MOUNT PLEASANT$$,#{state_id_wa},-32.0194,115.833223), - ($$6154$$,$$ALFRED COVE$$,#{state_id_wa},-32.034236,115.808606), - ($$6154$$,$$BOORAGOON$$,#{state_id_wa},-32.034236,115.808606), - ($$6154$$,$$MYAREE$$,#{state_id_wa},-32.034236,115.808606), - ($$6155$$,$$CANNING VALE$$,#{state_id_wa},-32.057985,115.918139), - ($$6155$$,$$CANNING VALE DC$$,#{state_id_wa},-32.057985,115.918139), - ($$6155$$,$$CANNING VALE EAST$$,#{state_id_wa},-32.057985,115.918139), - ($$6155$$,$$CANNING VALE SOUTH$$,#{state_id_wa},-32.057985,115.918139), - ($$6155$$,$$WILLETTON$$,#{state_id_wa},-32.057985,115.918139), - ($$6156$$,$$ATTADALE$$,#{state_id_wa},-32.02581,115.80135), - ($$6156$$,$$MELVILLE$$,#{state_id_wa},-32.02581,115.80135), - ($$6156$$,$$WILLAGEE$$,#{state_id_wa},-32.02581,115.80135), - ($$6156$$,$$WILLAGEE CENTRAL$$,#{state_id_wa},-32.02581,115.80135), - ($$6157$$,$$BICTON$$,#{state_id_wa},-32.02998,115.784534), - ($$6157$$,$$PALMYRA$$,#{state_id_wa},-32.02998,115.784534), - ($$6157$$,$$PALMYRA DC$$,#{state_id_wa},-32.02998,115.784534), - ($$6158$$,$$EAST FREMANTLE$$,#{state_id_wa},-32.044951,115.758095), - ($$6159$$,$$NORTH FREMANTLE$$,#{state_id_wa},-32.029041,115.751874), - ($$6160$$,$$FREMANTLE$$,#{state_id_wa},-32.035478,115.764573), - ($$6161$$,$$ROTTNEST ISLAND$$,#{state_id_wa},-32.007488,115.503938), - ($$6162$$,$$BEACONSFIELD$$,#{state_id_wa},-32.067527,115.764181), - ($$6162$$,$$SOUTH FREMANTLE$$,#{state_id_wa},-32.067527,115.764181), - ($$6162$$,$$WHITE GUM VALLEY$$,#{state_id_wa},-32.067527,115.764181), - ($$6163$$,$$BIBRA LAKE$$,#{state_id_wa},-32.092963,115.837476), - ($$6163$$,$$BIBRA LAKE DC$$,#{state_id_wa},-32.092963,115.837476), - ($$6163$$,$$COOLBELLUP$$,#{state_id_wa},-32.092963,115.837476), - ($$6163$$,$$HAMILTON HILL$$,#{state_id_wa},-32.092963,115.837476), - ($$6163$$,$$HILTON$$,#{state_id_wa},-32.092963,115.837476), - ($$6163$$,$$KARDINYA$$,#{state_id_wa},-32.092963,115.837476), - ($$6163$$,$$NORTH COOGEE$$,#{state_id_wa},-32.092963,115.837476), - ($$6163$$,$$NORTH LAKE$$,#{state_id_wa},-32.092963,115.837476), - ($$6163$$,$$O'CONNOR$$,#{state_id_wa},-32.092963,115.837476), - ($$6163$$,$$SAMSON$$,#{state_id_wa},-32.092963,115.837476), - ($$6163$$,$$SPEARWOOD$$,#{state_id_wa},-32.092963,115.837476), - ($$6164$$,$$ATWELL$$,#{state_id_wa},-32.132801,115.860591), - ($$6164$$,$$AUBIN GROVE$$,#{state_id_wa},-32.132801,115.860591), - ($$6164$$,$$BANJUP$$,#{state_id_wa},-32.132801,115.860591), - ($$6164$$,$$BEELIAR$$,#{state_id_wa},-32.132801,115.860591), - ($$6164$$,$$COCKBURN CENTRAL$$,#{state_id_wa},-32.132801,115.860591), - ($$6164$$,$$HAMMOND PARK$$,#{state_id_wa},-32.132801,115.860591), - ($$6164$$,$$JANDAKOT$$,#{state_id_wa},-32.132801,115.860591), - ($$6164$$,$$SOUTH LAKE$$,#{state_id_wa},-32.132801,115.860591), - ($$6164$$,$$SUCCESS$$,#{state_id_wa},-32.132801,115.860591), - ($$6164$$,$$YANGEBUP$$,#{state_id_wa},-32.132801,115.860591), - ($$6165$$,$$HOPE VALLEY$$,#{state_id_wa},-32.190027,115.801492), - ($$6165$$,$$NAVAL BASE$$,#{state_id_wa},-32.190027,115.801492), - ($$6166$$,$$COOGEE$$,#{state_id_wa},-32.116146,115.76579), - ($$6166$$,$$HENDERSON$$,#{state_id_wa},-32.116146,115.76579), - ($$6166$$,$$MUNSTER$$,#{state_id_wa},-32.116146,115.76579), - ($$6166$$,$$WATTLEUP$$,#{state_id_wa},-32.116146,115.76579), - ($$6167$$,$$ANKETELL$$,#{state_id_wa},-32.227693,115.870492), - ($$6167$$,$$BERTRAM$$,#{state_id_wa},-32.227693,115.870492), - ($$6167$$,$$CALISTA$$,#{state_id_wa},-32.227693,115.870492), - ($$6167$$,$$CASUARINA$$,#{state_id_wa},-32.227693,115.870492), - ($$6167$$,$$KWINANA BEACH$$,#{state_id_wa},-32.227693,115.870492), - ($$6167$$,$$KWINANA TOWN CENTRE$$,#{state_id_wa},-32.227693,115.870492), - ($$6167$$,$$MANDOGALUP$$,#{state_id_wa},-32.227693,115.870492), - ($$6167$$,$$MEDINA$$,#{state_id_wa},-32.227693,115.870492), - ($$6167$$,$$ORELIA$$,#{state_id_wa},-32.227693,115.870492), - ($$6167$$,$$PARMELIA$$,#{state_id_wa},-32.227693,115.870492), - ($$6167$$,$$POSTANS$$,#{state_id_wa},-32.227693,115.870492), - ($$6167$$,$$THE SPECTACLES$$,#{state_id_wa},-32.227693,115.870492), - ($$6167$$,$$WANDI$$,#{state_id_wa},-32.227693,115.870492), - ($$6168$$,$$COOLOONGUP$$,#{state_id_wa},-32.299743,115.762751), - ($$6168$$,$$EAST ROCKINGHAM$$,#{state_id_wa},-32.299743,115.762751), - ($$6168$$,$$GARDEN ISLAND$$,#{state_id_wa},-32.299743,115.762751), - ($$6168$$,$$HILLMAN$$,#{state_id_wa},-32.299743,115.762751), - ($$6168$$,$$PERON$$,#{state_id_wa},-32.299743,115.762751), - ($$6168$$,$$ROCKINGHAM$$,#{state_id_wa},-32.299743,115.762751), - ($$6168$$,$$ROCKINGHAM BEACH$$,#{state_id_wa},-32.299743,115.762751), - ($$6168$$,$$ROCKINGHAM DC$$,#{state_id_wa},-32.299743,115.762751), - ($$6169$$,$$SAFETY BAY$$,#{state_id_wa},-32.303433,115.711289), - ($$6169$$,$$SHOALWATER$$,#{state_id_wa},-32.303433,115.711289), - ($$6169$$,$$WAIKIKI$$,#{state_id_wa},-32.303433,115.711289), - ($$6169$$,$$WARNBRO$$,#{state_id_wa},-32.303433,115.711289), - ($$6170$$,$$LEDA$$,#{state_id_wa},-32.259689,115.805649), - ($$6170$$,$$WELLARD$$,#{state_id_wa},-32.259689,115.805649), - ($$6171$$,$$BALDIVIS$$,#{state_id_wa},-32.331964,115.821549), - ($$6172$$,$$PORT KENNEDY$$,#{state_id_wa},-32.37547,115.753147), - ($$6173$$,$$SECRET HARBOUR$$,#{state_id_wa},-32.402574,115.749186), - ($$6174$$,$$GOLDEN BAY$$,#{state_id_wa},-32.429853,115.750353), - ($$6175$$,$$SINGLETON$$,#{state_id_wa},-32.445807,115.757022), - ($$6176$$,$$KARNUP$$,#{state_id_wa},-32.364374,115.909164), - ($$6180$$,$$LAKELANDS$$,#{state_id_wa},-31.776998,115.86075), - ($$6180$$,$$PARKLANDS$$,#{state_id_wa},-31.776998,115.86075), - ($$6181$$,$$STAKE HILL$$,#{state_id_wa},0.0,0.0), - ($$6182$$,$$KERALUP$$,#{state_id_wa},0.0,0.0), - ($$6207$$,$$MYARA$$,#{state_id_wa},-32.487206,116.068209), - ($$6207$$,$$NAMBEELUP$$,#{state_id_wa},-32.487206,116.068209), - ($$6207$$,$$NORTH DANDALUP$$,#{state_id_wa},-32.487206,116.068209), - ($$6207$$,$$SOLUS$$,#{state_id_wa},-32.487206,116.068209), - ($$6207$$,$$WHITTAKER$$,#{state_id_wa},-32.487206,116.068209), - ($$6208$$,$$BLYTHEWOOD$$,#{state_id_wa},-32.644372,115.873516), - ($$6208$$,$$FAIRBRIDGE$$,#{state_id_wa},-32.644372,115.873516), - ($$6208$$,$$MEELON$$,#{state_id_wa},-32.644372,115.873516), - ($$6208$$,$$NIRIMBA$$,#{state_id_wa},-32.644372,115.873516), - ($$6208$$,$$NORTH YUNDERUP$$,#{state_id_wa},-32.644372,115.873516), - ($$6208$$,$$OAKLEY$$,#{state_id_wa},-32.644372,115.873516), - ($$6208$$,$$PINJARRA$$,#{state_id_wa},-32.644372,115.873516), - ($$6208$$,$$POINT GREY$$,#{state_id_wa},-32.644372,115.873516), - ($$6208$$,$$RAVENSWOOD$$,#{state_id_wa},-32.644372,115.873516), - ($$6208$$,$$SOUTH YUNDERUP$$,#{state_id_wa},-32.644372,115.873516), - ($$6208$$,$$WEST PINJARRA$$,#{state_id_wa},-32.644372,115.873516), - ($$6209$$,$$BARRAGUP$$,#{state_id_wa},-32.561118,115.771132), - ($$6209$$,$$FURNISSDALE$$,#{state_id_wa},-32.561118,115.771132), - ($$6210$$,$$COODANUP$$,#{state_id_wa},-32.550988,115.751804), - ($$6210$$,$$DUDLEY PARK$$,#{state_id_wa},-32.550988,115.751804), - ($$6210$$,$$ERSKINE$$,#{state_id_wa},-32.550988,115.751804), - ($$6210$$,$$FALCON$$,#{state_id_wa},-32.550988,115.751804), - ($$6210$$,$$GREENFIELDS$$,#{state_id_wa},-32.550988,115.751804), - ($$6210$$,$$HALLS HEAD$$,#{state_id_wa},-32.550988,115.751804), - ($$6210$$,$$MADORA BAY$$,#{state_id_wa},-32.550988,115.751804), - ($$6210$$,$$MANDURAH$$,#{state_id_wa},-32.550988,115.751804), - ($$6210$$,$$MANDURAH DC$$,#{state_id_wa},-32.550988,115.751804), - ($$6210$$,$$MANDURAH EAST$$,#{state_id_wa},-32.550988,115.751804), - ($$6210$$,$$MANDURAH NORTH$$,#{state_id_wa},-32.550988,115.751804), - ($$6210$$,$$MEADOW SPRINGS$$,#{state_id_wa},-32.550988,115.751804), - ($$6210$$,$$SAN REMO$$,#{state_id_wa},-32.550988,115.751804), - ($$6210$$,$$SILVER SANDS$$,#{state_id_wa},-32.550988,115.751804), - ($$6210$$,$$WANNANUP$$,#{state_id_wa},-32.550988,115.751804), - ($$6211$$,$$BOUVARD$$,#{state_id_wa},-32.693999,115.654696), - ($$6211$$,$$CLIFTON$$,#{state_id_wa},-32.693999,115.654696), - ($$6211$$,$$DAWESVILLE$$,#{state_id_wa},-32.693999,115.654696), - ($$6211$$,$$HERRON$$,#{state_id_wa},-32.693999,115.654696), - ($$6213$$,$$BANKSIADALE$$,#{state_id_wa},-32.623155,116.084814), - ($$6213$$,$$DWELLINGUP$$,#{state_id_wa},-32.623155,116.084814), - ($$6213$$,$$ETMILYN$$,#{state_id_wa},-32.623155,116.084814), - ($$6213$$,$$HOLYOAKE$$,#{state_id_wa},-32.623155,116.084814), - ($$6213$$,$$INGLEHOPE$$,#{state_id_wa},-32.623155,116.084814), - ($$6213$$,$$MARRINUP$$,#{state_id_wa},-32.623155,116.084814), - ($$6213$$,$$TEESDALE$$,#{state_id_wa},-32.623155,116.084814), - ($$6214$$,$$BIRCHMONT$$,#{state_id_wa},-32.725337,115.758098), - ($$6214$$,$$COOLUP$$,#{state_id_wa},-32.725337,115.758098), - ($$6214$$,$$WEST COOLUP$$,#{state_id_wa},-32.725337,115.758098), - ($$6215$$,$$HAMEL$$,#{state_id_wa},-32.880761,115.92388), - ($$6215$$,$$LAKE CLIFTON$$,#{state_id_wa},-32.880761,115.92388), - ($$6215$$,$$NANGA BROOK$$,#{state_id_wa},-32.880761,115.92388), - ($$6215$$,$$PRESTON BEACH$$,#{state_id_wa},-32.880761,115.92388), - ($$6215$$,$$WAGERUP$$,#{state_id_wa},-32.880761,115.92388), - ($$6215$$,$$WAROONA$$,#{state_id_wa},-32.880761,115.92388), - ($$6218$$,$$YARLOOP$$,#{state_id_wa},-32.955564,115.899665), - ($$6220$$,$$COOKERNUP$$,#{state_id_wa},-32.996681,115.893629), - ($$6220$$,$$HARVEY$$,#{state_id_wa},-32.996681,115.893629), - ($$6220$$,$$HOFFMAN$$,#{state_id_wa},-32.996681,115.893629), - ($$6220$$,$$MYALUP$$,#{state_id_wa},-32.996681,115.893629), - ($$6220$$,$$UDUC$$,#{state_id_wa},-32.996681,115.893629), - ($$6220$$,$$WARAWARRUP$$,#{state_id_wa},-32.996681,115.893629), - ($$6221$$,$$MORNINGTON$$,#{state_id_wa},-33.14636,115.943364), - ($$6221$$,$$WOKALUP$$,#{state_id_wa},-33.14636,115.943364), - ($$6223$$,$$BENGER$$,#{state_id_wa},-33.174244,115.862901), - ($$6224$$,$$BEELA$$,#{state_id_wa},-33.234958,115.914238), - ($$6224$$,$$BRUNSWICK$$,#{state_id_wa},-33.234958,115.914238), - ($$6225$$,$$ALLANSON$$,#{state_id_wa},-33.337529,116.098061), - ($$6225$$,$$BOWELLING$$,#{state_id_wa},-33.337529,116.098061), - ($$6225$$,$$BUCKINGHAM$$,#{state_id_wa},-33.337529,116.098061), - ($$6225$$,$$CARDIFF$$,#{state_id_wa},-33.337529,116.098061), - ($$6225$$,$$COLLIE$$,#{state_id_wa},-33.337529,116.098061), - ($$6225$$,$$COLLIE BURN$$,#{state_id_wa},-33.337529,116.098061), - ($$6225$$,$$HARRIS RIVER$$,#{state_id_wa},-33.337529,116.098061), - ($$6225$$,$$LYALLS MILL$$,#{state_id_wa},-33.337529,116.098061), - ($$6225$$,$$MCALINDEN$$,#{state_id_wa},-33.337529,116.098061), - ($$6225$$,$$MUJA$$,#{state_id_wa},-33.337529,116.098061), - ($$6225$$,$$MUMBALLUP$$,#{state_id_wa},-33.337529,116.098061), - ($$6225$$,$$MUNGALUP$$,#{state_id_wa},-33.337529,116.098061), - ($$6225$$,$$NOGGERUP$$,#{state_id_wa},-33.337529,116.098061), - ($$6225$$,$$PALMER$$,#{state_id_wa},-33.337529,116.098061), - ($$6225$$,$$PRESTON SETTLEMENT$$,#{state_id_wa},-33.337529,116.098061), - ($$6225$$,$$SHOTTS$$,#{state_id_wa},-33.337529,116.098061), - ($$6225$$,$$WORSLEY$$,#{state_id_wa},-33.337529,116.098061), - ($$6225$$,$$YOURDAMUNG LAKE$$,#{state_id_wa},-33.337529,116.098061), - ($$6226$$,$$ROELANDS$$,#{state_id_wa},-33.290377,115.827524), - ($$6227$$,$$BUREKUP$$,#{state_id_wa},-33.309543,115.809665), - ($$6228$$,$$WATERLOO$$,#{state_id_wa},-33.336552,115.771383), - ($$6229$$,$$PICTON$$,#{state_id_wa},-33.351411,115.69276), - ($$6229$$,$$PICTON EAST$$,#{state_id_wa},-33.351411,115.69276), - ($$6230$$,$$BUNBURY$$,#{state_id_wa},-33.327112,115.636993), - ($$6230$$,$$CAREY PARK$$,#{state_id_wa},-33.327112,115.636993), - ($$6230$$,$$COLLEGE GROVE$$,#{state_id_wa},-33.327112,115.636993), - ($$6230$$,$$DALYELLUP$$,#{state_id_wa},-33.327112,115.636993), - ($$6230$$,$$DAVENPORT$$,#{state_id_wa},-33.327112,115.636993), - ($$6230$$,$$EAST BUNBURY$$,#{state_id_wa},-33.327112,115.636993), - ($$6230$$,$$GELORUP$$,#{state_id_wa},-33.327112,115.636993), - ($$6230$$,$$GLEN IRIS$$,#{state_id_wa},-33.327112,115.636993), - ($$6230$$,$$PELICAN POINT$$,#{state_id_wa},-33.327112,115.636993), - ($$6230$$,$$SOUTH BUNBURY$$,#{state_id_wa},-33.327112,115.636993), - ($$6230$$,$$USHER$$,#{state_id_wa},-33.327112,115.636993), - ($$6230$$,$$VITTORIA$$,#{state_id_wa},-33.327112,115.636993), - ($$6230$$,$$WITHERS$$,#{state_id_wa},-33.327112,115.636993), - ($$6231$$,$$BUNBURY$$,#{state_id_wa},-33.364375,115.65556), - ($$6232$$,$$EATON$$,#{state_id_wa},-33.316625,115.704263), - ($$6232$$,$$MILLBRIDGE$$,#{state_id_wa},-33.316625,115.704263), - ($$6233$$,$$AUSTRALIND$$,#{state_id_wa},-33.279034,115.71443), - ($$6233$$,$$BINNINGUP$$,#{state_id_wa},-33.279034,115.71443), - ($$6233$$,$$LESCHENAULT$$,#{state_id_wa},-33.279034,115.71443), - ($$6233$$,$$PARKFIELD$$,#{state_id_wa},-33.279034,115.71443), - ($$6233$$,$$WELLESLEY$$,#{state_id_wa},-33.279034,115.71443), - ($$6236$$,$$CROOKED BROOK$$,#{state_id_wa},-33.46778,115.80944), - ($$6236$$,$$DARDANUP$$,#{state_id_wa},-33.46778,115.80944), - ($$6236$$,$$DARDANUP WEST$$,#{state_id_wa},-33.46778,115.80944), - ($$6236$$,$$FERGUSON$$,#{state_id_wa},-33.46778,115.80944), - ($$6236$$,$$HENTY$$,#{state_id_wa},-33.46778,115.80944), - ($$6236$$,$$PARADISE$$,#{state_id_wa},-33.46778,115.80944), - ($$6236$$,$$WELLINGTON FOREST$$,#{state_id_wa},-33.46778,115.80944), - ($$6236$$,$$WELLINGTON MILL$$,#{state_id_wa},-33.46778,115.80944), - ($$6237$$,$$BOYANUP$$,#{state_id_wa},-33.482745,115.727734), - ($$6237$$,$$ELGIN$$,#{state_id_wa},-33.482745,115.727734), - ($$6237$$,$$GWINDINUP$$,#{state_id_wa},-33.482745,115.727734), - ($$6237$$,$$NORTH BOYANUP$$,#{state_id_wa},-33.482745,115.727734), - ($$6237$$,$$STRATHAM$$,#{state_id_wa},-33.482745,115.727734), - ($$6237$$,$$THE PLAINS$$,#{state_id_wa},-33.482745,115.727734), - ($$6239$$,$$ARGYLE$$,#{state_id_wa},-33.548215,115.766342), - ($$6239$$,$$BEELERUP$$,#{state_id_wa},-33.548215,115.766342), - ($$6239$$,$$BROOKHAMPTON$$,#{state_id_wa},-33.548215,115.766342), - ($$6239$$,$$CHARLEY CREEK$$,#{state_id_wa},-33.548215,115.766342), - ($$6239$$,$$DONNYBROOK$$,#{state_id_wa},-33.548215,115.766342), - ($$6239$$,$$GLEN MERVYN$$,#{state_id_wa},-33.548215,115.766342), - ($$6239$$,$$PAYNEDALE$$,#{state_id_wa},-33.548215,115.766342), - ($$6239$$,$$QUEENWOOD$$,#{state_id_wa},-33.548215,115.766342), - ($$6239$$,$$THOMSON BROOK$$,#{state_id_wa},-33.548215,115.766342), - ($$6239$$,$$UPPER CAPEL$$,#{state_id_wa},-33.548215,115.766342), - ($$6239$$,$$YABBERUP$$,#{state_id_wa},-33.548215,115.766342), - ($$6240$$,$$LOWDEN$$,#{state_id_wa},-33.557507,115.986161), - ($$6243$$,$$WILGA$$,#{state_id_wa},-33.702847,116.285351), - ($$6243$$,$$WILGA WEST$$,#{state_id_wa},-33.702847,116.285351), - ($$6244$$,$$BOYUP BROOK$$,#{state_id_wa},-33.832927,116.388416), - ($$6244$$,$$CHOWERUP$$,#{state_id_wa},-33.832927,116.388416), - ($$6244$$,$$DINNINUP$$,#{state_id_wa},-33.832927,116.388416), - ($$6244$$,$$KULIKUP$$,#{state_id_wa},-33.832927,116.388416), - ($$6244$$,$$MAYANUP$$,#{state_id_wa},-33.832927,116.388416), - ($$6244$$,$$SCOTTS BROOK$$,#{state_id_wa},-33.832927,116.388416), - ($$6244$$,$$TONEBRIDGE$$,#{state_id_wa},-33.832927,116.388416), - ($$6244$$,$$TRIGWELL$$,#{state_id_wa},-33.832927,116.388416), - ($$6251$$,$$BRAZIER$$,#{state_id_wa},-33.76806,115.82778), - ($$6251$$,$$KIRUP$$,#{state_id_wa},-33.76806,115.82778), - ($$6251$$,$$NEWLANDS$$,#{state_id_wa},-33.76806,115.82778), - ($$6252$$,$$MULLALYUP$$,#{state_id_wa},-33.745171,115.946029), - ($$6253$$,$$BALINGUP$$,#{state_id_wa},-33.786518,115.98381), - ($$6253$$,$$GRIMWADE$$,#{state_id_wa},-33.786518,115.98381), - ($$6253$$,$$SOUTHAMPTON$$,#{state_id_wa},-33.786518,115.98381), - ($$6254$$,$$GREENBUSHES$$,#{state_id_wa},-33.848791,116.058769), - ($$6254$$,$$NORTH GREENBUSHES$$,#{state_id_wa},-33.848791,116.058769), - ($$6255$$,$$BENJINUP$$,#{state_id_wa},-33.79361,116.29583), - ($$6255$$,$$BRIDGETOWN$$,#{state_id_wa},-33.79361,116.29583), - ($$6255$$,$$CATTERICK$$,#{state_id_wa},-33.79361,116.29583), - ($$6255$$,$$HESTER$$,#{state_id_wa},-33.79361,116.29583), - ($$6255$$,$$HESTER BROOK$$,#{state_id_wa},-33.79361,116.29583), - ($$6255$$,$$KANGAROO GULLY$$,#{state_id_wa},-33.79361,116.29583), - ($$6255$$,$$WINNEJUP$$,#{state_id_wa},-33.79361,116.29583), - ($$6256$$,$$GLENLYNN$$,#{state_id_wa},-34.00511,116.15389), - ($$6256$$,$$KINGSTON$$,#{state_id_wa},-34.00511,116.15389), - ($$6256$$,$$MARANUP$$,#{state_id_wa},-34.00511,116.15389), - ($$6256$$,$$SUNNYSIDE$$,#{state_id_wa},-34.00511,116.15389), - ($$6256$$,$$WANDILLUP$$,#{state_id_wa},-34.00511,116.15389), - ($$6256$$,$$YORNUP$$,#{state_id_wa},-34.00511,116.15389), - ($$6258$$,$$BALBARRUP$$,#{state_id_wa},-34.227522,116.203528), - ($$6258$$,$$CROWEA$$,#{state_id_wa},-34.227522,116.203528), - ($$6258$$,$$DEANMILL$$,#{state_id_wa},-34.227522,116.203528), - ($$6258$$,$$DIAMOND TREE$$,#{state_id_wa},-34.227522,116.203528), - ($$6258$$,$$DINGUP$$,#{state_id_wa},-34.227522,116.203528), - ($$6258$$,$$DIXVALE$$,#{state_id_wa},-34.227522,116.203528), - ($$6258$$,$$DONNELLY RIVER$$,#{state_id_wa},-34.227522,116.203528), - ($$6258$$,$$GLENORAN$$,#{state_id_wa},-34.227522,116.203528), - ($$6258$$,$$JARDEE$$,#{state_id_wa},-34.227522,116.203528), - ($$6258$$,$$LAKE MUIR$$,#{state_id_wa},-34.227522,116.203528), - ($$6258$$,$$LINFARNE$$,#{state_id_wa},-34.227522,116.203528), - ($$6258$$,$$MANJIMUP$$,#{state_id_wa},-34.227522,116.203528), - ($$6258$$,$$MIDDLESEX$$,#{state_id_wa},-34.227522,116.203528), - ($$6258$$,$$MORDALUP$$,#{state_id_wa},-34.227522,116.203528), - ($$6258$$,$$PALGARUP$$,#{state_id_wa},-34.227522,116.203528), - ($$6258$$,$$PERUP$$,#{state_id_wa},-34.227522,116.203528), - ($$6258$$,$$QUINNINUP$$,#{state_id_wa},-34.227522,116.203528), - ($$6258$$,$$RINGBARK$$,#{state_id_wa},-34.227522,116.203528), - ($$6258$$,$$SMITH BROOK$$,#{state_id_wa},-34.227522,116.203528), - ($$6258$$,$$UPPER WARREN$$,#{state_id_wa},-34.227522,116.203528), - ($$6258$$,$$WILGARRUP$$,#{state_id_wa},-34.227522,116.203528), - ($$6258$$,$$YANMAH$$,#{state_id_wa},-34.227522,116.203528), - ($$6260$$,$$BEEDELUP$$,#{state_id_wa},-34.349615,115.935794), - ($$6260$$,$$BIDDELIA$$,#{state_id_wa},-34.349615,115.935794), - ($$6260$$,$$CALLCUP$$,#{state_id_wa},-34.349615,115.935794), - ($$6260$$,$$CHANNYBEARUP$$,#{state_id_wa},-34.349615,115.935794), - ($$6260$$,$$COLLINS$$,#{state_id_wa},-34.349615,115.935794), - ($$6260$$,$$EASTBROOK$$,#{state_id_wa},-34.349615,115.935794), - ($$6260$$,$$LAKE JASPER$$,#{state_id_wa},-34.349615,115.935794), - ($$6260$$,$$PEERABEELUP$$,#{state_id_wa},-34.349615,115.935794), - ($$6260$$,$$PEMBERTON$$,#{state_id_wa},-34.349615,115.935794), - ($$6260$$,$$YEAGARUP$$,#{state_id_wa},-34.349615,115.935794), - ($$6262$$,$$BOORARA BROOK$$,#{state_id_wa},-34.686036,116.213448), - ($$6262$$,$$MEERUP$$,#{state_id_wa},-34.686036,116.213448), - ($$6262$$,$$NORTHCLIFFE$$,#{state_id_wa},-34.686036,116.213448), - ($$6262$$,$$SHANNON$$,#{state_id_wa},-34.686036,116.213448), - ($$6262$$,$$WINDY HARBOUR$$,#{state_id_wa},-34.686036,116.213448), - ($$6271$$,$$CAPEL$$,#{state_id_wa},-33.557565,115.558897), - ($$6271$$,$$CAPEL RIVER$$,#{state_id_wa},-33.557565,115.558897), - ($$6271$$,$$FORREST BEACH$$,#{state_id_wa},-33.557565,115.558897), - ($$6271$$,$$PEPPERMINT GROVE BEACH$$,#{state_id_wa},-33.557565,115.558897), - ($$6271$$,$$STIRLING ESTATE$$,#{state_id_wa},-33.557565,115.558897), - ($$6275$$,$$BARRABUP$$,#{state_id_wa},-33.836425,115.66169), - ($$6275$$,$$CARLOTTA$$,#{state_id_wa},-33.836425,115.66169), - ($$6275$$,$$CUNDINUP$$,#{state_id_wa},-33.836425,115.66169), - ($$6275$$,$$DARRADUP$$,#{state_id_wa},-33.836425,115.66169), - ($$6275$$,$$EAST NANNUP$$,#{state_id_wa},-33.836425,115.66169), - ($$6275$$,$$JALBARRAGUP$$,#{state_id_wa},-33.836425,115.66169), - ($$6275$$,$$JARRAHWOOD$$,#{state_id_wa},-33.836425,115.66169), - ($$6275$$,$$NANNUP$$,#{state_id_wa},-33.836425,115.66169), - ($$6275$$,$$SCOTT RIVER EAST$$,#{state_id_wa},-33.836425,115.66169), - ($$6275$$,$$YOGANUP$$,#{state_id_wa},-33.836425,115.66169), - ($$6280$$,$$ABBA RIVER$$,#{state_id_wa},-33.684878,115.463341), - ($$6280$$,$$ABBEY$$,#{state_id_wa},-33.684878,115.463341), - ($$6280$$,$$ACTON PARK$$,#{state_id_wa},-33.684878,115.463341), - ($$6280$$,$$AMBERGATE$$,#{state_id_wa},-33.684878,115.463341), - ($$6280$$,$$ANNIEBROOK$$,#{state_id_wa},-33.684878,115.463341), - ($$6280$$,$$BOALLIA$$,#{state_id_wa},-33.684878,115.463341), - ($$6280$$,$$BOVELL$$,#{state_id_wa},-33.684878,115.463341), - ($$6280$$,$$BROADWATER$$,#{state_id_wa},-33.684878,115.463341), - ($$6280$$,$$BUSSELTON$$,#{state_id_wa},-33.684878,115.463341), - ($$6280$$,$$CARBUNUP RIVER$$,#{state_id_wa},-33.684878,115.463341), - ($$6280$$,$$CHAPMAN HILL$$,#{state_id_wa},-33.684878,115.463341), - ($$6280$$,$$GEOGRAPHE$$,#{state_id_wa},-33.684878,115.463341), - ($$6280$$,$$HITHERGREEN$$,#{state_id_wa},-33.684878,115.463341), - ($$6280$$,$$JINDONG$$,#{state_id_wa},-33.684878,115.463341), - ($$6280$$,$$KALGUP$$,#{state_id_wa},-33.684878,115.463341), - ($$6280$$,$$KALOORUP$$,#{state_id_wa},-33.684878,115.463341), - ($$6280$$,$$KEALY$$,#{state_id_wa},-33.684878,115.463341), - ($$6280$$,$$LUDLOW$$,#{state_id_wa},-33.684878,115.463341), - ($$6280$$,$$MARYBROOK$$,#{state_id_wa},-33.684878,115.463341), - ($$6280$$,$$METRICUP$$,#{state_id_wa},-33.684878,115.463341), - ($$6280$$,$$NORTH JINDONG$$,#{state_id_wa},-33.684878,115.463341), - ($$6280$$,$$REINSCOURT$$,#{state_id_wa},-33.684878,115.463341), - ($$6280$$,$$RUABON$$,#{state_id_wa},-33.684878,115.463341), - ($$6280$$,$$SABINA RIVER$$,#{state_id_wa},-33.684878,115.463341), - ($$6280$$,$$SIESTA PARK$$,#{state_id_wa},-33.684878,115.463341), - ($$6280$$,$$TUTUNUP$$,#{state_id_wa},-33.684878,115.463341), - ($$6280$$,$$VASSE$$,#{state_id_wa},-33.684878,115.463341), - ($$6280$$,$$WALSALL$$,#{state_id_wa},-33.684878,115.463341), - ($$6280$$,$$WEST BUSSELTON$$,#{state_id_wa},-33.684878,115.463341), - ($$6280$$,$$WILYABRUP$$,#{state_id_wa},-33.684878,115.463341), - ($$6280$$,$$WONNERUP$$,#{state_id_wa},-33.684878,115.463341), - ($$6280$$,$$YALYALUP$$,#{state_id_wa},-33.684878,115.463341), - ($$6280$$,$$YELVERTON$$,#{state_id_wa},-33.684878,115.463341), - ($$6280$$,$$YOONGARILLUP$$,#{state_id_wa},-33.684878,115.463341), - ($$6281$$,$$DUNSBOROUGH$$,#{state_id_wa},-33.615893,115.10657), - ($$6281$$,$$EAGLE BAY$$,#{state_id_wa},-33.615893,115.10657), - ($$6281$$,$$NATURALISTE$$,#{state_id_wa},-33.615893,115.10657), - ($$6281$$,$$QUEDJINUP$$,#{state_id_wa},-33.615893,115.10657), - ($$6281$$,$$QUINDALUP$$,#{state_id_wa},-33.615893,115.10657), - ($$6282$$,$$YALLINGUP$$,#{state_id_wa},-33.645861,115.032126), - ($$6282$$,$$YALLINGUP SIDING$$,#{state_id_wa},-33.645861,115.032126), - ($$6284$$,$$BAUDIN$$,#{state_id_wa},-33.865006,115.424214), - ($$6284$$,$$COWARAMUP$$,#{state_id_wa},-33.865006,115.424214), - ($$6284$$,$$GRACETOWN$$,#{state_id_wa},-33.865006,115.424214), - ($$6284$$,$$TREETON$$,#{state_id_wa},-33.865006,115.424214), - ($$6285$$,$$BRAMLEY$$,#{state_id_wa},-33.913943,115.025727), - ($$6285$$,$$BURNSIDE$$,#{state_id_wa},-33.913943,115.025727), - ($$6285$$,$$GNARABUP$$,#{state_id_wa},-33.913943,115.025727), - ($$6285$$,$$MARGARET RIVER$$,#{state_id_wa},-33.913943,115.025727), - ($$6285$$,$$OSMINGTON$$,#{state_id_wa},-33.913943,115.025727), - ($$6285$$,$$PREVELLY$$,#{state_id_wa},-33.913943,115.025727), - ($$6285$$,$$ROSA BROOK$$,#{state_id_wa},-33.913943,115.025727), - ($$6285$$,$$ROSA GLEN$$,#{state_id_wa},-33.913943,115.025727), - ($$6285$$,$$SCHROEDER$$,#{state_id_wa},-33.913943,115.025727), - ($$6286$$,$$BORANUP$$,#{state_id_wa},-34.13139,115.05389), - ($$6286$$,$$FOREST GROVE$$,#{state_id_wa},-34.13139,115.05389), - ($$6286$$,$$REDGATE$$,#{state_id_wa},-34.13139,115.05389), - ($$6286$$,$$WITCHCLIFFE$$,#{state_id_wa},-34.13139,115.05389), - ($$6288$$,$$ALEXANDRA BRIDGE$$,#{state_id_wa},-34.160621,115.199569), - ($$6288$$,$$COURTENAY$$,#{state_id_wa},-34.160621,115.199569), - ($$6288$$,$$HAMELIN BAY$$,#{state_id_wa},-34.160621,115.199569), - ($$6288$$,$$KARRIDALE$$,#{state_id_wa},-34.160621,115.199569), - ($$6288$$,$$NILLUP$$,#{state_id_wa},-34.160621,115.199569), - ($$6288$$,$$SCOTT RIVER$$,#{state_id_wa},-34.160621,115.199569), - ($$6288$$,$$WARNER GLEN$$,#{state_id_wa},-34.160621,115.199569), - ($$6290$$,$$AUGUSTA$$,#{state_id_wa},-34.315942,115.159731), - ($$6290$$,$$DEEPDENE$$,#{state_id_wa},-34.315942,115.159731), - ($$6290$$,$$EAST AUGUSTA$$,#{state_id_wa},-34.315942,115.159731), - ($$6290$$,$$KUDARDUP$$,#{state_id_wa},-34.315942,115.159731), - ($$6290$$,$$LEEUWIN$$,#{state_id_wa},-34.315942,115.159731), - ($$6290$$,$$MOLLOY ISLAND$$,#{state_id_wa},-34.315942,115.159731), - ($$6302$$,$$BADGIN$$,#{state_id_wa},-31.846267,117.011892), - ($$6302$$,$$BALLADONG$$,#{state_id_wa},-31.846267,117.011892), - ($$6302$$,$$BURGES$$,#{state_id_wa},-31.846267,117.011892), - ($$6302$$,$$CALJIE$$,#{state_id_wa},-31.846267,117.011892), - ($$6302$$,$$COLD HARBOUR$$,#{state_id_wa},-31.846267,117.011892), - ($$6302$$,$$DALIAK$$,#{state_id_wa},-31.846267,117.011892), - ($$6302$$,$$FLINT$$,#{state_id_wa},-31.846267,117.011892), - ($$6302$$,$$FLYNN$$,#{state_id_wa},-31.846267,117.011892), - ($$6302$$,$$GILGERING$$,#{state_id_wa},-31.846267,117.011892), - ($$6302$$,$$GREENHILLS$$,#{state_id_wa},-31.846267,117.011892), - ($$6302$$,$$GWAMBYGINE$$,#{state_id_wa},-31.846267,117.011892), - ($$6302$$,$$INKPEN$$,#{state_id_wa},-31.846267,117.011892), - ($$6302$$,$$KAURING$$,#{state_id_wa},-31.846267,117.011892), - ($$6302$$,$$MALEBELLING$$,#{state_id_wa},-31.846267,117.011892), - ($$6302$$,$$MOUNT HARDEY$$,#{state_id_wa},-31.846267,117.011892), - ($$6302$$,$$MOUNT OBSERVATION$$,#{state_id_wa},-31.846267,117.011892), - ($$6302$$,$$NARRALOGGAN$$,#{state_id_wa},-31.846267,117.011892), - ($$6302$$,$$QUELLINGTON$$,#{state_id_wa},-31.846267,117.011892), - ($$6302$$,$$ST RONANS$$,#{state_id_wa},-31.846267,117.011892), - ($$6302$$,$$TALBOT$$,#{state_id_wa},-31.846267,117.011892), - ($$6302$$,$$TALBOT WEST$$,#{state_id_wa},-31.846267,117.011892), - ($$6302$$,$$WILBERFORCE$$,#{state_id_wa},-31.846267,117.011892), - ($$6302$$,$$YORK$$,#{state_id_wa},-31.846267,117.011892), - ($$6304$$,$$BALLY BALLY$$,#{state_id_wa},-32.183978,117.116351), - ($$6304$$,$$BEVERLEY$$,#{state_id_wa},-32.183978,117.116351), - ($$6304$$,$$DALE$$,#{state_id_wa},-32.183978,117.116351), - ($$6304$$,$$EAST BEVERLEY$$,#{state_id_wa},-32.183978,117.116351), - ($$6304$$,$$KOKEBY$$,#{state_id_wa},-32.183978,117.116351), - ($$6304$$,$$MORBINNING$$,#{state_id_wa},-32.183978,117.116351), - ($$6304$$,$$WESTDALE$$,#{state_id_wa},-32.183978,117.116351), - ($$6306$$,$$ALDERSYDE$$,#{state_id_wa},-32.374225,117.305815), - ($$6306$$,$$BROOKTON$$,#{state_id_wa},-32.374225,117.305815), - ($$6306$$,$$BULYEE$$,#{state_id_wa},-32.374225,117.305815), - ($$6306$$,$$JELCOBINE$$,#{state_id_wa},-32.374225,117.305815), - ($$6306$$,$$KWEDA$$,#{state_id_wa},-32.374225,117.305815), - ($$6308$$,$$CODJATOTINE$$,#{state_id_wa},-32.67944,116.82194), - ($$6308$$,$$DWARDA$$,#{state_id_wa},-32.67944,116.82194), - ($$6308$$,$$EAST PINGELLY$$,#{state_id_wa},-32.67944,116.82194), - ($$6308$$,$$GILLIMANNING$$,#{state_id_wa},-32.67944,116.82194), - ($$6308$$,$$HASTINGS$$,#{state_id_wa},-32.67944,116.82194), - ($$6308$$,$$PINGELLY$$,#{state_id_wa},-32.67944,116.82194), - ($$6308$$,$$PUMPHREYS BRIDGE$$,#{state_id_wa},-32.67944,116.82194), - ($$6308$$,$$SPRINGS$$,#{state_id_wa},-32.67944,116.82194), - ($$6308$$,$$WANDERING$$,#{state_id_wa},-32.67944,116.82194), - ($$6308$$,$$WEST PINGELLY$$,#{state_id_wa},-32.67944,116.82194), - ($$6309$$,$$EAST POPANYINNING$$,#{state_id_wa},-32.63306,117.26028), - ($$6309$$,$$POPANYINNING$$,#{state_id_wa},-32.63306,117.26028), - ($$6309$$,$$STRATHERNE$$,#{state_id_wa},-32.63306,117.26028), - ($$6309$$,$$WEST POPANYINNING$$,#{state_id_wa},-32.63306,117.26028), - ($$6311$$,$$COMMODINE$$,#{state_id_wa},-32.76556,117.31056), - ($$6311$$,$$CONTINE$$,#{state_id_wa},-32.76556,117.31056), - ($$6311$$,$$CUBALLING$$,#{state_id_wa},-32.76556,117.31056), - ($$6311$$,$$DRYANDRA$$,#{state_id_wa},-32.76556,117.31056), - ($$6311$$,$$LOL GRAY$$,#{state_id_wa},-32.76556,117.31056), - ($$6311$$,$$TOWNSENDALE$$,#{state_id_wa},-32.76556,117.31056), - ($$6311$$,$$WARDERING$$,#{state_id_wa},-32.76556,117.31056), - ($$6311$$,$$YORNANING$$,#{state_id_wa},-32.76556,117.31056), - ($$6312$$,$$BOUNDAIN$$,#{state_id_wa},-32.946718,117.363191), - ($$6312$$,$$DUMBERNING$$,#{state_id_wa},-32.946718,117.363191), - ($$6312$$,$$HILLSIDE$$,#{state_id_wa},-32.946718,117.363191), - ($$6312$$,$$MINIGIN$$,#{state_id_wa},-32.946718,117.363191), - ($$6312$$,$$NARROGIN$$,#{state_id_wa},-32.946718,117.363191), - ($$6312$$,$$NARROGIN VALLEY$$,#{state_id_wa},-32.946718,117.363191), - ($$6312$$,$$NOMANS LAKE$$,#{state_id_wa},-32.946718,117.363191), - ($$6312$$,$$TOOLIBIN$$,#{state_id_wa},-32.946718,117.363191), - ($$6312$$,$$YILLIMINNING$$,#{state_id_wa},-32.946718,117.363191), - ($$6313$$,$$HIGHBURY$$,#{state_id_wa},-33.058981,117.148126), - ($$6315$$,$$ARTHUR RIVER$$,#{state_id_wa},-33.338053,117.034172), - ($$6315$$,$$BALLAYING$$,#{state_id_wa},-33.338053,117.034172), - ($$6315$$,$$CANCANNING$$,#{state_id_wa},-33.338053,117.034172), - ($$6315$$,$$COLLANILLING$$,#{state_id_wa},-33.338053,117.034172), - ($$6315$$,$$DONGOLOCKING$$,#{state_id_wa},-33.338053,117.034172), - ($$6315$$,$$GUNDARING$$,#{state_id_wa},-33.338053,117.034172), - ($$6315$$,$$JALORAN$$,#{state_id_wa},-33.338053,117.034172), - ($$6315$$,$$LIME LAKE$$,#{state_id_wa},-33.338053,117.034172), - ($$6315$$,$$MINDING$$,#{state_id_wa},-33.338053,117.034172), - ($$6315$$,$$PIESSEVILLE$$,#{state_id_wa},-33.338053,117.034172), - ($$6315$$,$$WAGIN$$,#{state_id_wa},-33.338053,117.034172), - ($$6315$$,$$WEDGECARRUP$$,#{state_id_wa},-33.338053,117.034172), - ($$6316$$,$$BOYERINE$$,#{state_id_wa},-33.497786,117.410324), - ($$6316$$,$$CARTMETICUP$$,#{state_id_wa},-33.497786,117.410324), - ($$6316$$,$$GLENCOE$$,#{state_id_wa},-33.497786,117.410324), - ($$6316$$,$$KENMARE$$,#{state_id_wa},-33.497786,117.410324), - ($$6316$$,$$WESTWOOD$$,#{state_id_wa},-33.497786,117.410324), - ($$6316$$,$$WOODANILLING$$,#{state_id_wa},-33.497786,117.410324), - ($$6317$$,$$BADGEBUP$$,#{state_id_wa},-33.633615,117.898909), - ($$6317$$,$$BULLOCK HILLS$$,#{state_id_wa},-33.633615,117.898909), - ($$6317$$,$$CARROLUP$$,#{state_id_wa},-33.633615,117.898909), - ($$6317$$,$$COBLININE$$,#{state_id_wa},-33.633615,117.898909), - ($$6317$$,$$COYRECUP$$,#{state_id_wa},-33.633615,117.898909), - ($$6317$$,$$DATATINE$$,#{state_id_wa},-33.633615,117.898909), - ($$6317$$,$$EWLYAMARTUP$$,#{state_id_wa},-33.633615,117.898909), - ($$6317$$,$$KATANNING$$,#{state_id_wa},-33.633615,117.898909), - ($$6317$$,$$MARRACOONDA$$,#{state_id_wa},-33.633615,117.898909), - ($$6317$$,$$MOOJEBING$$,#{state_id_wa},-33.633615,117.898909), - ($$6317$$,$$MURDONG$$,#{state_id_wa},-33.633615,117.898909), - ($$6317$$,$$PINWERNYING$$,#{state_id_wa},-33.633615,117.898909), - ($$6317$$,$$SOUTH DATATINE$$,#{state_id_wa},-33.633615,117.898909), - ($$6317$$,$$SOUTH GLENCOE$$,#{state_id_wa},-33.633615,117.898909), - ($$6318$$,$$BROOMEHILL$$,#{state_id_wa},-33.845435,117.638574), - ($$6318$$,$$BROOMEHILL EAST$$,#{state_id_wa},-33.845435,117.638574), - ($$6318$$,$$BROOMEHILL VILLAGE$$,#{state_id_wa},-33.845435,117.638574), - ($$6318$$,$$BROOMEHILL WEST$$,#{state_id_wa},-33.845435,117.638574), - ($$6320$$,$$BOBALONG$$,#{state_id_wa},-34.00806,117.56389), - ($$6320$$,$$BORDERDALE$$,#{state_id_wa},-34.00806,117.56389), - ($$6320$$,$$DARTNALL$$,#{state_id_wa},-34.00806,117.56389), - ($$6320$$,$$LAKE TOOLBRUNUP$$,#{state_id_wa},-34.00806,117.56389), - ($$6320$$,$$MOONIES HILL$$,#{state_id_wa},-34.00806,117.56389), - ($$6320$$,$$TAMBELLUP$$,#{state_id_wa},-34.00806,117.56389), - ($$6320$$,$$WANSBROUGH$$,#{state_id_wa},-34.00806,117.56389), - ($$6321$$,$$CRANBROOK$$,#{state_id_wa},-34.295645,117.555407), - ($$6322$$,$$TENTERDEN$$,#{state_id_wa},-34.364235,117.560022), - ($$6323$$,$$KENDENUP$$,#{state_id_wa},-34.498674,117.588587), - ($$6324$$,$$DENBARKER$$,#{state_id_wa},-34.715092,117.509062), - ($$6324$$,$$FOREST HILL$$,#{state_id_wa},-34.715092,117.509062), - ($$6324$$,$$MOUNT BARKER$$,#{state_id_wa},-34.715092,117.509062), - ($$6324$$,$$PERILLUP$$,#{state_id_wa},-34.715092,117.509062), - ($$6324$$,$$PORONGURUP$$,#{state_id_wa},-34.715092,117.509062), - ($$6324$$,$$SOUTH STIRLING$$,#{state_id_wa},-34.715092,117.509062), - ($$6324$$,$$TAKALARUP$$,#{state_id_wa},-34.715092,117.509062), - ($$6324$$,$$WOOGENELLUP$$,#{state_id_wa},-34.715092,117.509062), - ($$6326$$,$$NARRIKUP$$,#{state_id_wa},-34.774109,117.700334), - ($$6327$$,$$REDMOND$$,#{state_id_wa},-34.886188,117.693365), - ($$6327$$,$$REDMOND WEST$$,#{state_id_wa},-34.886188,117.693365), - ($$6328$$,$$CHEYNES$$,#{state_id_wa},-34.842731,118.342052), - ($$6328$$,$$GNOWELLEN$$,#{state_id_wa},-34.842731,118.342052), - ($$6328$$,$$GREEN RANGE$$,#{state_id_wa},-34.842731,118.342052), - ($$6328$$,$$KOJANEERUP SOUTH$$,#{state_id_wa},-34.842731,118.342052), - ($$6328$$,$$MANYPEAKS$$,#{state_id_wa},-34.842731,118.342052), - ($$6328$$,$$METTLER$$,#{state_id_wa},-34.842731,118.342052), - ($$6328$$,$$PALMDALE$$,#{state_id_wa},-34.842731,118.342052), - ($$6328$$,$$WELLSTEAD$$,#{state_id_wa},-34.842731,118.342052), - ($$6330$$,$$ALBANY$$,#{state_id_wa},-35.023873,117.883543), - ($$6330$$,$$BAYONET HEAD$$,#{state_id_wa},-35.023873,117.883543), - ($$6330$$,$$BIG GROVE$$,#{state_id_wa},-35.023873,117.883543), - ($$6330$$,$$BORNHOLM$$,#{state_id_wa},-35.023873,117.883543), - ($$6330$$,$$CENTENNIAL PARK$$,#{state_id_wa},-35.023873,117.883543), - ($$6330$$,$$COLLINGWOOD HEIGHTS$$,#{state_id_wa},-35.023873,117.883543), - ($$6330$$,$$COLLINGWOOD PARK$$,#{state_id_wa},-35.023873,117.883543), - ($$6330$$,$$CUTHBERT$$,#{state_id_wa},-35.023873,117.883543), - ($$6330$$,$$DROME$$,#{state_id_wa},-35.023873,117.883543), - ($$6330$$,$$ELLEKER$$,#{state_id_wa},-35.023873,117.883543), - ($$6330$$,$$EMU POINT$$,#{state_id_wa},-35.023873,117.883543), - ($$6330$$,$$FRENCHMAN BAY$$,#{state_id_wa},-35.023873,117.883543), - ($$6330$$,$$GLEDHOW$$,#{state_id_wa},-35.023873,117.883543), - ($$6330$$,$$GOODE BEACH$$,#{state_id_wa},-35.023873,117.883543), - ($$6330$$,$$GREEN VALLEY$$,#{state_id_wa},-35.023873,117.883543), - ($$6330$$,$$KALGAN$$,#{state_id_wa},-35.023873,117.883543), - ($$6330$$,$$KING RIVER$$,#{state_id_wa},-35.023873,117.883543), - ($$6330$$,$$KRONKUP$$,#{state_id_wa},-35.023873,117.883543), - ($$6330$$,$$LANGE$$,#{state_id_wa},-35.023873,117.883543), - ($$6330$$,$$LITTLE GROVE$$,#{state_id_wa},-35.023873,117.883543), - ($$6330$$,$$LOCKYER$$,#{state_id_wa},-35.023873,117.883543), - ($$6330$$,$$LOWER KING$$,#{state_id_wa},-35.023873,117.883543), - ($$6330$$,$$LOWLANDS$$,#{state_id_wa},-35.023873,117.883543), - ($$6330$$,$$MARBELUP$$,#{state_id_wa},-35.023873,117.883543), - ($$6330$$,$$MCKAIL$$,#{state_id_wa},-35.023873,117.883543), - ($$6330$$,$$MIDDLETON BEACH$$,#{state_id_wa},-35.023873,117.883543), - ($$6330$$,$$MILLBROOK$$,#{state_id_wa},-35.023873,117.883543), - ($$6330$$,$$MILPARA$$,#{state_id_wa},-35.023873,117.883543), - ($$6330$$,$$MIRA MAR$$,#{state_id_wa},-35.023873,117.883543), - ($$6330$$,$$MOUNT CLARENCE$$,#{state_id_wa},-35.023873,117.883543), - ($$6330$$,$$MOUNT ELPHINSTONE$$,#{state_id_wa},-35.023873,117.883543), - ($$6330$$,$$MOUNT MELVILLE$$,#{state_id_wa},-35.023873,117.883543), - ($$6330$$,$$NANARUP$$,#{state_id_wa},-35.023873,117.883543), - ($$6330$$,$$NAPIER$$,#{state_id_wa},-35.023873,117.883543), - ($$6330$$,$$NULLAKI$$,#{state_id_wa},-35.023873,117.883543), - ($$6330$$,$$ORANA$$,#{state_id_wa},-35.023873,117.883543), - ($$6330$$,$$PORT ALBANY$$,#{state_id_wa},-35.023873,117.883543), - ($$6330$$,$$ROBINSON$$,#{state_id_wa},-35.023873,117.883543), - ($$6330$$,$$SANDPATCH$$,#{state_id_wa},-35.023873,117.883543), - ($$6330$$,$$SEPPINGS$$,#{state_id_wa},-35.023873,117.883543), - ($$6330$$,$$SPENCER PARK$$,#{state_id_wa},-35.023873,117.883543), - ($$6330$$,$$TORBAY$$,#{state_id_wa},-35.023873,117.883543), - ($$6330$$,$$TORNDIRRUP$$,#{state_id_wa},-35.023873,117.883543), - ($$6330$$,$$VANCOUVER PENINSULA$$,#{state_id_wa},-35.023873,117.883543), - ($$6330$$,$$WALMSLEY$$,#{state_id_wa},-35.023873,117.883543), - ($$6330$$,$$WARRENUP$$,#{state_id_wa},-35.023873,117.883543), - ($$6330$$,$$WEST CAPE HOWE$$,#{state_id_wa},-35.023873,117.883543), - ($$6330$$,$$WILLYUNG$$,#{state_id_wa},-35.023873,117.883543), - ($$6330$$,$$YAKAMIA$$,#{state_id_wa},-35.023873,117.883543), - ($$6330$$,$$YOUNGS SIDING$$,#{state_id_wa},-35.023873,117.883543), - ($$6331$$,$$ALBANY DC$$,#{state_id_wa},0.0,0.0), - ($$6332$$,$$ALBANY PO$$,#{state_id_wa},-32.046559,115.974463), - ($$6333$$,$$BOW BRIDGE$$,#{state_id_wa},-34.967779,116.953419), - ($$6333$$,$$DENMARK$$,#{state_id_wa},-34.967779,116.953419), - ($$6333$$,$$HAY$$,#{state_id_wa},-34.967779,116.953419), - ($$6333$$,$$HAZELVALE$$,#{state_id_wa},-34.967779,116.953419), - ($$6333$$,$$KENTDALE$$,#{state_id_wa},-34.967779,116.953419), - ($$6333$$,$$KORDABUP$$,#{state_id_wa},-34.967779,116.953419), - ($$6333$$,$$MOUNT LINDESAY$$,#{state_id_wa},-34.967779,116.953419), - ($$6333$$,$$MOUNT ROMANCE$$,#{state_id_wa},-34.967779,116.953419), - ($$6333$$,$$NORNALUP$$,#{state_id_wa},-34.967779,116.953419), - ($$6333$$,$$OCEAN BEACH$$,#{state_id_wa},-34.967779,116.953419), - ($$6333$$,$$PARRYVILLE$$,#{state_id_wa},-34.967779,116.953419), - ($$6333$$,$$PEACEFUL BAY$$,#{state_id_wa},-34.967779,116.953419), - ($$6333$$,$$SCOTSDALE$$,#{state_id_wa},-34.967779,116.953419), - ($$6333$$,$$SHADFORTH$$,#{state_id_wa},-34.967779,116.953419), - ($$6333$$,$$TINGLEDALE$$,#{state_id_wa},-34.967779,116.953419), - ($$6333$$,$$TRENT$$,#{state_id_wa},-34.967779,116.953419), - ($$6333$$,$$WILLIAM BAY$$,#{state_id_wa},-34.967779,116.953419), - ($$6335$$,$$GNOWANGERUP$$,#{state_id_wa},-33.937265,118.007915), - ($$6335$$,$$JACKITUP$$,#{state_id_wa},-33.937265,118.007915), - ($$6335$$,$$KEBARINGUP$$,#{state_id_wa},-33.937265,118.007915), - ($$6335$$,$$PALLINUP$$,#{state_id_wa},-33.937265,118.007915), - ($$6336$$,$$COWALELLUP$$,#{state_id_wa},-34.095298,118.55156), - ($$6336$$,$$MILLS LAKE$$,#{state_id_wa},-34.095298,118.55156), - ($$6336$$,$$MINDARABIN$$,#{state_id_wa},-34.095298,118.55156), - ($$6336$$,$$NEEDILUP$$,#{state_id_wa},-34.095298,118.55156), - ($$6336$$,$$ONGERUP$$,#{state_id_wa},-34.095298,118.55156), - ($$6336$$,$$TOOMPUP$$,#{state_id_wa},-34.095298,118.55156), - ($$6337$$,$$FITZGERALD$$,#{state_id_wa},-33.753403,119.456942), - ($$6337$$,$$GAIRDNER$$,#{state_id_wa},-33.753403,119.456942), - ($$6337$$,$$JACUP$$,#{state_id_wa},-33.753403,119.456942), - ($$6337$$,$$JERRAMUNGUP$$,#{state_id_wa},-33.753403,119.456942), - ($$6337$$,$$WEST FITZGERALD$$,#{state_id_wa},-33.753403,119.456942), - ($$6338$$,$$AMELUP$$,#{state_id_wa},-34.238467,118.220878), - ($$6338$$,$$BORDEN$$,#{state_id_wa},-34.238467,118.220878), - ($$6338$$,$$BOXWOOD HILL$$,#{state_id_wa},-34.238467,118.220878), - ($$6338$$,$$BREMER BAY$$,#{state_id_wa},-34.238467,118.220878), - ($$6338$$,$$MAGITUP$$,#{state_id_wa},-34.238467,118.220878), - ($$6338$$,$$MONJEBUP$$,#{state_id_wa},-34.238467,118.220878), - ($$6338$$,$$NALYERLUP$$,#{state_id_wa},-34.238467,118.220878), - ($$6338$$,$$NORTH STIRLINGS$$,#{state_id_wa},-34.238467,118.220878), - ($$6338$$,$$STIRLING RANGE NATIONAL PARK$$,#{state_id_wa},-34.238467,118.220878), - ($$6341$$,$$NYABING$$,#{state_id_wa},-33.54048,118.14862), - ($$6343$$,$$PINGRUP$$,#{state_id_wa},-33.650048,118.51982), - ($$6346$$,$$FITZGERALD RIVER NATIONAL PARK$$,#{state_id_wa},-34.065324,119.594881), - ($$6346$$,$$JERDACUTTUP$$,#{state_id_wa},-34.065324,119.594881), - ($$6346$$,$$RAVENSTHORPE$$,#{state_id_wa},-34.065324,119.594881), - ($$6346$$,$$WEST RIVER$$,#{state_id_wa},-34.065324,119.594881), - ($$6348$$,$$HOPETOUN$$,#{state_id_wa},-33.870761,120.164668), - ($$6350$$,$$DUMBLEYUNG$$,#{state_id_wa},-33.314668,117.739924), - ($$6350$$,$$NAIRIBIN$$,#{state_id_wa},-33.314668,117.739924), - ($$6350$$,$$NIPPERING$$,#{state_id_wa},-33.314668,117.739924), - ($$6351$$,$$MOULYINNING$$,#{state_id_wa},-33.189372,117.922069), - ($$6351$$,$$NORTH MOULYINNING$$,#{state_id_wa},-33.189372,117.922069), - ($$6352$$,$$KUKERIN$$,#{state_id_wa},-33.188072,118.084827), - ($$6352$$,$$MERILUP$$,#{state_id_wa},-33.188072,118.084827), - ($$6352$$,$$NORTH KUKERIN$$,#{state_id_wa},-33.188072,118.084827), - ($$6352$$,$$SOUTH KUKERIN$$,#{state_id_wa},-33.188072,118.084827), - ($$6353$$,$$BEENONG$$,#{state_id_wa},-33.015389,118.661885), - ($$6353$$,$$BUNICHE$$,#{state_id_wa},-33.015389,118.661885), - ($$6353$$,$$KUENDER$$,#{state_id_wa},-33.015389,118.661885), - ($$6353$$,$$LAKE GRACE$$,#{state_id_wa},-33.015389,118.661885), - ($$6353$$,$$MALLEE HILL$$,#{state_id_wa},-33.015389,118.661885), - ($$6353$$,$$NEENDALING$$,#{state_id_wa},-33.015389,118.661885), - ($$6353$$,$$NORTH BURNGUP$$,#{state_id_wa},-33.015389,118.661885), - ($$6353$$,$$NORTH LAKE GRACE$$,#{state_id_wa},-33.015389,118.661885), - ($$6353$$,$$SOUTH LAKE GRACE$$,#{state_id_wa},-33.015389,118.661885), - ($$6353$$,$$TARIN ROCK$$,#{state_id_wa},-33.015389,118.661885), - ($$6355$$,$$DUNN ROCK$$,#{state_id_wa},-33.425931,119.524863), - ($$6355$$,$$EAST NEWDEGATE$$,#{state_id_wa},-33.425931,119.524863), - ($$6355$$,$$HOLT ROCK$$,#{state_id_wa},-33.425931,119.524863), - ($$6355$$,$$LAKE BIDDY$$,#{state_id_wa},-33.425931,119.524863), - ($$6355$$,$$LAKE CAMM$$,#{state_id_wa},-33.425931,119.524863), - ($$6355$$,$$LITTLE ITALY$$,#{state_id_wa},-33.425931,119.524863), - ($$6355$$,$$MAGENTA$$,#{state_id_wa},-33.425931,119.524863), - ($$6355$$,$$MOUNT SHERIDAN$$,#{state_id_wa},-33.425931,119.524863), - ($$6355$$,$$NEWDEGATE$$,#{state_id_wa},-33.425931,119.524863), - ($$6355$$,$$SOUTH NEWDEGATE$$,#{state_id_wa},-33.425931,119.524863), - ($$6355$$,$$VARLEY$$,#{state_id_wa},-33.425931,119.524863), - ($$6356$$,$$HATTER HILL$$,#{state_id_wa},-33.094628,119.661601), - ($$6356$$,$$LAKE KING$$,#{state_id_wa},-33.094628,119.661601), - ($$6356$$,$$MOUNT MADDEN$$,#{state_id_wa},-33.094628,119.661601), - ($$6357$$,$$PINGARING$$,#{state_id_wa},-32.755279,118.625704), - ($$6358$$,$$KARLGARIN$$,#{state_id_wa},-32.497849,118.710042), - ($$6359$$,$$FORRESTANIA$$,#{state_id_wa},-32.43944,119.7775), - ($$6359$$,$$HYDEN$$,#{state_id_wa},-32.43944,119.7775), - ($$6361$$,$$HARRISMITH$$,#{state_id_wa},-32.934005,117.883286), - ($$6361$$,$$TINCURRIN$$,#{state_id_wa},-32.934005,117.883286), - ($$6363$$,$$DUDININ$$,#{state_id_wa},-32.872391,117.902867), - ($$6363$$,$$WALYURIN$$,#{state_id_wa},-32.872391,117.902867), - ($$6365$$,$$JILAKIN$$,#{state_id_wa},-32.68361,118.43833), - ($$6365$$,$$JITARNING$$,#{state_id_wa},-32.68361,118.43833), - ($$6365$$,$$KULIN$$,#{state_id_wa},-32.68361,118.43833), - ($$6365$$,$$KULIN WEST$$,#{state_id_wa},-32.68361,118.43833), - ($$6367$$,$$KONDININ$$,#{state_id_wa},-32.495204,118.267818), - ($$6368$$,$$SOUTH KUMMININ$$,#{state_id_wa},-32.206644,118.334193), - ($$6369$$,$$MOUNT WALKER$$,#{state_id_wa},-32.070885,118.755515), - ($$6369$$,$$NAREMBEEN$$,#{state_id_wa},-32.070885,118.755515), - ($$6369$$,$$WADDERIN$$,#{state_id_wa},-32.070885,118.755515), - ($$6369$$,$$WEST HOLLETON$$,#{state_id_wa},-32.070885,118.755515), - ($$6369$$,$$WOOLOCUTTY$$,#{state_id_wa},-32.070885,118.755515), - ($$6370$$,$$EAST WICKEPIN$$,#{state_id_wa},-32.77417,117.7075), - ($$6370$$,$$KIRK ROCK$$,#{state_id_wa},-32.77417,117.7075), - ($$6370$$,$$MALYALLING$$,#{state_id_wa},-32.77417,117.7075), - ($$6370$$,$$WICKEPIN$$,#{state_id_wa},-32.77417,117.7075), - ($$6370$$,$$WOGOLIN$$,#{state_id_wa},-32.77417,117.7075), - ($$6372$$,$$YEALERING$$,#{state_id_wa},-32.598783,117.678635), - ($$6373$$,$$BULLARING$$,#{state_id_wa},-32.497516,117.743179), - ($$6375$$,$$ADAMSVALE$$,#{state_id_wa},-32.249413,117.72485), - ($$6375$$,$$BILBARIN$$,#{state_id_wa},-32.249413,117.72485), - ($$6375$$,$$CORRIGIN$$,#{state_id_wa},-32.249413,117.72485), - ($$6375$$,$$GORGE ROCK$$,#{state_id_wa},-32.249413,117.72485), - ($$6375$$,$$KUNJIN$$,#{state_id_wa},-32.249413,117.72485), - ($$6375$$,$$KURRENKUTTEN$$,#{state_id_wa},-32.249413,117.72485), - ($$6383$$,$$BADJALING$$,#{state_id_wa},-31.903057,117.44455), - ($$6383$$,$$BALKULING$$,#{state_id_wa},-31.903057,117.44455), - ($$6383$$,$$CUBBINE$$,#{state_id_wa},-31.903057,117.44455), - ($$6383$$,$$DANGIN$$,#{state_id_wa},-31.903057,117.44455), - ($$6383$$,$$DOODENANNING$$,#{state_id_wa},-31.903057,117.44455), - ($$6383$$,$$DULBELLING$$,#{state_id_wa},-31.903057,117.44455), - ($$6383$$,$$MOUNT STIRLING$$,#{state_id_wa},-31.903057,117.44455), - ($$6383$$,$$QUAIRADING$$,#{state_id_wa},-31.903057,117.44455), - ($$6383$$,$$SOUTH QUAIRADING$$,#{state_id_wa},-31.903057,117.44455), - ($$6383$$,$$WAMENUSKING$$,#{state_id_wa},-31.903057,117.44455), - ($$6383$$,$$YOTING$$,#{state_id_wa},-31.903057,117.44455), - ($$6384$$,$$PANTAPIN$$,#{state_id_wa},-31.951369,117.659469), - ($$6385$$,$$KWOLYIN$$,#{state_id_wa},-31.928107,117.761062), - ($$6386$$,$$SHACKLETON$$,#{state_id_wa},-31.932955,117.836849), - ($$6390$$,$$BANNISTER$$,#{state_id_wa},-32.673514,116.515171), - ($$6390$$,$$BODDINGTON$$,#{state_id_wa},-32.673514,116.515171), - ($$6390$$,$$CROSSMAN$$,#{state_id_wa},-32.673514,116.515171), - ($$6390$$,$$LOWER HOTHAM$$,#{state_id_wa},-32.673514,116.515171), - ($$6390$$,$$MARRADONG$$,#{state_id_wa},-32.673514,116.515171), - ($$6390$$,$$MOUNT COOKE$$,#{state_id_wa},-32.673514,116.515171), - ($$6390$$,$$MOUNT WELLS$$,#{state_id_wa},-32.673514,116.515171), - ($$6390$$,$$NORTH BANNISTER$$,#{state_id_wa},-32.673514,116.515171), - ($$6390$$,$$RANFORD$$,#{state_id_wa},-32.673514,116.515171), - ($$6390$$,$$UPPER MURRAY$$,#{state_id_wa},-32.673514,116.515171), - ($$6390$$,$$WURAMING$$,#{state_id_wa},-32.673514,116.515171), - ($$6391$$,$$QUINDANNING$$,#{state_id_wa},-33.042802,116.567636), - ($$6391$$,$$WILLIAMS$$,#{state_id_wa},-33.042802,116.567636), - ($$6392$$,$$BOKAL$$,#{state_id_wa},-33.47027,116.912981), - ($$6392$$,$$DARDADINE$$,#{state_id_wa},-33.47027,116.912981), - ($$6392$$,$$DARKAN$$,#{state_id_wa},-33.47027,116.912981), - ($$6392$$,$$MEEKING$$,#{state_id_wa},-33.47027,116.912981), - ($$6393$$,$$DURANILLIN$$,#{state_id_wa},-33.511435,116.678676), - ($$6393$$,$$MOODIARRUP$$,#{state_id_wa},-33.511435,116.678676), - ($$6394$$,$$BEAUFORT RIVER$$,#{state_id_wa},-33.56611,117.03194), - ($$6394$$,$$BOILUP$$,#{state_id_wa},-33.56611,117.03194), - ($$6394$$,$$BOSCABEL$$,#{state_id_wa},-33.56611,117.03194), - ($$6394$$,$$CHANGERUP$$,#{state_id_wa},-33.56611,117.03194), - ($$6394$$,$$MOKUP$$,#{state_id_wa},-33.56611,117.03194), - ($$6394$$,$$MURADUP$$,#{state_id_wa},-33.56611,117.03194), - ($$6394$$,$$ORCHID VALLEY$$,#{state_id_wa},-33.56611,117.03194), - ($$6394$$,$$QUALEUP$$,#{state_id_wa},-33.56611,117.03194), - ($$6395$$,$$CHERRY TREE POOL$$,#{state_id_wa},-33.700935,117.23224), - ($$6395$$,$$JINGALUP$$,#{state_id_wa},-33.700935,117.23224), - ($$6395$$,$$KOJONUP$$,#{state_id_wa},-33.700935,117.23224), - ($$6395$$,$$LUMEAH$$,#{state_id_wa},-33.700935,117.23224), - ($$6395$$,$$MOBRUP$$,#{state_id_wa},-33.700935,117.23224), - ($$6395$$,$$RYANSBROOK$$,#{state_id_wa},-33.700935,117.23224), - ($$6396$$,$$FRANKLAND RIVER$$,#{state_id_wa},-34.305719,116.978389), - ($$6397$$,$$ROCKY GULLY$$,#{state_id_wa},-34.482268,117.101117), - ($$6398$$,$$BROKE$$,#{state_id_wa},-34.915218,116.465109), - ($$6398$$,$$NORTH WALPOLE$$,#{state_id_wa},-34.915218,116.465109), - ($$6398$$,$$WALPOLE$$,#{state_id_wa},-34.915218,116.465109), - ($$6401$$,$$BUCKLAND$$,#{state_id_wa},-31.542525,116.61072), - ($$6401$$,$$BURLONG$$,#{state_id_wa},-31.542525,116.61072), - ($$6401$$,$$CUNJARDINE$$,#{state_id_wa},-31.542525,116.61072), - ($$6401$$,$$IRISHTOWN$$,#{state_id_wa},-31.542525,116.61072), - ($$6401$$,$$JENNACUBBINE$$,#{state_id_wa},-31.542525,116.61072), - ($$6401$$,$$JENNAPULLIN$$,#{state_id_wa},-31.542525,116.61072), - ($$6401$$,$$MALABAINE$$,#{state_id_wa},-31.542525,116.61072), - ($$6401$$,$$MEENAAR$$,#{state_id_wa},-31.542525,116.61072), - ($$6401$$,$$MOKINE$$,#{state_id_wa},-31.542525,116.61072), - ($$6401$$,$$MULUCKINE$$,#{state_id_wa},-31.542525,116.61072), - ($$6401$$,$$MUMBERKINE$$,#{state_id_wa},-31.542525,116.61072), - ($$6401$$,$$MURESK$$,#{state_id_wa},-31.542525,116.61072), - ($$6401$$,$$NORTHAM$$,#{state_id_wa},-31.542525,116.61072), - ($$6401$$,$$ROSSMORE$$,#{state_id_wa},-31.542525,116.61072), - ($$6401$$,$$SOUTHERN BROOK$$,#{state_id_wa},-31.542525,116.61072), - ($$6401$$,$$SPENCERS BROOK$$,#{state_id_wa},-31.542525,116.61072), - ($$6401$$,$$THROSSELL$$,#{state_id_wa},-31.542525,116.61072), - ($$6401$$,$$WONGAMINE$$,#{state_id_wa},-31.542525,116.61072), - ($$6403$$,$$GRASS VALLEY$$,#{state_id_wa},-31.603675,116.808925), - ($$6405$$,$$GREENWOODS VALLEY$$,#{state_id_wa},-31.50639,116.94222), - ($$6405$$,$$MECKERING$$,#{state_id_wa},-31.50639,116.94222), - ($$6405$$,$$QUELAGETTING$$,#{state_id_wa},-31.50639,116.94222), - ($$6405$$,$$WARDING EAST$$,#{state_id_wa},-31.50639,116.94222), - ($$6407$$,$$CUNDERDIN$$,#{state_id_wa},-31.651484,117.23437), - ($$6407$$,$$WAEEL$$,#{state_id_wa},-31.651484,117.23437), - ($$6407$$,$$WATERCARRIN$$,#{state_id_wa},-31.651484,117.23437), - ($$6407$$,$$WYOLA WEST$$,#{state_id_wa},-31.651484,117.23437), - ($$6407$$,$$YOUNDEGIN$$,#{state_id_wa},-31.651484,117.23437), - ($$6409$$,$$NORTH TAMMIN$$,#{state_id_wa},-31.51972,117.43111), - ($$6409$$,$$SOUTH TAMMIN$$,#{state_id_wa},-31.51972,117.43111), - ($$6409$$,$$TAMMIN$$,#{state_id_wa},-31.51972,117.43111), - ($$6410$$,$$DAADENNING CREEK$$,#{state_id_wa},-31.623099,117.591223), - ($$6410$$,$$KELLERBERRIN$$,#{state_id_wa},-31.623099,117.591223), - ($$6410$$,$$MOUNT CAROLINE$$,#{state_id_wa},-31.623099,117.591223), - ($$6410$$,$$NORTH KELLERBERRIN$$,#{state_id_wa},-31.623099,117.591223), - ($$6411$$,$$DOODLAKINE$$,#{state_id_wa},-31.609048,117.87881), - ($$6411$$,$$SOUTH DOODLAKINE$$,#{state_id_wa},-31.609048,117.87881), - ($$6412$$,$$BAANDEE$$,#{state_id_wa},-31.581509,117.991421), - ($$6412$$,$$NORTH BAANDEE$$,#{state_id_wa},-31.581509,117.991421), - ($$6413$$,$$HINES HILL$$,#{state_id_wa},-31.531315,118.076379), - ($$6414$$,$$NANGEENAN$$,#{state_id_wa},-31.468945,118.154649), - ($$6415$$,$$GOOMARIN$$,#{state_id_wa},-31.23887,118.41367), - ($$6415$$,$$KORBEL$$,#{state_id_wa},-31.23887,118.41367), - ($$6415$$,$$MERREDIN$$,#{state_id_wa},-31.23887,118.41367), - ($$6415$$,$$NOKANING$$,#{state_id_wa},-31.23887,118.41367), - ($$6415$$,$$NORPA$$,#{state_id_wa},-31.23887,118.41367), - ($$6415$$,$$TANDEGIN$$,#{state_id_wa},-31.23887,118.41367), - ($$6418$$,$$BRUCE ROCK$$,#{state_id_wa},-31.877222,118.148822), - ($$6419$$,$$ARDATH$$,#{state_id_wa},-32.032709,118.095466), - ($$6420$$,$$CRAMPHORNE$$,#{state_id_wa},-31.801918,118.557867), - ($$6420$$,$$MUNTADGIN$$,#{state_id_wa},-31.801918,118.557867), - ($$6421$$,$$BURRACOPPIN$$,#{state_id_wa},-31.397419,118.478039), - ($$6421$$,$$SOUTH BURRACOPPIN$$,#{state_id_wa},-31.397419,118.478039), - ($$6421$$,$$WARRALAKIN$$,#{state_id_wa},-31.397419,118.478039), - ($$6422$$,$$WALGOOLAN$$,#{state_id_wa},-31.372179,118.598819), - ($$6423$$,$$BOODAROCKIN$$,#{state_id_wa},-30.998738,118.855354), - ($$6423$$,$$CARRABIN$$,#{state_id_wa},-30.998738,118.855354), - ($$6423$$,$$WARRACHUPPIN$$,#{state_id_wa},-30.998738,118.855354), - ($$6423$$,$$WESTONIA$$,#{state_id_wa},-30.998738,118.855354), - ($$6424$$,$$BODALLIN$$,#{state_id_wa},-31.370551,118.851488), - ($$6424$$,$$NORTH BODALLIN$$,#{state_id_wa},-31.370551,118.851488), - ($$6424$$,$$SOUTH BODALLIN$$,#{state_id_wa},-31.370551,118.851488), - ($$6425$$,$$DULYALBIN$$,#{state_id_wa},-31.55778,119.14694), - ($$6425$$,$$MOORINE ROCK$$,#{state_id_wa},-31.55778,119.14694), - ($$6426$$,$$CORINTHIA$$,#{state_id_wa},-31.15361,119.14944), - ($$6426$$,$$GHOOLI$$,#{state_id_wa},-31.15361,119.14944), - ($$6426$$,$$HOLLETON$$,#{state_id_wa},-31.15361,119.14944), - ($$6426$$,$$MARVEL LOCH$$,#{state_id_wa},-31.15361,119.14944), - ($$6426$$,$$MOUNT HAMPTON$$,#{state_id_wa},-31.15361,119.14944), - ($$6426$$,$$MOUNT HOLLAND$$,#{state_id_wa},-31.15361,119.14944), - ($$6426$$,$$MOUNT JACKSON$$,#{state_id_wa},-31.15361,119.14944), - ($$6426$$,$$MOUNT PALMER$$,#{state_id_wa},-31.15361,119.14944), - ($$6426$$,$$PARKER RANGE$$,#{state_id_wa},-31.15361,119.14944), - ($$6426$$,$$SKELETON ROCK$$,#{state_id_wa},-31.15361,119.14944), - ($$6426$$,$$SOUTH YILGARN$$,#{state_id_wa},-31.15361,119.14944), - ($$6426$$,$$SOUTHERN CROSS$$,#{state_id_wa},-31.15361,119.14944), - ($$6426$$,$$TURKEY HILL$$,#{state_id_wa},-31.15361,119.14944), - ($$6426$$,$$YELLOWDINE$$,#{state_id_wa},-31.15361,119.14944), - ($$6427$$,$$KOOLYANOBBING$$,#{state_id_wa},-31.113663,119.402403), - ($$6428$$,$$BABAKIN$$,#{state_id_wa},-32.126295,118.022769), - ($$6429$$,$$BOORABBIN$$,#{state_id_wa},-30.95306,120.15778), - ($$6429$$,$$BULLABULLING$$,#{state_id_wa},-30.95306,120.15778), - ($$6429$$,$$COOLGARDIE$$,#{state_id_wa},-30.95306,120.15778), - ($$6429$$,$$KARRAMINDIE$$,#{state_id_wa},-30.95306,120.15778), - ($$6429$$,$$LONDONDERRY$$,#{state_id_wa},-30.95306,120.15778), - ($$6429$$,$$MOUNT BURGES$$,#{state_id_wa},-30.95306,120.15778), - ($$6429$$,$$VICTORIA ROCK$$,#{state_id_wa},-30.95306,120.15778), - ($$6429$$,$$WALLAROO$$,#{state_id_wa},-30.95306,120.15778), - ($$6430$$,$$BINDULI$$,#{state_id_wa},-30.805,121.37556), - ($$6430$$,$$BROADWOOD$$,#{state_id_wa},-30.805,121.37556), - ($$6430$$,$$HANNANS$$,#{state_id_wa},-30.805,121.37556), - ($$6430$$,$$KALGOORLIE$$,#{state_id_wa},-30.805,121.37556), - ($$6430$$,$$KARLKURLA$$,#{state_id_wa},-30.805,121.37556), - ($$6430$$,$$LAMINGTON$$,#{state_id_wa},-30.805,121.37556), - ($$6430$$,$$MULLINGAR$$,#{state_id_wa},-30.805,121.37556), - ($$6430$$,$$PICCADILLY$$,#{state_id_wa},-30.805,121.37556), - ($$6430$$,$$SOMERVILLE$$,#{state_id_wa},-30.805,121.37556), - ($$6430$$,$$SOUTH KALGOORLIE$$,#{state_id_wa},-30.805,121.37556), - ($$6430$$,$$WEST KALGOORLIE$$,#{state_id_wa},-30.805,121.37556), - ($$6430$$,$$WEST LAMINGTON$$,#{state_id_wa},-30.805,121.37556), - ($$6430$$,$$WILLIAMSTOWN$$,#{state_id_wa},-30.805,121.37556), - ($$6430$$,$$YILKARI$$,#{state_id_wa},-30.805,121.37556), - ($$6431$$,$$BOORARA$$,#{state_id_wa},-30.80194,121.63667), - ($$6431$$,$$BROWN HILL$$,#{state_id_wa},-30.80194,121.63667), - ($$6431$$,$$BULONG$$,#{state_id_wa},-30.80194,121.63667), - ($$6431$$,$$EMU FLAT$$,#{state_id_wa},-30.80194,121.63667), - ($$6431$$,$$FEYSVILLE$$,#{state_id_wa},-30.80194,121.63667), - ($$6431$$,$$KANOWNA$$,#{state_id_wa},-30.80194,121.63667), - ($$6431$$,$$KOOKYNIE$$,#{state_id_wa},-30.80194,121.63667), - ($$6431$$,$$KURNALPI$$,#{state_id_wa},-30.80194,121.63667), - ($$6431$$,$$LAKEWOOD$$,#{state_id_wa},-30.80194,121.63667), - ($$6431$$,$$ORA BANDA$$,#{state_id_wa},-30.80194,121.63667), - ($$6431$$,$$PLUMRIDGE LAKES$$,#{state_id_wa},-30.80194,121.63667), - ($$6431$$,$$TRAFALGAR$$,#{state_id_wa},-30.80194,121.63667), - ($$6431$$,$$WARBURTON$$,#{state_id_wa},-30.80194,121.63667), - ($$6432$$,$$BOULDER$$,#{state_id_wa},-30.781898,121.488736), - ($$6432$$,$$FIMISTON$$,#{state_id_wa},-30.781898,121.488736), - ($$6432$$,$$SOUTH BOULDER$$,#{state_id_wa},-30.781898,121.488736), - ($$6432$$,$$VICTORY HEIGHTS$$,#{state_id_wa},-30.781898,121.488736), - ($$6433$$,$$HANNANS PO$$,#{state_id_wa},-31.883396,115.922464), - ($$6433$$,$$KALGOORLIE PO$$,#{state_id_wa},-31.883396,115.922464), - ($$6433$$,$$KALGOORLIE PO$$,#{state_id_wa},-31.883396,115.922464), - ($$6434$$,$$CUNDEELEE$$,#{state_id_wa},-30.504142,123.124853), - ($$6434$$,$$FORREST$$,#{state_id_wa},-30.504142,123.124853), - ($$6434$$,$$PARKESTON$$,#{state_id_wa},-30.504142,123.124853), - ($$6434$$,$$RAWLINNA$$,#{state_id_wa},-30.504142,123.124853), - ($$6434$$,$$ZANTHUS$$,#{state_id_wa},-30.504142,123.124853), - ($$6436$$,$$MENZIES$$,#{state_id_wa},-29.691504,121.029042), - ($$6436$$,$$ULARRING$$,#{state_id_wa},-29.691504,121.029042), - ($$6437$$,$$LEINSTER$$,#{state_id_wa},-27.451985,120.545063), - ($$6437$$,$$SIR SAMUEL$$,#{state_id_wa},-27.451985,120.545063), - ($$6438$$,$$LAKE DARLOT$$,#{state_id_wa},-27.79056,121.58028), - ($$6438$$,$$LEONORA$$,#{state_id_wa},-27.79056,121.58028), - ($$6440$$,$$BANDYA$$,#{state_id_wa},-27.91009,122.327326), - ($$6440$$,$$BEADELL$$,#{state_id_wa},-27.91009,122.327326), - ($$6440$$,$$COSMO NEWBERY$$,#{state_id_wa},-27.91009,122.327326), - ($$6440$$,$$LAKE WELLS$$,#{state_id_wa},-27.91009,122.327326), - ($$6440$$,$$LAVERTON$$,#{state_id_wa},-27.91009,122.327326), - ($$6440$$,$$NEALE$$,#{state_id_wa},-27.91009,122.327326), - ($$6442$$,$$KAMBALDA EAST$$,#{state_id_wa},-31.208026,121.618365), - ($$6442$$,$$KAMBALDA WEST$$,#{state_id_wa},-31.208026,121.618365), - ($$6443$$,$$BALLADONIA$$,#{state_id_wa},-32.466106,123.832359), - ($$6443$$,$$CAIGUNA$$,#{state_id_wa},-32.466106,123.832359), - ($$6443$$,$$COCKLEBIDDY$$,#{state_id_wa},-32.466106,123.832359), - ($$6443$$,$$DUNDAS$$,#{state_id_wa},-32.466106,123.832359), - ($$6443$$,$$EUCLA$$,#{state_id_wa},-32.466106,123.832359), - ($$6443$$,$$FRASER RANGE$$,#{state_id_wa},-32.466106,123.832359), - ($$6443$$,$$HIGGINSVILLE$$,#{state_id_wa},-32.466106,123.832359), - ($$6443$$,$$MADURA$$,#{state_id_wa},-32.466106,123.832359), - ($$6443$$,$$MUNDRABILLA$$,#{state_id_wa},-32.466106,123.832359), - ($$6443$$,$$NORSEMAN$$,#{state_id_wa},-32.466106,123.832359), - ($$6443$$,$$WIDGIEMOOLTHA$$,#{state_id_wa},-32.466106,123.832359), - ($$6445$$,$$NORTH CASCADE$$,#{state_id_wa},-32.91,121.10083), - ($$6445$$,$$SALMON GUMS$$,#{state_id_wa},-32.91,121.10083), - ($$6446$$,$$GRASS PATCH$$,#{state_id_wa},-33.228449,121.718155), - ($$6447$$,$$LORT RIVER$$,#{state_id_wa},-33.35444,121.39139), - ($$6447$$,$$MOUNT NEY$$,#{state_id_wa},-33.35444,121.39139), - ($$6447$$,$$SCADDAN$$,#{state_id_wa},-33.35444,121.39139), - ($$6447$$,$$WITTENOOM HILLS$$,#{state_id_wa},-33.35444,121.39139), - ($$6448$$,$$GIBSON$$,#{state_id_wa},-33.644029,121.809646), - ($$6450$$,$$BANDY CREEK$$,#{state_id_wa},-33.820967,121.93876), - ($$6450$$,$$BEAUMONT$$,#{state_id_wa},-33.820967,121.93876), - ($$6450$$,$$BOYATUP$$,#{state_id_wa},-33.820967,121.93876), - ($$6450$$,$$CAPE LE GRAND$$,#{state_id_wa},-33.820967,121.93876), - ($$6450$$,$$CASCADE$$,#{state_id_wa},-33.820967,121.93876), - ($$6450$$,$$CASTLETOWN$$,#{state_id_wa},-33.820967,121.93876), - ($$6450$$,$$CHADWICK$$,#{state_id_wa},-33.820967,121.93876), - ($$6450$$,$$CONDINGUP$$,#{state_id_wa},-33.820967,121.93876), - ($$6450$$,$$COOMALBIDGUP$$,#{state_id_wa},-33.820967,121.93876), - ($$6450$$,$$DALYUP$$,#{state_id_wa},-33.820967,121.93876), - ($$6450$$,$$EAST MUNGLINUP$$,#{state_id_wa},-33.820967,121.93876), - ($$6450$$,$$ESPERANCE$$,#{state_id_wa},-33.820967,121.93876), - ($$6450$$,$$HOWICK$$,#{state_id_wa},-33.820967,121.93876), - ($$6450$$,$$MERIVALE$$,#{state_id_wa},-33.820967,121.93876), - ($$6450$$,$$MONJINGUP$$,#{state_id_wa},-33.820967,121.93876), - ($$6450$$,$$MUNGLINUP$$,#{state_id_wa},-33.820967,121.93876), - ($$6450$$,$$MYRUP$$,#{state_id_wa},-33.820967,121.93876), - ($$6450$$,$$NERIDUP$$,#{state_id_wa},-33.820967,121.93876), - ($$6450$$,$$NULSEN$$,#{state_id_wa},-33.820967,121.93876), - ($$6450$$,$$PINK LAKE$$,#{state_id_wa},-33.820967,121.93876), - ($$6450$$,$$SINCLAIR$$,#{state_id_wa},-33.820967,121.93876), - ($$6450$$,$$WEST BEACH$$,#{state_id_wa},-33.820967,121.93876), - ($$6450$$,$$WINDABOUT$$,#{state_id_wa},-33.820967,121.93876), - ($$6452$$,$$BURAMINYA$$,#{state_id_wa},-32.91833,122.895), - ($$6452$$,$$CAPE ARID$$,#{state_id_wa},-32.91833,122.895), - ($$6452$$,$$ISRAELITE BAY$$,#{state_id_wa},-32.91833,122.895), - ($$6460$$,$$GOOMALLING$$,#{state_id_wa},-31.298531,116.826959), - ($$6460$$,$$HULONGINE$$,#{state_id_wa},-31.298531,116.826959), - ($$6460$$,$$KARRANADGIN$$,#{state_id_wa},-31.298531,116.826959), - ($$6460$$,$$UCARTY WEST$$,#{state_id_wa},-31.298531,116.826959), - ($$6460$$,$$WALYORMOURING$$,#{state_id_wa},-31.298531,116.826959), - ($$6461$$,$$DOWERIN$$,#{state_id_wa},-31.195622,117.031905), - ($$6461$$,$$KOOMBERKINE$$,#{state_id_wa},-31.195622,117.031905), - ($$6462$$,$$HINDMARSH$$,#{state_id_wa},-31.26972,117.23056), - ($$6462$$,$$MINNIVALE$$,#{state_id_wa},-31.26972,117.23056), - ($$6462$$,$$UCARTY$$,#{state_id_wa},-31.26972,117.23056), - ($$6463$$,$$BENJABERRING$$,#{state_id_wa},-31.141869,117.288577), - ($$6465$$,$$MANMANNING$$,#{state_id_wa},-30.865319,117.044964), - ($$6466$$,$$CADOUX$$,#{state_id_wa},-30.768036,117.135417), - ($$6467$$,$$BURAKIN$$,#{state_id_wa},-30.525059,117.1727), - ($$6468$$,$$GOODLANDS$$,#{state_id_wa},-30.10833,117.20889), - ($$6468$$,$$KALANNIE$$,#{state_id_wa},-30.10833,117.20889), - ($$6468$$,$$PETRUDOR$$,#{state_id_wa},-30.10833,117.20889), - ($$6470$$,$$KULJA$$,#{state_id_wa},-30.344349,117.338352), - ($$6472$$,$$BEACON$$,#{state_id_wa},-30.451404,117.865646), - ($$6472$$,$$BIMBIJY$$,#{state_id_wa},-30.451404,117.865646), - ($$6472$$,$$CLEARY$$,#{state_id_wa},-30.451404,117.865646), - ($$6472$$,$$KARROUN HILL$$,#{state_id_wa},-30.451404,117.865646), - ($$6472$$,$$MOUROUBRA$$,#{state_id_wa},-30.451404,117.865646), - ($$6472$$,$$REMLAP$$,#{state_id_wa},-30.451404,117.865646), - ($$6472$$,$$TAMPU$$,#{state_id_wa},-30.451404,117.865646), - ($$6473$$,$$NORTH WIALKI$$,#{state_id_wa},-30.33139,118.22306), - ($$6473$$,$$WIALKI$$,#{state_id_wa},-30.33139,118.22306), - ($$6475$$,$$BADGERIN ROCK$$,#{state_id_wa},-30.691457,117.274368), - ($$6475$$,$$BOORALAMING$$,#{state_id_wa},-30.691457,117.274368), - ($$6475$$,$$DUKIN$$,#{state_id_wa},-30.691457,117.274368), - ($$6475$$,$$KOORDA$$,#{state_id_wa},-30.691457,117.274368), - ($$6475$$,$$LAKE MARGARETTE$$,#{state_id_wa},-30.691457,117.274368), - ($$6475$$,$$MOLLERIN$$,#{state_id_wa},-30.691457,117.274368), - ($$6475$$,$$NEWCARLBEON$$,#{state_id_wa},-30.691457,117.274368), - ($$6476$$,$$GABBIN$$,#{state_id_wa},-30.799257,117.679456), - ($$6477$$,$$BENCUBBIN$$,#{state_id_wa},-30.811701,117.861684), - ($$6477$$,$$WELBUNGIN$$,#{state_id_wa},-30.811701,117.861684), - ($$6479$$,$$BARBALIN$$,#{state_id_wa},-30.881541,118.11395), - ($$6479$$,$$BONNIE ROCK$$,#{state_id_wa},-30.881541,118.11395), - ($$6479$$,$$DANDANNING$$,#{state_id_wa},-30.881541,118.11395), - ($$6479$$,$$ELACHBUTTING$$,#{state_id_wa},-30.881541,118.11395), - ($$6479$$,$$KARLONING$$,#{state_id_wa},-30.881541,118.11395), - ($$6479$$,$$LAKE BROWN$$,#{state_id_wa},-30.881541,118.11395), - ($$6479$$,$$MUKINBUDIN$$,#{state_id_wa},-30.881541,118.11395), - ($$6479$$,$$WATTONING$$,#{state_id_wa},-30.881541,118.11395), - ($$6479$$,$$WILGOYNE$$,#{state_id_wa},-30.881541,118.11395), - ($$6480$$,$$NUKARNI$$,#{state_id_wa},-31.2939,118.201877), - ($$6484$$,$$BULLFINCH$$,#{state_id_wa},-30.984212,119.116614), - ($$6484$$,$$ENNUIN$$,#{state_id_wa},-30.984212,119.116614), - ($$6484$$,$$LAKE DEBORAH$$,#{state_id_wa},-30.984212,119.116614), - ($$6485$$,$$COWCOWING$$,#{state_id_wa},-30.992936,117.454903), - ($$6485$$,$$KORRELOCKING$$,#{state_id_wa},-30.992936,117.454903), - ($$6485$$,$$NALKAIN$$,#{state_id_wa},-30.992936,117.454903), - ($$6485$$,$$NEMBUDDING$$,#{state_id_wa},-30.992936,117.454903), - ($$6485$$,$$WYALKATCHEM$$,#{state_id_wa},-30.992936,117.454903), - ($$6487$$,$$NORTH YELBENI$$,#{state_id_wa},-31.06056,117.66167), - ($$6487$$,$$SOUTH YELBENI$$,#{state_id_wa},-31.06056,117.66167), - ($$6487$$,$$YELBENI$$,#{state_id_wa},-31.06056,117.66167), - ($$6488$$,$$NORTH TRAYNING$$,#{state_id_wa},-31.04111,117.79278), - ($$6488$$,$$SOUTH TRAYNING$$,#{state_id_wa},-31.04111,117.79278), - ($$6488$$,$$TRAYNING$$,#{state_id_wa},-31.04111,117.79278), - ($$6489$$,$$KUNUNOPPIN$$,#{state_id_wa},-31.112821,117.918886), - ($$6489$$,$$NORTH KUNUNOPPIN$$,#{state_id_wa},-31.112821,117.918886), - ($$6489$$,$$SOUTH KUNUNOPPIN$$,#{state_id_wa},-31.112821,117.918886), - ($$6490$$,$$BURRAN ROCK$$,#{state_id_wa},-31.260227,118.008744), - ($$6490$$,$$CHANDLER$$,#{state_id_wa},-31.260227,118.008744), - ($$6490$$,$$ELABBIN$$,#{state_id_wa},-31.260227,118.008744), - ($$6490$$,$$KWELKAN$$,#{state_id_wa},-31.260227,118.008744), - ($$6490$$,$$NUNGARIN$$,#{state_id_wa},-31.260227,118.008744), - ($$6490$$,$$TALGOMINE$$,#{state_id_wa},-31.260227,118.008744), - ($$6501$$,$$MUCHEA$$,#{state_id_wa},-31.604231,115.985004), - ($$6502$$,$$BINDOON$$,#{state_id_wa},-31.385163,116.096186), - ($$6502$$,$$BINDOON TRAINING AREA$$,#{state_id_wa},-31.385163,116.096186), - ($$6503$$,$$BAMBUN$$,#{state_id_wa},-31.419695,115.9014), - ($$6503$$,$$BEERMULLAH$$,#{state_id_wa},-31.419695,115.9014), - ($$6503$$,$$BOONANARRING$$,#{state_id_wa},-31.419695,115.9014), - ($$6503$$,$$BREERA$$,#{state_id_wa},-31.419695,115.9014), - ($$6503$$,$$COONABIDGEE$$,#{state_id_wa},-31.419695,115.9014), - ($$6503$$,$$COWALLA$$,#{state_id_wa},-31.419695,115.9014), - ($$6503$$,$$CULLALLA$$,#{state_id_wa},-31.419695,115.9014), - ($$6503$$,$$GINGIN$$,#{state_id_wa},-31.419695,115.9014), - ($$6503$$,$$GINGINUP$$,#{state_id_wa},-31.419695,115.9014), - ($$6503$$,$$GRANVILLE$$,#{state_id_wa},-31.419695,115.9014), - ($$6503$$,$$LENNARD BROOK$$,#{state_id_wa},-31.419695,115.9014), - ($$6503$$,$$MINDARRA$$,#{state_id_wa},-31.419695,115.9014), - ($$6503$$,$$MOONDAH$$,#{state_id_wa},-31.419695,115.9014), - ($$6503$$,$$MOORE RIVER NATIONAL PARK$$,#{state_id_wa},-31.419695,115.9014), - ($$6503$$,$$MUCKENBURRA$$,#{state_id_wa},-31.419695,115.9014), - ($$6503$$,$$NEERGABBY$$,#{state_id_wa},-31.419695,115.9014), - ($$6503$$,$$ORANGE SPRINGS$$,#{state_id_wa},-31.419695,115.9014), - ($$6503$$,$$RED GULLY$$,#{state_id_wa},-31.419695,115.9014), - ($$6503$$,$$WANERIE$$,#{state_id_wa},-31.419695,115.9014), - ($$6503$$,$$YEAL$$,#{state_id_wa},-31.419695,115.9014), - ($$6504$$,$$MOOLIABEENEE$$,#{state_id_wa},-31.327595,116.02722), - ($$6505$$,$$WANNAMAL$$,#{state_id_wa},-31.173278,115.931415), - ($$6506$$,$$MOGUMBER$$,#{state_id_wa},-31.0235,115.962385), - ($$6507$$,$$CATABY$$,#{state_id_wa},-30.736585,115.54195), - ($$6507$$,$$COOLJARLOO$$,#{state_id_wa},-30.736585,115.54195), - ($$6507$$,$$DANDARAGAN$$,#{state_id_wa},-30.736585,115.54195), - ($$6507$$,$$MIMEGARRA$$,#{state_id_wa},-30.736585,115.54195), - ($$6507$$,$$REGANS FORD$$,#{state_id_wa},-30.736585,115.54195), - ($$6507$$,$$YATHROO$$,#{state_id_wa},-30.736585,115.54195), - ($$6509$$,$$GLENTROMIE$$,#{state_id_wa},-30.886908,116.244326), - ($$6509$$,$$NEW NORCIA$$,#{state_id_wa},-30.886908,116.244326), - ($$6509$$,$$WADDINGTON$$,#{state_id_wa},-30.886908,116.244326), - ($$6509$$,$$YARAWINDAH$$,#{state_id_wa},-30.886908,116.244326), - ($$6510$$,$$BARBERTON$$,#{state_id_wa},-30.729825,116.026509), - ($$6510$$,$$BERKSHIRE VALLEY$$,#{state_id_wa},-30.729825,116.026509), - ($$6510$$,$$GILLINGARRA$$,#{state_id_wa},-30.729825,116.026509), - ($$6510$$,$$KOOJAN$$,#{state_id_wa},-30.729825,116.026509), - ($$6510$$,$$MOORA$$,#{state_id_wa},-30.729825,116.026509), - ($$6510$$,$$WALEBING$$,#{state_id_wa},-30.729825,116.026509), - ($$6511$$,$$CERVANTES$$,#{state_id_wa},-30.498083,115.081138), - ($$6512$$,$$COOMBERDALE$$,#{state_id_wa},-30.443603,116.041424), - ($$6512$$,$$NAMBAN$$,#{state_id_wa},-30.443603,116.041424), - ($$6513$$,$$GUNYIDI$$,#{state_id_wa},-30.145253,116.076219), - ($$6513$$,$$WATHEROO$$,#{state_id_wa},-30.145253,116.076219), - ($$6514$$,$$GREEN HEAD$$,#{state_id_wa},-30.063852,114.968731), - ($$6514$$,$$LEEMAN$$,#{state_id_wa},-30.063852,114.968731), - ($$6515$$,$$COOROW$$,#{state_id_wa},-29.882389,116.02162), - ($$6515$$,$$EGANU$$,#{state_id_wa},-29.882389,116.02162), - ($$6515$$,$$MARCHAGEE$$,#{state_id_wa},-29.882389,116.02162), - ($$6515$$,$$WADDY FOREST$$,#{state_id_wa},-29.882389,116.02162), - ($$6516$$,$$JURIEN BAY$$,#{state_id_wa},-30.307885,115.03648), - ($$6517$$,$$CARNAMAH$$,#{state_id_wa},-29.688487,115.886125), - ($$6518$$,$$ENEABBA$$,#{state_id_wa},-29.817799,115.267594), - ($$6518$$,$$WARRADARGE$$,#{state_id_wa},-29.817799,115.267594), - ($$6519$$,$$ARRINO$$,#{state_id_wa},-29.440061,115.628368), - ($$6519$$,$$ARROWSMITH EAST$$,#{state_id_wa},-29.440061,115.628368), - ($$6519$$,$$DUDAWA$$,#{state_id_wa},-29.440061,115.628368), - ($$6519$$,$$KADATHINNI$$,#{state_id_wa},-29.440061,115.628368), - ($$6519$$,$$THREE SPRINGS$$,#{state_id_wa},-29.440061,115.628368), - ($$6519$$,$$WOMARDEN$$,#{state_id_wa},-29.440061,115.628368), - ($$6521$$,$$BADGINGARRA$$,#{state_id_wa},-30.387022,115.496702), - ($$6521$$,$$BOOTHENDARRA$$,#{state_id_wa},-30.387022,115.496702), - ($$6521$$,$$GREY$$,#{state_id_wa},-30.387022,115.496702), - ($$6521$$,$$HILL RIVER$$,#{state_id_wa},-30.387022,115.496702), - ($$6521$$,$$NAMBUNG$$,#{state_id_wa},-30.387022,115.496702), - ($$6522$$,$$BUNDANOON$$,#{state_id_wa},-29.29944,115.49222), - ($$6522$$,$$HOLMWOOD$$,#{state_id_wa},-29.29944,115.49222), - ($$6522$$,$$IKEWA$$,#{state_id_wa},-29.29944,115.49222), - ($$6522$$,$$LOCKIER$$,#{state_id_wa},-29.29944,115.49222), - ($$6522$$,$$MINGENEW$$,#{state_id_wa},-29.29944,115.49222), - ($$6522$$,$$MOORIARY$$,#{state_id_wa},-29.29944,115.49222), - ($$6522$$,$$MOUNT BUDD$$,#{state_id_wa},-29.29944,115.49222), - ($$6522$$,$$NANGETTY$$,#{state_id_wa},-29.29944,115.49222), - ($$6522$$,$$YANDANOOKA$$,#{state_id_wa},-29.29944,115.49222), - ($$6522$$,$$YARRAGADEE$$,#{state_id_wa},-29.29944,115.49222), - ($$6525$$,$$ALLANOOKA$$,#{state_id_wa},-29.047262,115.029078), - ($$6525$$,$$ARROWSMITH$$,#{state_id_wa},-29.047262,115.029078), - ($$6525$$,$$BONNIEFIELD$$,#{state_id_wa},-29.047262,115.029078), - ($$6525$$,$$BOOKARA$$,#{state_id_wa},-29.047262,115.029078), - ($$6525$$,$$DONGARA$$,#{state_id_wa},-29.047262,115.029078), - ($$6525$$,$$IRWIN$$,#{state_id_wa},-29.047262,115.029078), - ($$6525$$,$$MILO$$,#{state_id_wa},-29.047262,115.029078), - ($$6525$$,$$MOUNT ADAMS$$,#{state_id_wa},-29.047262,115.029078), - ($$6525$$,$$MOUNT HORNER$$,#{state_id_wa},-29.047262,115.029078), - ($$6525$$,$$PORT DENISON$$,#{state_id_wa},-29.047262,115.029078), - ($$6525$$,$$SPRINGFIELD$$,#{state_id_wa},-29.047262,115.029078), - ($$6525$$,$$YARDARINO$$,#{state_id_wa},-29.047262,115.029078), - ($$6528$$,$$MOUNT HILL$$,#{state_id_wa},-28.9825,114.9075), - ($$6528$$,$$SOUTH GREENOUGH$$,#{state_id_wa},-28.9825,114.9075), - ($$6528$$,$$WALKAWAY$$,#{state_id_wa},-28.9825,114.9075), - ($$6530$$,$$BEACHLANDS$$,#{state_id_wa},-28.788893,114.59948), - ($$6530$$,$$BERESFORD$$,#{state_id_wa},-28.788893,114.59948), - ($$6530$$,$$BLUFF POINT$$,#{state_id_wa},-28.788893,114.59948), - ($$6530$$,$$GERALDTON$$,#{state_id_wa},-28.788893,114.59948), - ($$6530$$,$$GERALDTON DC$$,#{state_id_wa},-28.788893,114.59948), - ($$6530$$,$$HOUTMAN ABROLHOS$$,#{state_id_wa},-28.788893,114.59948), - ($$6530$$,$$KARLOO$$,#{state_id_wa},-28.788893,114.59948), - ($$6530$$,$$MAHOMETS FLATS$$,#{state_id_wa},-28.788893,114.59948), - ($$6530$$,$$MERU$$,#{state_id_wa},-28.788893,114.59948), - ($$6530$$,$$MORESBY$$,#{state_id_wa},-28.788893,114.59948), - ($$6530$$,$$MOUNT TARCOOLA$$,#{state_id_wa},-28.788893,114.59948), - ($$6530$$,$$RANGEWAY$$,#{state_id_wa},-28.788893,114.59948), - ($$6530$$,$$SPALDING$$,#{state_id_wa},-28.788893,114.59948), - ($$6530$$,$$STRATHALBYN$$,#{state_id_wa},-28.788893,114.59948), - ($$6530$$,$$SUNSET BEACH$$,#{state_id_wa},-28.788893,114.59948), - ($$6530$$,$$TARCOOLA BEACH$$,#{state_id_wa},-28.788893,114.59948), - ($$6530$$,$$UTAKARRA$$,#{state_id_wa},-28.788893,114.59948), - ($$6530$$,$$WAGGRAKINE$$,#{state_id_wa},-28.788893,114.59948), - ($$6530$$,$$WANDINA$$,#{state_id_wa},-28.788893,114.59948), - ($$6530$$,$$WEBBERTON$$,#{state_id_wa},-28.788893,114.59948), - ($$6530$$,$$WEST END$$,#{state_id_wa},-28.788893,114.59948), - ($$6530$$,$$WONTHELLA$$,#{state_id_wa},-28.788893,114.59948), - ($$6530$$,$$WOORREE$$,#{state_id_wa},-28.788893,114.59948), - ($$6531$$,$$GERALDTON PO$$,#{state_id_wa},-32.278396,115.740693), - ($$6532$$,$$AJANA$$,#{state_id_wa},-27.948607,114.61132), - ($$6532$$,$$BINNU$$,#{state_id_wa},-27.948607,114.61132), - ($$6532$$,$$BOOTENAL$$,#{state_id_wa},-27.948607,114.61132), - ($$6532$$,$$BRINGO$$,#{state_id_wa},-27.948607,114.61132), - ($$6532$$,$$BULLER$$,#{state_id_wa},-27.948607,114.61132), - ($$6532$$,$$BURMA ROAD$$,#{state_id_wa},-27.948607,114.61132), - ($$6532$$,$$CAPE BURNEY$$,#{state_id_wa},-27.948607,114.61132), - ($$6532$$,$$CARRARANG$$,#{state_id_wa},-27.948607,114.61132), - ($$6532$$,$$COBURN$$,#{state_id_wa},-27.948607,114.61132), - ($$6532$$,$$COOLCALALAYA$$,#{state_id_wa},-27.948607,114.61132), - ($$6532$$,$$DARTMOOR$$,#{state_id_wa},-27.948607,114.61132), - ($$6532$$,$$DEEPDALE$$,#{state_id_wa},-27.948607,114.61132), - ($$6532$$,$$DINDILOA$$,#{state_id_wa},-27.948607,114.61132), - ($$6532$$,$$DRUMMOND COVE$$,#{state_id_wa},-27.948607,114.61132), - ($$6532$$,$$DURAWAH$$,#{state_id_wa},-27.948607,114.61132), - ($$6532$$,$$EAST CHAPMAN$$,#{state_id_wa},-27.948607,114.61132), - ($$6532$$,$$EAST NABAWA$$,#{state_id_wa},-27.948607,114.61132), - ($$6532$$,$$EAST YUNA$$,#{state_id_wa},-27.948607,114.61132), - ($$6532$$,$$ELLENDALE$$,#{state_id_wa},-27.948607,114.61132), - ($$6532$$,$$ERADU$$,#{state_id_wa},-27.948607,114.61132), - ($$6532$$,$$ERADU SOUTH$$,#{state_id_wa},-27.948607,114.61132), - ($$6532$$,$$EURARDY$$,#{state_id_wa},-27.948607,114.61132), - ($$6532$$,$$GEORGINA$$,#{state_id_wa},-27.948607,114.61132), - ($$6532$$,$$GLENFIELD$$,#{state_id_wa},-27.948607,114.61132), - ($$6532$$,$$GREENOUGH$$,#{state_id_wa},-27.948607,114.61132), - ($$6532$$,$$HAMELIN POOL$$,#{state_id_wa},-27.948607,114.61132), - ($$6532$$,$$HICKETY$$,#{state_id_wa},-27.948607,114.61132), - ($$6532$$,$$HOWATHARRA$$,#{state_id_wa},-27.948607,114.61132), - ($$6532$$,$$KOJARENA$$,#{state_id_wa},-27.948607,114.61132), - ($$6532$$,$$MARRAH$$,#{state_id_wa},-27.948607,114.61132), - ($$6532$$,$$MEADOW$$,#{state_id_wa},-27.948607,114.61132), - ($$6532$$,$$MINNENOOKA$$,#{state_id_wa},-27.948607,114.61132), - ($$6532$$,$$MOONYOONOOKA$$,#{state_id_wa},-27.948607,114.61132), - ($$6532$$,$$MOUNT ERIN$$,#{state_id_wa},-27.948607,114.61132), - ($$6532$$,$$NABAWA$$,#{state_id_wa},-27.948607,114.61132), - ($$6532$$,$$NANSON$$,#{state_id_wa},-27.948607,114.61132), - ($$6532$$,$$NARALING$$,#{state_id_wa},-27.948607,114.61132), - ($$6532$$,$$NARNGULU$$,#{state_id_wa},-27.948607,114.61132), - ($$6532$$,$$NARRA TARRA$$,#{state_id_wa},-27.948607,114.61132), - ($$6532$$,$$NERREN NERREN$$,#{state_id_wa},-27.948607,114.61132), - ($$6532$$,$$NOLBA$$,#{state_id_wa},-27.948607,114.61132), - ($$6532$$,$$NORTH ERADU$$,#{state_id_wa},-27.948607,114.61132), - ($$6532$$,$$NORTHERN GULLY$$,#{state_id_wa},-27.948607,114.61132), - ($$6532$$,$$OAKAJEE$$,#{state_id_wa},-27.948607,114.61132), - ($$6532$$,$$ROCKWELL$$,#{state_id_wa},-27.948607,114.61132), - ($$6532$$,$$RUDDS GULLY$$,#{state_id_wa},-27.948607,114.61132), - ($$6532$$,$$SANDSPRINGS$$,#{state_id_wa},-27.948607,114.61132), - ($$6532$$,$$SOUTH YUNA$$,#{state_id_wa},-27.948607,114.61132), - ($$6532$$,$$TAMALA$$,#{state_id_wa},-27.948607,114.61132), - ($$6532$$,$$TIBRADDEN$$,#{state_id_wa},-27.948607,114.61132), - ($$6532$$,$$TOOLONGA$$,#{state_id_wa},-27.948607,114.61132), - ($$6532$$,$$VALENTINE$$,#{state_id_wa},-27.948607,114.61132), - ($$6532$$,$$WANDANA$$,#{state_id_wa},-27.948607,114.61132), - ($$6532$$,$$WEST BINNU$$,#{state_id_wa},-27.948607,114.61132), - ($$6532$$,$$WHITE PEAK$$,#{state_id_wa},-27.948607,114.61132), - ($$6532$$,$$WICHERINA$$,#{state_id_wa},-27.948607,114.61132), - ($$6532$$,$$WICHERINA SOUTH$$,#{state_id_wa},-27.948607,114.61132), - ($$6532$$,$$YETNA$$,#{state_id_wa},-27.948607,114.61132), - ($$6532$$,$$YUNA$$,#{state_id_wa},-27.948607,114.61132), - ($$6535$$,$$ALMA$$,#{state_id_wa},-28.233375,114.693575), - ($$6535$$,$$BOWES$$,#{state_id_wa},-28.233375,114.693575), - ($$6535$$,$$EAST BOWES$$,#{state_id_wa},-28.233375,114.693575), - ($$6535$$,$$GREGORY$$,#{state_id_wa},-28.233375,114.693575), - ($$6535$$,$$HORROCKS$$,#{state_id_wa},-28.233375,114.693575), - ($$6535$$,$$ISSEKA$$,#{state_id_wa},-28.233375,114.693575), - ($$6535$$,$$NORTHAMPTON$$,#{state_id_wa},-28.233375,114.693575), - ($$6535$$,$$OGILVIE$$,#{state_id_wa},-28.233375,114.693575), - ($$6535$$,$$SANDY GULLY$$,#{state_id_wa},-28.233375,114.693575), - ($$6535$$,$$YALLABATHARRA$$,#{state_id_wa},-28.233375,114.693575), - ($$6536$$,$$KALBARRI$$,#{state_id_wa},-27.710568,114.164418), - ($$6536$$,$$KALBARRI NATIONAL PARK$$,#{state_id_wa},-27.710568,114.164418), - ($$6536$$,$$ZUYTDORP$$,#{state_id_wa},-27.710568,114.164418), - ($$6537$$,$$DENHAM$$,#{state_id_wa},-25.927885,113.533715), - ($$6537$$,$$DIRK HARTOG ISLAND$$,#{state_id_wa},-25.927885,113.533715), - ($$6537$$,$$FRANCOIS PERON NATIONAL PARK$$,#{state_id_wa},-25.927885,113.533715), - ($$6537$$,$$MONKEY MIA$$,#{state_id_wa},-25.927885,113.533715), - ($$6537$$,$$NANGA$$,#{state_id_wa},-25.927885,113.533715), - ($$6537$$,$$SHARK BAY$$,#{state_id_wa},-25.927885,113.533715), - ($$6537$$,$$USELESS LOOP$$,#{state_id_wa},-25.927885,113.533715), - ($$6556$$,$$BEECHINA$$,#{state_id_wa},-31.860734,116.315042), - ($$6556$$,$$CHIDLOW$$,#{state_id_wa},-31.860734,116.315042), - ($$6556$$,$$GORRIE$$,#{state_id_wa},-31.860734,116.315042), - ($$6556$$,$$MALMALLING$$,#{state_id_wa},-31.860734,116.315042), - ($$6556$$,$$THE LAKES$$,#{state_id_wa},-31.860734,116.315042), - ($$6558$$,$$WOOROLOO$$,#{state_id_wa},-31.804012,116.312779), - ($$6560$$,$$WUNDOWIE$$,#{state_id_wa},-31.753412,116.385152), - ($$6562$$,$$BAKERS HILL$$,#{state_id_wa},-31.747506,116.459591), - ($$6562$$,$$COPLEY$$,#{state_id_wa},-31.747506,116.459591), - ($$6562$$,$$WOOTTATING$$,#{state_id_wa},-31.747506,116.459591), - ($$6564$$,$$CLACKLINE$$,#{state_id_wa},-31.719218,116.506842), - ($$6566$$,$$BEJOORDING$$,#{state_id_wa},-31.383465,116.533656), - ($$6566$$,$$CARANI$$,#{state_id_wa},-31.383465,116.533656), - ($$6566$$,$$COONDLE$$,#{state_id_wa},-31.383465,116.533656), - ($$6566$$,$$CULHAM$$,#{state_id_wa},-31.383465,116.533656), - ($$6566$$,$$DUMBARTON$$,#{state_id_wa},-31.383465,116.533656), - ($$6566$$,$$HODDYS WELL$$,#{state_id_wa},-31.383465,116.533656), - ($$6566$$,$$KATRINE$$,#{state_id_wa},-31.383465,116.533656), - ($$6566$$,$$NUNILE$$,#{state_id_wa},-31.383465,116.533656), - ($$6566$$,$$TOODYAY$$,#{state_id_wa},-31.383465,116.533656), - ($$6566$$,$$WEST TOODYAY$$,#{state_id_wa},-31.383465,116.533656), - ($$6567$$,$$DEWARS POOL$$,#{state_id_wa},-31.459208,116.419958), - ($$6567$$,$$JULIMAR$$,#{state_id_wa},-31.459208,116.419958), - ($$6567$$,$$MOONDYNE$$,#{state_id_wa},-31.459208,116.419958), - ($$6568$$,$$BOLGART$$,#{state_id_wa},-31.274938,116.508618), - ($$6568$$,$$WATTENING$$,#{state_id_wa},-31.274938,116.508618), - ($$6568$$,$$WYENING$$,#{state_id_wa},-31.274938,116.508618), - ($$6569$$,$$CALINGIRI$$,#{state_id_wa},-31.091752,116.44884), - ($$6569$$,$$OLD PLAINS$$,#{state_id_wa},-31.091752,116.44884), - ($$6571$$,$$YERECOIN$$,#{state_id_wa},-30.922107,116.389442), - ($$6572$$,$$PIAWANING$$,#{state_id_wa},0.0,0.0), - ($$6574$$,$$BINDI BINDI$$,#{state_id_wa},-30.627746,116.364029), - ($$6574$$,$$GABALONG$$,#{state_id_wa},-30.627746,116.364029), - ($$6575$$,$$MILING$$,#{state_id_wa},-30.49034,116.360856), - ($$6603$$,$$KONNONGORRING$$,#{state_id_wa},-31.010506,116.761929), - ($$6603$$,$$LAKE HINDS$$,#{state_id_wa},-31.010506,116.761929), - ($$6603$$,$$LAKE NINAN$$,#{state_id_wa},-31.010506,116.761929), - ($$6603$$,$$MOCARDY$$,#{state_id_wa},-31.010506,116.761929), - ($$6603$$,$$WONGAN HILLS$$,#{state_id_wa},-31.010506,116.761929), - ($$6605$$,$$KONDUT$$,#{state_id_wa},-30.708747,116.699538), - ($$6606$$,$$BALLIDU$$,#{state_id_wa},-30.600411,116.772852), - ($$6606$$,$$EAST BALLIDU$$,#{state_id_wa},-30.600411,116.772852), - ($$6606$$,$$WEST BALLIDU$$,#{state_id_wa},-30.600411,116.772852), - ($$6608$$,$$EAST DAMBORING$$,#{state_id_wa},-30.47972,116.80611), - ($$6608$$,$$MARNE$$,#{state_id_wa},-30.47972,116.80611), - ($$6608$$,$$PITHARA$$,#{state_id_wa},-30.47972,116.80611), - ($$6609$$,$$DALWALLINU$$,#{state_id_wa},-30.27811,116.662428), - ($$6609$$,$$NUGADONG$$,#{state_id_wa},-30.27811,116.662428), - ($$6609$$,$$XANTIPPE$$,#{state_id_wa},-30.27811,116.662428), - ($$6612$$,$$JIBBERDING$$,#{state_id_wa},-30.023398,116.784823), - ($$6612$$,$$MIAMOON$$,#{state_id_wa},-30.023398,116.784823), - ($$6612$$,$$PAYNES FIND$$,#{state_id_wa},-30.023398,116.784823), - ($$6612$$,$$WUBIN$$,#{state_id_wa},-30.023398,116.784823), - ($$6613$$,$$BUNTINE$$,#{state_id_wa},-29.986601,116.571121), - ($$6614$$,$$MAYA$$,#{state_id_wa},-29.881696,116.502487), - ($$6616$$,$$LATHAM$$,#{state_id_wa},-29.756602,116.444694), - ($$6620$$,$$PERENJORI$$,#{state_id_wa},-29.443139,116.288189), - ($$6620$$,$$ROTHSAY$$,#{state_id_wa},-29.443139,116.288189), - ($$6623$$,$$BOWGADA$$,#{state_id_wa},-29.330125,116.152284), - ($$6623$$,$$BUNJIL$$,#{state_id_wa},-29.330125,116.152284), - ($$6623$$,$$GUTHA$$,#{state_id_wa},-29.330125,116.152284), - ($$6623$$,$$KOOLANOOKA$$,#{state_id_wa},-29.330125,116.152284), - ($$6623$$,$$MORAWA$$,#{state_id_wa},-29.330125,116.152284), - ($$6623$$,$$PINTHARUKA$$,#{state_id_wa},-29.330125,116.152284), - ($$6625$$,$$MERKANOOKA$$,#{state_id_wa},0.0,0.0), - ($$6627$$,$$CANNA$$,#{state_id_wa},-28.896856,115.861125), - ($$6628$$,$$TARDUN$$,#{state_id_wa},-28.792926,115.74985), - ($$6630$$,$$DEVILS CREEK$$,#{state_id_wa},-28.644761,115.478764), - ($$6630$$,$$MULLEWA$$,#{state_id_wa},-28.644761,115.478764), - ($$6630$$,$$MURCHISON$$,#{state_id_wa},-28.644761,115.478764), - ($$6630$$,$$NERRAMYNE$$,#{state_id_wa},-28.644761,115.478764), - ($$6630$$,$$NUNIERRA$$,#{state_id_wa},-28.644761,115.478764), - ($$6630$$,$$WEST CASUARINAS$$,#{state_id_wa},-28.644761,115.478764), - ($$6630$$,$$WONGOONDY$$,#{state_id_wa},-28.644761,115.478764), - ($$6630$$,$$WOOLGORONG$$,#{state_id_wa},-28.644761,115.478764), - ($$6631$$,$$PINDAR$$,#{state_id_wa},-28.477433,115.791146), - ($$6632$$,$$AMBANIA$$,#{state_id_wa},-28.695321,115.118798), - ($$6632$$,$$TENINDEWA$$,#{state_id_wa},-28.695321,115.118798), - ($$6635$$,$$SOUTH MURCHISON$$,#{state_id_wa},-27.316335,116.571226), - ($$6635$$,$$YALGOO$$,#{state_id_wa},-27.316335,116.571226), - ($$6638$$,$$COOLADAR HILL$$,#{state_id_wa},-28.4825,118.16833), - ($$6638$$,$$DAGGAR HILLS$$,#{state_id_wa},-28.4825,118.16833), - ($$6638$$,$$MOUNT MAGNET$$,#{state_id_wa},-28.4825,118.16833), - ($$6638$$,$$PAYNESVILLE$$,#{state_id_wa},-28.4825,118.16833), - ($$6639$$,$$SANDSTONE$$,#{state_id_wa},-28.204848,119.650272), - ($$6640$$,$$CUE$$,#{state_id_wa},-27.4231,117.898965), - ($$6640$$,$$EAST MURCHISON$$,#{state_id_wa},-27.4231,117.898965), - ($$6640$$,$$LAKE AUSTIN$$,#{state_id_wa},-27.4231,117.898965), - ($$6640$$,$$REEDY$$,#{state_id_wa},-27.4231,117.898965), - ($$6640$$,$$WELD RANGE$$,#{state_id_wa},-27.4231,117.898965), - ($$6642$$,$$ANGELO RIVER$$,#{state_id_wa},-24.078602,118.010422), - ($$6642$$,$$CAPRICORN$$,#{state_id_wa},-24.078602,118.010422), - ($$6642$$,$$KUMARINA$$,#{state_id_wa},-24.078602,118.010422), - ($$6642$$,$$MEEKATHARRA$$,#{state_id_wa},-24.078602,118.010422), - ($$6642$$,$$PEAK HILL$$,#{state_id_wa},-24.078602,118.010422), - ($$6646$$,$$LAKE CARNEGIE$$,#{state_id_wa},-26.344277,122.256427), - ($$6646$$,$$LITTLE SANDY DESERT$$,#{state_id_wa},-26.344277,122.256427), - ($$6646$$,$$WILUNA$$,#{state_id_wa},-26.344277,122.256427), - ($$6701$$,$$BABBAGE ISLAND$$,#{state_id_wa},-24.874403,113.632427), - ($$6701$$,$$BERNIER ISLAND$$,#{state_id_wa},-24.874403,113.632427), - ($$6701$$,$$BROCKMAN$$,#{state_id_wa},-24.874403,113.632427), - ($$6701$$,$$BROWN RANGE$$,#{state_id_wa},-24.874403,113.632427), - ($$6701$$,$$CARBLA$$,#{state_id_wa},-24.874403,113.632427), - ($$6701$$,$$CARNARVON$$,#{state_id_wa},-24.874403,113.632427), - ($$6701$$,$$CORAL BAY$$,#{state_id_wa},-24.874403,113.632427), - ($$6701$$,$$DORRE ISLAND$$,#{state_id_wa},-24.874403,113.632427), - ($$6701$$,$$EAST CARNARVON$$,#{state_id_wa},-24.874403,113.632427), - ($$6701$$,$$GILROYD$$,#{state_id_wa},-24.874403,113.632427), - ($$6701$$,$$GREYS PLAIN$$,#{state_id_wa},-24.874403,113.632427), - ($$6701$$,$$INGGARDA$$,#{state_id_wa},-24.874403,113.632427), - ($$6701$$,$$KENNEDY RANGE$$,#{state_id_wa},-24.874403,113.632427), - ($$6701$$,$$KINGSFORD$$,#{state_id_wa},-24.874403,113.632427), - ($$6701$$,$$LYNDON$$,#{state_id_wa},-24.874403,113.632427), - ($$6701$$,$$MACLEOD$$,#{state_id_wa},-24.874403,113.632427), - ($$6701$$,$$MASSEY BAY$$,#{state_id_wa},-24.874403,113.632427), - ($$6701$$,$$MINILYA$$,#{state_id_wa},-24.874403,113.632427), - ($$6701$$,$$MORGANTOWN$$,#{state_id_wa},-24.874403,113.632427), - ($$6701$$,$$NINGALOO$$,#{state_id_wa},-24.874403,113.632427), - ($$6701$$,$$NORTH PLANTATIONS$$,#{state_id_wa},-24.874403,113.632427), - ($$6701$$,$$SOUTH CARNARVON$$,#{state_id_wa},-24.874403,113.632427), - ($$6701$$,$$SOUTH PLANTATIONS$$,#{state_id_wa},-24.874403,113.632427), - ($$6701$$,$$TALISKER$$,#{state_id_wa},-24.874403,113.632427), - ($$6701$$,$$WOODLEIGH$$,#{state_id_wa},-24.874403,113.632427), - ($$6701$$,$$WOORAMEL$$,#{state_id_wa},-24.874403,113.632427), - ($$6701$$,$$YALARDY$$,#{state_id_wa},-24.874403,113.632427), - ($$6701$$,$$YANDOO CREEK$$,#{state_id_wa},-24.874403,113.632427), - ($$6705$$,$$EAST LYONS RIVER$$,#{state_id_wa},-24.22778,117.37583), - ($$6705$$,$$GASCOYNE JUNCTION$$,#{state_id_wa},-24.22778,117.37583), - ($$6705$$,$$GASCOYNE RIVER$$,#{state_id_wa},-24.22778,117.37583), - ($$6705$$,$$WEST LYONS RIVER$$,#{state_id_wa},-24.22778,117.37583), - ($$6707$$,$$CAPE RANGE NATIONAL PARK$$,#{state_id_wa},-22.158615,113.920333), - ($$6707$$,$$EXMOUTH$$,#{state_id_wa},-22.158615,113.920333), - ($$6707$$,$$EXMOUTH GULF$$,#{state_id_wa},-22.158615,113.920333), - ($$6707$$,$$LEARMONTH$$,#{state_id_wa},-22.158615,113.920333), - ($$6707$$,$$NORTH WEST CAPE$$,#{state_id_wa},-22.158615,113.920333), - ($$6710$$,$$CANE$$,#{state_id_wa},-22.095,115.85528), - ($$6710$$,$$ONSLOW$$,#{state_id_wa},-22.095,115.85528), - ($$6710$$,$$PEEDAMULLA$$,#{state_id_wa},-22.095,115.85528), - ($$6710$$,$$TALANDJI$$,#{state_id_wa},-22.095,115.85528), - ($$6710$$,$$YANNARIE$$,#{state_id_wa},-22.095,115.85528), - ($$6711$$,$$THEVENARD ISLAND$$,#{state_id_wa},-21.458,114.998249), - ($$6712$$,$$BARROW ISLAND$$,#{state_id_wa},-20.86322,115.407403), - ($$6713$$,$$DAMPIER$$,#{state_id_wa},-20.661726,116.707075), - ($$6713$$,$$DAMPIER ARCHIPELAGO$$,#{state_id_wa},-20.661726,116.707075), - ($$6714$$,$$ANTONYMYRE$$,#{state_id_wa},-20.663163,117.093371), - ($$6714$$,$$BALLA BALLA$$,#{state_id_wa},-20.663163,117.093371), - ($$6714$$,$$BAYNTON$$,#{state_id_wa},-20.663163,117.093371), - ($$6714$$,$$BULGARRA$$,#{state_id_wa},-20.663163,117.093371), - ($$6714$$,$$BURRUP$$,#{state_id_wa},-20.663163,117.093371), - ($$6714$$,$$CLEAVERVILLE$$,#{state_id_wa},-20.663163,117.093371), - ($$6714$$,$$COOYA POOYA$$,#{state_id_wa},-20.663163,117.093371), - ($$6714$$,$$GAP RIDGE$$,#{state_id_wa},-20.663163,117.093371), - ($$6714$$,$$GNOOREA$$,#{state_id_wa},-20.663163,117.093371), - ($$6714$$,$$KARRATHA$$,#{state_id_wa},-20.663163,117.093371), - ($$6714$$,$$KARRATHA INDUSTRIAL ESTATE$$,#{state_id_wa},-20.663163,117.093371), - ($$6714$$,$$MAITLAND$$,#{state_id_wa},-20.663163,117.093371), - ($$6714$$,$$MARDIE$$,#{state_id_wa},-20.663163,117.093371), - ($$6714$$,$$MILLARS WELL$$,#{state_id_wa},-20.663163,117.093371), - ($$6714$$,$$MOUNT ANKETELL$$,#{state_id_wa},-20.663163,117.093371), - ($$6714$$,$$MULATAGA$$,#{state_id_wa},-20.663163,117.093371), - ($$6714$$,$$NICKOL$$,#{state_id_wa},-20.663163,117.093371), - ($$6714$$,$$PEGS CREEK$$,#{state_id_wa},-20.663163,117.093371), - ($$6714$$,$$SHERLOCK$$,#{state_id_wa},-20.663163,117.093371), - ($$6714$$,$$STOVE HILL$$,#{state_id_wa},-20.663163,117.093371), - ($$6716$$,$$FORTESCUE$$,#{state_id_wa},-21.654285,116.129747), - ($$6716$$,$$HAMERSLEY RANGE$$,#{state_id_wa},-21.654285,116.129747), - ($$6716$$,$$MILLSTREAM$$,#{state_id_wa},-21.654285,116.129747), - ($$6716$$,$$PANNAWONICA$$,#{state_id_wa},-21.654285,116.129747), - ($$6718$$,$$ROEBOURNE$$,#{state_id_wa},-20.77542,117.146619), - ($$6718$$,$$WHIM CREEK$$,#{state_id_wa},-20.77542,117.146619), - ($$6720$$,$$COSSACK$$,#{state_id_wa},-20.67833,117.18861), - ($$6720$$,$$POINT SAMSON$$,#{state_id_wa},-20.67833,117.18861), - ($$6720$$,$$WICKHAM$$,#{state_id_wa},-20.67833,117.18861), - ($$6721$$,$$INDEE$$,#{state_id_wa},-20.786363,118.598409), - ($$6721$$,$$MUNDABULLANGANA$$,#{state_id_wa},-20.786363,118.598409), - ($$6721$$,$$PARDOO$$,#{state_id_wa},-20.786363,118.598409), - ($$6721$$,$$PORT HEDLAND$$,#{state_id_wa},-20.786363,118.598409), - ($$6721$$,$$REDBANK$$,#{state_id_wa},-20.786363,118.598409), - ($$6721$$,$$STRELLEY$$,#{state_id_wa},-20.786363,118.598409), - ($$6721$$,$$WALLAREENYA$$,#{state_id_wa},-20.786363,118.598409), - ($$6721$$,$$WEDGEFIELD$$,#{state_id_wa},-20.786363,118.598409), - ($$6722$$,$$BOODARIE$$,#{state_id_wa},-20.395562,118.572647), - ($$6722$$,$$DE GREY$$,#{state_id_wa},-20.395562,118.572647), - ($$6722$$,$$FINUCANE$$,#{state_id_wa},-20.395562,118.572647), - ($$6722$$,$$PIPPINGARRA$$,#{state_id_wa},-20.395562,118.572647), - ($$6722$$,$$SOUTH HEDLAND$$,#{state_id_wa},-20.395562,118.572647), - ($$6725$$,$$BILINGURR$$,#{state_id_wa},-17.914172,122.247317), - ($$6725$$,$$BROOME$$,#{state_id_wa},-17.914172,122.247317), - ($$6725$$,$$DAMPIER PENINSULA$$,#{state_id_wa},-17.914172,122.247317), - ($$6725$$,$$DJUGUN$$,#{state_id_wa},-17.914172,122.247317), - ($$6725$$,$$EIGHTY MILE BEACH$$,#{state_id_wa},-17.914172,122.247317), - ($$6725$$,$$GINGERAH$$,#{state_id_wa},-17.914172,122.247317), - ($$6725$$,$$LAGRANGE$$,#{state_id_wa},-17.914172,122.247317), - ($$6725$$,$$MINYIRR$$,#{state_id_wa},-17.914172,122.247317), - ($$6725$$,$$ROEBUCK$$,#{state_id_wa},-17.914172,122.247317), - ($$6725$$,$$WATERBANK$$,#{state_id_wa},-17.914172,122.247317), - ($$6726$$,$$CABLE BEACH$$,#{state_id_wa},-17.950181,122.196423), - ($$6728$$,$$CAMBALLIN$$,#{state_id_wa},-17.986747,124.178459), - ($$6728$$,$$DERBY$$,#{state_id_wa},-17.986747,124.178459), - ($$6728$$,$$GEEGULLY CREEK$$,#{state_id_wa},-17.986747,124.178459), - ($$6728$$,$$KIMBOLTON$$,#{state_id_wa},-17.986747,124.178459), - ($$6728$$,$$KING LEOPOLD RANGES$$,#{state_id_wa},-17.986747,124.178459), - ($$6728$$,$$MEDA$$,#{state_id_wa},-17.986747,124.178459), - ($$6728$$,$$ST GEORGE RANGES$$,#{state_id_wa},-17.986747,124.178459), - ($$6728$$,$$WILLARE$$,#{state_id_wa},-17.986747,124.178459), - ($$6731$$,$$COCKATOO ISLAND$$,#{state_id_wa},-16.094014,123.612159), - ($$6733$$,$$KOOLAN ISLAND$$,#{state_id_wa},-16.132888,123.740164), - ($$6740$$,$$DRYSDALE RIVER$$,#{state_id_wa},-14.910744,126.666239), - ($$6740$$,$$KALUMBURU$$,#{state_id_wa},-14.910744,126.666239), - ($$6740$$,$$MITCHELL PLATEAU$$,#{state_id_wa},-14.910744,126.666239), - ($$6740$$,$$OOMBULGURRI$$,#{state_id_wa},-14.910744,126.666239), - ($$6740$$,$$PRINCE REGENT RIVER$$,#{state_id_wa},-14.910744,126.666239), - ($$6740$$,$$WYNDHAM$$,#{state_id_wa},-14.910744,126.666239), - ($$6743$$,$$CAMBRIDGE GULF$$,#{state_id_wa},-15.174208,128.550721), - ($$6743$$,$$DURACK$$,#{state_id_wa},-15.174208,128.550721), - ($$6743$$,$$GIBB$$,#{state_id_wa},-15.174208,128.550721), - ($$6743$$,$$KUNUNURRA$$,#{state_id_wa},-15.174208,128.550721), - ($$6743$$,$$LAKE ARGYLE$$,#{state_id_wa},-15.174208,128.550721), - ($$6743$$,$$WARMUN$$,#{state_id_wa},-15.174208,128.550721), - ($$6751$$,$$CHICHESTER$$,#{state_id_wa},-21.905,118.09056), - ($$6751$$,$$INNAWANGA$$,#{state_id_wa},-21.905,118.09056), - ($$6751$$,$$JUNA DOWNS$$,#{state_id_wa},-21.905,118.09056), - ($$6751$$,$$KARIJINI$$,#{state_id_wa},-21.905,118.09056), - ($$6751$$,$$MOUNT SHEILA$$,#{state_id_wa},-21.905,118.09056), - ($$6751$$,$$MULGA DOWNS$$,#{state_id_wa},-21.905,118.09056), - ($$6751$$,$$NANUTARRA$$,#{state_id_wa},-21.905,118.09056), - ($$6751$$,$$ROCKLEA$$,#{state_id_wa},-21.905,118.09056), - ($$6751$$,$$TOM PRICE$$,#{state_id_wa},-21.905,118.09056), - ($$6751$$,$$WITTENOOM$$,#{state_id_wa},-21.905,118.09056), - ($$6753$$,$$NEWMAN$$,#{state_id_wa},-23.357288,119.737169), - ($$6754$$,$$PARABURDOO$$,#{state_id_wa},-23.168811,117.74817), - ($$6758$$,$$NULLAGINE$$,#{state_id_wa},-21.91096,120.197457), - ($$6760$$,$$MARBLE BAR$$,#{state_id_wa},-21.171437,119.743907), - ($$6762$$,$$TELFER$$,#{state_id_wa},-21.713675,122.230159), - ($$6765$$,$$FITZROY CROSSING$$,#{state_id_wa},-18.194162,125.568887), - ($$6765$$,$$MOUNT HARDMAN$$,#{state_id_wa},-18.194162,125.568887), - ($$6770$$,$$HALLS CREEK$$,#{state_id_wa},-18.224393,127.66743), - ($$6770$$,$$MCBEATH$$,#{state_id_wa},-18.224393,127.66743), - ($$6770$$,$$MUELLER RANGES$$,#{state_id_wa},-18.224393,127.66743), - ($$6770$$,$$ORD RIVER$$,#{state_id_wa},-18.224393,127.66743), - ($$6770$$,$$PURNULULU$$,#{state_id_wa},-18.224393,127.66743), - ($$6770$$,$$STURT CREEK$$,#{state_id_wa},-18.224393,127.66743), - ($$6770$$,$$TANAMI$$,#{state_id_wa},-18.224393,127.66743), - ($$6798$$,$$CHRISTMAS ISLAND$$,#{state_id_wa},-10.487053,105.64067), - ($$6799$$,$$HOME ISLAND COCOS (KEELING) ISLANDS$$,#{state_id_wa},-12.169719,96.83152), - ($$6799$$,$$WEST ISLAND COCOS (KEELING) ISLANDS$$,#{state_id_wa},-12.169719,96.83152), - ($$6800$$,$$PERTH$$,#{state_id_wa},-31.99212,115.763228), - ($$6803$$,$$NORTHBRIDGE$$,#{state_id_wa},0.0,0.0), - ($$6809$$,$$PERTH$$,#{state_id_wa},-31.99212,115.763228), - ($$6817$$,$$PERTH$$,#{state_id_wa},-31.99212,115.763228), - ($$6820$$,$$PERTH$$,#{state_id_wa},-31.99212,115.763228), - ($$6827$$,$$PERTH$$,#{state_id_wa},-31.99212,115.763228), - ($$6830$$,$$PERTH$$,#{state_id_wa},-31.99212,115.763228), - ($$6831$$,$$PERTH ST GEORGES TCE$$,#{state_id_wa},-31.95505,115.857527), - ($$6832$$,$$PERTH ADELAIDE TCE$$,#{state_id_wa},-31.958153,115.866732), - ($$6837$$,$$PERTH$$,#{state_id_wa},-31.99212,115.763228), - ($$6838$$,$$PERTH$$,#{state_id_wa},-31.99212,115.763228), - ($$6839$$,$$PERTH$$,#{state_id_wa},-31.99212,115.763228), - ($$6840$$,$$PERTH$$,#{state_id_wa},-31.99212,115.763228), - ($$6841$$,$$PERTH$$,#{state_id_wa},-31.99212,115.763228), - ($$6842$$,$$PERTH$$,#{state_id_wa},-31.99212,115.763228), - ($$6843$$,$$PERTH$$,#{state_id_wa},-31.99212,115.763228), - ($$6844$$,$$PERTH$$,#{state_id_wa},-31.99212,115.763228), - ($$6845$$,$$PERTH$$,#{state_id_wa},-31.99212,115.763228), - ($$6846$$,$$PERTH$$,#{state_id_wa},-31.99212,115.763228), - ($$6847$$,$$PERTH$$,#{state_id_wa},-31.99212,115.763228), - ($$6848$$,$$PERTH$$,#{state_id_wa},-31.99212,115.763228), - ($$6849$$,$$PERTH BC$$,#{state_id_wa},0.0,0.0), - ($$6850$$,$$CLOISTERS SQUARE PO$$,#{state_id_wa},-33.663414,115.332906), - ($$6865$$,$$NORTHBRIDGE$$,#{state_id_wa},0.0,0.0), - ($$6872$$,$$WEST PERTH$$,#{state_id_wa},-31.94319,115.876395), - ($$6892$$,$$EAST PERTH$$,#{state_id_wa},-31.777103,115.817894), - ($$6900$$,$$LEEDERVILLE$$,#{state_id_wa},0.0,0.0), - ($$6901$$,$$WEST LEEDERVILLE$$,#{state_id_wa},-31.944548,115.875924), - ($$6902$$,$$LEEDERVILLE$$,#{state_id_wa},0.0,0.0), - ($$6903$$,$$LEEDERVILLE$$,#{state_id_wa},0.0,0.0), - ($$6904$$,$$SUBIACO PO$$,#{state_id_wa},-31.943654,115.834628), - ($$6905$$,$$NORTHLANDS PO$$,#{state_id_wa},0.0,0.0), - ($$6906$$,$$NORTH PERTH$$,#{state_id_wa},-31.982001,115.760961), - ($$6907$$,$$NEDLANDS$$,#{state_id_wa},0.0,0.0), - ($$6909$$,$$NEDLANDS$$,#{state_id_wa},0.0,0.0), - ($$6910$$,$$CLAREMONT$$,#{state_id_wa},-31.981493,115.774504), - ($$6911$$,$$COTTESLOE$$,#{state_id_wa},-32.403407,115.761982), - ($$6912$$,$$MOSMAN PARK$$,#{state_id_wa},-32.011748,115.763135), - ($$6913$$,$$WEMBLEY$$,#{state_id_wa},0.0,0.0), - ($$6914$$,$$BALCATTA$$,#{state_id_wa},-31.861596,115.815211), - ($$6915$$,$$MOUNT HAWTHORN$$,#{state_id_wa},-31.954127,115.848262), - ($$6916$$,$$OSBORNE PARK$$,#{state_id_wa},-31.885883,115.804802), - ($$6916$$,$$OSBORNE PARK DC$$,#{state_id_wa},-31.885883,115.804802), - ($$6917$$,$$OSBORNE PARK$$,#{state_id_wa},-31.885883,115.804802), - ($$6918$$,$$INNALOO$$,#{state_id_wa},0.0,0.0), - ($$6919$$,$$JOONDALUP DC$$,#{state_id_wa},0.0,0.0), - ($$6920$$,$$NORTH BEACH$$,#{state_id_wa},-31.859408,115.775442), - ($$6921$$,$$KARRINYUP$$,#{state_id_wa},-31.882332,115.791881), - ($$6922$$,$$SCARBOROUGH$$,#{state_id_wa},-31.901385,115.79501), - ($$6923$$,$$HILLARYS$$,#{state_id_wa},0.0,0.0), - ($$6924$$,$$GREENWOOD$$,#{state_id_wa},-32.559825,115.797347), - ($$6925$$,$$WALLISTON DC$$,#{state_id_wa},0.0,0.0), - ($$6926$$,$$KALAMUNDA$$,#{state_id_wa},-31.939027,116.012518), - ($$6929$$,$$MOUNT LAWLEY$$,#{state_id_wa},-31.954127,115.848262), - ($$6931$$,$$MAYLANDS$$,#{state_id_wa},0.0,0.0), - ($$6932$$,$$INGLEWOOD$$,#{state_id_wa},-31.923239,115.882764), - ($$6933$$,$$BAYSWATER$$,#{state_id_wa},-31.907152,115.896545), - ($$6934$$,$$BASSENDEAN$$,#{state_id_wa},-31.915103,115.921101), - ($$6935$$,$$GUILDFORD$$,#{state_id_wa},-31.923723,115.921132), - ($$6936$$,$$MIDLAND DC$$,#{state_id_wa},0.0,0.0), - ($$6937$$,$$TUART HILL$$,#{state_id_wa},-31.896748,115.846807), - ($$6938$$,$$TUART HILL$$,#{state_id_wa},-31.896748,115.846807), - ($$6939$$,$$TUART HILL$$,#{state_id_wa},-31.896748,115.846807), - ($$6940$$,$$TUART HILL$$,#{state_id_wa},-31.896748,115.846807), - ($$6941$$,$$MIRRABOOKA$$,#{state_id_wa},-31.842045,115.855186), - ($$6942$$,$$BASSENDEAN DC$$,#{state_id_wa},0.0,0.0), - ($$6943$$,$$MORLEY$$,#{state_id_wa},-31.887921,115.89292), - ($$6944$$,$$MALAGA$$,#{state_id_wa},-31.864862,115.895533), - ($$6945$$,$$MALAGA DC$$,#{state_id_wa},0.0,0.0), - ($$6946$$,$$WANNEROO$$,#{state_id_wa},-31.617559,115.715081), - ($$6947$$,$$WANGARA DC$$,#{state_id_wa},0.0,0.0), - ($$6951$$,$$SOUTH PERTH$$,#{state_id_wa},-32.068705,115.821722), - ($$6952$$,$$COMO$$,#{state_id_wa},-31.891696,115.817919), - ($$6953$$,$$APPLECROSS$$,#{state_id_wa},0.0,0.0), - ($$6954$$,$$BOORAGOON$$,#{state_id_wa},0.0,0.0), - ($$6955$$,$$WILLETTON$$,#{state_id_wa},0.0,0.0), - ($$6956$$,$$MELVILLE$$,#{state_id_wa},-31.988065,115.853588), - ($$6957$$,$$PALMYRA$$,#{state_id_wa},0.0,0.0), - ($$6958$$,$$ROYAL AUSTRALIAN NAVY WARSHIPS$$,#{state_id_wa},0.0,0.0), - ($$6959$$,$$FREMANTLE$$,#{state_id_wa},-32.067114,115.986396), - ($$6960$$,$$MYAREE BC$$,#{state_id_wa},0.0,0.0), - ($$6961$$,$$PALMYRA DC$$,#{state_id_wa},0.0,0.0), - ($$6963$$,$$HAMILTON HILL$$,#{state_id_wa},-32.076945,115.788706), - ($$6964$$,$$SUCCESS$$,#{state_id_wa},-32.022746,115.860179), - ($$6965$$,$$BIBRA LAKE DC$$,#{state_id_wa},0.0,0.0), - ($$6966$$,$$KWINANA$$,#{state_id_wa},-32.143195,115.859834), - ($$6967$$,$$ROCKINGHAM DC$$,#{state_id_wa},0.0,0.0), - ($$6968$$,$$ROCKINGHAM$$,#{state_id_wa},-32.107766,115.782689), - ($$6969$$,$$ROCKINGHAM BEACH$$,#{state_id_wa},-32.264021,115.745072), - ($$6970$$,$$CANNING VALE DC$$,#{state_id_wa},0.0,0.0), - ($$6979$$,$$VICTORIA PARK$$,#{state_id_wa},-31.954583,115.896195), - ($$6980$$,$$CANNINGTON$$,#{state_id_wa},0.0,0.0), - ($$6981$$,$$EAST VICTORIA PARK$$,#{state_id_wa},-31.951832,115.876964), - ($$6982$$,$$BENTLEY$$,#{state_id_wa},-31.869817,116.169125), - ($$6983$$,$$BENTLEY DC$$,#{state_id_wa},0.0,0.0), - ($$6984$$,$$BELMONT$$,#{state_id_wa},-31.965766,115.933458), - ($$6985$$,$$CLOVERDALE$$,#{state_id_wa},-33.625285,115.600155), - ($$6986$$,$$WELSHPOOL DC$$,#{state_id_wa},0.0,0.0), - ($$6987$$,$$CANNINGTON$$,#{state_id_wa},0.0,0.0), - ($$6988$$,$$THORNLIE$$,#{state_id_wa},-32.050009,115.964764), - ($$6989$$,$$MADDINGTON$$,#{state_id_wa},-32.039597,116.009575), - ($$6990$$,$$GOSNELLS$$,#{state_id_wa},-32.060407,116.008461), - ($$6991$$,$$KELMSCOTT$$,#{state_id_wa},0.0,0.0), - ($$6992$$,$$ARMADALE$$,#{state_id_wa},-31.964503,115.920395), - ($$6997$$,$$KELMSCOTT DC$$,#{state_id_wa},0.0,0.0), - ($$7000$$,$$BATHURST STREET PO$$,#{state_id_tas},-42.874031,147.326354), - ($$7000$$,$$GLEBE$$,#{state_id_tas},-42.874031,147.326354), - ($$7000$$,$$HOBART$$,#{state_id_tas},-42.874031,147.326354), - ($$7000$$,$$MOUNT STUART$$,#{state_id_tas},-42.874031,147.326354), - ($$7000$$,$$NORTH HOBART$$,#{state_id_tas},-42.874031,147.326354), - ($$7000$$,$$QUEENS DOMAIN$$,#{state_id_tas},-42.874031,147.326354), - ($$7000$$,$$WEST HOBART$$,#{state_id_tas},-42.874031,147.326354), - ($$7001$$,$$HOBART$$,#{state_id_tas},-42.837499,147.506162), - ($$7002$$,$$NORTH HOBART$$,#{state_id_tas},-42.899691,147.446349), - ($$7004$$,$$BATTERY POINT$$,#{state_id_tas},-42.892767,147.333242), - ($$7004$$,$$SOUTH HOBART$$,#{state_id_tas},-42.892767,147.333242), - ($$7005$$,$$DYNNYRNE$$,#{state_id_tas},-42.901018,147.314131), - ($$7005$$,$$LOWER SANDY BAY$$,#{state_id_tas},-42.901018,147.314131), - ($$7005$$,$$SANDY BAY$$,#{state_id_tas},-42.901018,147.314131), - ($$7005$$,$$UNIVERSITY OF TASMANIA$$,#{state_id_tas},-42.901018,147.314131), - ($$7006$$,$$SANDY BAY$$,#{state_id_tas},-42.908108,147.344395), - ($$7007$$,$$MOUNT NELSON$$,#{state_id_tas},-42.92092,147.32303), - ($$7007$$,$$TOLMANS HILL$$,#{state_id_tas},-42.92092,147.32303), - ($$7008$$,$$LENAH VALLEY$$,#{state_id_tas},-42.865842,147.27797), - ($$7008$$,$$NEW TOWN$$,#{state_id_tas},-42.865842,147.27797), - ($$7009$$,$$DERWENT PARK$$,#{state_id_tas},-42.835352,147.291007), - ($$7009$$,$$LUTANA$$,#{state_id_tas},-42.835352,147.291007), - ($$7009$$,$$MOONAH$$,#{state_id_tas},-42.835352,147.291007), - ($$7009$$,$$WEST MOONAH$$,#{state_id_tas},-42.835352,147.291007), - ($$7010$$,$$DOWSING POINT$$,#{state_id_tas},-42.822986,147.302899), - ($$7010$$,$$GLENORCHY$$,#{state_id_tas},-42.822986,147.302899), - ($$7010$$,$$GOODWOOD$$,#{state_id_tas},-42.822986,147.302899), - ($$7010$$,$$MONTROSE$$,#{state_id_tas},-42.822986,147.302899), - ($$7010$$,$$ROSETTA$$,#{state_id_tas},-42.822986,147.302899), - ($$7011$$,$$AUSTINS FERRY$$,#{state_id_tas},-42.766405,147.243418), - ($$7011$$,$$BERRIEDALE$$,#{state_id_tas},-42.766405,147.243418), - ($$7011$$,$$CHIGWELL$$,#{state_id_tas},-42.766405,147.243418), - ($$7011$$,$$CLAREMONT$$,#{state_id_tas},-42.766405,147.243418), - ($$7012$$,$$COLLINSVALE$$,#{state_id_tas},-42.842698,147.189242), - ($$7012$$,$$GLENLUSK$$,#{state_id_tas},-42.842698,147.189242), - ($$7015$$,$$GEILSTON BAY$$,#{state_id_tas},-42.836405,147.350958), - ($$7015$$,$$LINDISFARNE$$,#{state_id_tas},-42.836405,147.350958), - ($$7015$$,$$ROSE BAY$$,#{state_id_tas},-42.836405,147.350958), - ($$7016$$,$$RISDON VALE$$,#{state_id_tas},-42.815584,147.378336), - ($$7017$$,$$GRASSTREE HILL$$,#{state_id_tas},-42.782045,147.361328), - ($$7017$$,$$HONEYWOOD$$,#{state_id_tas},-42.782045,147.361328), - ($$7017$$,$$OLD BEACH$$,#{state_id_tas},-42.782045,147.361328), - ($$7017$$,$$OTAGO$$,#{state_id_tas},-42.782045,147.361328), - ($$7017$$,$$RISDON$$,#{state_id_tas},-42.782045,147.361328), - ($$7017$$,$$TEA TREE$$,#{state_id_tas},-42.782045,147.361328), - ($$7018$$,$$BELLERIVE$$,#{state_id_tas},-42.874817,147.367625), - ($$7018$$,$$HOWRAH$$,#{state_id_tas},-42.874817,147.367625), - ($$7018$$,$$MONTAGU BAY$$,#{state_id_tas},-42.874817,147.367625), - ($$7018$$,$$MORNINGTON$$,#{state_id_tas},-42.874817,147.367625), - ($$7018$$,$$ROSNY$$,#{state_id_tas},-42.874817,147.367625), - ($$7018$$,$$ROSNY PARK$$,#{state_id_tas},-42.874817,147.367625), - ($$7018$$,$$TRANMERE$$,#{state_id_tas},-42.874817,147.367625), - ($$7018$$,$$WARRANE$$,#{state_id_tas},-42.874817,147.367625), - ($$7019$$,$$CLARENDON VALE$$,#{state_id_tas},-42.893193,147.44655), - ($$7019$$,$$OAKDOWNS$$,#{state_id_tas},-42.893193,147.44655), - ($$7019$$,$$ROKEBY$$,#{state_id_tas},-42.893193,147.44655), - ($$7020$$,$$CLIFTON BEACH$$,#{state_id_tas},-42.98954,147.521516), - ($$7020$$,$$SANDFORD$$,#{state_id_tas},-42.98954,147.521516), - ($$7021$$,$$LAUDERDALE$$,#{state_id_tas},-42.904736,147.477128), - ($$7022$$,$$SOUTH ARM$$,#{state_id_tas},-43.028461,147.41684), - ($$7023$$,$$OPOSSUM BAY$$,#{state_id_tas},-42.990339,147.402212), - ($$7024$$,$$CREMORNE$$,#{state_id_tas},-42.951952,147.522229), - ($$7025$$,$$DULCOT$$,#{state_id_tas},-42.787354,147.416285), - ($$7025$$,$$RICHMOND$$,#{state_id_tas},-42.787354,147.416285), - ($$7026$$,$$CAMPANIA$$,#{state_id_tas},-42.664221,147.421446), - ($$7027$$,$$COLEBROOK$$,#{state_id_tas},-42.534353,147.364102), - ($$7030$$,$$APSLEY$$,#{state_id_tas},-42.41812,147.138308), - ($$7030$$,$$ARTHURS LAKE$$,#{state_id_tas},-42.41812,147.138308), - ($$7030$$,$$BAGDAD$$,#{state_id_tas},-42.41812,147.138308), - ($$7030$$,$$BAGDAD NORTH$$,#{state_id_tas},-42.41812,147.138308), - ($$7030$$,$$BOTHWELL$$,#{state_id_tas},-42.41812,147.138308), - ($$7030$$,$$BRIDGEWATER$$,#{state_id_tas},-42.41812,147.138308), - ($$7030$$,$$BRIGHTON$$,#{state_id_tas},-42.41812,147.138308), - ($$7030$$,$$BROADMARSH$$,#{state_id_tas},-42.41812,147.138308), - ($$7030$$,$$CRAMPS BAY$$,#{state_id_tas},-42.41812,147.138308), - ($$7030$$,$$DROMEDARY$$,#{state_id_tas},-42.41812,147.138308), - ($$7030$$,$$DYSART$$,#{state_id_tas},-42.41812,147.138308), - ($$7030$$,$$ELDERSLIE$$,#{state_id_tas},-42.41812,147.138308), - ($$7030$$,$$FLINTSTONE$$,#{state_id_tas},-42.41812,147.138308), - ($$7030$$,$$GAGEBROOK$$,#{state_id_tas},-42.41812,147.138308), - ($$7030$$,$$GRANTON$$,#{state_id_tas},-42.41812,147.138308), - ($$7030$$,$$HERDSMANS COVE$$,#{state_id_tas},-42.41812,147.138308), - ($$7030$$,$$HERMITAGE$$,#{state_id_tas},-42.41812,147.138308), - ($$7030$$,$$INTERLAKEN$$,#{state_id_tas},-42.41812,147.138308), - ($$7030$$,$$JERICHO$$,#{state_id_tas},-42.41812,147.138308), - ($$7030$$,$$KEMPTON$$,#{state_id_tas},-42.41812,147.138308), - ($$7030$$,$$LAKE SORELL$$,#{state_id_tas},-42.41812,147.138308), - ($$7030$$,$$LIAWENEE$$,#{state_id_tas},-42.41812,147.138308), - ($$7030$$,$$LOWER MARSHES$$,#{state_id_tas},-42.41812,147.138308), - ($$7030$$,$$MANGALORE$$,#{state_id_tas},-42.41812,147.138308), - ($$7030$$,$$MELTON MOWBRAY$$,#{state_id_tas},-42.41812,147.138308), - ($$7030$$,$$MIENA$$,#{state_id_tas},-42.41812,147.138308), - ($$7030$$,$$MILLERS BLUFF$$,#{state_id_tas},-42.41812,147.138308), - ($$7030$$,$$MORASS BAY$$,#{state_id_tas},-42.41812,147.138308), - ($$7030$$,$$PELHAM$$,#{state_id_tas},-42.41812,147.138308), - ($$7030$$,$$PONTVILLE$$,#{state_id_tas},-42.41812,147.138308), - ($$7030$$,$$SHANNON$$,#{state_id_tas},-42.41812,147.138308), - ($$7030$$,$$STEPPES$$,#{state_id_tas},-42.41812,147.138308), - ($$7030$$,$$TODS CORNER$$,#{state_id_tas},-42.41812,147.138308), - ($$7030$$,$$WADDAMANA$$,#{state_id_tas},-42.41812,147.138308), - ($$7030$$,$$WILBURVILLE$$,#{state_id_tas},-42.41812,147.138308), - ($$7050$$,$$ALBION HEIGHTS$$,#{state_id_tas},-42.956086,147.322982), - ($$7050$$,$$KINGSTON$$,#{state_id_tas},-42.956086,147.322982), - ($$7050$$,$$KINGSTON BEACH$$,#{state_id_tas},-42.956086,147.322982), - ($$7051$$,$$KINGSTON$$,#{state_id_tas},-42.987394,147.327238), - ($$7052$$,$$BLACKMANS BAY$$,#{state_id_tas},-42.995485,147.322456), - ($$7053$$,$$BONNET HILL$$,#{state_id_tas},-42.972024,147.331126), - ($$7053$$,$$TAROONA$$,#{state_id_tas},-42.972024,147.331126), - ($$7054$$,$$BARRETTA$$,#{state_id_tas},-43.045835,147.264843), - ($$7054$$,$$CONINGHAM$$,#{state_id_tas},-43.045835,147.264843), - ($$7054$$,$$ELECTRONA$$,#{state_id_tas},-43.045835,147.264843), - ($$7054$$,$$FERN TREE$$,#{state_id_tas},-43.045835,147.264843), - ($$7054$$,$$HOWDEN$$,#{state_id_tas},-43.045835,147.264843), - ($$7054$$,$$LESLIE VALE$$,#{state_id_tas},-43.045835,147.264843), - ($$7054$$,$$LOWER SNUG$$,#{state_id_tas},-43.045835,147.264843), - ($$7054$$,$$MARGATE$$,#{state_id_tas},-43.045835,147.264843), - ($$7054$$,$$NEIKA$$,#{state_id_tas},-43.045835,147.264843), - ($$7054$$,$$RIDGEWAY$$,#{state_id_tas},-43.045835,147.264843), - ($$7054$$,$$SNUG$$,#{state_id_tas},-43.045835,147.264843), - ($$7054$$,$$TINDERBOX$$,#{state_id_tas},-43.045835,147.264843), - ($$7054$$,$$WELLINGTON PARK$$,#{state_id_tas},-43.045835,147.264843), - ($$7055$$,$$HUNTINGFIELD$$,#{state_id_tas},-42.991603,147.289063), - ($$7109$$,$$CRABTREE$$,#{state_id_tas},-42.963329,147.078587), - ($$7109$$,$$CRADOC$$,#{state_id_tas},-42.963329,147.078587), - ($$7109$$,$$GLAZIERS BAY$$,#{state_id_tas},-42.963329,147.078587), - ($$7109$$,$$GLEN HUON$$,#{state_id_tas},-42.963329,147.078587), - ($$7109$$,$$GLENDEVIE$$,#{state_id_tas},-42.963329,147.078587), - ($$7109$$,$$GROVE$$,#{state_id_tas},-42.963329,147.078587), - ($$7109$$,$$HASTINGS$$,#{state_id_tas},-42.963329,147.078587), - ($$7109$$,$$HUONVILLE$$,#{state_id_tas},-42.963329,147.078587), - ($$7109$$,$$IDA BAY$$,#{state_id_tas},-42.963329,147.078587), - ($$7109$$,$$JUDBURY$$,#{state_id_tas},-42.963329,147.078587), - ($$7109$$,$$LONNAVALE$$,#{state_id_tas},-42.963329,147.078587), - ($$7109$$,$$LOWER LONGLEY$$,#{state_id_tas},-42.963329,147.078587), - ($$7109$$,$$LOWER WATTLE GROVE$$,#{state_id_tas},-42.963329,147.078587), - ($$7109$$,$$LUCASTON$$,#{state_id_tas},-42.963329,147.078587), - ($$7109$$,$$LUNE RIVER$$,#{state_id_tas},-42.963329,147.078587), - ($$7109$$,$$LYMINGTON$$,#{state_id_tas},-42.963329,147.078587), - ($$7109$$,$$MOUNTAIN RIVER$$,#{state_id_tas},-42.963329,147.078587), - ($$7109$$,$$PETCHEYS BAY$$,#{state_id_tas},-42.963329,147.078587), - ($$7109$$,$$RAMINEA$$,#{state_id_tas},-42.963329,147.078587), - ($$7109$$,$$RANELAGH$$,#{state_id_tas},-42.963329,147.078587), - ($$7109$$,$$RECHERCHE$$,#{state_id_tas},-42.963329,147.078587), - ($$7109$$,$$SOUTHPORT$$,#{state_id_tas},-42.963329,147.078587), - ($$7109$$,$$SOUTHPORT LAGOON$$,#{state_id_tas},-42.963329,147.078587), - ($$7109$$,$$STRATHBLANE$$,#{state_id_tas},-42.963329,147.078587), - ($$7109$$,$$WATERLOO$$,#{state_id_tas},-42.963329,147.078587), - ($$7109$$,$$WATTLE GROVE$$,#{state_id_tas},-42.963329,147.078587), - ($$7109$$,$$WOODSTOCK$$,#{state_id_tas},-42.963329,147.078587), - ($$7112$$,$$ABELS BAY$$,#{state_id_tas},-43.23333,147.098032), - ($$7112$$,$$CHARLOTTE COVE$$,#{state_id_tas},-43.23333,147.098032), - ($$7112$$,$$CYGNET$$,#{state_id_tas},-43.23333,147.098032), - ($$7112$$,$$DEEP BAY$$,#{state_id_tas},-43.23333,147.098032), - ($$7112$$,$$EGGS AND BACON BAY$$,#{state_id_tas},-43.23333,147.098032), - ($$7112$$,$$GARDEN ISLAND CREEK$$,#{state_id_tas},-43.23333,147.098032), - ($$7112$$,$$GARDNERS BAY$$,#{state_id_tas},-43.23333,147.098032), - ($$7112$$,$$NICHOLLS RIVULET$$,#{state_id_tas},-43.23333,147.098032), - ($$7112$$,$$RANDALLS BAY$$,#{state_id_tas},-43.23333,147.098032), - ($$7112$$,$$VERONA SANDS$$,#{state_id_tas},-43.23333,147.098032), - ($$7113$$,$$FRANKLIN$$,#{state_id_tas},-43.089828,147.010508), - ($$7116$$,$$BROOKS BAY$$,#{state_id_tas},-43.232823,147.025877), - ($$7116$$,$$CAIRNS BAY$$,#{state_id_tas},-43.232823,147.025877), - ($$7116$$,$$CASTLE FORBES BAY$$,#{state_id_tas},-43.232823,147.025877), - ($$7116$$,$$GEEVESTON$$,#{state_id_tas},-43.232823,147.025877), - ($$7116$$,$$POLICE POINT$$,#{state_id_tas},-43.232823,147.025877), - ($$7116$$,$$PORT HUON$$,#{state_id_tas},-43.232823,147.025877), - ($$7116$$,$$SURGES BAY$$,#{state_id_tas},-43.232823,147.025877), - ($$7116$$,$$SURVEYORS BAY$$,#{state_id_tas},-43.232823,147.025877), - ($$7117$$,$$DOVER$$,#{state_id_tas},-43.314133,147.015191), - ($$7119$$,$$STONOR$$,#{state_id_tas},-42.397107,147.375552), - ($$7120$$,$$ANDOVER$$,#{state_id_tas},-42.329996,147.460643), - ($$7120$$,$$ANTILL PONDS$$,#{state_id_tas},-42.329996,147.460643), - ($$7120$$,$$BADEN$$,#{state_id_tas},-42.329996,147.460643), - ($$7120$$,$$LEMONT$$,#{state_id_tas},-42.329996,147.460643), - ($$7120$$,$$LEVENDALE$$,#{state_id_tas},-42.329996,147.460643), - ($$7120$$,$$MOUNT SEYMOUR$$,#{state_id_tas},-42.329996,147.460643), - ($$7120$$,$$OATLANDS$$,#{state_id_tas},-42.329996,147.460643), - ($$7120$$,$$PARATTAH$$,#{state_id_tas},-42.329996,147.460643), - ($$7120$$,$$PAWTELLA$$,#{state_id_tas},-42.329996,147.460643), - ($$7120$$,$$RHYNDASTON$$,#{state_id_tas},-42.329996,147.460643), - ($$7120$$,$$STONEHENGE$$,#{state_id_tas},-42.329996,147.460643), - ($$7120$$,$$SWANSTON$$,#{state_id_tas},-42.329996,147.460643), - ($$7120$$,$$TIBERIAS$$,#{state_id_tas},-42.329996,147.460643), - ($$7120$$,$$TUNBRIDGE$$,#{state_id_tas},-42.329996,147.460643), - ($$7120$$,$$TUNNACK$$,#{state_id_tas},-42.329996,147.460643), - ($$7120$$,$$WHITEFOORD$$,#{state_id_tas},-42.329996,147.460643), - ($$7120$$,$$WOODBURY$$,#{state_id_tas},-42.329996,147.460643), - ($$7120$$,$$WOODSDALE$$,#{state_id_tas},-42.329996,147.460643), - ($$7120$$,$$YORK PLAINS$$,#{state_id_tas},-42.329996,147.460643), - ($$7139$$,$$STRATHGORDON$$,#{state_id_tas},0.0,0.0), - ($$7140$$,$$BLACK HILLS$$,#{state_id_tas},-42.726483,147.030188), - ($$7140$$,$$BOYER$$,#{state_id_tas},-42.726483,147.030188), - ($$7140$$,$$BRADYS LAKE$$,#{state_id_tas},-42.726483,147.030188), - ($$7140$$,$$BRONTE PARK$$,#{state_id_tas},-42.726483,147.030188), - ($$7140$$,$$BUSHY PARK$$,#{state_id_tas},-42.726483,147.030188), - ($$7140$$,$$BUTLERS GORGE$$,#{state_id_tas},-42.726483,147.030188), - ($$7140$$,$$DEE$$,#{state_id_tas},-42.726483,147.030188), - ($$7140$$,$$DERWENT BRIDGE$$,#{state_id_tas},-42.726483,147.030188), - ($$7140$$,$$ELLENDALE$$,#{state_id_tas},-42.726483,147.030188), - ($$7140$$,$$FENTONBURY$$,#{state_id_tas},-42.726483,147.030188), - ($$7140$$,$$FITZGERALD$$,#{state_id_tas},-42.726483,147.030188), - ($$7140$$,$$FLORENTINE$$,#{state_id_tas},-42.726483,147.030188), - ($$7140$$,$$GLENFERN$$,#{state_id_tas},-42.726483,147.030188), - ($$7140$$,$$GLENORA$$,#{state_id_tas},-42.726483,147.030188), - ($$7140$$,$$GRETNA$$,#{state_id_tas},-42.726483,147.030188), - ($$7140$$,$$HAMILTON$$,#{state_id_tas},-42.726483,147.030188), - ($$7140$$,$$HAYES$$,#{state_id_tas},-42.726483,147.030188), - ($$7140$$,$$HOLLOW TREE$$,#{state_id_tas},-42.726483,147.030188), - ($$7140$$,$$KARANJA$$,#{state_id_tas},-42.726483,147.030188), - ($$7140$$,$$LACHLAN$$,#{state_id_tas},-42.726483,147.030188), - ($$7140$$,$$LAKE ST CLAIR$$,#{state_id_tas},-42.726483,147.030188), - ($$7140$$,$$LAWITTA$$,#{state_id_tas},-42.726483,147.030188), - ($$7140$$,$$LITTLE PINE LAGOON$$,#{state_id_tas},-42.726483,147.030188), - ($$7140$$,$$LONDON LAKES$$,#{state_id_tas},-42.726483,147.030188), - ($$7140$$,$$MACQUARIE PLAINS$$,#{state_id_tas},-42.726483,147.030188), - ($$7140$$,$$MAGRA$$,#{state_id_tas},-42.726483,147.030188), - ($$7140$$,$$MALBINA$$,#{state_id_tas},-42.726483,147.030188), - ($$7140$$,$$MAYDENA$$,#{state_id_tas},-42.726483,147.030188), - ($$7140$$,$$MEADOWBANK$$,#{state_id_tas},-42.726483,147.030188), - ($$7140$$,$$MOLESWORTH$$,#{state_id_tas},-42.726483,147.030188), - ($$7140$$,$$MOOGARA$$,#{state_id_tas},-42.726483,147.030188), - ($$7140$$,$$MOUNT FIELD$$,#{state_id_tas},-42.726483,147.030188), - ($$7140$$,$$MOUNT LLOYD$$,#{state_id_tas},-42.726483,147.030188), - ($$7140$$,$$NATIONAL PARK$$,#{state_id_tas},-42.726483,147.030188), - ($$7140$$,$$NEW NORFOLK$$,#{state_id_tas},-42.726483,147.030188), - ($$7140$$,$$OSTERLEY$$,#{state_id_tas},-42.726483,147.030188), - ($$7140$$,$$OUSE$$,#{state_id_tas},-42.726483,147.030188), - ($$7140$$,$$PLENTY$$,#{state_id_tas},-42.726483,147.030188), - ($$7140$$,$$ROSEGARLAND$$,#{state_id_tas},-42.726483,147.030188), - ($$7140$$,$$SORELL CREEK$$,#{state_id_tas},-42.726483,147.030188), - ($$7140$$,$$STRICKLAND$$,#{state_id_tas},-42.726483,147.030188), - ($$7140$$,$$STYX$$,#{state_id_tas},-42.726483,147.030188), - ($$7140$$,$$TARRALEAH$$,#{state_id_tas},-42.726483,147.030188), - ($$7140$$,$$TYENNA$$,#{state_id_tas},-42.726483,147.030188), - ($$7140$$,$$UXBRIDGE$$,#{state_id_tas},-42.726483,147.030188), - ($$7140$$,$$VICTORIA VALLEY$$,#{state_id_tas},-42.726483,147.030188), - ($$7140$$,$$WAYATINAH$$,#{state_id_tas},-42.726483,147.030188), - ($$7140$$,$$WESTERWAY$$,#{state_id_tas},-42.726483,147.030188), - ($$7150$$,$$ADVENTURE BAY$$,#{state_id_tas},-43.355794,147.326117), - ($$7150$$,$$ALLENS RIVULET$$,#{state_id_tas},-43.355794,147.326117), - ($$7150$$,$$ALONNAH$$,#{state_id_tas},-43.355794,147.326117), - ($$7150$$,$$APOLLO BAY$$,#{state_id_tas},-43.355794,147.326117), - ($$7150$$,$$BARNES BAY$$,#{state_id_tas},-43.355794,147.326117), - ($$7150$$,$$DENNES POINT$$,#{state_id_tas},-43.355794,147.326117), - ($$7150$$,$$GORDON$$,#{state_id_tas},-43.355794,147.326117), - ($$7150$$,$$GREAT BAY$$,#{state_id_tas},-43.355794,147.326117), - ($$7150$$,$$KAOOTA$$,#{state_id_tas},-43.355794,147.326117), - ($$7150$$,$$KILLORA$$,#{state_id_tas},-43.355794,147.326117), - ($$7150$$,$$LONGLEY$$,#{state_id_tas},-43.355794,147.326117), - ($$7150$$,$$LUNAWANNA$$,#{state_id_tas},-43.355794,147.326117), - ($$7150$$,$$NORTH BRUNY$$,#{state_id_tas},-43.355794,147.326117), - ($$7150$$,$$OYSTER COVE$$,#{state_id_tas},-43.355794,147.326117), - ($$7150$$,$$PELVERATA$$,#{state_id_tas},-43.355794,147.326117), - ($$7150$$,$$SANDFLY$$,#{state_id_tas},-43.355794,147.326117), - ($$7150$$,$$SIMPSONS BAY$$,#{state_id_tas},-43.355794,147.326117), - ($$7150$$,$$SOUTH BRUNY$$,#{state_id_tas},-43.355794,147.326117), - ($$7150$$,$$UPPER WOODSTOCK$$,#{state_id_tas},-43.355794,147.326117), - ($$7151$$,$$CASEY$$,#{state_id_tas},-41.518719,146.64266), - ($$7151$$,$$DAVIS$$,#{state_id_tas},-41.518719,146.64266), - ($$7151$$,$$MACQUARIE$$,#{state_id_tas},-41.518719,146.64266), - ($$7151$$,$$MACQUARIE ISLAND$$,#{state_id_tas},-41.518719,146.64266), - ($$7151$$,$$MAWSON$$,#{state_id_tas},-41.518719,146.64266), - ($$7155$$,$$KETTERING$$,#{state_id_tas},-43.125179,147.247926), - ($$7162$$,$$BIRCHS BAY$$,#{state_id_tas},-43.177141,147.236472), - ($$7162$$,$$WOODBRIDGE$$,#{state_id_tas},-43.177141,147.236472), - ($$7163$$,$$FLOWERPOT$$,#{state_id_tas},-43.199084,147.250871), - ($$7163$$,$$MIDDLETON$$,#{state_id_tas},-43.199084,147.250871), - ($$7170$$,$$ACTON PARK$$,#{state_id_tas},-42.865743,147.469888), - ($$7170$$,$$CAMBRIDGE$$,#{state_id_tas},-42.865743,147.469888), - ($$7170$$,$$MOUNT RUMNEY$$,#{state_id_tas},-42.865743,147.469888), - ($$7170$$,$$ROCHES BEACH$$,#{state_id_tas},-42.865743,147.469888), - ($$7170$$,$$SEVEN MILE BEACH$$,#{state_id_tas},-42.865743,147.469888), - ($$7171$$,$$MIDWAY POINT$$,#{state_id_tas},-42.803335,147.53244), - ($$7171$$,$$PENNA$$,#{state_id_tas},-42.803335,147.53244), - ($$7172$$,$$NUGENT$$,#{state_id_tas},-42.715674,147.750994), - ($$7172$$,$$ORIELTON$$,#{state_id_tas},-42.715674,147.750994), - ($$7172$$,$$PAWLEENA$$,#{state_id_tas},-42.715674,147.750994), - ($$7172$$,$$SORELL$$,#{state_id_tas},-42.715674,147.750994), - ($$7172$$,$$WATTLE HILL$$,#{state_id_tas},-42.715674,147.750994), - ($$7173$$,$$CARLTON$$,#{state_id_tas},-42.866624,147.644995), - ($$7173$$,$$CARLTON RIVER$$,#{state_id_tas},-42.866624,147.644995), - ($$7173$$,$$CONNELLYS MARSH$$,#{state_id_tas},-42.866624,147.644995), - ($$7173$$,$$DODGES FERRY$$,#{state_id_tas},-42.866624,147.644995), - ($$7173$$,$$FORCETT$$,#{state_id_tas},-42.866624,147.644995), - ($$7173$$,$$LEWISHAM$$,#{state_id_tas},-42.866624,147.644995), - ($$7173$$,$$PRIMROSE SANDS$$,#{state_id_tas},-42.866624,147.644995), - ($$7174$$,$$COPPING$$,#{state_id_tas},0.0,0.0), - ($$7175$$,$$BREAM CREEK$$,#{state_id_tas},-42.806711,147.833762), - ($$7175$$,$$MARION BAY$$,#{state_id_tas},-42.806711,147.833762), - ($$7176$$,$$KELLEVIE$$,#{state_id_tas},-42.779441,147.813416), - ($$7177$$,$$BOOMER BAY$$,#{state_id_tas},-42.867523,147.828542), - ($$7177$$,$$DUNALLEY$$,#{state_id_tas},-42.867523,147.828542), - ($$7178$$,$$MURDUNNA$$,#{state_id_tas},-42.948312,147.867031), - ($$7179$$,$$EAGLEHAWK NECK$$,#{state_id_tas},-43.014304,147.924747), - ($$7180$$,$$TARANNA$$,#{state_id_tas},-43.056138,147.865176), - ($$7182$$,$$FORTESCUE$$,#{state_id_tas},-43.126921,147.950684), - ($$7182$$,$$PORT ARTHUR$$,#{state_id_tas},-43.126921,147.950684), - ($$7183$$,$$HIGHCROFT$$,#{state_id_tas},0.0,0.0), - ($$7184$$,$$NUBEENA$$,#{state_id_tas},-43.098729,147.742353), - ($$7184$$,$$STORMLEA$$,#{state_id_tas},-43.098729,147.742353), - ($$7184$$,$$WHITE BEACH$$,#{state_id_tas},-43.098729,147.742353), - ($$7185$$,$$PREMAYDENA$$,#{state_id_tas},-43.051628,147.778419), - ($$7186$$,$$SALTWATER RIVER$$,#{state_id_tas},-43.032005,147.729979), - ($$7186$$,$$SLOPING MAIN$$,#{state_id_tas},-43.032005,147.729979), - ($$7187$$,$$KOONYA$$,#{state_id_tas},-43.058075,147.812286), - ($$7190$$,$$APSLAWN$$,#{state_id_tas},-41.956623,148.164872), - ($$7190$$,$$BUCKLAND$$,#{state_id_tas},-41.956623,148.164872), - ($$7190$$,$$CRANBROOK$$,#{state_id_tas},-41.956623,148.164872), - ($$7190$$,$$DOLPHIN SANDS$$,#{state_id_tas},-41.956623,148.164872), - ($$7190$$,$$LITTLE SWANPORT$$,#{state_id_tas},-41.956623,148.164872), - ($$7190$$,$$ORFORD$$,#{state_id_tas},-41.956623,148.164872), - ($$7190$$,$$PONTYPOOL$$,#{state_id_tas},-41.956623,148.164872), - ($$7190$$,$$RHEBAN$$,#{state_id_tas},-41.956623,148.164872), - ($$7190$$,$$ROCKY HILLS$$,#{state_id_tas},-41.956623,148.164872), - ($$7190$$,$$RUNNYMEDE$$,#{state_id_tas},-41.956623,148.164872), - ($$7190$$,$$SPRING BEACH$$,#{state_id_tas},-41.956623,148.164872), - ($$7190$$,$$SWANSEA$$,#{state_id_tas},-41.956623,148.164872), - ($$7190$$,$$TRIABUNNA$$,#{state_id_tas},-41.956623,148.164872), - ($$7209$$,$$ROSS$$,#{state_id_tas},-42.214948,147.780288), - ($$7209$$,$$TOOMS LAKE$$,#{state_id_tas},-42.214948,147.780288), - ($$7210$$,$$CAMPBELL TOWN$$,#{state_id_tas},-41.928807,147.49362), - ($$7210$$,$$LAKE LEAKE$$,#{state_id_tas},-41.928807,147.49362), - ($$7211$$,$$CLEVELAND$$,#{state_id_tas},-41.814856,147.415889), - ($$7211$$,$$CONARA$$,#{state_id_tas},-41.814856,147.415889), - ($$7211$$,$$EPPING FOREST$$,#{state_id_tas},-41.814856,147.415889), - ($$7212$$,$$BEN LOMOND$$,#{state_id_tas},-41.505959,147.607663), - ($$7212$$,$$BLESSINGTON$$,#{state_id_tas},-41.505959,147.607663), - ($$7212$$,$$BURNS CREEK$$,#{state_id_tas},-41.505959,147.607663), - ($$7212$$,$$DEDDINGTON$$,#{state_id_tas},-41.505959,147.607663), - ($$7212$$,$$EVANDALE$$,#{state_id_tas},-41.505959,147.607663), - ($$7212$$,$$NILE$$,#{state_id_tas},-41.505959,147.607663), - ($$7212$$,$$UPPER BLESSINGTON$$,#{state_id_tas},-41.505959,147.607663), - ($$7212$$,$$WESTERN JUNCTION$$,#{state_id_tas},-41.505959,147.607663), - ($$7213$$,$$AVOCA$$,#{state_id_tas},-41.782149,147.720441), - ($$7213$$,$$ROSSARDEN$$,#{state_id_tas},-41.782149,147.720441), - ($$7213$$,$$ROYAL GEORGE$$,#{state_id_tas},-41.782149,147.720441), - ($$7214$$,$$FINGAL$$,#{state_id_tas},-41.638682,147.967174), - ($$7214$$,$$MANGANA$$,#{state_id_tas},-41.638682,147.967174), - ($$7214$$,$$MATHINNA$$,#{state_id_tas},-41.638682,147.967174), - ($$7214$$,$$UPPER ESK$$,#{state_id_tas},-41.638682,147.967174), - ($$7215$$,$$BEAUMARIS$$,#{state_id_tas},-41.419818,148.27604), - ($$7215$$,$$BICHENO$$,#{state_id_tas},-41.419818,148.27604), - ($$7215$$,$$CHAIN OF LAGOONS$$,#{state_id_tas},-41.419818,148.27604), - ($$7215$$,$$COLES BAY$$,#{state_id_tas},-41.419818,148.27604), - ($$7215$$,$$CORNWALL$$,#{state_id_tas},-41.419818,148.27604), - ($$7215$$,$$DOUGLAS RIVER$$,#{state_id_tas},-41.419818,148.27604), - ($$7215$$,$$FALMOUTH$$,#{state_id_tas},-41.419818,148.27604), - ($$7215$$,$$FOUR MILE CREEK$$,#{state_id_tas},-41.419818,148.27604), - ($$7215$$,$$FREYCINET$$,#{state_id_tas},-41.419818,148.27604), - ($$7215$$,$$FRIENDLY BEACHES$$,#{state_id_tas},-41.419818,148.27604), - ($$7215$$,$$GRAY$$,#{state_id_tas},-41.419818,148.27604), - ($$7215$$,$$SCAMANDER$$,#{state_id_tas},-41.419818,148.27604), - ($$7215$$,$$SEYMOUR$$,#{state_id_tas},-41.419818,148.27604), - ($$7215$$,$$ST MARYS$$,#{state_id_tas},-41.419818,148.27604), - ($$7215$$,$$UPPER SCAMANDER$$,#{state_id_tas},-41.419818,148.27604), - ($$7216$$,$$AKAROA$$,#{state_id_tas},-41.304515,148.316711), - ($$7216$$,$$BINALONG BAY$$,#{state_id_tas},-41.304515,148.316711), - ($$7216$$,$$GOSHEN$$,#{state_id_tas},-41.304515,148.316711), - ($$7216$$,$$GOULDS COUNTRY$$,#{state_id_tas},-41.304515,148.316711), - ($$7216$$,$$LOTTAH$$,#{state_id_tas},-41.304515,148.316711), - ($$7216$$,$$PYENGANA$$,#{state_id_tas},-41.304515,148.316711), - ($$7216$$,$$ST HELENS$$,#{state_id_tas},-41.304515,148.316711), - ($$7216$$,$$STIEGLITZ$$,#{state_id_tas},-41.304515,148.316711), - ($$7216$$,$$THE GARDENS$$,#{state_id_tas},-41.304515,148.316711), - ($$7248$$,$$INVERMAY$$,#{state_id_tas},-41.415798,147.133782), - ($$7248$$,$$MAYFIELD$$,#{state_id_tas},-41.415798,147.133782), - ($$7248$$,$$MOWBRAY$$,#{state_id_tas},-41.415798,147.133782), - ($$7248$$,$$NEWNHAM$$,#{state_id_tas},-41.415798,147.133782), - ($$7248$$,$$ROCHERLEA$$,#{state_id_tas},-41.415798,147.133782), - ($$7249$$,$$KINGS MEADOWS$$,#{state_id_tas},-41.467848,147.158352), - ($$7249$$,$$PUNCHBOWL$$,#{state_id_tas},-41.467848,147.158352), - ($$7249$$,$$SOUTH LAUNCESTON$$,#{state_id_tas},-41.467848,147.158352), - ($$7249$$,$$YOUNGTOWN$$,#{state_id_tas},-41.467848,147.158352), - ($$7250$$,$$BLACKSTONE HEIGHTS$$,#{state_id_tas},-41.464971,147.078832), - ($$7250$$,$$EAST LAUNCESTON$$,#{state_id_tas},-41.464971,147.078832), - ($$7250$$,$$LAUNCESTON$$,#{state_id_tas},-41.464971,147.078832), - ($$7250$$,$$NEWSTEAD$$,#{state_id_tas},-41.464971,147.078832), - ($$7250$$,$$NORWOOD$$,#{state_id_tas},-41.464971,147.078832), - ($$7250$$,$$NORWOOD AVENUE PO$$,#{state_id_tas},-41.464971,147.078832), - ($$7250$$,$$PROSPECT$$,#{state_id_tas},-41.464971,147.078832), - ($$7250$$,$$PROSPECT VALE$$,#{state_id_tas},-41.464971,147.078832), - ($$7250$$,$$RAVENSWOOD$$,#{state_id_tas},-41.464971,147.078832), - ($$7250$$,$$RIVERSIDE$$,#{state_id_tas},-41.464971,147.078832), - ($$7250$$,$$ST LEONARDS$$,#{state_id_tas},-41.464971,147.078832), - ($$7250$$,$$SUMMERHILL$$,#{state_id_tas},-41.464971,147.078832), - ($$7250$$,$$TRAVELLERS REST$$,#{state_id_tas},-41.464971,147.078832), - ($$7250$$,$$TREVALLYN$$,#{state_id_tas},-41.464971,147.078832), - ($$7250$$,$$WAVERLEY$$,#{state_id_tas},-41.464971,147.078832), - ($$7250$$,$$WEST LAUNCESTON$$,#{state_id_tas},-41.464971,147.078832), - ($$7252$$,$$BEECHFORD$$,#{state_id_tas},-41.02797,146.951248), - ($$7252$$,$$DILSTON$$,#{state_id_tas},-41.02797,146.951248), - ($$7252$$,$$HILLWOOD$$,#{state_id_tas},-41.02797,146.951248), - ($$7252$$,$$LEFROY$$,#{state_id_tas},-41.02797,146.951248), - ($$7252$$,$$LULWORTH$$,#{state_id_tas},-41.02797,146.951248), - ($$7252$$,$$MOUNT DIRECTION$$,#{state_id_tas},-41.02797,146.951248), - ($$7252$$,$$PIPERS RIVER$$,#{state_id_tas},-41.02797,146.951248), - ($$7252$$,$$STONY HEAD$$,#{state_id_tas},-41.02797,146.951248), - ($$7252$$,$$SWAN BAY$$,#{state_id_tas},-41.02797,146.951248), - ($$7252$$,$$WEYMOUTH$$,#{state_id_tas},-41.02797,146.951248), - ($$7252$$,$$WINDERMERE$$,#{state_id_tas},-41.02797,146.951248), - ($$7253$$,$$BELL BAY$$,#{state_id_tas},-41.13138,146.867715), - ($$7253$$,$$GEORGE TOWN$$,#{state_id_tas},-41.13138,146.867715), - ($$7253$$,$$LONG REACH$$,#{state_id_tas},-41.13138,146.867715), - ($$7253$$,$$LOW HEAD$$,#{state_id_tas},-41.13138,146.867715), - ($$7254$$,$$BELLINGHAM$$,#{state_id_tas},-41.016616,147.167212), - ($$7254$$,$$GOLCONDA$$,#{state_id_tas},-41.016616,147.167212), - ($$7254$$,$$LEBRINA$$,#{state_id_tas},-41.016616,147.167212), - ($$7254$$,$$PIPERS BROOK$$,#{state_id_tas},-41.016616,147.167212), - ($$7254$$,$$RETREAT$$,#{state_id_tas},-41.016616,147.167212), - ($$7254$$,$$TUNNEL$$,#{state_id_tas},-41.016616,147.167212), - ($$7254$$,$$WYENA$$,#{state_id_tas},-41.016616,147.167212), - ($$7255$$,$$BLUE ROCKS$$,#{state_id_tas},-40.041015,147.94561), - ($$7255$$,$$EMITA$$,#{state_id_tas},-40.041015,147.94561), - ($$7255$$,$$KILLIECRANKIE$$,#{state_id_tas},-40.041015,147.94561), - ($$7255$$,$$LACKRANA$$,#{state_id_tas},-40.041015,147.94561), - ($$7255$$,$$LADY BARRON$$,#{state_id_tas},-40.041015,147.94561), - ($$7255$$,$$LEEKA$$,#{state_id_tas},-40.041015,147.94561), - ($$7255$$,$$LOCCOTA$$,#{state_id_tas},-40.041015,147.94561), - ($$7255$$,$$LUGHRATA$$,#{state_id_tas},-40.041015,147.94561), - ($$7255$$,$$MEMANA$$,#{state_id_tas},-40.041015,147.94561), - ($$7255$$,$$PALANA$$,#{state_id_tas},-40.041015,147.94561), - ($$7255$$,$$RANGA$$,#{state_id_tas},-40.041015,147.94561), - ($$7255$$,$$STRZELECKI$$,#{state_id_tas},-40.041015,147.94561), - ($$7255$$,$$WHITEMARK$$,#{state_id_tas},-40.041015,147.94561), - ($$7255$$,$$WINGAROO$$,#{state_id_tas},-40.041015,147.94561), - ($$7256$$,$$BUNGAREE$$,#{state_id_tas},-39.7806,143.904354), - ($$7256$$,$$CURRIE$$,#{state_id_tas},-39.7806,143.904354), - ($$7256$$,$$EGG LAGOON$$,#{state_id_tas},-39.7806,143.904354), - ($$7256$$,$$GRASSY$$,#{state_id_tas},-39.7806,143.904354), - ($$7256$$,$$LOORANA$$,#{state_id_tas},-39.7806,143.904354), - ($$7256$$,$$LYMWOOD$$,#{state_id_tas},-39.7806,143.904354), - ($$7256$$,$$NARACOOPA$$,#{state_id_tas},-39.7806,143.904354), - ($$7256$$,$$NUGARA$$,#{state_id_tas},-39.7806,143.904354), - ($$7256$$,$$PEARSHAPE$$,#{state_id_tas},-39.7806,143.904354), - ($$7256$$,$$PEGARAH$$,#{state_id_tas},-39.7806,143.904354), - ($$7256$$,$$REEKARA$$,#{state_id_tas},-39.7806,143.904354), - ($$7256$$,$$SEA ELEPHANT$$,#{state_id_tas},-39.7806,143.904354), - ($$7256$$,$$SURPRISE BAY$$,#{state_id_tas},-39.7806,143.904354), - ($$7256$$,$$WICKHAM$$,#{state_id_tas},-39.7806,143.904354), - ($$7256$$,$$YAMBACOONA$$,#{state_id_tas},-39.7806,143.904354), - ($$7256$$,$$YARRA CREEK$$,#{state_id_tas},-39.7806,143.904354), - ($$7257$$,$$CAPE BARREN ISLAND$$,#{state_id_tas},-40.391808,148.232722), - ($$7258$$,$$BREADALBANE$$,#{state_id_tas},-41.527814,147.187315), - ($$7258$$,$$RELBIA$$,#{state_id_tas},-41.527814,147.187315), - ($$7258$$,$$WHITE HILLS$$,#{state_id_tas},-41.527814,147.187315), - ($$7259$$,$$MYRTLE BANK$$,#{state_id_tas},-41.294679,147.351311), - ($$7259$$,$$NUNAMARA$$,#{state_id_tas},-41.294679,147.351311), - ($$7259$$,$$PATERSONIA$$,#{state_id_tas},-41.294679,147.351311), - ($$7259$$,$$TARGA$$,#{state_id_tas},-41.294679,147.351311), - ($$7259$$,$$TAYENE$$,#{state_id_tas},-41.294679,147.351311), - ($$7260$$,$$BLUMONT$$,#{state_id_tas},-41.154156,147.411004), - ($$7260$$,$$CUCKOO$$,#{state_id_tas},-41.154156,147.411004), - ($$7260$$,$$FORESTER$$,#{state_id_tas},-41.154156,147.411004), - ($$7260$$,$$JETSONVILLE$$,#{state_id_tas},-41.154156,147.411004), - ($$7260$$,$$KAMONA$$,#{state_id_tas},-41.154156,147.411004), - ($$7260$$,$$LIETINNA$$,#{state_id_tas},-41.154156,147.411004), - ($$7260$$,$$LISLE$$,#{state_id_tas},-41.154156,147.411004), - ($$7260$$,$$NABOWLA$$,#{state_id_tas},-41.154156,147.411004), - ($$7260$$,$$NORTH SCOTTSDALE$$,#{state_id_tas},-41.154156,147.411004), - ($$7260$$,$$SCOTTSDALE$$,#{state_id_tas},-41.154156,147.411004), - ($$7260$$,$$SOUTH SPRINGFIELD$$,#{state_id_tas},-41.154156,147.411004), - ($$7260$$,$$SPRINGFIELD$$,#{state_id_tas},-41.154156,147.411004), - ($$7260$$,$$TONGANAH$$,#{state_id_tas},-41.154156,147.411004), - ($$7260$$,$$TULENDEENA$$,#{state_id_tas},-41.154156,147.411004), - ($$7260$$,$$WEST SCOTTSDALE$$,#{state_id_tas},-41.154156,147.411004), - ($$7261$$,$$BRANXHOLM$$,#{state_id_tas},-41.168485,147.738812), - ($$7261$$,$$WARRENTINNA$$,#{state_id_tas},-41.168485,147.738812), - ($$7262$$,$$BRIDPORT$$,#{state_id_tas},-41.002975,147.394214), - ($$7262$$,$$TOMAHAWK$$,#{state_id_tas},-41.002975,147.394214), - ($$7262$$,$$WATERHOUSE$$,#{state_id_tas},-41.002975,147.394214), - ($$7263$$,$$ALBERTON$$,#{state_id_tas},-41.293087,147.789324), - ($$7263$$,$$LEGERWOOD$$,#{state_id_tas},-41.293087,147.789324), - ($$7263$$,$$RINGAROOMA$$,#{state_id_tas},-41.293087,147.789324), - ($$7263$$,$$TALAWA$$,#{state_id_tas},-41.293087,147.789324), - ($$7263$$,$$TRENAH$$,#{state_id_tas},-41.293087,147.789324), - ($$7264$$,$$ANSONS BAY$$,#{state_id_tas},-40.893601,147.878822), - ($$7264$$,$$BOOBYALLA$$,#{state_id_tas},-40.893601,147.878822), - ($$7264$$,$$CAPE PORTLAND$$,#{state_id_tas},-40.893601,147.878822), - ($$7264$$,$$DERBY$$,#{state_id_tas},-40.893601,147.878822), - ($$7264$$,$$EDDYSTONE$$,#{state_id_tas},-40.893601,147.878822), - ($$7264$$,$$EDDYSTONE POINT$$,#{state_id_tas},-40.893601,147.878822), - ($$7264$$,$$GLADSTONE$$,#{state_id_tas},-40.893601,147.878822), - ($$7264$$,$$HERRICK$$,#{state_id_tas},-40.893601,147.878822), - ($$7264$$,$$MOORINA$$,#{state_id_tas},-40.893601,147.878822), - ($$7264$$,$$MUSSELROE BAY$$,#{state_id_tas},-40.893601,147.878822), - ($$7264$$,$$PIONEER$$,#{state_id_tas},-40.893601,147.878822), - ($$7264$$,$$RUSHY LAGOON$$,#{state_id_tas},-40.893601,147.878822), - ($$7264$$,$$SOUTH MOUNT CAMERON$$,#{state_id_tas},-40.893601,147.878822), - ($$7264$$,$$TELITA$$,#{state_id_tas},-40.893601,147.878822), - ($$7264$$,$$WELDBOROUGH$$,#{state_id_tas},-40.893601,147.878822), - ($$7265$$,$$BANCA$$,#{state_id_tas},-41.023289,147.801828), - ($$7265$$,$$WINNALEAH$$,#{state_id_tas},-41.023289,147.801828), - ($$7267$$,$$BANGOR$$,#{state_id_tas},-41.217518,147.137044), - ($$7267$$,$$KAROOLA$$,#{state_id_tas},-41.217518,147.137044), - ($$7267$$,$$LALLA$$,#{state_id_tas},-41.217518,147.137044), - ($$7267$$,$$LOWER TURNERS MARSH$$,#{state_id_tas},-41.217518,147.137044), - ($$7267$$,$$TURNERS MARSH$$,#{state_id_tas},-41.217518,147.137044), - ($$7268$$,$$LILYDALE$$,#{state_id_tas},-41.250344,147.217213), - ($$7268$$,$$NORTH LILYDALE$$,#{state_id_tas},-41.250344,147.217213), - ($$7268$$,$$UNDERWOOD$$,#{state_id_tas},-41.250344,147.217213), - ($$7270$$,$$BADGER HEAD$$,#{state_id_tas},-41.101676,146.674021), - ($$7270$$,$$BEACONSFIELD$$,#{state_id_tas},-41.101676,146.674021), - ($$7270$$,$$BEAUTY POINT$$,#{state_id_tas},-41.101676,146.674021), - ($$7270$$,$$CLARENCE POINT$$,#{state_id_tas},-41.101676,146.674021), - ($$7270$$,$$FLOWERY GULLY$$,#{state_id_tas},-41.101676,146.674021), - ($$7270$$,$$GREENS BEACH$$,#{state_id_tas},-41.101676,146.674021), - ($$7270$$,$$KAYENA$$,#{state_id_tas},-41.101676,146.674021), - ($$7270$$,$$KELSO$$,#{state_id_tas},-41.101676,146.674021), - ($$7270$$,$$ROWELLA$$,#{state_id_tas},-41.101676,146.674021), - ($$7270$$,$$SIDMOUTH$$,#{state_id_tas},-41.101676,146.674021), - ($$7270$$,$$YORK TOWN$$,#{state_id_tas},-41.101676,146.674021), - ($$7275$$,$$BLACKWALL$$,#{state_id_tas},-41.30513,146.966495), - ($$7275$$,$$DEVIOT$$,#{state_id_tas},-41.30513,146.966495), - ($$7275$$,$$EXETER$$,#{state_id_tas},-41.30513,146.966495), - ($$7275$$,$$FRANKFORD$$,#{state_id_tas},-41.30513,146.966495), - ($$7275$$,$$GLENGARRY$$,#{state_id_tas},-41.30513,146.966495), - ($$7275$$,$$HOLWELL$$,#{state_id_tas},-41.30513,146.966495), - ($$7275$$,$$LANENA$$,#{state_id_tas},-41.30513,146.966495), - ($$7275$$,$$LOIRA$$,#{state_id_tas},-41.30513,146.966495), - ($$7275$$,$$NOTLEY HILLS$$,#{state_id_tas},-41.30513,146.966495), - ($$7275$$,$$ROBIGANA$$,#{state_id_tas},-41.30513,146.966495), - ($$7275$$,$$SWAN POINT$$,#{state_id_tas},-41.30513,146.966495), - ($$7275$$,$$WINKLEIGH$$,#{state_id_tas},-41.30513,146.966495), - ($$7276$$,$$GRAVELLY BEACH$$,#{state_id_tas},-41.276981,146.977952), - ($$7277$$,$$BRIDGENORTH$$,#{state_id_tas},-41.378544,146.974938), - ($$7277$$,$$GRINDELWALD$$,#{state_id_tas},-41.378544,146.974938), - ($$7277$$,$$LEGANA$$,#{state_id_tas},-41.378544,146.974938), - ($$7277$$,$$ROSEVEARS$$,#{state_id_tas},-41.378544,146.974938), - ($$7290$$,$$HADSPEN$$,#{state_id_tas},-41.502702,147.062503), - ($$7291$$,$$CARRICK$$,#{state_id_tas},-41.533413,147.010575), - ($$7292$$,$$HAGLEY$$,#{state_id_tas},-41.526154,146.896855), - ($$7292$$,$$QUAMBY BEND$$,#{state_id_tas},-41.526154,146.896855), - ($$7292$$,$$ROSEVALE$$,#{state_id_tas},-41.526154,146.896855), - ($$7292$$,$$SELBOURNE$$,#{state_id_tas},-41.526154,146.896855), - ($$7292$$,$$WESTWOOD$$,#{state_id_tas},-41.526154,146.896855), - ($$7300$$,$$DEVON HILLS$$,#{state_id_tas},-41.550696,147.187219), - ($$7300$$,$$PERTH$$,#{state_id_tas},-41.550696,147.187219), - ($$7300$$,$$POWRANNA$$,#{state_id_tas},-41.550696,147.187219), - ($$7301$$,$$BISHOPSBOURNE$$,#{state_id_tas},-41.618529,146.986471), - ($$7301$$,$$BLACKWOOD CREEK$$,#{state_id_tas},-41.618529,146.986471), - ($$7301$$,$$LIFFEY$$,#{state_id_tas},-41.618529,146.986471), - ($$7301$$,$$LONGFORD$$,#{state_id_tas},-41.618529,146.986471), - ($$7301$$,$$TOIBERRY$$,#{state_id_tas},-41.618529,146.986471), - ($$7302$$,$$BRACKNELL$$,#{state_id_tas},-41.652009,146.937733), - ($$7302$$,$$CRESSY$$,#{state_id_tas},-41.652009,146.937733), - ($$7302$$,$$POATINA$$,#{state_id_tas},-41.652009,146.937733), - ($$7303$$,$$BIRRALEE$$,#{state_id_tas},-41.383287,146.833281), - ($$7303$$,$$CLUAN$$,#{state_id_tas},-41.383287,146.833281), - ($$7303$$,$$EXTON$$,#{state_id_tas},-41.383287,146.833281), - ($$7303$$,$$OAKS$$,#{state_id_tas},-41.383287,146.833281), - ($$7303$$,$$OSMASTON$$,#{state_id_tas},-41.383287,146.833281), - ($$7303$$,$$WESTBURY$$,#{state_id_tas},-41.383287,146.833281), - ($$7303$$,$$WHITEMORE$$,#{state_id_tas},-41.383287,146.833281), - ($$7304$$,$$BRANDUM$$,#{state_id_tas},-41.821399,146.67507), - ($$7304$$,$$BREONA$$,#{state_id_tas},-41.821399,146.67507), - ($$7304$$,$$CAVESIDE$$,#{state_id_tas},-41.821399,146.67507), - ($$7304$$,$$CENTRAL PLATEAU$$,#{state_id_tas},-41.821399,146.67507), - ($$7304$$,$$CHUDLEIGH$$,#{state_id_tas},-41.821399,146.67507), - ($$7304$$,$$DAIRY PLAINS$$,#{state_id_tas},-41.821399,146.67507), - ($$7304$$,$$DELORAINE$$,#{state_id_tas},-41.821399,146.67507), - ($$7304$$,$$DOCTORS POINT$$,#{state_id_tas},-41.821399,146.67507), - ($$7304$$,$$DUNORLAN$$,#{state_id_tas},-41.821399,146.67507), - ($$7304$$,$$ELIZABETH TOWN$$,#{state_id_tas},-41.821399,146.67507), - ($$7304$$,$$GOLDEN VALLEY$$,#{state_id_tas},-41.821399,146.67507), - ($$7304$$,$$JACKEYS MARSH$$,#{state_id_tas},-41.821399,146.67507), - ($$7304$$,$$KIMBERLEY$$,#{state_id_tas},-41.821399,146.67507), - ($$7304$$,$$LIENA$$,#{state_id_tas},-41.821399,146.67507), - ($$7304$$,$$MAYBERRY$$,#{state_id_tas},-41.821399,146.67507), - ($$7304$$,$$MEANDER$$,#{state_id_tas},-41.821399,146.67507), - ($$7304$$,$$MERSEY FOREST$$,#{state_id_tas},-41.821399,146.67507), - ($$7304$$,$$MOLE CREEK$$,#{state_id_tas},-41.821399,146.67507), - ($$7304$$,$$MOLTEMA$$,#{state_id_tas},-41.821399,146.67507), - ($$7304$$,$$MONTANA$$,#{state_id_tas},-41.821399,146.67507), - ($$7304$$,$$NEEDLES$$,#{state_id_tas},-41.821399,146.67507), - ($$7304$$,$$PARKHAM$$,#{state_id_tas},-41.821399,146.67507), - ($$7304$$,$$QUAMBY BROOK$$,#{state_id_tas},-41.821399,146.67507), - ($$7304$$,$$RED HILLS$$,#{state_id_tas},-41.821399,146.67507), - ($$7304$$,$$REEDY MARSH$$,#{state_id_tas},-41.821399,146.67507), - ($$7304$$,$$REYNOLDS NECK$$,#{state_id_tas},-41.821399,146.67507), - ($$7304$$,$$WEEGENA$$,#{state_id_tas},-41.821399,146.67507), - ($$7304$$,$$WEETAH$$,#{state_id_tas},-41.821399,146.67507), - ($$7304$$,$$WESTERN CREEK$$,#{state_id_tas},-41.821399,146.67507), - ($$7305$$,$$MERSEYLEA$$,#{state_id_tas},-41.343613,146.473493), - ($$7305$$,$$RAILTON$$,#{state_id_tas},-41.343613,146.473493), - ($$7305$$,$$SUNNYSIDE$$,#{state_id_tas},-41.343613,146.473493), - ($$7306$$,$$ACACIA HILLS$$,#{state_id_tas},-41.280491,146.324953), - ($$7306$$,$$BARRINGTON$$,#{state_id_tas},-41.280491,146.324953), - ($$7306$$,$$BEULAH$$,#{state_id_tas},-41.280491,146.324953), - ($$7306$$,$$CETHANA$$,#{state_id_tas},-41.280491,146.324953), - ($$7306$$,$$CLAUDE ROAD$$,#{state_id_tas},-41.280491,146.324953), - ($$7306$$,$$CRADLE MOUNTAIN$$,#{state_id_tas},-41.280491,146.324953), - ($$7306$$,$$GOWRIE PARK$$,#{state_id_tas},-41.280491,146.324953), - ($$7306$$,$$LORINNA$$,#{state_id_tas},-41.280491,146.324953), - ($$7306$$,$$LOWER BARRINGTON$$,#{state_id_tas},-41.280491,146.324953), - ($$7306$$,$$LOWER BEULAH$$,#{state_id_tas},-41.280491,146.324953), - ($$7306$$,$$MIDDLESEX$$,#{state_id_tas},-41.280491,146.324953), - ($$7306$$,$$NOOK$$,#{state_id_tas},-41.280491,146.324953), - ($$7306$$,$$NOWHERE ELSE$$,#{state_id_tas},-41.280491,146.324953), - ($$7306$$,$$PARADISE$$,#{state_id_tas},-41.280491,146.324953), - ($$7306$$,$$PROMISED LAND$$,#{state_id_tas},-41.280491,146.324953), - ($$7306$$,$$ROLAND$$,#{state_id_tas},-41.280491,146.324953), - ($$7306$$,$$SHEFFIELD$$,#{state_id_tas},-41.280491,146.324953), - ($$7306$$,$$STAVERTON$$,#{state_id_tas},-41.280491,146.324953), - ($$7306$$,$$STOODLEY$$,#{state_id_tas},-41.280491,146.324953), - ($$7306$$,$$WEST KENTISH$$,#{state_id_tas},-41.280491,146.324953), - ($$7307$$,$$BAKERS BEACH$$,#{state_id_tas},-41.185628,146.61064), - ($$7307$$,$$HARFORD$$,#{state_id_tas},-41.185628,146.61064), - ($$7307$$,$$HAWLEY BEACH$$,#{state_id_tas},-41.185628,146.61064), - ($$7307$$,$$LATROBE$$,#{state_id_tas},-41.185628,146.61064), - ($$7307$$,$$MORIARTY$$,#{state_id_tas},-41.185628,146.61064), - ($$7307$$,$$NORTHDOWN$$,#{state_id_tas},-41.185628,146.61064), - ($$7307$$,$$PORT SORELL$$,#{state_id_tas},-41.185628,146.61064), - ($$7307$$,$$SASSAFRAS$$,#{state_id_tas},-41.185628,146.61064), - ($$7307$$,$$SHEARWATER$$,#{state_id_tas},-41.185628,146.61064), - ($$7307$$,$$SQUEAKING POINT$$,#{state_id_tas},-41.185628,146.61064), - ($$7307$$,$$THIRLSTANE$$,#{state_id_tas},-41.185628,146.61064), - ($$7307$$,$$WESLEY VALE$$,#{state_id_tas},-41.185628,146.61064), - ($$7310$$,$$ABERDEEN$$,#{state_id_tas},-41.241121,146.324486), - ($$7310$$,$$AMBLESIDE$$,#{state_id_tas},-41.241121,146.324486), - ($$7310$$,$$DEVONPORT$$,#{state_id_tas},-41.241121,146.324486), - ($$7310$$,$$DON$$,#{state_id_tas},-41.241121,146.324486), - ($$7310$$,$$EAST DEVONPORT$$,#{state_id_tas},-41.241121,146.324486), - ($$7310$$,$$ERRIBA$$,#{state_id_tas},-41.241121,146.324486), - ($$7310$$,$$EUGENANA$$,#{state_id_tas},-41.241121,146.324486), - ($$7310$$,$$FORTH$$,#{state_id_tas},-41.241121,146.324486), - ($$7310$$,$$FORTHSIDE$$,#{state_id_tas},-41.241121,146.324486), - ($$7310$$,$$KINDRED$$,#{state_id_tas},-41.241121,146.324486), - ($$7310$$,$$LILLICO$$,#{state_id_tas},-41.241121,146.324486), - ($$7310$$,$$LOWER WILMOT$$,#{state_id_tas},-41.241121,146.324486), - ($$7310$$,$$MELROSE$$,#{state_id_tas},-41.241121,146.324486), - ($$7310$$,$$MIANDETTA$$,#{state_id_tas},-41.241121,146.324486), - ($$7310$$,$$MOINA$$,#{state_id_tas},-41.241121,146.324486), - ($$7310$$,$$PALOONA$$,#{state_id_tas},-41.241121,146.324486), - ($$7310$$,$$QUOIBA$$,#{state_id_tas},-41.241121,146.324486), - ($$7310$$,$$SOUTH SPREYTON$$,#{state_id_tas},-41.241121,146.324486), - ($$7310$$,$$SPREYTON$$,#{state_id_tas},-41.241121,146.324486), - ($$7310$$,$$STONY RISE$$,#{state_id_tas},-41.241121,146.324486), - ($$7310$$,$$TARLETON$$,#{state_id_tas},-41.241121,146.324486), - ($$7310$$,$$TUGRAH$$,#{state_id_tas},-41.241121,146.324486), - ($$7310$$,$$WEST DEVONPORT$$,#{state_id_tas},-41.241121,146.324486), - ($$7310$$,$$WILMOT$$,#{state_id_tas},-41.241121,146.324486), - ($$7315$$,$$ABBOTSHAM$$,#{state_id_tas},-41.211306,146.175577), - ($$7315$$,$$CASTRA$$,#{state_id_tas},-41.211306,146.175577), - ($$7315$$,$$GAWLER$$,#{state_id_tas},-41.211306,146.175577), - ($$7315$$,$$GUNNS PLAINS$$,#{state_id_tas},-41.211306,146.175577), - ($$7315$$,$$LEITH$$,#{state_id_tas},-41.211306,146.175577), - ($$7315$$,$$LOONGANA$$,#{state_id_tas},-41.211306,146.175577), - ($$7315$$,$$NIETTA$$,#{state_id_tas},-41.211306,146.175577), - ($$7315$$,$$NORTH MOTTON$$,#{state_id_tas},-41.211306,146.175577), - ($$7315$$,$$PRESTON$$,#{state_id_tas},-41.211306,146.175577), - ($$7315$$,$$SOUTH NIETTA$$,#{state_id_tas},-41.211306,146.175577), - ($$7315$$,$$SOUTH PRESTON$$,#{state_id_tas},-41.211306,146.175577), - ($$7315$$,$$SPALFORD$$,#{state_id_tas},-41.211306,146.175577), - ($$7315$$,$$SPRENT$$,#{state_id_tas},-41.211306,146.175577), - ($$7315$$,$$TURNERS BEACH$$,#{state_id_tas},-41.211306,146.175577), - ($$7315$$,$$ULVERSTONE$$,#{state_id_tas},-41.211306,146.175577), - ($$7315$$,$$UPPER CASTRA$$,#{state_id_tas},-41.211306,146.175577), - ($$7315$$,$$WEST ULVERSTONE$$,#{state_id_tas},-41.211306,146.175577), - ($$7316$$,$$CAMENA$$,#{state_id_tas},-41.167417,145.966402), - ($$7316$$,$$CUPRONA$$,#{state_id_tas},-41.167417,145.966402), - ($$7316$$,$$HEYBRIDGE$$,#{state_id_tas},-41.167417,145.966402), - ($$7316$$,$$HOWTH$$,#{state_id_tas},-41.167417,145.966402), - ($$7316$$,$$LOYETEA$$,#{state_id_tas},-41.167417,145.966402), - ($$7316$$,$$PENGUIN$$,#{state_id_tas},-41.167417,145.966402), - ($$7316$$,$$PRESERVATION BAY$$,#{state_id_tas},-41.167417,145.966402), - ($$7316$$,$$RIANA$$,#{state_id_tas},-41.167417,145.966402), - ($$7316$$,$$SOUTH RIANA$$,#{state_id_tas},-41.167417,145.966402), - ($$7316$$,$$SULPHUR CREEK$$,#{state_id_tas},-41.167417,145.966402), - ($$7316$$,$$WEST PINE$$,#{state_id_tas},-41.167417,145.966402), - ($$7320$$,$$ACTON$$,#{state_id_tas},-41.070069,145.893987), - ($$7320$$,$$BROOKLYN$$,#{state_id_tas},-41.070069,145.893987), - ($$7320$$,$$BURNIE$$,#{state_id_tas},-41.070069,145.893987), - ($$7320$$,$$CAMDALE$$,#{state_id_tas},-41.070069,145.893987), - ($$7320$$,$$COOEE$$,#{state_id_tas},-41.070069,145.893987), - ($$7320$$,$$DOWNLANDS$$,#{state_id_tas},-41.070069,145.893987), - ($$7320$$,$$EMU HEIGHTS$$,#{state_id_tas},-41.070069,145.893987), - ($$7320$$,$$HAVENVIEW$$,#{state_id_tas},-41.070069,145.893987), - ($$7320$$,$$HILLCREST$$,#{state_id_tas},-41.070069,145.893987), - ($$7320$$,$$MONTELLO$$,#{state_id_tas},-41.070069,145.893987), - ($$7320$$,$$OCEAN VISTA$$,#{state_id_tas},-41.070069,145.893987), - ($$7320$$,$$PARK GROVE$$,#{state_id_tas},-41.070069,145.893987), - ($$7320$$,$$PARKLANDS$$,#{state_id_tas},-41.070069,145.893987), - ($$7320$$,$$ROMAINE$$,#{state_id_tas},-41.070069,145.893987), - ($$7320$$,$$ROUND HILL$$,#{state_id_tas},-41.070069,145.893987), - ($$7320$$,$$SHOREWELL PARK$$,#{state_id_tas},-41.070069,145.893987), - ($$7320$$,$$SOUTH BURNIE$$,#{state_id_tas},-41.070069,145.893987), - ($$7320$$,$$UPPER BURNIE$$,#{state_id_tas},-41.070069,145.893987), - ($$7320$$,$$WIVENHOE$$,#{state_id_tas},-41.070069,145.893987), - ($$7321$$,$$BLACK RIVER$$,#{state_id_tas},-40.850586,145.308924), - ($$7321$$,$$BOAT HARBOUR$$,#{state_id_tas},-40.850586,145.308924), - ($$7321$$,$$BOAT HARBOUR BEACH$$,#{state_id_tas},-40.850586,145.308924), - ($$7321$$,$$CHASM CREEK$$,#{state_id_tas},-40.850586,145.308924), - ($$7321$$,$$CORINNA$$,#{state_id_tas},-40.850586,145.308924), - ($$7321$$,$$COWRIE POINT$$,#{state_id_tas},-40.850586,145.308924), - ($$7321$$,$$CRAYFISH CREEK$$,#{state_id_tas},-40.850586,145.308924), - ($$7321$$,$$DETENTION$$,#{state_id_tas},-40.850586,145.308924), - ($$7321$$,$$EAST CAM$$,#{state_id_tas},-40.850586,145.308924), - ($$7321$$,$$EAST RIDGLEY$$,#{state_id_tas},-40.850586,145.308924), - ($$7321$$,$$EDGCUMBE BEACH$$,#{state_id_tas},-40.850586,145.308924), - ($$7321$$,$$GUILDFORD$$,#{state_id_tas},-40.850586,145.308924), - ($$7321$$,$$HAMPSHIRE$$,#{state_id_tas},-40.850586,145.308924), - ($$7321$$,$$HELLYER$$,#{state_id_tas},-40.850586,145.308924), - ($$7321$$,$$HIGHCLERE$$,#{state_id_tas},-40.850586,145.308924), - ($$7321$$,$$LUINA$$,#{state_id_tas},-40.850586,145.308924), - ($$7321$$,$$MAWBANNA$$,#{state_id_tas},-40.850586,145.308924), - ($$7321$$,$$MONTUMANA$$,#{state_id_tas},-40.850586,145.308924), - ($$7321$$,$$MOOREVILLE$$,#{state_id_tas},-40.850586,145.308924), - ($$7321$$,$$NATONE$$,#{state_id_tas},-40.850586,145.308924), - ($$7321$$,$$PARRAWE$$,#{state_id_tas},-40.850586,145.308924), - ($$7321$$,$$PORT LATTA$$,#{state_id_tas},-40.850586,145.308924), - ($$7321$$,$$RIDGLEY$$,#{state_id_tas},-40.850586,145.308924), - ($$7321$$,$$ROCKY CAPE$$,#{state_id_tas},-40.850586,145.308924), - ($$7321$$,$$SAVAGE RIVER$$,#{state_id_tas},-40.850586,145.308924), - ($$7321$$,$$SISTERS BEACH$$,#{state_id_tas},-40.850586,145.308924), - ($$7321$$,$$STOWPORT$$,#{state_id_tas},-40.850586,145.308924), - ($$7321$$,$$TEWKESBURY$$,#{state_id_tas},-40.850586,145.308924), - ($$7321$$,$$TULLAH$$,#{state_id_tas},-40.850586,145.308924), - ($$7321$$,$$UPPER NATONE$$,#{state_id_tas},-40.850586,145.308924), - ($$7321$$,$$UPPER STOWPORT$$,#{state_id_tas},-40.850586,145.308924), - ($$7321$$,$$WARATAH$$,#{state_id_tas},-40.850586,145.308924), - ($$7321$$,$$WEST MOOREVILLE$$,#{state_id_tas},-40.850586,145.308924), - ($$7321$$,$$WEST RIDGLEY$$,#{state_id_tas},-40.850586,145.308924), - ($$7321$$,$$WILTSHIRE$$,#{state_id_tas},-40.850586,145.308924), - ($$7322$$,$$SOMERSET$$,#{state_id_tas},-41.035186,145.828259), - ($$7325$$,$$CALDER$$,#{state_id_tas},-41.07007,145.629108), - ($$7325$$,$$DOCTORS ROCKS$$,#{state_id_tas},-41.07007,145.629108), - ($$7325$$,$$ELLIOTT$$,#{state_id_tas},-41.07007,145.629108), - ($$7325$$,$$FLOWERDALE$$,#{state_id_tas},-41.07007,145.629108), - ($$7325$$,$$HENRIETTA$$,#{state_id_tas},-41.07007,145.629108), - ($$7325$$,$$LAPOINYA$$,#{state_id_tas},-41.07007,145.629108), - ($$7325$$,$$MEUNNA$$,#{state_id_tas},-41.07007,145.629108), - ($$7325$$,$$MILABENA$$,#{state_id_tas},-41.07007,145.629108), - ($$7325$$,$$MOORLEAH$$,#{state_id_tas},-41.07007,145.629108), - ($$7325$$,$$MOUNT HICKS$$,#{state_id_tas},-41.07007,145.629108), - ($$7325$$,$$MYALLA$$,#{state_id_tas},-41.07007,145.629108), - ($$7325$$,$$OLDINA$$,#{state_id_tas},-41.07007,145.629108), - ($$7325$$,$$OONAH$$,#{state_id_tas},-41.07007,145.629108), - ($$7325$$,$$PREOLENNA$$,#{state_id_tas},-41.07007,145.629108), - ($$7325$$,$$SISTERS CREEK$$,#{state_id_tas},-41.07007,145.629108), - ($$7325$$,$$TABLE CAPE$$,#{state_id_tas},-41.07007,145.629108), - ($$7325$$,$$TAKONE$$,#{state_id_tas},-41.07007,145.629108), - ($$7325$$,$$WEST TAKONE$$,#{state_id_tas},-41.07007,145.629108), - ($$7325$$,$$WYNYARD$$,#{state_id_tas},-41.07007,145.629108), - ($$7325$$,$$YOLLA$$,#{state_id_tas},-41.07007,145.629108), - ($$7330$$,$$ALCOMIE$$,#{state_id_tas},-40.934871,145.185229), - ($$7330$$,$$ARTHUR RIVER$$,#{state_id_tas},-40.934871,145.185229), - ($$7330$$,$$BRITTONS SWAMP$$,#{state_id_tas},-40.934871,145.185229), - ($$7330$$,$$BROADMEADOWS$$,#{state_id_tas},-40.934871,145.185229), - ($$7330$$,$$CHRISTMAS HILLS$$,#{state_id_tas},-40.934871,145.185229), - ($$7330$$,$$COUTA ROCKS$$,#{state_id_tas},-40.934871,145.185229), - ($$7330$$,$$EDITH CREEK$$,#{state_id_tas},-40.934871,145.185229), - ($$7330$$,$$FOREST$$,#{state_id_tas},-40.934871,145.185229), - ($$7330$$,$$IRISHTOWN$$,#{state_id_tas},-40.934871,145.185229), - ($$7330$$,$$LILEAH$$,#{state_id_tas},-40.934871,145.185229), - ($$7330$$,$$MARRAWAH$$,#{state_id_tas},-40.934871,145.185229), - ($$7330$$,$$MELLA$$,#{state_id_tas},-40.934871,145.185229), - ($$7330$$,$$MENGHA$$,#{state_id_tas},-40.934871,145.185229), - ($$7330$$,$$MONTAGU$$,#{state_id_tas},-40.934871,145.185229), - ($$7330$$,$$NABAGEENA$$,#{state_id_tas},-40.934871,145.185229), - ($$7330$$,$$NELSON BAY$$,#{state_id_tas},-40.934871,145.185229), - ($$7330$$,$$REDPA$$,#{state_id_tas},-40.934871,145.185229), - ($$7330$$,$$ROGER RIVER$$,#{state_id_tas},-40.934871,145.185229), - ($$7330$$,$$SCOPUS$$,#{state_id_tas},-40.934871,145.185229), - ($$7330$$,$$SCOTCHTOWN$$,#{state_id_tas},-40.934871,145.185229), - ($$7330$$,$$SMITHTON$$,#{state_id_tas},-40.934871,145.185229), - ($$7330$$,$$SOUTH FOREST$$,#{state_id_tas},-40.934871,145.185229), - ($$7330$$,$$TEMMA$$,#{state_id_tas},-40.934871,145.185229), - ($$7330$$,$$THREE HUMMOCK ISLAND$$,#{state_id_tas},-40.934871,145.185229), - ($$7330$$,$$TOGARI$$,#{state_id_tas},-40.934871,145.185229), - ($$7330$$,$$TROWUTTA$$,#{state_id_tas},-40.934871,145.185229), - ($$7330$$,$$WEST MONTAGU$$,#{state_id_tas},-40.934871,145.185229), - ($$7330$$,$$WOOLNORTH$$,#{state_id_tas},-40.934871,145.185229), - ($$7331$$,$$STANLEY$$,#{state_id_tas},-40.78944,145.271733), - ($$7466$$,$$GORMANSTON$$,#{state_id_tas},-42.071594,145.597453), - ($$7467$$,$$LAKE MARGARET$$,#{state_id_tas},-42.004882,145.543511), - ($$7467$$,$$QUEENSTOWN$$,#{state_id_tas},-42.004882,145.543511), - ($$7468$$,$$MACQUARIE HEADS$$,#{state_id_tas},-42.215853,145.200621), - ($$7468$$,$$STRAHAN$$,#{state_id_tas},-42.215853,145.200621), - ($$7469$$,$$GRANVILLE HARBOUR$$,#{state_id_tas},-41.807866,145.035296), - ($$7469$$,$$RENISON BELL$$,#{state_id_tas},-41.807866,145.035296), - ($$7469$$,$$TRIAL HARBOUR$$,#{state_id_tas},-41.807866,145.035296), - ($$7469$$,$$ZEEHAN$$,#{state_id_tas},-41.807866,145.035296), - ($$7470$$,$$ROSEBERY$$,#{state_id_tas},-41.779947,145.538888), - ($$7800$$,$$HOBART$$,#{state_id_tas},-41.495839,147.172006), - ($$7802$$,$$HOBART$$,#{state_id_tas},-41.495839,147.172006), - ($$7803$$,$$HOBART$$,#{state_id_tas},-41.495839,147.172006), - ($$7804$$,$$HOBART$$,#{state_id_tas},-41.495839,147.172006), - ($$7805$$,$$HOBART$$,#{state_id_tas},-41.495839,147.172006), - ($$7806$$,$$HOBART$$,#{state_id_tas},-41.495839,147.172006), - ($$7807$$,$$HOBART$$,#{state_id_tas},-41.495839,147.172006), - ($$7808$$,$$HOBART$$,#{state_id_tas},-41.495839,147.172006), - ($$7809$$,$$HOBART$$,#{state_id_tas},-41.495839,147.172006), - ($$7810$$,$$HOBART$$,#{state_id_tas},-41.495839,147.172006), - ($$7811$$,$$HOBART$$,#{state_id_tas},-41.495839,147.172006), - ($$7812$$,$$HOBART$$,#{state_id_tas},-41.495839,147.172006), - ($$7813$$,$$HOBART$$,#{state_id_tas},-41.495839,147.172006), - ($$7814$$,$$HOBART$$,#{state_id_tas},-41.495839,147.172006), - ($$7823$$,$$HOBART$$,#{state_id_tas},-41.495839,147.172006), - ($$7824$$,$$HOBART$$,#{state_id_tas},-41.495839,147.172006), - ($$7827$$,$$HOBART$$,#{state_id_tas},-41.495839,147.172006), - ($$7828$$,$$HOBART$$,#{state_id_tas},-41.495839,147.172006), - ($$7829$$,$$HOBART$$,#{state_id_tas},-41.495839,147.172006), - ($$7845$$,$$HOBART$$,#{state_id_tas},-41.495839,147.172006), - ($$7850$$,$$HOBART$$,#{state_id_tas},-41.495839,147.172006), - ($$7901$$,$$LAUNCESTON$$,#{state_id_tas},-41.348227,148.139695), - ($$7902$$,$$LAUNCESTON$$,#{state_id_tas},-41.348227,148.139695), - ($$7903$$,$$LAUNCESTON$$,#{state_id_tas},-41.348227,148.139695), - ($$7904$$,$$LAUNCESTON$$,#{state_id_tas},-41.348227,148.139695), - ($$7905$$,$$LAUNCESTON$$,#{state_id_tas},-41.348227,148.139695), - ($$7906$$,$$LAUNCESTON$$,#{state_id_tas},-41.348227,148.139695), - ($$7907$$,$$LAUNCESTON$$,#{state_id_tas},-41.348227,148.139695), - ($$7908$$,$$LAUNCESTON$$,#{state_id_tas},-41.348227,148.139695), - ($$7909$$,$$LAUNCESTON$$,#{state_id_tas},-41.348227,148.139695), - ($$7910$$,$$LAUNCESTON$$,#{state_id_tas},-41.348227,148.139695), - ($$7911$$,$$LAUNCESTON$$,#{state_id_tas},-41.348227,148.139695), - ($$7912$$,$$LAUNCESTON$$,#{state_id_tas},-41.348227,148.139695), - ($$7913$$,$$LAUNCESTON$$,#{state_id_tas},-41.348227,148.139695), - ($$7914$$,$$LAUNCESTON$$,#{state_id_tas},-41.348227,148.139695), - ($$7915$$,$$BURNIE$$,#{state_id_tas},0.0,0.0), - ($$7916$$,$$BURNIE$$,#{state_id_tas},0.0,0.0), - ($$7917$$,$$BURNIE$$,#{state_id_tas},0.0,0.0), - ($$7918$$,$$BURNIE$$,#{state_id_tas},0.0,0.0), - ($$7919$$,$$BURNIE$$,#{state_id_tas},0.0,0.0), - ($$7920$$,$$BURNIE$$,#{state_id_tas},0.0,0.0), - ($$7921$$,$$BURNIE$$,#{state_id_tas},0.0,0.0), - ($$7922$$,$$LAUNCESTON$$,#{state_id_tas},-41.348227,148.139695), - ($$7923$$,$$LAUNCESTON$$,#{state_id_tas},-41.348227,148.139695), - ($$8001$$,$$MELBOURNE$$,#{state_id_vic},-38.365017,144.76592), - ($$8002$$,$$EAST MELBOURNE$$,#{state_id_vic},-38.105449,145.147855), - ($$8003$$,$$COLLINS STREET EAST$$,#{state_id_vic},0.0,0.0), - ($$8004$$,$$ST KILDA ROAD$$,#{state_id_vic},-37.836219,144.975549), - ($$8005$$,$$WORLD TRADE CENTRE$$,#{state_id_vic},-37.822262,144.954856), - ($$8006$$,$$ABECKETT STREET$$,#{state_id_vic},-37.809696,144.959314), - ($$8007$$,$$COLLINS STREET WEST$$,#{state_id_vic},0.0,0.0), - ($$8008$$,$$ST KILDA ROAD CENTRAL$$,#{state_id_vic},0.0,0.0), - ($$8009$$,$$FLINDERS LANE$$,#{state_id_vic},-37.817201,144.964531), - ($$8010$$,$$LAW COURTS$$,#{state_id_vic},-38.185857,146.293728), - ($$8011$$,$$LITTLE LONSDALE STREET$$,#{state_id_vic},-37.811301,144.961819), - ($$8012$$,$$DOCKLANDS$$,#{state_id_vic},0.0,0.0), - ($$8045$$,$$MELBOURNE$$,#{state_id_vic},-38.365017,144.76592), - ($$8051$$,$$MELBOURNE$$,#{state_id_vic},-38.365017,144.76592), - ($$8060$$,$$MELBOURNE$$,#{state_id_vic},-38.365017,144.76592), - ($$8061$$,$$MELBOURNE$$,#{state_id_vic},-38.365017,144.76592), - ($$8066$$,$$MELBOURNE$$,#{state_id_vic},-38.365017,144.76592), - ($$8069$$,$$MELBOURNE$$,#{state_id_vic},-38.365017,144.76592), - ($$8070$$,$$MELBOURNE$$,#{state_id_vic},-38.365017,144.76592), - ($$8071$$,$$MELBOURNE$$,#{state_id_vic},-38.365017,144.76592), - ($$8102$$,$$MELBOURNE$$,#{state_id_vic},-38.365017,144.76592), - ($$8103$$,$$MELBOURNE$$,#{state_id_vic},-38.365017,144.76592), - ($$8107$$,$$MELBOURNE$$,#{state_id_vic},-38.365017,144.76592), - ($$8108$$,$$MELBOURNE$$,#{state_id_vic},-38.365017,144.76592), - ($$8111$$,$$MELBOURNE$$,#{state_id_vic},-38.365017,144.76592), - ($$8120$$,$$MELBOURNE$$,#{state_id_vic},-38.365017,144.76592), - ($$8205$$,$$MELBOURNE$$,#{state_id_vic},-38.365017,144.76592), - ($$8383$$,$$MELBOURNE$$,#{state_id_vic},-38.365017,144.76592), - ($$8386$$,$$MELBOURNE$$,#{state_id_vic},-38.365017,144.76592), - ($$8388$$,$$MELBOURNE$$,#{state_id_vic},-38.365017,144.76592), - ($$8390$$,$$MELBOURNE$$,#{state_id_vic},-38.365017,144.76592), - ($$8393$$,$$MELBOURNE$$,#{state_id_vic},-38.365017,144.76592), - ($$8394$$,$$MELBOURNE$$,#{state_id_vic},-38.365017,144.76592), - ($$8396$$,$$MELBOURNE$$,#{state_id_vic},-38.365017,144.76592), - ($$8399$$,$$MELBOURNE$$,#{state_id_vic},-38.365017,144.76592), - ($$8576$$,$$IVANHOE$$,#{state_id_vic},-37.764016,145.044798), - ($$8627$$,$$CAMBERWELL$$,#{state_id_vic},-37.836011,145.062173), - ($$8873$$,$$PORT MELBOURNE$$,#{state_id_vic},-37.846333,144.885746), - ($$9000$$,$$BRISBANE$$,#{state_id_qld},-27.603479,152.823141), - ($$9001$$,$$BRISBANE$$,#{state_id_qld},-27.603479,152.823141), - ($$9002$$,$$BRISBANE$$,#{state_id_qld},-27.603479,152.823141), - ($$9005$$,$$BRISBANE$$,#{state_id_qld},-27.603479,152.823141), - ($$9007$$,$$BRISBANE$$,#{state_id_qld},-27.603479,152.823141), - ($$9008$$,$$BRISBANE$$,#{state_id_qld},-27.603479,152.823141), - ($$9009$$,$$BRISBANE$$,#{state_id_qld},-27.603479,152.823141), - ($$9010$$,$$BRISBANE$$,#{state_id_qld},-27.603479,152.823141), - ($$9013$$,$$BRISBANE$$,#{state_id_qld},-27.603479,152.823141), - ($$9015$$,$$BRISBANE$$,#{state_id_qld},-27.603479,152.823141), - ($$9016$$,$$BRISBANE GPO BOXES$$,#{state_id_qld},0.0,0.0), - ($$9017$$,$$BRISBANE GPO BOXES$$,#{state_id_qld},0.0,0.0), - ($$9018$$,$$BRISBANE GPO BOXES$$,#{state_id_qld},0.0,0.0), - ($$9019$$,$$BRISBANE GPO BOXES$$,#{state_id_qld},0.0,0.0), - ($$9020$$,$$BRISBANE$$,#{state_id_qld},-27.603479,152.823141), - ($$9021$$,$$BRISBANE GPO BOXES$$,#{state_id_qld},0.0,0.0), - ($$9022$$,$$BRISBANE GPO BOXES$$,#{state_id_qld},0.0,0.0), - ($$9023$$,$$BRISBANE GPO BOXES$$,#{state_id_qld},0.0,0.0), - ($$9464$$,$$NORTHGATE MC$$,#{state_id_qld},0.0,0.0), - ($$9726$$,$$GOLD COAST MC$$,#{state_id_qld},0.0,0.0), - ($$9728$$,$$GOLD COAST MC$$,#{state_id_qld},0.0,0.0), - ($$9729$$,$$GOLD COAST MC$$,#{state_id_qld},0.0,0.0), - ($$5233$$,$$Kenton Valley$$,#{state_id_sa},-34.854443,138.893616), - ($$2609$$,$$Canberra Airport$$,#{state_id_act},-35.3069,149.19), - ($$2476$$,$$Acacia Creek$$,#{state_id_nsw},-28.3733,152.321), - ($$2828$$,$$Armatree$$,#{state_id_nsw},-31.4495,148.375), - ($$2575$$,$$Balaclava$$,#{state_id_nsw},-34.4397,150.477), - ($$2795$$,$$Bathurst West$$,#{state_id_nsw},-33.4222,149.575), - ($$2826$$,$$Bogan$$,#{state_id_nsw},-33.1366,148.173), - ($$2800$$,$$Boree$$,#{state_id_nsw},-33.2787,148.937), - ($$2824$$,$$Bullagreen$$,#{state_id_nsw},-31.2959,147.884), - ($$2800$$,$$Byng$$,#{state_id_nsw},-33.2819,149.103), - ($$2171$$,$$Carnes Hill$$,#{state_id_nsw},-33.9357,150.843), - ($$2717$$,$$Coomealla$$,#{state_id_nsw},-34.0854,142.075), - ($$2652$$,$$Cowabbie$$,#{state_id_nsw},-34.6295,146.803), - ($$2870$$,$$Daroobalgie$$,#{state_id_nsw},-33.3372,148.026), - ($$2530$$,$$Dombarton$$,#{state_id_nsw},-34.4841,150.776), - ($$2372$$,$$Dumaresq Valley$$,#{state_id_nsw},-29.1147,151.461), - ($$2174$$,$$Edmondson Park$$,#{state_id_nsw},-33.9562,150.863), - ($$2818$$,$$Geurie$$,#{state_id_nsw},-32.3927,148.828), - ($$2790$$,$$Good Forest$$,#{state_id_nsw},-33.4644,116.047), - ($$2838$$,$$Goodooga$$,#{state_id_nsw},-29.1961,147.656), - ($$2829$$,$$Gungalman$$,#{state_id_nsw},-30.6746,148.191), - ($$2852$$,$$Guntawang$$,#{state_id_nsw},-32.4,149.48), - ($$1635$$,$$Hornsby Westfield$$,#{state_id_nsw},-33.7007,151.097), - ($$2089$$,$$Kurraba Point$$,#{state_id_nsw},-33.8436,151.223), - ($$2715$$,$$Kyalite$$,#{state_id_nsw},-34.7162,143.547), - ($$2476$$,$$Lower Acacia Creek$$,#{state_id_nsw},-28.4335,152.241), - ($$2734$$,$$Mallan$$,#{state_id_nsw},-34.9015,138.525), - ($$2575$$,$$Mandemar$$,#{state_id_nsw},-34.4391,150.42), - ($$2734$$,$$Murray Downs$$,#{state_id_nsw},-35.3461,143.599), - ($$2826$$,$$Nevertire$$,#{state_id_nsw},-24.0027,151.189), - ($$2648$$,$$Pine Camp$$,#{state_id_nsw},-33.7891,141.138), - ($$2824$$,$$Pine Clump$$,#{state_id_nsw},-31.4513,148.164), - ($$2648$$,$$Pomona$$,#{state_id_nsw},-34.0229,141.889), - ($$2818$$,$$Ponto$$,#{state_id_nsw},-37.9867,145.264), - ($$2006$$,$$Sydney University$$,#{state_id_nsw},-33.8862,151.184), - ($$2347$$,$$Thirldene$$,#{state_id_nsw},-30.3384,150.925), - ($$2829$$,$$Tooloon$$,#{state_id_nsw},-30.9523,148.39), - ($$2852$$,$$Two Mile Flat$$,#{state_id_nsw},-32.4185,149.315), - ($$885$$,$$Alyangula$$,#{state_id_nt},-13.8491,136.418), - ($$815$$,$$Charles Darwin University$$,#{state_id_nt},-12.3721,130.87), - ($$839$$,$$Coolalinga$$,#{state_id_nt},-16.5042,145.435), - ($$853$$,$$Tindal$$,#{state_id_nt},-29.62,152.82), - ($$834$$,$$Virginia$$,#{state_id_nt},-12.5543,131.029), - ($$822$$,$$Wurrumiyanga$$,#{state_id_nt},-33.3893,115.632), - ($$4000$$,$$Brisbane City$$,#{state_id_qld},-27.4661,153.024), - ($$4505$$,$$Burpengary East$$,#{state_id_qld},-27.1394,152.979), - ($$4002$$,$$City East$$,#{state_id_qld},-33.8929,151.258), - ($$4381$$,$$Fletcher$$,#{state_id_qld},-28.7742,151.865), - ($$4680$$,$$Gladstone Central$$,#{state_id_qld},-23.8512,151.264), - ($$4680$$,$$Gladstone Harbour$$,#{state_id_qld},-23.837,151.256), - ($$4222$$,$$Griffith University$$,#{state_id_qld},-27.959,153.382), - ($$4871$$,$$Mirriwinni$$,#{state_id_qld},-17.3986,145.909), - ($$4856$$,$$No. 4 Branch$$,#{state_id_qld},-17.7811,145.969), - ($$4859$$,$$No. 6 Branch$$,#{state_id_qld},-17.5964,145.971), - ($$4000$$,$$Petrie Terrace$$,#{state_id_qld},-27.4646,153.013), - ($$4694$$,$$Targinnie$$,#{state_id_qld},-23.797,151.109), - ($$4005$$,$$Teneriffe$$,#{state_id_qld},-27.462,153.047), - ($$5111$$,$$Edinburgh Raaf$$,#{state_id_sa},-34.7165,138.645), - ($$5090$$,$$Hope Valley$$,#{state_id_sa},-34.8438,138.702), - ($$5220$$,$$Parndana$$,#{state_id_sa},-35.7856,137.259), - ($$5151$$,$$Piccadilly$$,#{state_id_sa},-34.983,138.726), - ($$5356$$,$$Stonefield$$,#{state_id_sa},-34.36,139.28), - ($$5330$$,$$Wigley Flat$$,#{state_id_sa},-34.1764,140.262), - ($$5653$$,$$Yaninee$$,#{state_id_sa},-32.9493,135.274), - ($$7174$$,$$Copping$$,#{state_id_tas},-42.8199,147.801), - ($$7183$$,$$Highcroft$$,#{state_id_tas},-43.1387,147.768), - ($$7213$$,$$Storys Creek$$,#{state_id_tas},-41.7081,147.703), - ($$7139$$,$$Strathgordon$$,#{state_id_tas},-42.7669,146.045), - ($$3477$$,$$Avon Plains$$,#{state_id_vic},-36.5653,142.919), - ($$3564$$,$$Bamawm Extension$$,#{state_id_vic},-36.2123,144.617), - ($$3477$$,$$Beazleys Bridge$$,#{state_id_vic},-36.7002,143.166), - ($$3762$$,$$Bylands$$,#{state_id_vic},-37.3535,144.962), - ($$3477$$,$$Carapooee$$,#{state_id_vic},-36.7118,143.315), - ($$3477$$,$$Carapooee West$$,#{state_id_vic},-36.7184,143.236), - ($$3496$$,$$Cliffside$$,#{state_id_vic},-31.7611,115.782), - ($$8003$$,$$Collins Street East$$,#{state_id_vic},-37.8176,144.959), - ($$8007$$,$$Collins Street West$$,#{state_id_vic},-37.8176,144.959), - ($$3477$$,$$Coonooer Bridge$$,#{state_id_vic},-36.4737,143.315), - ($$3477$$,$$Coonooer West$$,#{state_id_vic},-36.4242,143.216), - ($$3041$$,$$Cross Keys$$,#{state_id_vic},-37.7448,144.93), - ($$3103$$,$$Deepdene$$,#{state_id_vic},-37.8128,145.068), - ($$3888$$,$$Delegate River$$,#{state_id_vic},-37.0619,148.8), - ($$3875$$,$$Eastwood$$,#{state_id_vic},-37.7994,147.631), - ($$3265$$,$$Glenormiston$$,#{state_id_vic},-38.1499,142.965), - ($$3477$$,$$Gooroc$$,#{state_id_vic},-36.467,143.202), - ($$3477$$,$$Gowar East$$,#{state_id_vic},-36.5479,143.415), - ($$3477$$,$$Gre Gre$$,#{state_id_vic},-36.6615,143.058), - ($$3477$$,$$Gre Gre North$$,#{state_id_vic},-36.5765,143.044), - ($$3477$$,$$Gre Gre South$$,#{state_id_vic},-36.6793,143.009), - ($$3374$$,$$Great Western$$,#{state_id_vic},-37.144,142.853), - ($$3920$$,$$Hmas Cerberus$$,#{state_id_vic},-38.3943,145.209), - ($$3364$$,$$Joyces Creek$$,#{state_id_vic},-37.159,143.966), - ($$3477$$,$$Kooreh$$,#{state_id_vic},-36.6412,143.385), - ($$3477$$,$$Marnoo East$$,#{state_id_vic},-36.6683,142.956), - ($$3871$$,$$Milford Grange$$,#{state_id_vic},-38.5282,146.208), - ($$3433$$,$$Monegeetta$$,#{state_id_vic},-37.4133,144.749), - ($$3477$$,$$Moolerr$$,#{state_id_vic},-36.6385,143.209), - ($$3477$$,$$Moyreisk$$,#{state_id_vic},-36.8948,143.38), - ($$3551$$,$$Myrtle Creek$$,#{state_id_vic},-36.8858,144.383), - ($$3042$$,$$Niddrie North$$,#{state_id_vic},-37.7365,144.89), - ($$3477$$,$$Paradise$$,#{state_id_vic},-36.8312,143.109), - ($$3477$$,$$Redbank$$,#{state_id_vic},-36.9393,143.333), - ($$3940$$,$$Rosebud West$$,#{state_id_vic},-38.3633,144.878), - ($$3477$$,$$Rostron$$,#{state_id_vic},-36.7782,143.181), - ($$3477$$,$$Slaty Creek$$,#{state_id_vic},-36.5415,143.302), - ($$3761$$,$$St Andrews$$,#{state_id_vic},-37.6029,145.269), - ($$3477$$,$$St Arnaud East$$,#{state_id_vic},-36.623,143.314), - ($$3477$$,$$St Arnaud North$$,#{state_id_vic},-36.593,143.214), - ($$8008$$,$$St Kilda Road Central$$,#{state_id_vic},-37.8506,144.98), - ($$3004$$,$$St Kilda Road Melbourne$$,#{state_id_vic},-37.8369,144.976), - ($$3496$$,$$Stewart$$,#{state_id_vic},-34.3225,142.256), - ($$3103$$,$$Stradbroke Park$$,#{state_id_vic},-27.4913,153.406), - ($$3477$$,$$Stuart Mill$$,#{state_id_vic},-36.808,143.289), - ($$3477$$,$$Sutherland$$,#{state_id_vic},-36.5415,143.187), - ($$3477$$,$$Swanwater$$,#{state_id_vic},-36.5394,143.115), - ($$3698$$,$$Tawonga South$$,#{state_id_vic},-36.7358,147.159), - ($$3146$$,$$Tooronga$$,#{state_id_vic},-37.8547,145.042), - ($$3477$$,$$Tottington$$,#{state_id_vic},-36.7769,143.121), - ($$3477$$,$$Traynors Lagoon$$,#{state_id_vic},-36.5889,142.944), - ($$3756$$,$$Wallan East$$,#{state_id_vic},-37.4173,145.007), - ($$3959$$,$$Waratah North$$,#{state_id_vic},-38.7709,146.073), - ($$3477$$,$$Winjallok$$,#{state_id_vic},-36.8117,143.174), - ($$3451$$,$$Woodbrook$$,#{state_id_vic},-37.0277,144.2), - ($$3496$$,$$Yatpool$$,#{state_id_vic},-34.36,142.18), - ($$6055$$,$$Dayton$$,#{state_id_wa},-31.8517,115.975), - ($$6077$$,$$Gnangara$$,#{state_id_wa},-31.7555,115.862), - ($$6923$$,$$Hillarys$$,#{state_id_wa},-37.1866,143.254), - ($$6077$$,$$Jandabup$$,#{state_id_wa},-31.7555,115.862), - ($$6079$$,$$Lexia$$,#{state_id_wa},-31.7944,115.92), - ($$6078$$,$$Mariginiup$$,#{state_id_wa},-31.709,115.846), - ($$6079$$,$$Melaleuca$$,#{state_id_wa},-31.6718,115.909), - ($$6625$$,$$Merkanooka$$,#{state_id_wa},-29.1666,115.877), - ($$6572$$,$$Piawaning$$,#{state_id_wa},-30.841,116.388), - ($$6078$$,$$Pinjar$$,#{state_id_wa},-31.709,115.846), - ($$6181$$,$$Stake Hill$$,#{state_id_wa},-32.4898,115.811), - ($$6060$$,$$Yokine South$$,#{state_id_wa},-31.9097,115.849); - ") - end -end diff --git a/knapsack_rspec_report.json b/knapsack_rspec_report.json new file mode 100644 index 0000000000..9578580ffd --- /dev/null +++ b/knapsack_rspec_report.json @@ -0,0 +1,173 @@ +{ + "spec/controllers/admin/accounts_and_billing_settings_controller_spec.rb": 5.547292709350586, + "spec/controllers/admin/business_model_configuration_controller_spec.rb": 0.3683593273162842, + "spec/controllers/admin/customers_controller_spec.rb": 0.8933048248291016, + "spec/controllers/admin/enterprises_controller_spec.rb": 5.984264850616455, + "spec/controllers/admin/order_cycles_controller_spec.rb": 2.839667558670044, + "spec/controllers/api/enterprises_controller_spec.rb": 0.2780017852783203, + "spec/controllers/api/order_cycles_controller_spec.rb": 1.8730568885803223, + "spec/controllers/base_controller_spec.rb": 0.02932429313659668, + "spec/controllers/cart_controller_spec.rb": 1.062530517578125, + "spec/controllers/checkout_controller_spec.rb": 1.6658811569213867, + "spec/controllers/enterprise_confirmations_controller_spec.rb": 1.1228001117706299, + "spec/controllers/enterprises_controller_spec.rb": 2.2625372409820557, + "spec/controllers/groups_controller_spec.rb": 0.40616846084594727, + "spec/controllers/registration_controller_spec.rb": 0.2145981788635254, + "spec/controllers/shop_controller_spec.rb": 5.298644304275513, + "spec/controllers/shops_controller_spec.rb": 0.2002561092376709, + "spec/controllers/spree/admin/adjustments_controller_spec.rb": 1.023233413696289, + "spec/controllers/spree/admin/base_controller_spec.rb": 0.28871917724609375, + "spec/controllers/spree/admin/line_items_controller_spec.rb": 14.042466402053833, + "spec/controllers/spree/admin/orders_controller_spec.rb": 12.639750480651855, + "spec/controllers/spree/admin/overview_controller_spec.rb": 0.691641092300415, + "spec/controllers/spree/admin/payment_methods_controller_spec.rb": 0.7098217010498047, + "spec/controllers/spree/admin/products_controller_spec.rb": 1.4383087158203125, + "spec/controllers/spree/admin/reports_controller_spec.rb": 47.79633665084839, + "spec/controllers/spree/admin/search_controller_spec.rb": 0.9386723041534424, + "spec/controllers/spree/admin/variants_controller_spec.rb": 2.0663084983825684, + "spec/controllers/spree/api/line_items_controller_spec.rb": 0.4743325710296631, + "spec/controllers/spree/api/products_controller_spec.rb": 8.339523792266846, + "spec/controllers/spree/api/variants_controller_spec.rb": 4.835069179534912, + "spec/controllers/spree/checkout_controller_spec.rb": 0.687798023223877, + "spec/controllers/spree/orders_controller_spec.rb": 1.7623963356018066, + "spec/controllers/spree/paypal_controller_spec.rb": 0.437147855758667, + "spec/controllers/spree/store_controller_spec.rb": 0.03699040412902832, + "spec/controllers/spree/user_sessions_controller_spec.rb": 0.09967947006225586, + "spec/controllers/user_passwords_controller_spec.rb": 0.31070899963378906, + "spec/controllers/user_registrations_controller_spec.rb": 0.36581993103027344, + "spec/features/admin/account_spec.rb": 0.32449865341186523, + "spec/features/admin/accounts_and_billing_settings_spec.rb": 15.864763259887695, + "spec/features/admin/adjustments_spec.rb": 6.825028896331787, + "spec/features/admin/authentication_spec.rb": 22.29801869392395, + "spec/features/admin/bulk_order_management_spec.rb": 112.38913011550903, + "spec/features/admin/bulk_product_update_spec.rb": 59.00568914413452, + "spec/features/admin/business_model_configuration_spec.rb": 2.5152199268341064, + "spec/features/admin/cms_spec.rb": 2.5085999965667725, + "spec/features/admin/content_spec.rb": 1.2907540798187256, + "spec/features/admin/customers_spec.rb": 33.99929761886597, + "spec/features/admin/enterprise_fees_spec.rb": 13.33712100982666, + "spec/features/admin/enterprise_groups_spec.rb": 8.689672231674194, + "spec/features/admin/enterprise_relationships_spec.rb": 7.257282733917236, + "spec/features/admin/enterprise_roles_spec.rb": 5.535412788391113, + "spec/features/admin/enterprise_user_spec.rb": 2.5493221282958984, + "spec/features/admin/enterprises/index_spec.rb": 5.77092719078064, + "spec/features/admin/enterprises_spec.rb": 34.78606820106506, + "spec/features/admin/image_settings_spec.rb": 0.4501008987426758, + "spec/features/admin/order_cycles_spec.rb": 64.186044216156, + "spec/features/admin/orders_spec.rb": 49.190918922424316, + "spec/features/admin/overview_spec.rb": 5.788672208786011, + "spec/features/admin/payment_method_spec.rb": 15.959310531616211, + "spec/features/admin/products_spec.rb": 21.46337914466858, + "spec/features/admin/reports_spec.rb": 150.51152086257935, + "spec/features/admin/shipping_methods_spec.rb": 8.671862363815308, + "spec/features/admin/tax_settings_spec.rb": 0.7941949367523193, + "spec/features/admin/variant_overrides_spec.rb": 29.70982050895691, + "spec/features/admin/variants_spec.rb": 5.565031290054321, + "spec/features/consumer/authentication_spec.rb": 12.449390649795532, + "spec/features/consumer/groups_spec.rb": 1.545715093612671, + "spec/features/consumer/producers_spec.rb": 3.3242862224578857, + "spec/features/consumer/registration_spec.rb": 2.421873092651367, + "spec/features/consumer/shopping/cart_spec.rb": 1.6924467086791992, + "spec/features/consumer/shopping/checkout_auth_spec.rb": 8.496914863586426, + "spec/features/consumer/shopping/checkout_spec.rb": 39.204933881759644, + "spec/features/consumer/shopping/shopping_spec.rb": 23.358332633972168, + "spec/features/consumer/shopping/variant_overrides_spec.rb": 58.16736888885498, + "spec/features/consumer/shops_spec.rb": 6.636866092681885, + "spec/helpers/admin/business_model_configuration_helper_spec.rb": 0.2595028877258301, + "spec/helpers/checkout_helper_spec.rb": 0.10617446899414062, + "spec/helpers/groups_helper_spec.rb": 0.007729053497314453, + "spec/helpers/html_helper_spec.rb": 0.05157279968261719, + "spec/helpers/injection_helper_spec.rb": 0.6142556667327881, + "spec/helpers/navigation_helper_spec.rb": 0.02951979637145996, + "spec/helpers/order_cycles_helper_spec.rb": 0.5953588485717773, + "spec/helpers/products_helper_spec.rb": 0.009511232376098633, + "spec/helpers/shared_helper_spec.rb": 0.017564058303833008, + "spec/helpers/shop_helper_spec.rb": 0.05760025978088379, + "spec/jobs/confirm_order_job_spec.rb": 0.0458524227142334, + "spec/jobs/confirm_signup_job_spec.rb": 0.021564006805419922, + "spec/jobs/finalize_account_invoices_spec.rb": 4.505181312561035, + "spec/jobs/order_cycle_notification_job_spec.rb": 2.0606272220611572, + "spec/jobs/update_account_invoices_spec.rb": 18.434475898742676, + "spec/jobs/update_billable_periods_spec.rb": 4.850176572799683, + "spec/jobs/welcome_enterprise_job_spec.rb": 0.07065534591674805, + "spec/lib/open_food_network/bulk_coop_report_spec.rb": 4.789663553237915, + "spec/lib/open_food_network/customers_report_spec.rb": 2.419727325439453, + "spec/lib/open_food_network/distribution_change_validator_spec.rb": 0.10607743263244629, + "spec/lib/open_food_network/enterprise_fee_applicator_spec.rb": 0.7333858013153076, + "spec/lib/open_food_network/enterprise_fee_calculator_spec.rb": 7.406745195388794, + "spec/lib/open_food_network/enterprise_injection_data_spec.rb": 0.291548490524292, + "spec/lib/open_food_network/enterprise_issue_validator_spec.rb": 0.09764814376831055, + "spec/lib/open_food_network/feature_toggle_spec.rb": 0.010193109512329102, + "spec/lib/open_food_network/group_buy_report_spec.rb": 3.708569049835205, + "spec/lib/open_food_network/last_used_address_spec.rb": 0.0254666805267334, + "spec/lib/open_food_network/lettuce_share_report_spec.rb": 2.3206725120544434, + "spec/lib/open_food_network/option_value_namer_spec.rb": 0.06185555458068848, + "spec/lib/open_food_network/order_and_distributor_report_spec.rb": 1.0406858921051025, + "spec/lib/open_food_network/order_cycle_form_applicator_spec.rb": 4.533008337020874, + "spec/lib/open_food_network/order_cycle_management_report_spec.rb": 2.036308526992798, + "spec/lib/open_food_network/order_cycle_permissions_spec.rb": 23.74185061454773, + "spec/lib/open_food_network/order_grouper_spec.rb": 0.029039621353149414, + "spec/lib/open_food_network/orders_and_fulfillments_report_spec.rb": 5.135573148727417, + "spec/lib/open_food_network/packing_report_spec.rb": 5.088447093963623, + "spec/lib/open_food_network/permissions_spec.rb": 8.881855249404907, + "spec/lib/open_food_network/products_and_inventory_report_spec.rb": 3.55375337600708, + "spec/lib/open_food_network/referer_parser_spec.rb": 0.014271259307861328, + "spec/lib/open_food_network/reports/report_spec.rb": 0.02238297462463379, + "spec/lib/open_food_network/reports/row_spec.rb": 0.0031762123107910156, + "spec/lib/open_food_network/reports/rule_spec.rb": 0.013959169387817383, + "spec/lib/open_food_network/sales_tax_report_spec.rb": 0.10717129707336426, + "spec/lib/open_food_network/scope_variant_to_hub_spec.rb": 2.4846229553222656, + "spec/lib/open_food_network/user_balance_calculator_spec.rb": 3.4277901649475098, + "spec/lib/open_food_network/users_and_enterprises_report_spec.rb": 0.40532779693603516, + "spec/lib/open_food_network/xero_invoices_report_spec.rb": 1.1586685180664062, + "spec/lib/spree/product_filters_spec.rb": 0.13163042068481445, + "spec/mailers/enterprise_mailer_spec.rb": 0.4537942409515381, + "spec/mailers/order_mailer_spec.rb": 1.452355146408081, + "spec/mailers/producer_mailer_spec.rb": 8.775528192520142, + "spec/mailers/user_mailer_spec.rb": 0.057527780532836914, + "spec/models/adjustment_metadata_spec.rb": 0.22016620635986328, + "spec/models/billable_period_spec.rb": 2.06524658203125, + "spec/models/calculator/weight_spec.rb": 0.009344100952148438, + "spec/models/cart_spec.rb": 4.099429130554199, + "spec/models/customer_spec.rb": 0.07328605651855469, + "spec/models/enterprise_caching_spec.rb": 0.8475983142852783, + "spec/models/enterprise_fee_spec.rb": 3.1999905109405518, + "spec/models/enterprise_group_spec.rb": 0.30861926078796387, + "spec/models/enterprise_relationship_spec.rb": 2.1849746704101562, + "spec/models/enterprise_spec.rb": 17.679611682891846, + "spec/models/exchange_spec.rb": 13.899227857589722, + "spec/models/model_set_spec.rb": 0.22760748863220215, + "spec/models/order_cycle_spec.rb": 10.680967569351196, + "spec/models/product_distribution_spec.rb": 2.227938413619995, + "spec/models/spree/ability_spec.rb": 15.278357028961182, + "spec/models/spree/addresses_spec.rb": 0.055602312088012695, + "spec/models/spree/adjustment_spec.rb": 9.196375846862793, + "spec/models/spree/classification_spec.rb": 0.161299467086792, + "spec/models/spree/image_spec.rb": 0.007464408874511719, + "spec/models/spree/line_item_spec.rb": 13.545411586761475, + "spec/models/spree/order_populator_spec.rb": 1.635932207107544, + "spec/models/spree/order_spec.rb": 10.645411968231201, + "spec/models/spree/payment_method_spec.rb": 0.0733034610748291, + "spec/models/spree/payment_spec.rb": 1.691227912902832, + "spec/models/spree/preferences/file_configuration_spec.rb": 0.03429675102233887, + "spec/models/spree/product_spec.rb": 17.406191110610962, + "spec/models/spree/shipping_method_spec.rb": 3.0447566509246826, + "spec/models/spree/tax_rate_spec.rb": 0.44750261306762695, + "spec/models/spree/taxon_spec.rb": 0.553098201751709, + "spec/models/spree/user_spec.rb": 1.2693369388580322, + "spec/models/spree/variant_spec.rb": 13.75825023651123, + "spec/models/variant_override_spec.rb": 4.086935520172119, + "spec/performance/injection_helper_spec.rb": 6.890667676925659, + "spec/performance/orders_controller_spec.rb": 0.031180143356323242, + "spec/performance/shop_controller_spec.rb": 18.19426918029785, + "spec/requests/large_request_spec.rb": 0.02229022979736328, + "spec/requests/shop_spec.rb": 1.0012562274932861, + "spec/serializers/admin/enterprise_serializer_spec.rb": 0.10484433174133301, + "spec/serializers/admin/exchange_serializer_spec.rb": 0.7569985389709473, + "spec/serializers/admin/for_order_cycle/enterprise_serializer_spec.rb": 0.4293792247772217, + "spec/serializers/admin/index_enterprise_serializer_spec.rb": 1.2506742477416992, + "spec/serializers/admin/variant_override_serializer_spec.rb": 0.38981151580810547, + "spec/serializers/enterprise_serializer_spec.rb": 0.3511006832122803, + "spec/serializers/spree/product_serializer_spec.rb": 0.26622653007507324, + "spec/serializers/spree/variant_serializer_spec.rb": 0.30304574966430664 +} diff --git a/lib/discourse/single_sign_on.rb b/lib/discourse/single_sign_on.rb new file mode 100644 index 0000000000..046a2d677c --- /dev/null +++ b/lib/discourse/single_sign_on.rb @@ -0,0 +1,107 @@ +# This class is the reference implementation of a SSO provider from Discourse. + +module Discourse + class SingleSignOn + ACCESSORS = [:nonce, :name, :username, :email, :avatar_url, :avatar_force_update, :require_activation, + :about_me, :external_id, :return_sso_url, :admin, :moderator, :suppress_welcome_message] + FIXNUMS = [] + BOOLS = [:avatar_force_update, :admin, :moderator, :require_activation, :suppress_welcome_message] + NONCE_EXPIRY_TIME = 10.minutes + + attr_accessor(*ACCESSORS) + attr_accessor :sso_secret, :sso_url + + def self.sso_secret + raise RuntimeError, "sso_secret not implemented on class, be sure to set it on instance" + end + + def self.sso_url + raise RuntimeError, "sso_url not implemented on class, be sure to set it on instance" + end + + def self.parse(payload, sso_secret = nil) + sso = new + sso.sso_secret = sso_secret if sso_secret + + parsed = Rack::Utils.parse_query(payload) + if sso.sign(parsed["sso"]) != parsed["sig"] + diags = "\n\nsso: #{parsed["sso"]}\n\nsig: #{parsed["sig"]}\n\nexpected sig: #{sso.sign(parsed["sso"])}" + if parsed["sso"] =~ /[^a-zA-Z0-9=\r\n\/+]/m + raise RuntimeError, "The SSO field should be Base64 encoded, using only A-Z, a-z, 0-9, +, /, and = characters. Your input contains characters we don't understand as Base64, see http://en.wikipedia.org/wiki/Base64 #{diags}" + else + raise RuntimeError, "Bad signature for payload #{diags}" + end + end + + decoded = Base64.decode64(parsed["sso"]) + decoded_hash = Rack::Utils.parse_query(decoded) + + ACCESSORS.each do |k| + val = decoded_hash[k.to_s] + val = val.to_i if FIXNUMS.include? k + if BOOLS.include? k + val = ["true", "false"].include?(val) ? val == "true" : nil + end + sso.send("#{k}=", val) + end + + decoded_hash.each do |k,v| + # 1234567 + # custom. + # + if k[0..6] == "custom." + field = k[7..-1] + sso.custom_fields[field] = v + end + end + + sso + end + + def sso_secret + @sso_secret || self.class.sso_secret + end + + def sso_url + @sso_url || self.class.sso_url + end + + def custom_fields + @custom_fields ||= {} + end + + + def sign(payload) + OpenSSL::HMAC.hexdigest("sha256", sso_secret, payload) + end + + + def to_url(base_url=nil) + base = "#{base_url || sso_url}" + "#{base}#{base.include?('?') ? '&' : '?'}#{payload}" + end + + def payload + payload = Base64.encode64(unsigned_payload) + "sso=#{CGI::escape(payload)}&sig=#{sign(payload)}" + end + + def unsigned_payload + payload = {} + ACCESSORS.each do |k| + next if (val = send k) == nil + + payload[k] = val + end + + if @custom_fields + @custom_fields.each do |k,v| + payload["custom.#{k}"] = v.to_s + end + end + + Rack::Utils.build_query(payload) + end + + end +end diff --git a/lib/open_food_network/accounts_and_billing_settings_validator.rb b/lib/open_food_network/accounts_and_billing_settings_validator.rb new file mode 100644 index 0000000000..0269f9ff43 --- /dev/null +++ b/lib/open_food_network/accounts_and_billing_settings_validator.rb @@ -0,0 +1,41 @@ +# This class is a lightweight model used to validate preferences for accounts and billing settings +# when they are submitted to the AccountsAndBillingSettingsController + +module OpenFoodNetwork + class AccountsAndBillingSettingsValidator + include ActiveModel::Validations + + attr_accessor :accounts_distributor_id, :default_accounts_payment_method_id, :default_accounts_shipping_method_id + attr_accessor :auto_update_invoices, :auto_finalize_invoices + + validate :ensure_accounts_distributor_set + validate :ensure_default_payment_method_set + validate :ensure_default_shipping_method_set + # validate :ensure_billing_info_collected, unless: lambda { create_invoices_for_enterprise_users == '0' } + + def initialize(attr, button=nil) + attr.each { |k,v| instance_variable_set("@#{k}", v) } + @button = button + end + + def ensure_accounts_distributor_set + unless Enterprise.find_by_id(accounts_distributor_id) + errors.add(:accounts_distributor, "must be set if you wish to create invoices for enterprise users.") + end + end + + def ensure_default_payment_method_set + unless Enterprise.find_by_id(accounts_distributor_id) && + Enterprise.find_by_id(accounts_distributor_id).payment_methods.find_by_id(default_accounts_payment_method_id) + errors.add(:default_payment_method, "must be set if you wish to create invoices for enterprise users.") + end + end + + def ensure_default_shipping_method_set + unless Enterprise.find_by_id(accounts_distributor_id) && + Enterprise.find_by_id(accounts_distributor_id).shipping_methods.find_by_id(default_accounts_shipping_method_id) + errors.add(:default_shipping_method, "must be set if you wish to create invoices for enterprise users.") + end + end + end +end diff --git a/lib/open_food_network/bill_calculator.rb b/lib/open_food_network/bill_calculator.rb new file mode 100644 index 0000000000..84457d6530 --- /dev/null +++ b/lib/open_food_network/bill_calculator.rb @@ -0,0 +1,19 @@ +module OpenFoodNetwork + class BillCalculator + attr_accessor :turnover, :fixed, :rate, :cap, :tax_rate + + def initialize(opts={}) + @turnover = opts[:turnover] || 0 + @fixed = opts[:fixed] || Spree::Config[:account_invoices_monthly_fixed] + @rate = opts[:rate] || Spree::Config[:account_invoices_monthly_rate] + @cap = opts[:cap] || Spree::Config[:account_invoices_monthly_cap] + @tax_rate = opts[:tax_rate] || Spree::Config[:account_invoices_tax_rate] + end + + def bill + bill = fixed + (turnover * rate) + bill = cap > 0 ? [bill, cap].min : bill + bill * (1 + tax_rate) + end + end +end diff --git a/lib/open_food_network/bulk_coop_report.rb b/lib/open_food_network/bulk_coop_report.rb new file mode 100644 index 0000000000..03d6d21924 --- /dev/null +++ b/lib/open_food_network/bulk_coop_report.rb @@ -0,0 +1,124 @@ +require 'open_food_network/reports/bulk_coop_supplier_report' +require 'open_food_network/reports/bulk_coop_allocation_report' + +module OpenFoodNetwork + class BulkCoopReport + attr_reader :params + def initialize(user, params = {}) + @params = params + @user = user + + @supplier_report = OpenFoodNetwork::Reports::BulkCoopSupplierReport.new + @allocation_report = OpenFoodNetwork::Reports::BulkCoopAllocationReport.new + end + + def header + case params[:report_type] + when "bulk_coop_supplier_report" + @supplier_report.header + when "bulk_coop_allocation" + @allocation_report.header + when "bulk_coop_packing_sheets" + ["Customer", "Product", "Variant", "Sum Total"] + when "bulk_coop_customer_payments" + ["Customer", "Date of Order", "Total Cost", "Amount Owing", "Amount Paid"] + else + ["Supplier", "Product", "Bulk Unit Size", "Variant", "Weight", "Sum Total", "Sum Max Total", "Units Required", "Remainder"] + end + end + + def search + permissions.visible_orders.complete.not_state(:canceled).search(params[:q]) + end + + def table_items + orders = search.result + + line_items = permissions.visible_line_items.merge(Spree::LineItem.where(order_id: orders)) + + line_items_with_hidden_details = + permissions.editable_line_items.empty? ? line_items : line_items.where('"spree_line_items"."id" NOT IN (?)', permissions.editable_line_items) + + line_items.select{ |li| line_items_with_hidden_details.include? li }.each do |line_item| + # TODO We should really be hiding customer code here too, but until we + # have an actual association between order and customer, it's a bit tricky + line_item.order.bill_address.andand.assign_attributes(firstname: "HIDDEN", lastname: "", phone: "", address1: "", address2: "", city: "", zipcode: "", state: nil) + line_item.order.ship_address.andand.assign_attributes(firstname: "HIDDEN", lastname: "", phone: "", address1: "", address2: "", city: "", zipcode: "", state: nil) + line_item.order.assign_attributes(email: "HIDDEN") + end + line_items + end + + def rules + case params[:report_type] + when "bulk_coop_supplier_report" + @supplier_report.rules + when "bulk_coop_allocation" + @allocation_report.rules + when "bulk_coop_packing_sheets" + [ { group_by: proc { |li| li.product }, + sort_by: proc { |product| product.name } }, + { group_by: proc { |li| li.full_name }, + sort_by: proc { |full_name| full_name } }, + { group_by: proc { |li| li.order }, + sort_by: proc { |order| order.to_s } } ] + when "bulk_coop_customer_payments" + [ { group_by: proc { |li| li.order }, + sort_by: proc { |order| order.completed_at } } ] + else + [ { group_by: proc { |li| li.product.supplier }, + sort_by: proc { |supplier| supplier.name } }, + { group_by: proc { |li| li.product }, + sort_by: proc { |product| product.name }, + summary_columns: [ proc { |lis| lis.first.product.supplier.name }, + proc { |lis| lis.first.product.name }, + proc { |lis| lis.first.product.group_buy_unit_size || 0.0 }, + proc { |lis| "" }, + proc { |lis| "" }, + proc { |lis| lis.sum { |li| li.quantity * (li.weight_from_unit_value || 0) } }, + proc { |lis| lis.sum { |li| (li.max_quantity || 0) * (li.weight_from_unit_value || 0) } }, + proc { |lis| ( (lis.first.product.group_buy_unit_size || 0).zero? ? 0 : ( lis.sum { |li| ( [li.max_quantity || 0, li.quantity || 0].max ) * (li.weight_from_unit_value || 0) } / lis.first.product.group_buy_unit_size ) ).floor }, + proc { |lis| lis.sum { |li| ( [li.max_quantity || 0, li.quantity || 0].max ) * (li.weight_from_unit_value || 0) } - ( ( (lis.first.product.group_buy_unit_size || 0).zero? ? 0 : ( lis.sum { |li| ( [li.max_quantity || 0, li.quantity || 0].max ) * (li.weight_from_unit_value || 0) } / lis.first.product.group_buy_unit_size ) ).floor * (lis.first.product.group_buy_unit_size || 0) ) } ] }, + { group_by: proc { |li| li.full_name }, + sort_by: proc { |full_name| full_name } } ] + end + end + + def columns + case params[:report_type] + when "bulk_coop_supplier_report" + @supplier_report.columns + when "bulk_coop_allocation" + @allocation_report.columns + when "bulk_coop_packing_sheets" + [ proc { |lis| lis.first.order.bill_address.firstname + " " + lis.first.order.bill_address.lastname }, + proc { |lis| lis.first.product.name }, + proc { |lis| lis.first.full_name }, + proc { |lis| lis.sum { |li| li.quantity } } ] + when "bulk_coop_customer_payments" + [ proc { |lis| lis.first.order.bill_address.firstname + " " + lis.first.order.bill_address.lastname }, + proc { |lis| lis.first.order.completed_at.to_s }, + proc { |lis| lis.map { |li| li.order }.uniq.sum { |o| o.total } }, + proc { |lis| lis.map { |li| li.order }.uniq.sum { |o| o.outstanding_balance } }, + proc { |lis| lis.map { |li| li.order }.uniq.sum { |o| o.payment_total } } ] + else + [ proc { |lis| lis.first.product.supplier.name }, + proc { |lis| lis.first.product.name }, + proc { |lis| lis.first.product.group_buy_unit_size || 0.0 }, + proc { |lis| lis.first.full_name }, + proc { |lis| lis.first.weight_from_unit_value || 0 }, + proc { |lis| lis.sum { |li| li.quantity } }, + proc { |lis| lis.sum { |li| li.max_quantity || 0 } }, + proc { |lis| "" }, + proc { |lis| "" } ] + end + end + + private + + def permissions + return @permissions unless @permissions.nil? + @permissions = OpenFoodNetwork::Permissions.new(@user) + end + end +end diff --git a/lib/open_food_network/business_model_configuration_validator.rb b/lib/open_food_network/business_model_configuration_validator.rb new file mode 100644 index 0000000000..d83d94ffc1 --- /dev/null +++ b/lib/open_food_network/business_model_configuration_validator.rb @@ -0,0 +1,20 @@ +# This class is a lightweight model used to validate preferences for business model configuration +# when they are submitted to the BusinessModelConfigurationController + +module OpenFoodNetwork + class BusinessModelConfigurationValidator + include ActiveModel::Validations + + attr_accessor :account_invoices_monthly_fixed, :account_invoices_monthly_rate, :account_invoices_monthly_cap, :account_invoices_tax_rate + + validates :account_invoices_monthly_fixed, presence: true, numericality: { greater_than_or_equal_to: 0 } + validates :account_invoices_monthly_rate, presence: true, numericality: { greater_than_or_equal_to: 0, less_than_or_equal_to: 1 } + validates :account_invoices_monthly_cap, presence: true, numericality: { greater_than_or_equal_to: 0 } + validates :account_invoices_tax_rate, presence: true, numericality: { greater_than_or_equal_to: 0, less_than_or_equal_to: 1 } + + def initialize(attr, button=nil) + attr.each { |k,v| instance_variable_set("@#{k}", v) } + @button = button + end + end +end diff --git a/lib/open_food_network/enterprise_fee_applicator.rb b/lib/open_food_network/enterprise_fee_applicator.rb index 070cb4a91c..4962bc148e 100644 --- a/lib/open_food_network/enterprise_fee_applicator.rb +++ b/lib/open_food_network/enterprise_fee_applicator.rb @@ -35,43 +35,8 @@ module OpenFoodNetwork tax_rates = enterprise_fee.tax_category ? enterprise_fee.tax_category.tax_rates.match(order) : [] tax_rates.sum do |rate| - compute_tax rate, adjustment.amount + rate.compute_tax adjustment.amount end end - - # Apply a TaxRate to a particular amount. TaxRates normally compute against - # LineItems or Orders, so we mock out a line item here to fit the interface - # that our calculator (usually DefaultTax) expects. - def compute_tax(tax_rate, amount) - product = OpenStruct.new tax_category: tax_rate.tax_category - line_item = Spree::LineItem.new quantity: 1 - line_item.define_singleton_method(:product) { product } - line_item.define_singleton_method(:price) { amount } - - # The enterprise fee adjustments for which we're calculating tax are always inclusive of - # tax. However, there's nothing to stop an admin from setting one up with a tax rate - # that's marked as not inclusive of tax, and that would result in the DefaultTax - # calculator generating a slightly incorrect value. Therefore, we treat the tax - # rate as inclusive of tax for the calculations below, regardless of its original - # setting. - with_tax_included_in_price(tax_rate) do - tax_rate.calculator.compute line_item - end - end - - def with_tax_included_in_price(tax_rate) - old_included_in_price = tax_rate.included_in_price - - tax_rate.included_in_price = true - tax_rate.calculator.calculable.included_in_price = true - - result = yield - - tax_rate.included_in_price = old_included_in_price - tax_rate.calculator.calculable.included_in_price = old_included_in_price - - result - end end end - diff --git a/lib/open_food_network/enterprise_fee_calculator.rb b/lib/open_food_network/enterprise_fee_calculator.rb index c8070f2ab2..d65a80c0dc 100644 --- a/lib/open_food_network/enterprise_fee_calculator.rb +++ b/lib/open_food_network/enterprise_fee_calculator.rb @@ -7,17 +7,35 @@ module OpenFoodNetwork @order_cycle = order_cycle end + def indexed_fees_for(variant) + load_enterprise_fees unless @indexed_enterprise_fees + + indexed_enterprise_fees_for(variant).sum do |enterprise_fee| + calculate_fee_for variant, enterprise_fee + end + end + + def indexed_fees_by_type_for(variant) + load_enterprise_fees unless @indexed_enterprise_fees + + indexed_enterprise_fees_for(variant).inject({}) do |fees, enterprise_fee| + fees[enterprise_fee.fee_type.to_sym] ||= 0 + fees[enterprise_fee.fee_type.to_sym] += calculate_fee_for variant, enterprise_fee + fees + end.select { |fee_type, amount| amount > 0 } + end + def fees_for(variant) per_item_enterprise_fee_applicators_for(variant).sum do |applicator| - calculate_fee_for variant, applicator + calculate_fee_for variant, applicator.enterprise_fee end end def fees_by_type_for(variant) per_item_enterprise_fee_applicators_for(variant).inject({}) do |fees, applicator| fees[applicator.enterprise_fee.fee_type.to_sym] ||= 0 - fees[applicator.enterprise_fee.fee_type.to_sym] += calculate_fee_for variant, applicator + fees[applicator.enterprise_fee.fee_type.to_sym] += calculate_fee_for variant, applicator.enterprise_fee fees end.select { |fee_type, amount| amount > 0 } end @@ -45,12 +63,50 @@ module OpenFoodNetwork private - def calculate_fee_for(variant, applicator) + def load_enterprise_fees + @indexed_enterprise_fees = {} + + exchange_fees = per_item_enterprise_fees_with_exchange_details + load_exchange_fees exchange_fees + load_coordinator_fees + end + + def per_item_enterprise_fees_with_exchange_details + EnterpriseFee. + per_item. + joins(:exchanges => :exchange_variants). + where('exchanges.order_cycle_id = ?', @order_cycle.id). + merge(Exchange.supplying_to(@distributor)). + select('enterprise_fees.*, exchange_variants.variant_id AS variant_id') + end + + def load_exchange_fees(exchange_fees) + exchange_fees.each do |enterprise_fee| + @indexed_enterprise_fees[enterprise_fee.variant_id.to_i] ||= [] + @indexed_enterprise_fees[enterprise_fee.variant_id.to_i] << enterprise_fee + end + end + + def load_coordinator_fees + @order_cycle.coordinator_fees.per_item.each do |enterprise_fee| + @order_cycle.variants.map(&:id).each do |variant_id| + @indexed_enterprise_fees[variant_id] ||= [] + @indexed_enterprise_fees[variant_id] << enterprise_fee + end + end + end + + def indexed_enterprise_fees_for(variant) + @indexed_enterprise_fees[variant.id] || [] + end + + + def calculate_fee_for(variant, enterprise_fee) # Spree's Calculator interface accepts Orders or LineItems, # so we meet that interface with a struct. # Amount is faked, this is a method on LineItem line_item = OpenStruct.new variant: variant, quantity: 1, amount: variant.price - applicator.enterprise_fee.compute_amount(line_item) + enterprise_fee.compute_amount(line_item) end def per_item_enterprise_fee_applicators_for(variant) diff --git a/lib/open_food_network/enterprise_injection_data.rb b/lib/open_food_network/enterprise_injection_data.rb new file mode 100644 index 0000000000..87516007c6 --- /dev/null +++ b/lib/open_food_network/enterprise_injection_data.rb @@ -0,0 +1,27 @@ +module OpenFoodNetwork + class EnterpriseInjectionData + def active_distributors + @active_distributors ||= Enterprise.distributors_with_active_order_cycles + end + + def earliest_closing_times + @earliest_closing_times ||= OrderCycle.earliest_closing_times + end + + def shipping_method_services + @shipping_method_services ||= Spree::ShippingMethod.services + end + + def relatives + @relatives ||= EnterpriseRelationship.relatives(true) + end + + def supplied_taxons + @supplied_taxons ||= Spree::Taxon.supplied_taxons + end + + def distributed_taxons + @distributed_taxons ||= Spree::Taxon.distributed_taxons + end + end +end diff --git a/lib/open_food_network/enterprise_issue_validator.rb b/lib/open_food_network/enterprise_issue_validator.rb new file mode 100644 index 0000000000..8fa88c6c74 --- /dev/null +++ b/lib/open_food_network/enterprise_issue_validator.rb @@ -0,0 +1,72 @@ +module OpenFoodNetwork + class EnterpriseIssueValidator + include Rails.application.routes.url_helpers + include Spree::Core::UrlHelpers + + def initialize(enterprise) + @enterprise = enterprise + end + + def issues + issues = [] + + issues << { + description: "#{@enterprise.name} currently has no shipping methods.", + link: "Create New" + } unless shipping_methods_ok? + + issues << { + description: "#{@enterprise.name} currently has no payment methods.", + link: "Create New" + } unless payment_methods_ok? + + issues << { + description: "Email confirmation is pending. We've sent a confirmation email to #{@enterprise.email}.", + link: "Resend Email" + } unless confirmed? + + issues + end + + def issues_summary(opts={}) + if !opts[:confirmation_only] && !shipping_methods_ok? && !payment_methods_ok? + 'no shipping or payment methods' + elsif !opts[:confirmation_only] && !shipping_methods_ok? + 'no shipping methods' + elsif !opts[:confirmation_only] && !payment_methods_ok? + 'no payment methods' + elsif !confirmed? + 'unconfirmed' + end + end + + def warnings + warnings = [] + + warnings << { + description: "#{@enterprise.name} is not visible and so cannot be found on the map or in searches", + link: "Edit" + } unless @enterprise.visible + + warnings + end + + + private + + def shipping_methods_ok? + # Refactor into boolean + return true unless @enterprise.is_distributor + @enterprise.shipping_methods.any? + end + + def payment_methods_ok? + return true unless @enterprise.is_distributor + @enterprise.payment_methods.available.any? + end + + def confirmed? + @enterprise.confirmed? + end + end +end diff --git a/lib/open_food_network/last_used_address.rb b/lib/open_food_network/last_used_address.rb new file mode 100644 index 0000000000..66c06fceb6 --- /dev/null +++ b/lib/open_food_network/last_used_address.rb @@ -0,0 +1,28 @@ +module OpenFoodNetwork + class LastUsedAddress + def initialize(email) + @email = email + end + + def last_used_bill_address + recent_orders.detect(&:bill_address).andand.bill_address + end + + def last_used_ship_address + recent_orders.detect { |o| + o.ship_address && o.shipping_method.andand.require_ship_address + }.andand.ship_address + end + + + private + + def recent_orders + Spree::Order. + order("id DESC"). + where(email: @email). + where("state != 'cart'"). + limit(8) + end + end +end diff --git a/lib/open_food_network/lettuce_share_report.rb b/lib/open_food_network/lettuce_share_report.rb new file mode 100644 index 0000000000..43fe8529c5 --- /dev/null +++ b/lib/open_food_network/lettuce_share_report.rb @@ -0,0 +1,79 @@ +require 'open_food_network/products_and_inventory_report_base' + +module OpenFoodNetwork + class LettuceShareReport < ProductsAndInventoryReportBase + def header + [ + "PRODUCT", + "Description", + "Qty", + "Pack Size", + "Unit", + "Unit Price", + "Total", + "GST incl.", + "Grower and growing method", + "Taxon" + ] + end + + def table + variants.select { |v| v.in_stock? } + .map do |variant| + [ + variant.product.name, + variant.full_name, + '', + OptionValueNamer.new(variant).value, + OptionValueNamer.new(variant).unit, + variant.price, + '', + gst(variant), + grower_and_method(variant), + variant.product.primary_taxon.name + ] + end + end + + + private + + def gst(variant) + tax_category = variant.product.tax_category + if tax_category && tax_category.tax_rates.present? + tax_rate = tax_category.tax_rates.first + line_item = mock_line_item(variant, tax_category) + tax_rate.calculator.compute line_item + else + 0 + end + end + + def mock_line_item(variant, tax_category) + product = OpenStruct.new tax_category: tax_category + line_item = Spree::LineItem.new quantity: 1 + line_item.define_singleton_method(:product) { variant.product } + line_item.define_singleton_method(:price) { variant.price } + line_item + end + + def grower_and_method(variant) + cert = certification(variant) + + result = producer_name(variant) + result += " (#{cert})" if cert.present? + result + end + + def producer_name(variant) + variant.product.supplier.name + end + + def certification(variant) + variant.product.properties_including_inherited.map do |p| + "#{p[:name]} - #{p[:value]}" + end.join(', ') + end + + end +end diff --git a/lib/open_food_network/option_value_namer.rb b/lib/open_food_network/option_value_namer.rb index 2c2383906d..6a77d9cb35 100644 --- a/lib/open_food_network/option_value_namer.rb +++ b/lib/open_food_network/option_value_namer.rb @@ -15,6 +15,17 @@ module OpenFoodNetwork name_fields.join ' ' end + def value + value, _ = option_value_value_unit + value + end + + def unit + _, unit = option_value_value_unit + unit + end + + private def value_scaled? diff --git a/lib/open_food_network/order_and_distributor_report.rb b/lib/open_food_network/order_and_distributor_report.rb index 2662b176dd..570087e493 100644 --- a/lib/open_food_network/order_and_distributor_report.rb +++ b/lib/open_food_network/order_and_distributor_report.rb @@ -1,4 +1,3 @@ - module OpenFoodNetwork class OrderAndDistributorReport @@ -8,23 +7,25 @@ module OpenFoodNetwork def header ["Order date", "Order Id", - "Customer Name","Customer Email", "Customer Phone", "Customer City", - "SKU", "Item name", "Variant", "Quantity", "Max Quantity", "Cost", "Shipping cost", - "Payment method", - "Distributor", "Distributor address", "Distributor city", "Distributor postcode", "Shipping instructions"] + "Customer Name","Customer Email", "Customer Phone", "Customer City", + "SKU", "Item name", "Variant", "Quantity", "Max Quantity", "Cost", "Shipping cost", + "Payment method", + "Distributor", "Distributor address", "Distributor city", "Distributor postcode", "Shipping instructions"] end def table order_and_distributor_details = [] + @orders.each do |order| order.line_items.each do |line_item| order_and_distributor_details << [order.created_at, order.id, order.bill_address.full_name, order.email, order.bill_address.phone, order.bill_address.city, - line_item.product.sku, line_item.product.name, line_item.variant.options_text, line_item.quantity, line_item.max_quantity, line_item.price * line_item.quantity, line_item.distribution_fee, + line_item.product.sku, line_item.product.name, line_item.options_text, line_item.quantity, line_item.max_quantity, line_item.price * line_item.quantity, line_item.distribution_fee, order.payments.first.andand.payment_method.andand.name, order.distributor.andand.name, order.distributor.address.address1, order.distributor.address.city, order.distributor.address.zipcode, order.special_instructions ] end end + order_and_distributor_details end end diff --git a/lib/open_food_network/order_cycle_form_applicator.rb b/lib/open_food_network/order_cycle_form_applicator.rb index 69ba284cd8..3e81d6c9f1 100644 --- a/lib/open_food_network/order_cycle_form_applicator.rb +++ b/lib/open_food_network/order_cycle_form_applicator.rb @@ -1,3 +1,5 @@ +require 'open_food_network/order_cycle_permissions' + module OpenFoodNetwork # There are two translator classes on the boundary between Angular and Rails: On the Angular side, @@ -21,10 +23,12 @@ module OpenFoodNetwork if exchange_exists?(exchange[:enterprise_id], @order_cycle.coordinator_id, true) update_exchange(exchange[:enterprise_id], @order_cycle.coordinator_id, true, - {variant_ids: variant_ids, enterprise_fee_ids: enterprise_fee_ids}) + {variant_ids: variant_ids, enterprise_fee_ids: enterprise_fee_ids, + receival_instructions: exchange[:receival_instructions]}) else add_exchange(exchange[:enterprise_id], @order_cycle.coordinator_id, true, - {variant_ids: variant_ids, enterprise_fee_ids: enterprise_fee_ids}) + {variant_ids: variant_ids, enterprise_fee_ids: enterprise_fee_ids, + receival_instructions: exchange[:receival_instructions],}) end end @@ -35,12 +39,16 @@ module OpenFoodNetwork if exchange_exists?(@order_cycle.coordinator_id, exchange[:enterprise_id], false) update_exchange(@order_cycle.coordinator_id, exchange[:enterprise_id], false, - {variant_ids: variant_ids, enterprise_fee_ids: enterprise_fee_ids, - pickup_time: exchange[:pickup_time], pickup_instructions: exchange[:pickup_instructions]}) + {variant_ids: variant_ids, + enterprise_fee_ids: enterprise_fee_ids, + pickup_time: exchange[:pickup_time], + pickup_instructions: exchange[:pickup_instructions]}) else add_exchange(@order_cycle.coordinator_id, exchange[:enterprise_id], false, - {variant_ids: variant_ids, enterprise_fee_ids: enterprise_fee_ids, - pickup_time: exchange[:pickup_time], pickup_instructions: exchange[:pickup_instructions]}) + {variant_ids: variant_ids, + enterprise_fee_ids: enterprise_fee_ids, + pickup_time: exchange[:pickup_time], + pickup_instructions: exchange[:pickup_instructions]}) end end @@ -149,7 +157,7 @@ module OpenFoodNetwork variants[variant_id.to_i] = value if permitted.include?(variant_id.to_i) end - variants.select { |k, v| v }.keys.map { |k| k.to_i }.sort + variants_to_a variants end def outgoing_exchange_variant_ids(attrs) @@ -162,10 +170,33 @@ module OpenFoodNetwork # Only change visibility for variants I have permission to edit attrs[:variants].each do |variant_id, value| - variants[variant_id.to_i] = value if permitted.include?(variant_id.to_i) + variant_id = variant_id.to_i + + variants = update_outgoing_variants(variants, permitted, variant_id, value) end - variants.select { |k, v| v }.keys.map { |k| k.to_i }.sort + variants_to_a variants + end + + def update_outgoing_variants(variants, permitted, variant_id, value) + if !incoming_variant_ids.include? variant_id + # When a variant has been removed from incoming but remains + # in outgoing, remove it from outgoing too + variants[variant_id] = false + + elsif permitted.include? variant_id + variants[variant_id] = value + end + + variants + end + + def incoming_variant_ids + @order_cycle.supplied_variants.map &:id + end + + def variants_to_a(variants) + variants.select { |k, v| v }.keys.map(&:to_i).sort end end end diff --git a/lib/open_food_network/order_cycle_management_report.rb b/lib/open_food_network/order_cycle_management_report.rb index b6e65f0a61..ea238f7792 100644 --- a/lib/open_food_network/order_cycle_management_report.rb +++ b/lib/open_food_network/order_cycle_management_report.rb @@ -16,24 +16,27 @@ module OpenFoodNetwork end end - def table + def search + Spree::Order.complete.where("spree_orders.state != ?", :canceled).distributed_by_user(@user).managed_by(@user).search(params[:q]) + end + + def orders + filter search.result + end + + def table_items if is_payment_methods? orders.map { |o| payment_method_row o } else orders.map { |o| delivery_row o } - end + end end - def orders - filter Spree::Order.managed_by(@user).distributed_by_user(@user).complete.where("spree_orders.state != ?", :canceled) + def filter(search_result) + filter_to_payment_method filter_to_shipping_method filter_to_order_cycle search_result end - def filter(orders) - filter_to_order_cycle filter_to_payment_method filter_to_shipping_method orders - end - - - private + private def payment_method_row(order) ba = order.billing_address diff --git a/lib/open_food_network/order_cycle_permissions.rb b/lib/open_food_network/order_cycle_permissions.rb index 08fe7b2ee4..f728348630 100644 --- a/lib/open_food_network/order_cycle_permissions.rb +++ b/lib/open_food_network/order_cycle_permissions.rb @@ -128,6 +128,8 @@ module OpenFoodNetwork producers = related_enterprises_granting(:add_to_order_cycle, to: [hub], scope: Enterprise.is_primary_producer) permitted_variants = Spree::Variant.joins(:product).where('spree_products.supplier_id IN (?)', producers) + hub_variants = Spree::Variant.joins(:product).where('spree_products.supplier_id = (?)', hub) + # PLUS any variants that are already in an outgoing exchange of this hub, so things don't break # TODO: Remove this when all P-OC are sorted out active_variants = [] @@ -135,7 +137,7 @@ module OpenFoodNetwork active_variants = exchange.variants end - Spree::Variant.where(id: coordinator_variants | permitted_variants | active_variants) + Spree::Variant.where(id: coordinator_variants | hub_variants | permitted_variants | active_variants) else # Any variants produced by MY PRODUCERS that are in this order cycle, where my producer has granted P-OC to the hub producers = related_enterprises_granting(:add_to_order_cycle, to: [hub], scope: managed_participating_producers) @@ -165,6 +167,8 @@ module OpenFoodNetwork producers = related_enterprises_granting(:add_to_order_cycle, to: [hub], scope: Enterprise.is_primary_producer) permitted_variants = Spree::Variant.joins(:product).where('spree_products.supplier_id IN (?)', producers) + hub_variants = Spree::Variant.joins(:product).where('spree_products.supplier_id = (?)', hub) + # PLUS any variants that are already in an outgoing exchange of this hub, so things don't break # TODO: Remove this when all P-OC are sorted out active_variants = [] @@ -172,7 +176,7 @@ module OpenFoodNetwork active_variants = exchange.variants end - Spree::Variant.where(id: coordinator_variants | permitted_variants | active_variants) + Spree::Variant.where(id: coordinator_variants | hub_variants | permitted_variants | active_variants) else # Any of my managed producers in this order cycle granted P-OC by the hub granted_producers = related_enterprises_granted(:add_to_order_cycle, by: [hub], scope: managed_participating_producers) diff --git a/lib/open_food_network/order_grouper.rb b/lib/open_food_network/order_grouper.rb index c0a0fc51ea..c24d1069b8 100644 --- a/lib/open_food_network/order_grouper.rb +++ b/lib/open_food_network/order_grouper.rb @@ -8,7 +8,7 @@ module OpenFoodNetwork def build_tree(items, remaining_rules) rules = remaining_rules.clone - unless rules.empty? + if rules.any? rule = rules.delete_at(0) # Remove current rule for subsequent groupings group_and_sort(rule, rules, items) else @@ -20,7 +20,7 @@ module OpenFoodNetwork branch = {} groups = items.group_by { |item| rule[:group_by].call(item) } sorted_groups = groups.sort_by { |key, value| rule[:sort_by].call(key) } - sorted_groups.each do |property,items_by_property| + sorted_groups.each do |property, items_by_property| branch[property] = build_tree(items_by_property, remaining_rules) branch[property][:summary_row] = { items: items_by_property, columns: rule[:summary_columns] } unless rule[:summary_columns] == nil || is_leaf_node(branch[property]) end @@ -44,15 +44,15 @@ module OpenFoodNetwork end def table(items) - tree = build_tree(items,@rules) + tree = build_tree(items, @rules) table = build_table(tree) table end - + private - + def is_leaf_node(node) node.is_a? Array end end -end \ No newline at end of file +end diff --git a/lib/open_food_network/orders_and_fulfillments_report.rb b/lib/open_food_network/orders_and_fulfillments_report.rb new file mode 100644 index 0000000000..52d07771a2 --- /dev/null +++ b/lib/open_food_network/orders_and_fulfillments_report.rb @@ -0,0 +1,262 @@ +include Spree::ReportsHelper + +module OpenFoodNetwork + class OrdersAndFulfillmentsReport + attr_reader :params + def initialize(user, params = {}) + @params = params + @user = user + end + + def header + case params[:report_type] + when "order_cycle_supplier_totals" + ["Producer", "Product", "Variant", "Amount", "Total Units", "Curr. Cost per Unit", "Total Cost", "Status", "Incoming Transport"] + when "order_cycle_supplier_totals_by_distributor" + ["Producer", "Product", "Variant", "To Hub", "Amount", "Curr. Cost per Unit", "Total Cost", "Shipping Method"] + when "order_cycle_distributor_totals_by_supplier" + ["Hub", "Producer", "Product", "Variant", "Amount", "Curr. Cost per Unit", "Total Cost", "Total Shipping Cost", "Shipping Method"] + when "order_cycle_customer_totals" + ["Hub", "Customer", "Email", "Phone", "Producer", "Product", "Variant", + "Amount", "Item (#{currency_symbol})", "Item + Fees (#{currency_symbol})", "Admin & Handling (#{currency_symbol})", "Ship (#{currency_symbol})", "Total (#{currency_symbol})", "Paid?", + "Shipping", "Delivery?", + "Ship Street", "Ship Street 2", "Ship City", "Ship Postcode", "Ship State", + "Comments", "SKU", + "Order Cycle", "Payment Method", "Customer Code", "Tags", + "Billing Street 1", "Billing Street 2", "Billing City", "Billing Postcode", "Billing State" + ] + else + ["Producer", "Product", "Variant", "Amount", "Curr. Cost per Unit", "Total Cost", "Status", "Incoming Transport"] + end + + end + + def search + permissions.visible_orders.complete.not_state(:canceled).search(params[:q]) + end + + def table_items + orders = search.result + + line_items = permissions.visible_line_items.merge(Spree::LineItem.where(order_id: orders)) + line_items = line_items.supplied_by_any(params[:supplier_id_in]) if params[:supplier_id_in].present? + + # If empty array is passed in, the where clause will return all line_items, which is bad + line_items_with_hidden_details = + permissions.editable_line_items.empty? ? line_items : line_items.where('"spree_line_items"."id" NOT IN (?)', permissions.editable_line_items) + + line_items.select{ |li| line_items_with_hidden_details.include? li }.each do |line_item| + # TODO We should really be hiding customer code here too, but until we + # have an actual association between order and customer, it's a bit tricky + line_item.order.bill_address.andand.assign_attributes(firstname: "HIDDEN", lastname: "", phone: "", address1: "", address2: "", city: "", zipcode: "", state: nil) + line_item.order.ship_address.andand.assign_attributes(firstname: "HIDDEN", lastname: "", phone: "", address1: "", address2: "", city: "", zipcode: "", state: nil) + line_item.order.assign_attributes(email: "HIDDEN") + end + line_items + end + + def rules + case params[:report_type] + when "order_cycle_supplier_totals" + [ { group_by: proc { |line_item| line_item.product.supplier }, + sort_by: proc { |supplier| supplier.name } }, + { group_by: proc { |line_item| line_item.product }, + sort_by: proc { |product| product.name } }, + { group_by: proc { |line_item| line_item.full_name }, + sort_by: proc { |full_name| full_name } } ] + when "order_cycle_supplier_totals_by_distributor" + [ { group_by: proc { |line_item| line_item.product.supplier }, + sort_by: proc { |supplier| supplier.name } }, + { group_by: proc { |line_item| line_item.product }, + sort_by: proc { |product| product.name } }, + { group_by: proc { |line_item| line_item.full_name }, + sort_by: proc { |full_name| full_name }, + summary_columns: [ proc { |line_items| "" }, + proc { |line_items| "" }, + proc { |line_items| "" }, + proc { |line_items| "TOTAL" }, + proc { |line_items| "" }, + proc { |line_items| "" }, + proc { |line_items| line_items.sum { |li| li.amount } }, + proc { |line_items| "" } ] }, + { group_by: proc { |line_item| line_item.order.distributor }, + sort_by: proc { |distributor| distributor.name } } ] + when "order_cycle_distributor_totals_by_supplier" + [ { group_by: proc { |line_item| line_item.order.distributor }, + sort_by: proc { |distributor| distributor.name }, + summary_columns: [ proc { |line_items| "" }, + proc { |line_items| "TOTAL" }, + proc { |line_items| "" }, + proc { |line_items| "" }, + proc { |line_items| "" }, + proc { |line_items| "" }, + proc { |line_items| line_items.sum { |li| li.amount } }, + proc { |line_items| line_items.map { |li| li.order }.uniq.sum { |o| o.ship_total } }, + proc { |line_items| "" } ] }, + { group_by: proc { |line_item| line_item.product.supplier }, + sort_by: proc { |supplier| supplier.name } }, + { group_by: proc { |line_item| line_item.product }, + sort_by: proc { |product| product.name } }, + { group_by: proc { |line_item| line_item.full_name }, + sort_by: proc { |full_name| full_name } } ] + when "order_cycle_customer_totals" + [ { group_by: proc { |line_item| line_item.order.distributor }, + sort_by: proc { |distributor| distributor.name } }, + { group_by: proc { |line_item| line_item.order }, + sort_by: proc { |order| order.bill_address.lastname + " " + order.bill_address.firstname }, + summary_columns: [ + proc { |line_items| line_items.first.order.distributor.name }, + proc { |line_items| line_items.first.order.bill_address.firstname + " " + line_items.first.order.bill_address.lastname }, + proc { |line_items| "" }, + proc { |line_items| "" }, + proc { |line_items| "" }, + proc { |line_items| "TOTAL" }, + proc { |line_items| "" }, + + proc { |line_items| "" }, + proc { |line_items| line_items.sum { |li| li.amount } }, + proc { |line_items| line_items.sum { |li| li.amount_with_adjustments } }, + proc { |line_items| line_items.map { |li| li.order }.uniq.sum { |o| o.admin_and_handling_total } }, + proc { |line_items| line_items.map { |li| li.order }.uniq.sum { |o| o.ship_total } }, + proc { |line_items| line_items.map { |li| li.order }.uniq.sum { |o| o.total } }, + proc { |line_items| line_items.all? { |li| li.order.paid? } ? "Yes" : "No" }, + + proc { |line_items| "" }, + proc { |line_items| "" }, + + proc { |line_items| "" }, + proc { |line_items| "" }, + proc { |line_items| "" }, + proc { |line_items| "" }, + proc { |line_items| "" }, + + proc { |line_items| line_items.first.order.special_instructions } , + proc { |line_items| "" }, + + proc { |line_items| line_items.first.order.order_cycle.andand.name }, + proc { |line_items| line_items.first.order.payments.first.andand.payment_method.andand.name }, + proc { |line_items| "" }, + proc { |line_items| "" }, + + proc { |line_items| "" }, + proc { |line_items| "" }, + proc { |line_items| "" }, + proc { |line_items| "" }, + proc { |line_items| "" } + ] }, + + { group_by: proc { |line_item| line_item.product }, + sort_by: proc { |product| product.name } }, + { group_by: proc { |line_item| line_item.full_name }, + sort_by: proc { |full_name| full_name } } ] + else + [ { group_by: proc { |line_item| line_item.product.supplier }, + sort_by: proc { |supplier| supplier.name } }, + { group_by: proc { |line_item| line_item.product }, + sort_by: proc { |product| product.name } }, + { group_by: proc { |line_item| line_item.full_name }, + sort_by: proc { |full_name| full_name } } ] + end + end + + def columns + case params[:report_type] + when "order_cycle_supplier_totals" + [ proc { |line_items| line_items.first.product.supplier.name }, + proc { |line_items| line_items.first.product.name }, + proc { |line_items| line_items.first.full_name }, + proc { |line_items| line_items.sum { |li| li.quantity } }, + proc { |line_items| total_units(line_items) }, + proc { |line_items| line_items.first.price }, + proc { |line_items| line_items.sum { |li| li.amount } }, + proc { |line_items| "" }, + proc { |line_items| "incoming transport" } ] + when "order_cycle_supplier_totals_by_distributor" + [ proc { |line_items| line_items.first.product.supplier.name }, + proc { |line_items| line_items.first.product.name }, + proc { |line_items| line_items.first.full_name }, + proc { |line_items| line_items.first.order.distributor.name }, + proc { |line_items| line_items.sum { |li| li.quantity } }, + proc { |line_items| line_items.first.price }, + proc { |line_items| line_items.sum { |li| li.amount } }, + proc { |line_items| "shipping method" } ] + when "order_cycle_distributor_totals_by_supplier" + [ proc { |line_items| line_items.first.order.distributor.name }, + proc { |line_items| line_items.first.product.supplier.name }, + proc { |line_items| line_items.first.product.name }, + proc { |line_items| line_items.first.full_name }, + proc { |line_items| line_items.sum { |li| li.quantity } }, + proc { |line_items| line_items.first.price }, + proc { |line_items| line_items.sum { |li| li.amount } }, + proc { |line_items| "" }, + proc { |line_items| "shipping method" } ] + when "order_cycle_customer_totals" + rsa = proc { |line_items| line_items.first.order.shipping_method.andand.require_ship_address } + [ + proc { |line_items| line_items.first.order.distributor.name }, + proc { |line_items| line_items.first.order.bill_address.firstname + " " + line_items.first.order.bill_address.lastname }, + proc { |line_items| line_items.first.order.email }, + proc { |line_items| line_items.first.order.bill_address.phone }, + proc { |line_items| line_items.first.product.supplier.name }, + proc { |line_items| line_items.first.product.name }, + proc { |line_items| line_items.first.full_name }, + + proc { |line_items| line_items.sum { |li| li.quantity } }, + proc { |line_items| line_items.sum { |li| li.amount } }, + proc { |line_items| line_items.sum { |li| li.amount_with_adjustments } }, + proc { |line_items| "" }, + proc { |line_items| "" }, + proc { |line_items| "" }, + proc { |line_items| "" }, + + proc { |line_items| line_items.first.order.shipping_method.andand.name }, + proc { |line_items| rsa.call(line_items) ? 'Y' : 'N' }, + + proc { |line_items| line_items.first.order.ship_address.andand.address1 if rsa.call(line_items) }, + proc { |line_items| line_items.first.order.ship_address.andand.address2 if rsa.call(line_items) }, + proc { |line_items| line_items.first.order.ship_address.andand.city if rsa.call(line_items) }, + proc { |line_items| line_items.first.order.ship_address.andand.zipcode if rsa.call(line_items) }, + proc { |line_items| line_items.first.order.ship_address.andand.state if rsa.call(line_items) }, + + proc { |line_items| "" }, + proc { |line_items| line_items.first.product.sku }, + + proc { |line_items| line_items.first.order.order_cycle.andand.name }, + proc { |line_items| line_items.first.order.payments.first.andand.payment_method.andand.name }, + proc { |line_items| line_items.first.order.user.andand.customer_of(line_items.first.order.distributor).andand.code }, + proc { |line_items| line_items.first.order.user.andand.customer_of(line_items.first.order.distributor).andand.tags.andand.join(', ') }, + + proc { |line_items| line_items.first.order.bill_address.andand.address1 }, + proc { |line_items| line_items.first.order.bill_address.andand.address2 }, + proc { |line_items| line_items.first.order.bill_address.andand.city }, + proc { |line_items| line_items.first.order.bill_address.andand.zipcode }, + proc { |line_items| line_items.first.order.bill_address.andand.state } ] + else + [ proc { |line_items| line_items.first.product.supplier.name }, + proc { |line_items| line_items.first.product.name }, + proc { |line_items| line_items.first.full_name }, + proc { |line_items| line_items.sum { |li| li.quantity } }, + proc { |line_items| line_items.first.price }, + proc { |line_items| line_items.sum { |li| li.quantity * li.price } }, + proc { |line_items| "" }, + proc { |line_items| "incoming transport" } ] + end + end + + private + + def permissions + return @permissions unless @permissions.nil? + @permissions = OpenFoodNetwork::Permissions.new(@user) + end + + def total_units(line_items) + return " " if line_items.map{ |li| li.unit_value.nil? }.any? + total_units = line_items.sum do |li| + scale_factor = ( li.product.variant_unit == 'weight' ? 1000 : 1 ) + li.quantity * li.unit_value / scale_factor + end + total_units.round(3) + end + end +end diff --git a/lib/open_food_network/packing_report.rb b/lib/open_food_network/packing_report.rb new file mode 100644 index 0000000000..61c2a09db5 --- /dev/null +++ b/lib/open_food_network/packing_report.rb @@ -0,0 +1,142 @@ +module OpenFoodNetwork + class PackingReport + attr_reader :params + def initialize(user, params = {}) + @params = params + @user = user + end + + def header + if is_by_customer? + ["Hub", "Code", "First Name", "Last Name", "Supplier", "Product", "Variant", "Quantity", "TempControlled?"] + else + ["Hub", "Supplier", "Code", "First Name", "Last Name", "Product", "Variant", "Quantity", "TempControlled?"] + end + end + + def search + permissions.visible_orders.complete.not_state(:canceled).search(params[:q]) + end + + def table_items + orders = search.result + + line_items = permissions.visible_line_items.merge(Spree::LineItem.where(order_id: orders)) + line_items = line_items.supplied_by_any(params[:supplier_id_in]) if params[:supplier_id_in].present? + + line_items_with_hidden_details = + permissions.editable_line_items.empty? ? line_items : line_items.where('"spree_line_items"."id" NOT IN (?)', permissions.editable_line_items) + + line_items.select{ |li| line_items_with_hidden_details.include? li }.each do |line_item| + # TODO We should really be hiding customer code here too, but until we + # have an actual association between order and customer, it's a bit tricky + line_item.order.bill_address.andand.assign_attributes(firstname: "HIDDEN", lastname: "", phone: "", address1: "", address2: "", city: "", zipcode: "", state: nil) + line_item.order.ship_address.andand.assign_attributes(firstname: "HIDDEN", lastname: "", phone: "", address1: "", address2: "", city: "", zipcode: "", state: nil) + line_item.order.assign_attributes(email: "HIDDEN") + end + line_items + end + + def rules + if is_by_customer? +# customer_rows orders +# table_items = @line_items + + [ + { group_by: proc { |line_item| line_item.order.distributor }, + sort_by: proc { |distributor| distributor.name } }, + { group_by: proc { |line_item| line_item.order }, + sort_by: proc { |order| order.bill_address.lastname }, + summary_columns: [ proc { |line_items| "" }, + proc { |line_items| "" }, + proc { |line_items| "" }, + proc { |line_items| "" }, + proc { |line_items| "" }, + proc { |line_items| "TOTAL ITEMS" }, + proc { |line_items| "" }, + proc { |line_items| line_items.sum { |li| li.quantity } }, + proc { |line_items| "" } ] }, + { group_by: proc { |line_item| line_item.product.supplier }, + sort_by: proc { |supplier| supplier.name } }, + { group_by: proc { |line_item| line_item.product }, + sort_by: proc { |product| product.name } }, + { group_by: proc { |line_item| line_item.full_name }, + sort_by: proc { |full_name| full_name } } ] + else +# supplier_rows orders +# table_items = supplier_rows orders +# + [ { group_by: proc { |line_item| line_item.order.distributor }, + sort_by: proc { |distributor| distributor.name } }, + { group_by: proc { |line_item| line_item.product.supplier }, + sort_by: proc { |supplier| supplier.name }, + summary_columns: [ proc { |line_items| "" }, + proc { |line_items| "" }, + proc { |line_items| "" }, + proc { |line_items| "" }, + proc { |line_items| "" }, + proc { |line_items| "TOTAL ITEMS" }, + proc { |line_items| "" }, + proc { |line_items| line_items.sum { |li| li.quantity } }, + proc { |line_items| "" } ] }, + { group_by: proc { |line_item| line_item.product }, + sort_by: proc { |product| product.name } }, + { group_by: proc { |line_item| line_item.full_name }, + sort_by: proc { |full_name| full_name } }, + { group_by: proc { |line_item| line_item.order.bill_address.lastname }, + sort_by: proc { |lastname| lastname } } ] + end + end + + def columns + if is_by_customer? + [ proc { |line_items| line_items.first.order.distributor.name }, + proc { |line_items| customer_code(line_items.first.order.email) }, + proc { |line_items| line_items.first.order.bill_address.firstname }, + proc { |line_items| line_items.first.order.bill_address.lastname }, + proc { |line_items| line_items.first.product.supplier.name }, + proc { |line_items| line_items.first.product.name }, + proc { |line_items| line_items.first.full_name }, + proc { |line_items| line_items.sum { |li| li.quantity } }, + proc { |line_items| is_temperature_controlled?(line_items.first) } + ] + else + [ + proc { |line_items| line_items.first.order.distributor.name }, + proc { |line_items| line_items.first.product.supplier.name }, + proc { |line_items| customer_code(line_items.first.order.email) }, + proc { |line_items| line_items.first.order.bill_address.firstname }, + proc { |line_items| line_items.first.order.bill_address.lastname }, + proc { |line_items| line_items.first.product.name }, + proc { |line_items| line_items.first.full_name }, + proc { |line_items| line_items.sum { |li| li.quantity } }, + proc { |line_items| is_temperature_controlled?(line_items.first) } + ] + end + end + + private + + def permissions + return @permissions unless @permissions.nil? + @permissions = OpenFoodNetwork::Permissions.new(@user) + end + + def is_temperature_controlled?(line_item) + if line_item.product.shipping_category.andand.temperature_controlled + "Yes" + else + "No" + end + end + + def is_by_customer? + params[:report_type] == "pack_by_customer" + end + + def customer_code(email) + customer = Customer.where(email: email).first + customer.nil? ? "" : customer.code + end + end +end diff --git a/lib/open_food_network/paperclippable.rb b/lib/open_food_network/paperclippable.rb new file mode 100644 index 0000000000..78b4d1a100 --- /dev/null +++ b/lib/open_food_network/paperclippable.rb @@ -0,0 +1,45 @@ +# Allow use of Paperclip's has_attached_file on non-ActiveRecord classes +# https://gist.github.com/basgys/5712426 + +module OpenFoodNetwork + module Paperclippable + def self.included(base) + base.send :extend, ActiveModel::Naming + base.send :extend, ActiveModel::Callbacks + base.send :include, ActiveModel::Validations + base.send :include, Paperclip::Glue + + # Paperclip required callbacks + base.send :define_model_callbacks, :save, only: [:after] + base.send :define_model_callbacks, :commit, only: [:after] + base.send :define_model_callbacks, :destroy, only: [:before, :after] + + # Initialise an ID + base.send :attr_accessor, :id + base.instance_variable_set :@id, 1 + end + + # ActiveModel requirements + def to_model + self + end + + def valid?() true end + def new_record?() true end + def destroyed?() true end + + def save + run_callbacks :save do + end + true + end + + def errors + obj = Object.new + def obj.[](key) [] end + def obj.full_messages() [] end + def obj.any?() false end + obj + end + end +end diff --git a/lib/open_food_network/payments_report.rb b/lib/open_food_network/payments_report.rb new file mode 100644 index 0000000000..4872da6b2f --- /dev/null +++ b/lib/open_food_network/payments_report.rb @@ -0,0 +1,101 @@ +module OpenFoodNetwork + class PaymentsReport + attr_reader :params + def initialize(user, params = {}) + @params = params + @user = user + end + + def header + case params[:report_type] + when "payments_by_payment_type" + ["Payment State", "Distributor", "Payment Type", "Total (#{currency_symbol})"] + when "itemised_payment_totals" + ["Payment State", "Distributor", "Product Total (#{currency_symbol})", "Shipping Total (#{currency_symbol})", "Outstanding Balance (#{currency_symbol})", "Total (#{currency_symbol})"] + when "payment_totals" + ["Payment State", "Distributor", "Product Total (#{currency_symbol})", "Shipping Total (#{currency_symbol})", "Total (#{currency_symbol})", "EFT (#{currency_symbol})", "PayPal (#{currency_symbol})", "Outstanding Balance (#{currency_symbol})"] + else + ["Payment State", "Distributor", "Payment Type", "Total (#{currency_symbol})"] + end + end + + def search + Spree::Order.complete.not_state(:canceled).managed_by(@user).search(params[:q]) + end + + def table_items + orders = search.result + payments = orders.map { |o| o.payments.select { |payment| payment.completed? } }.flatten # Only select completed payments + case params[:report_type] + when "payments_by_payment_type" + payments + when "itemised_payment_totals" + orders + when "payment_totals" + orders + else + payments + end + end + + def rules + case params[:report_type] + when "payments_by_payment_type" + [ { group_by: proc { |payment| payment.order.payment_state }, + sort_by: proc { |payment_state| payment_state } }, + { group_by: proc { |payment| payment.order.distributor }, + sort_by: proc { |distributor| distributor.name } }, + { group_by: proc { |payment| Spree::PaymentMethod.unscoped { payment.payment_method } }, + sort_by: proc { |method| method.name } } ] + when "itemised_payment_totals" + [ { group_by: proc { |order| order.payment_state }, + sort_by: proc { |payment_state| payment_state } }, + { group_by: proc { |order| order.distributor }, + sort_by: proc { |distributor| distributor.name } } ] + when "payment_totals" + [ { group_by: proc { |order| order.payment_state }, + sort_by: proc { |payment_state| payment_state } }, + { group_by: proc { |order| order.distributor }, + sort_by: proc { |distributor| distributor.name } } ] + else + [ { group_by: proc { |payment| payment.order.payment_state }, + sort_by: proc { |payment_state| payment_state } }, + { group_by: proc { |payment| payment.order.distributor }, + sort_by: proc { |distributor| distributor.name } }, + { group_by: proc { |payment| payment.payment_method }, + sort_by: proc { |method| method.name } } ] + end + end + + def columns + case params[:report_type] + when "payments_by_payment_type" + [ proc { |payments| payments.first.order.payment_state }, + proc { |payments| payments.first.order.distributor.name }, + proc { |payments| payments.first.payment_method.name }, + proc { |payments| payments.sum { |payment| payment.amount } } ] + when "itemised_payment_totals" + [ proc { |orders| orders.first.payment_state }, + proc { |orders| orders.first.distributor.name }, + proc { |orders| orders.sum { |o| o.item_total } }, + proc { |orders| orders.sum { |o| o.ship_total } }, + proc { |orders| orders.sum { |o| o.outstanding_balance } }, + proc { |orders| orders.sum { |o| o.total } } ] + when "payment_totals" + [ proc { |orders| orders.first.payment_state }, + proc { |orders| orders.first.distributor.name }, + proc { |orders| orders.sum { |o| o.item_total } }, + proc { |orders| orders.sum { |o| o.ship_total } }, + proc { |orders| orders.sum { |o| o.total } }, + proc { |orders| orders.sum { |o| o.payments.select { |payment| payment.completed? && (payment.payment_method.name.to_s.include? "EFT") }.sum { |payment| payment.amount } } }, + proc { |orders| orders.sum { |o| o.payments.select { |payment| payment.completed? && (payment.payment_method.name.to_s.include? "PayPal") }.sum{ |payment| payment.amount } } }, + proc { |orders| orders.sum { |o| o.outstanding_balance } } ] + else + [ proc { |payments| payments.first.order.payment_state }, + proc { |payments| payments.first.order.distributor.name }, + proc { |payments| payments.first.payment_method.name }, + proc { |payments| payments.sum { |payment| payment.amount } } ] + end + end + end +end diff --git a/lib/open_food_network/permalink_generator.rb b/lib/open_food_network/permalink_generator.rb new file mode 100644 index 0000000000..99d923fd1e --- /dev/null +++ b/lib/open_food_network/permalink_generator.rb @@ -0,0 +1,22 @@ +module PermalinkGenerator + def self.included(base) + base.extend(ClassMethods) + end + + module ClassMethods + def find_available_value(existing, requested) + return requested unless existing.include?(requested) + used_indices = existing.map do |p| + p.slice!(/^#{requested}/) + p.match(/^\d+$/).to_s.to_i + end + options = (1..used_indices.length + 1).to_a - used_indices + requested + options.first.to_s + end + end + + def create_unique_permalink(requested) + existing = self.class.where('id != ?', id).where("permalink LIKE ?", "#{requested}%").pluck(:permalink) + self.class.find_available_value(existing, requested) + end +end diff --git a/lib/open_food_network/permissions.rb b/lib/open_food_network/permissions.rb index 87baa533ad..2e6329bd4b 100644 --- a/lib/open_food_network/permissions.rb +++ b/lib/open_food_network/permissions.rb @@ -15,7 +15,7 @@ module OpenFoodNetwork managed_and_related_enterprises_with :add_to_order_cycle end - def order_cycle_enterprises + def visible_enterprises # Return enterprises that the user manages and those that have granted P-OC to managed enterprises managed_and_related_enterprises_granting :add_to_order_cycle end diff --git a/lib/open_food_network/products_and_inventory_report.rb b/lib/open_food_network/products_and_inventory_report.rb index 163f9a4f50..fe6783bad3 100644 --- a/lib/open_food_network/products_and_inventory_report.rb +++ b/lib/open_food_network/products_and_inventory_report.rb @@ -1,116 +1,42 @@ +require 'open_food_network/products_and_inventory_report_base' + module OpenFoodNetwork - - class ProductsAndInventoryReport - attr_reader :params - def initialize(user, params = {}) - @user = user - @params = params - end - + class ProductsAndInventoryReport < ProductsAndInventoryReportBase def header [ - "Supplier", - "Producer Suburb", - "Product", - "Product Properties", - "Taxons", - "Variant Value", - "Price", - "Group Buy Unit Quantity", - "Amount" - ] + "Supplier", + "Producer Suburb", + "Product", + "Product Properties", + "Taxons", + "Variant Value", + "Price", + "Group Buy Unit Quantity", + "Amount", + "SKU" + ] end def table variants.map do |variant| - [variant.product.supplier.name, - variant.product.supplier.address.city, - variant.product.name, - variant.product.properties.map(&:name).join(", "), - variant.product.taxons.map(&:name).join(", "), - variant.full_name, - variant.price, - variant.product.group_buy_unit_size, - "" + [ + variant.product.supplier.name, + variant.product.supplier.address.city, + variant.product.name, + variant.product.properties.map(&:name).join(", "), + variant.product.taxons.map(&:name).join(", "), + variant.full_name, + variant.price, + variant.product.group_buy_unit_size, + "", + sku_for(variant) ] end end - def permissions - return @permissions unless @permissions.nil? - @permissions = OpenFoodNetwork::Permissions.new(@user) - end - - def visible_products - return @visible_products unless @visible_products.nil? - @visible_products = permissions.visible_products - end - - def variants - filter(child_variants) + filter(master_variants) - end - - def child_variants - Spree::Variant.where(:is_master => false) - .joins(:product) - .merge(visible_products) - .order("spree_products.name") - end - - def master_variants - Spree::Variant.where(:is_master => true) - .joins(:product) - .where("(select spree_variants.id from spree_variants as other_spree_variants - WHERE other_spree_variants.product_id = spree_variants.product_id - AND other_spree_variants.is_master = 'f' LIMIT 1) IS NULL") - .merge(visible_products) - .order("spree_products.name") - end - - def filter(variants) - # NOTE: Ordering matters. - # filter_to_order_cycle and filter_to_distributor return Arrays not Arel - filter_to_distributor filter_to_order_cycle filter_on_hand filter_to_supplier filter_not_deleted variants - end - - def filter_not_deleted(variants) - variants.not_deleted - end - - def filter_on_hand(variants) - if params[:report_type] == "inventory" - variants.where("spree_variants.count_on_hand > 0") - else - variants - end - end - - def filter_to_supplier(variants) - if params[:supplier_id].to_i > 0 - variants.where("spree_products.supplier_id = ?", params[:supplier_id]) - else - variants - end - end - - def filter_to_distributor(variants) - if params[:distributor_id].to_i > 0 - distributor = Enterprise.find params[:distributor_id] - variants.select do |v| - Enterprise.distributing_product(v.product_id).include? distributor - end - else - variants - end - end - - def filter_to_order_cycle(variants) - if params[:order_cycle_id].to_i > 0 - order_cycle = OrderCycle.find params[:order_cycle_id] - variants.select! { |v| order_cycle.variants.include? v } - else - variants - end + def sku_for(variant) + return variant.sku unless variant.sku.blank? + variant.product.sku end end end diff --git a/lib/open_food_network/products_and_inventory_report_base.rb b/lib/open_food_network/products_and_inventory_report_base.rb new file mode 100644 index 0000000000..7641c056ea --- /dev/null +++ b/lib/open_food_network/products_and_inventory_report_base.rb @@ -0,0 +1,73 @@ +module OpenFoodNetwork + class ProductsAndInventoryReportBase + attr_reader :params + + def initialize(user, params = {}) + @user = user + @params = params + end + + def permissions + @permissions ||= OpenFoodNetwork::Permissions.new(@user) + end + + def visible_products + @visible_products ||= permissions.visible_products + end + + def variants + filter(child_variants) + end + + def child_variants + Spree::Variant. + where(is_master: false). + joins(:product). + merge(visible_products). + order('spree_products.name') + end + + def filter(variants) + filter_to_distributor filter_to_order_cycle filter_on_hand filter_to_supplier filter_not_deleted variants + end + + def filter_not_deleted(variants) + variants.not_deleted + end + + def filter_on_hand(variants) + if params[:report_type] == 'inventory' + variants.where('spree_variants.count_on_hand > 0') + else + variants + end + end + + def filter_to_supplier(variants) + if params[:supplier_id].to_i > 0 + variants.where("spree_products.supplier_id = ?", params[:supplier_id]) + else + variants + end + end + + def filter_to_distributor(variants) + if params[:distributor_id].to_i > 0 + distributor = Enterprise.find params[:distributor_id] + scoper = OpenFoodNetwork::ScopeVariantToHub.new(distributor) + variants.in_distributor(distributor).each { |v| scoper.scope(v) } + else + variants + end + end + + def filter_to_order_cycle(variants) + if params[:order_cycle_id].to_i > 0 + order_cycle = OrderCycle.find params[:order_cycle_id] + variants.where(id: order_cycle.variants) + else + variants + end + end + end +end diff --git a/lib/open_food_network/rack_request_blocker.rb b/lib/open_food_network/rack_request_blocker.rb new file mode 100644 index 0000000000..71f1c450e2 --- /dev/null +++ b/lib/open_food_network/rack_request_blocker.rb @@ -0,0 +1,77 @@ +# Copied from http://blog.salsify.com/engineering/tearing-capybara-ajax-tests +# https://gist.github.com/jturkel/9317269/raw/ff7838684370fd8a468ffe1e5ce1f3e46ba39951/rack_request_blocker.rb + +require 'atomic' + +# Rack middleware that keeps track of the number of active requests and can block new requests. +class RackRequestBlocker + + @@num_active_requests = Atomic.new(0) + @@block_requests = Atomic.new(false) + + # Returns the number of requests the server is currently processing. + def self.num_active_requests + @@num_active_requests.value + end + + # Prevents the server from accepting new requests. Any new requests will return an HTTP + # 503 status. + def self.block_requests! + @@block_requests.value = true + end + + # Allows the server to accept requests again. + def self.allow_requests! + @@block_requests.value = false + end + + def initialize(app) + @app = app + end + + def call(env) + increment_active_requests + if block_requests? + block_request(env) + else + @app.call(env) + end + ensure + decrement_active_requests + end + + def self.wait_for_requests_complete + self.block_requests! + max_wait_time = 30 + polling_interval = 0.01 + wait_until = Time.now + max_wait_time.seconds + while true + return if self.num_active_requests == 0 + if Time.now > wait_until + raise "Failed waiting for completing requests, #{self.num_active_requests} running." + else + sleep(polling_interval) + end + end + ensure + self.allow_requests! + end + + private + + def block_requests? + @@block_requests.value + end + + def block_request(env) + [503, {}, []] + end + + def increment_active_requests + @@num_active_requests.update { |v| v + 1 } + end + + def decrement_active_requests + @@num_active_requests.update { |v| v - 1 } + end +end diff --git a/lib/open_food_network/referer_parser.rb b/lib/open_food_network/referer_parser.rb new file mode 100644 index 0000000000..b90ef21829 --- /dev/null +++ b/lib/open_food_network/referer_parser.rb @@ -0,0 +1,17 @@ +module OpenFoodNetwork + class RefererParser + def self.path(referer) + parse_uri(referer).andand.path if referer + end + + def self.parse_uri(string) + begin + # TODO: make this operation obsolete by fixing URLs generated by AngularJS + string.sub!('##', '#') + URI(string) + rescue URI::InvalidURIError + nil + end + end + end +end diff --git a/lib/open_food_network/reports/bulk_coop_allocation_report.rb b/lib/open_food_network/reports/bulk_coop_allocation_report.rb new file mode 100644 index 0000000000..11fb3070f8 --- /dev/null +++ b/lib/open_food_network/reports/bulk_coop_allocation_report.rb @@ -0,0 +1,50 @@ +require 'open_food_network/reports/bulk_coop_report' + +module OpenFoodNetwork::Reports + class BulkCoopAllocationReport < BulkCoopReport + header "Customer", "Product", "Bulk Unit Size", "Variant", "Variant value", "Variant unit", "Weight", "Sum Total", "Total Available", "Unallocated", "Max quantity excess" + + organise do + group { |li| li.product } + sort &:name + + summary_row do + column { |lis| "TOTAL" } + column { |lis| product_name(lis) } + column { |lis| group_buy_unit_size_f(lis) } + column { |lis| "" } + column { |lis| "" } + column { |lis| "" } + column { |lis| "" } + column { |lis| total_amount(lis) } + column { |lis| total_available(lis) } + column { |lis| remainder(lis) } + column { |lis| max_quantity_excess(lis) } + end + + organise do + group { |li| li.full_name } + sort { |full_name| full_name } + + organise do + group { |li| li.order } + sort { |order| order.to_s } + end + end + end + + columns do + column { |lis| lis.first.order.bill_address.firstname + " " + lis.first.order.bill_address.lastname } + column { |lis| lis.first.product.name } + column { |lis| lis.first.product.group_buy_unit_size || 0.0 } + column { |lis| lis.first.full_name } + column { |lis| OpenFoodNetwork::OptionValueNamer.new(lis.first).value } + column { |lis| OpenFoodNetwork::OptionValueNamer.new(lis.first).unit } + column { |lis| lis.first.weight_from_unit_value || 0 } + column { |lis| total_amount(lis) } + column { |lis| "" } + column { |lis| "" } + column { |lis| "" } + end + end +end diff --git a/lib/open_food_network/reports/bulk_coop_report.rb b/lib/open_food_network/reports/bulk_coop_report.rb new file mode 100644 index 0000000000..b743b64e29 --- /dev/null +++ b/lib/open_food_network/reports/bulk_coop_report.rb @@ -0,0 +1,69 @@ +require 'open_food_network/reports/report' + +module OpenFoodNetwork::Reports + class BulkCoopReport < Report + + + private + + class << self + + def supplier_name(lis) + lis.first.variant.product.supplier.name + end + + def product_name(lis) + lis.first.variant.product.name + end + + def group_buy_unit_size(lis) + (lis.first.variant.product.group_buy_unit_size || 0.0) / + (lis.first.product.variant_unit_scale || 1) + end + + def group_buy_unit_size_f(lis) + group_buy_unit_size(lis) + end + + def total_amount(lis) + lis.sum { |li| scaled_final_weight_volume(li) } + end + + def units_required(lis) + if group_buy_unit_size(lis).zero? + 0 + else + ( total_amount(lis) / group_buy_unit_size(lis) ).ceil + end + end + + def total_available(lis) + units_required(lis) * group_buy_unit_size(lis) + end + + def remainder(lis) + remainder = total_available(lis) - total_amount(lis) + remainder >= 0 ? remainder : '' + end + + def max_quantity_excess(lis) + max_quantity_amount(lis) - total_amount(lis) + end + + def max_quantity_amount(lis) + lis.sum do |li| + max_quantity = [li.max_quantity || 0, li.quantity || 0].max + max_quantity * scaled_unit_value(li.variant) + end + end + + def scaled_final_weight_volume(li) + (li.final_weight_volume || 0) / (li.product.variant_unit_scale || 1) + end + + def scaled_unit_value(v) + (v.unit_value || 0) / (v.product.variant_unit_scale || 1) + end + end + end +end diff --git a/lib/open_food_network/reports/bulk_coop_supplier_report.rb b/lib/open_food_network/reports/bulk_coop_supplier_report.rb new file mode 100644 index 0000000000..b37e8d00a5 --- /dev/null +++ b/lib/open_food_network/reports/bulk_coop_supplier_report.rb @@ -0,0 +1,50 @@ +require 'open_food_network/reports/bulk_coop_report' + +module OpenFoodNetwork::Reports + class BulkCoopSupplierReport < BulkCoopReport + header "Supplier", "Product", "Bulk Unit Size", "Variant", "Variant value", "Variant unit", "Weight", "Sum Total", "Units Required", "Unallocated", "Max quantity excess" + + organise do + group { |li| li.product.supplier } + sort &:name + + organise do + group { |li| li.product } + sort &:name + + summary_row do + column { |lis| supplier_name(lis) } + column { |lis| product_name(lis) } + column { |lis| group_buy_unit_size_f(lis) } + column { |lis| "" } + column { |lis| "" } + column { |lis| "" } + column { |lis| "" } + column { |lis| total_amount(lis) } + column { |lis| units_required(lis) } + column { |lis| remainder(lis) } + column { |lis| max_quantity_excess(lis) } + end + + organise do + group { |li| li.full_name } + sort { |full_name| full_name } + end + end + end + + columns do + column { |lis| supplier_name(lis) } + column { |lis| product_name(lis) } + column { |lis| group_buy_unit_size_f(lis) } + column { |lis| lis.first.full_name } + column { |lis| OpenFoodNetwork::OptionValueNamer.new(lis.first).value } + column { |lis| OpenFoodNetwork::OptionValueNamer.new(lis.first).unit } + column { |lis| lis.first.weight_from_unit_value || 0 } + column { |lis| total_amount(lis) } + column { |lis| '' } + column { |lis| '' } + column { |lis| '' } + end + end +end diff --git a/lib/open_food_network/reports/report.rb b/lib/open_food_network/reports/report.rb new file mode 100644 index 0000000000..d575fcd014 --- /dev/null +++ b/lib/open_food_network/reports/report.rb @@ -0,0 +1,46 @@ +require 'open_food_network/reports/row' +require 'open_food_network/reports/rule' + +module OpenFoodNetwork::Reports + class Report + class_attribute :_header, :_columns, :_rules_head + + # -- API + def header + self._header + end + + def columns + self._columns.to_a + end + + def rules + # Flatten linked list and return as hashes + rules = [] + + rule = self._rules_head + while rule + rules << rule + rule = rule.next + end + + rules.map &:to_h + end + + + # -- DSL + def self.header(*columns) + self._header = columns + end + + def self.columns(&block) + self._columns = Row.new + Blockenspiel.invoke block, self._columns + end + + def self.organise(&block) + self._rules_head = Rule.new + Blockenspiel.invoke block, self._rules_head + end + end +end diff --git a/lib/open_food_network/reports/row.rb b/lib/open_food_network/reports/row.rb new file mode 100644 index 0000000000..01c8dd192e --- /dev/null +++ b/lib/open_food_network/reports/row.rb @@ -0,0 +1,17 @@ +module OpenFoodNetwork::Reports + class Row + include Blockenspiel::DSL + + def initialize + @columns = [] + end + + def column(&block) + @columns << block + end + + def to_a + @columns + end + end +end diff --git a/lib/open_food_network/reports/rule.rb b/lib/open_food_network/reports/rule.rb new file mode 100644 index 0000000000..e2f94f32a2 --- /dev/null +++ b/lib/open_food_network/reports/rule.rb @@ -0,0 +1,32 @@ +require 'open_food_network/reports/row' + +module OpenFoodNetwork::Reports + class Rule + include Blockenspiel::DSL + attr_reader :next + + def group(&block) + @group = block + end + + def sort(&block) + @sort = block + end + + def summary_row(&block) + @summary_row = Row.new + Blockenspiel.invoke block, @summary_row + end + + def organise(&block) + @next = Rule.new + Blockenspiel.invoke block, @next + end + + def to_h + h = {group_by: @group, sort_by: @sort} + h.merge!({summary_columns: @summary_row.to_a}) if @summary_row + h + end + end +end diff --git a/lib/open_food_network/sales_tax_report.rb b/lib/open_food_network/sales_tax_report.rb index 46e2cd234e..0d41b24ec1 100644 --- a/lib/open_food_network/sales_tax_report.rb +++ b/lib/open_food_network/sales_tax_report.rb @@ -1,9 +1,11 @@ module OpenFoodNetwork class SalesTaxReport include Spree::ReportsHelper + attr_accessor :user, :params - def initialize orders - @orders = orders + def initialize(user, params) + @user = user + @params = params end def header @@ -12,8 +14,17 @@ module OpenFoodNetwork "Total Tax (#{currency_symbol})", "Customer", "Distributor"] end + def search + permissions = OpenFoodNetwork::Permissions.new(user) + permissions.editable_orders.complete.not_state(:canceled).search(params[:q]) + end + + def orders + search.result + end + def table - @orders.map do |order| + orders.map do |order| totals = totals_of order.line_items shipping_cost = shipping_cost_for order diff --git a/lib/open_food_network/scope_product_to_hub.rb b/lib/open_food_network/scope_product_to_hub.rb index b214abc0d8..d3e018f295 100644 --- a/lib/open_food_network/scope_product_to_hub.rb +++ b/lib/open_food_network/scope_product_to_hub.rb @@ -1,16 +1,23 @@ require 'open_food_network/scope_variant_to_hub' module OpenFoodNetwork - module ScopeProductToHub - def variants_distributed_by(order_cycle, distributor) - super.each { |v| v.scope_to_hub @hub } - end - end - - module ProductScopableToHub - def scope_to_hub(hub) - extend OpenFoodNetwork::ScopeProductToHub + class ScopeProductToHub + def initialize(hub) @hub = hub + @variant_overrides = VariantOverride.indexed @hub + end + + def scope(product) + product.send :extend, OpenFoodNetwork::ScopeProductToHub::ScopeProductToHub + product.instance_variable_set :@hub, @hub + product.instance_variable_set :@variant_overrides, @variant_overrides + end + + + module ScopeProductToHub + def variants_distributed_by(order_cycle, distributor) + super.each { |v| ScopeVariantToHub.new(@hub, @variant_overrides).scope(v) } + end end end end diff --git a/lib/open_food_network/scope_variant_to_hub.rb b/lib/open_food_network/scope_variant_to_hub.rb index 91ed634043..37a2455616 100644 --- a/lib/open_food_network/scope_variant_to_hub.rb +++ b/lib/open_food_network/scope_variant_to_hub.rb @@ -1,30 +1,55 @@ module OpenFoodNetwork - module ScopeVariantToHub - def price - VariantOverride.price_for(@hub, self) || super + class ScopeVariantToHub + def initialize(hub, variant_overrides=nil) + @hub = hub + @variant_overrides = variant_overrides || VariantOverride.indexed(@hub) end - def price_in(currency) - Spree::Price.new(amount: price, currency: currency) + def scope(variant) + variant.send :extend, OpenFoodNetwork::ScopeVariantToHub::ScopeVariantToHub + variant.instance_variable_set :@hub, @hub + variant.instance_variable_set :@variant_override, @variant_overrides[variant] end - def count_on_hand - VariantOverride.count_on_hand_for(@hub, self) || super - end - def decrement!(attribute, by=1) - if attribute == :count_on_hand && VariantOverride.stock_overridden?(@hub, self) - VariantOverride.decrement_stock! @hub, self, by - else - super + module ScopeVariantToHub + def price + @variant_override.andand.price || super + end + + def price_in(currency) + Spree::Price.new(amount: price, currency: currency) + end + + def count_on_hand + @variant_override.andand.count_on_hand || super + end + + def on_demand + if @variant_override.andand.on_demand.nil? + if @variant_override.andand.count_on_hand.present? + # If we're overriding the stock level of an on_demand variant, show it as not + # on_demand, so our stock control can take effect. + false + else + super + end + else + @variant_override.andand.on_demand + end + end + + def decrement!(attribute, by=1) + if attribute == :count_on_hand && @variant_override.andand.stock_overridden? + @variant_override.decrement_stock! by + else + super + end + end + + def sku + @variant_override.andand.sku || super end end end - - module VariantScopableToHub - def scope_to_hub(hub) - extend OpenFoodNetwork::ScopeVariantToHub - @hub = hub - end - end end diff --git a/lib/open_food_network/variant_and_line_item_naming.rb b/lib/open_food_network/variant_and_line_item_naming.rb new file mode 100644 index 0000000000..c192f05100 --- /dev/null +++ b/lib/open_food_network/variant_and_line_item_naming.rb @@ -0,0 +1,79 @@ +# This module is included in both the Spree::Variant and Spree::LineItem model decorators +# It contains all of our logic for creating and naming option values (which are associated +# with both models) and methods for printing human readable "names" for instances of these models. + +require 'open_food_network/option_value_namer' + +module OpenFoodNetwork + module VariantAndLineItemNaming + + # Copied and modified from Spree::Variant + def options_text + values = self.option_values.joins(:option_type).order("#{Spree::OptionType.table_name}.position asc") + + values.map! &:presentation # This line changed + + values.to_sentence({ :words_connector => ", ", :two_words_connector => ", " }) + end + + def product_and_full_name + return "#{product.name} - #{full_name}" unless full_name.start_with? product.name + full_name + end + + # Used like "product.name - full_name", preferably using product_and_full_name method above. + # This returns, for a product with name "Bread": + # Bread - 1kg # if display_name blank + # Bread - Spelt Sourdough, 1kg # if display_name is "Spelt Sourdough, 1kg" + # Bread - 1kg Spelt Sourdough # if unit_to_display is "1kg Spelt Sourdough" + # Bread - Spelt Sourdough (1kg) # if display_name is "Spelt Sourdough" and unit_to_display is "1kg" + def full_name + return unit_to_display if display_name.blank? + return display_name if display_name.downcase.include? unit_to_display.downcase + return unit_to_display if unit_to_display.downcase.include? display_name.downcase + "#{display_name} (#{unit_to_display})" + end + + def name_to_display + return product.name if display_name.blank? + display_name + end + + def unit_to_display + return options_text if !self.has_attribute?(:display_as) || display_as.blank? + display_as + end + + + def update_units + delete_unit_option_values + + option_type = self.product.variant_unit_option_type + if option_type + name = option_value_name + ov = Spree::OptionValue.where(option_type_id: option_type, name: name, presentation: name).first || Spree::OptionValue.create!({option_type: option_type, name: name, presentation: name}, without_protection: true) + option_values << ov + end + end + + def delete_unit_option_values + ovs = self.option_values.where(option_type_id: Spree::Product.all_variant_unit_option_types) + self.option_values.destroy ovs + end + + def weight_from_unit_value + (unit_value || 0) / 1000 if self.product.variant_unit == 'weight' + end + + private + + def option_value_name + if self.has_attribute?(:display_as) && display_as.present? + display_as + else + option_value_namer = OpenFoodNetwork::OptionValueNamer.new self + option_value_namer.name + end + end + end +end diff --git a/lib/open_food_network/xero_invoices_report.rb b/lib/open_food_network/xero_invoices_report.rb new file mode 100644 index 0000000000..331c8b204c --- /dev/null +++ b/lib/open_food_network/xero_invoices_report.rb @@ -0,0 +1,213 @@ +module OpenFoodNetwork + class XeroInvoicesReport + def initialize(user, opts={}) + @user = user + + @opts = opts. + reject { |k, v| v.blank? }. + reverse_merge({report_type: 'summary', + invoice_date: Time.zone.today, + due_date: Time.zone.today + 1.month, + account_code: 'food sales'}) + end + + def header + %w(*ContactName EmailAddress POAddressLine1 POAddressLine2 POAddressLine3 POAddressLine4 POCity PORegion POPostalCode POCountry *InvoiceNumber Reference *InvoiceDate *DueDate InventoryItemCode *Description *Quantity *UnitAmount Discount *AccountCode *TaxType TrackingName1 TrackingOption1 TrackingName2 TrackingOption2 Currency BrandingTheme Paid?) + end + + def search + permissions = OpenFoodNetwork::Permissions.new(@user) + permissions.editable_orders.complete.not_state(:canceled).search(@opts[:q]) + end + + def orders + search.result.reorder('id DESC') + end + + def table + rows = [] + + orders.each_with_index do |order, i| + invoice_number = invoice_number_for(order, i) + rows += detail_rows_for_order(order, invoice_number, @opts) if detail? + rows += summary_rows_for_order(order, invoice_number, @opts) + end + + rows.compact + end + + + private + + def detail_rows_for_order(order, invoice_number, opts) + rows = [] + + rows += line_item_detail_rows(order, invoice_number, opts) + rows += adjustment_detail_rows(order, invoice_number, opts) + + rows + end + + def line_item_detail_rows(order, invoice_number, opts) + order.line_items.map do |line_item| + line_item_detail_row(line_item, invoice_number, opts) + end + end + + def line_item_detail_row(line_item, invoice_number, opts) + row(line_item.order, + line_item.product.sku, + line_item.product_and_full_name, + line_item.quantity.to_s, + line_item.price.to_s, + invoice_number, + tax_type(line_item), + opts) + end + + def adjustment_detail_rows(order, invoice_number, opts) + adjustments(order).map do |adjustment| + adjustment_detail_row(adjustment, invoice_number, opts) + end + end + + def adjustment_detail_row(adjustment, invoice_number, opts) + row(adjustment_order(adjustment), + '', + adjustment.label, + 1, + adjustment.amount, + invoice_number, + tax_type(adjustment), + opts) + end + + def summary_rows_for_order(order, invoice_number, opts) + rows = [] + + rows += produce_summary_rows(order, invoice_number, opts) unless detail? + rows += fee_summary_rows(order, invoice_number, opts) unless detail? && order.account_invoice? + rows += shipping_summary_rows(order, invoice_number, opts) + rows += admin_adjustment_summary_rows(order, invoice_number, opts) unless detail? + + rows + end + + def produce_summary_rows(order, invoice_number, opts) + [summary_row(order, 'Total untaxable produce (no tax)', total_untaxable_products(order), invoice_number, 'GST Free Income', opts), + summary_row(order, 'Total taxable produce (tax inclusive)', total_taxable_products(order), invoice_number, 'GST on Income', opts)] + end + + def fee_summary_rows(order, invoice_number, opts) + [summary_row(order, 'Total untaxable fees (no tax)', total_untaxable_fees(order), invoice_number, 'GST Free Income', opts), + summary_row(order, 'Total taxable fees (tax inclusive)', total_taxable_fees(order), invoice_number, 'GST on Income', opts)] + end + + def shipping_summary_rows(order, invoice_number, opts) + [summary_row(order, 'Delivery Shipping Cost (tax inclusive)', total_shipping(order), invoice_number, tax_on_shipping_s(order), opts)] + end + + def admin_adjustment_summary_rows(order, invoice_number, opts) + [summary_row(order, 'Total untaxable admin adjustments (no tax)', total_untaxable_admin_adjustments(order), invoice_number, 'GST Free Income', opts), + summary_row(order, 'Total taxable admin adjustments (tax inclusive)', total_taxable_admin_adjustments(order), invoice_number, 'GST on Income', opts)] + end + + def summary_row(order, description, amount, invoice_number, tax_type, opts={}) + row order, '', description, '1', amount, invoice_number, tax_type, opts + end + + def row(order, sku, description, quantity, amount, invoice_number, tax_type, opts={}) + return nil if amount == 0 + + [order.bill_address.andand.full_name, + order.email, + order.bill_address.andand.address1, + order.bill_address.andand.address2, + '', + '', + order.bill_address.andand.city, + order.bill_address.andand.state, + order.bill_address.andand.zipcode, + order.bill_address.andand.country.andand.name, + invoice_number, + order.number, + opts[:invoice_date], + opts[:due_date], + sku, + description, + quantity, + amount, + '', + opts[:account_code], + tax_type, + '', + '', + '', + '', + Spree::Config.currency, + '', + order.paid? ? 'Y' : 'N' + ] + end + + def adjustments(order) + account_invoice_adjustments(order) + order.adjustments.admin + end + + def account_invoice_adjustments(order) + order.adjustments. + billable_period. + select { |a| a.source.present? } + end + + def adjustment_order(adjustment) + adjustment.source.andand.account_invoice.andand.order || + (adjustment.adjustable.is_a?(Spree::Order) ? adjustment.adjustable : nil) + end + + def invoice_number_for(order, i) + @opts[:initial_invoice_number] ? @opts[:initial_invoice_number].to_i+i : order.number + end + + def total_untaxable_products(order) + order.line_items.without_tax.sum &:amount + end + + def total_taxable_products(order) + order.line_items.with_tax.sum &:amount + end + + def total_untaxable_fees(order) + order.adjustments.enterprise_fee.without_tax.sum &:amount + end + + def total_taxable_fees(order) + order.adjustments.enterprise_fee.with_tax.sum &:amount + end + + def total_shipping(order) + order.adjustments.shipping.sum &:amount + end + + def tax_on_shipping_s(order) + tax_on_shipping = order.adjustments.shipping.sum(&:included_tax) > 0 + tax_on_shipping ? 'GST on Income' : 'GST Free Income' + end + + def total_untaxable_admin_adjustments(order) + order.adjustments.admin.without_tax.sum &:amount + end + + def total_taxable_admin_adjustments(order) + order.adjustments.admin.with_tax.sum &:amount + end + + def detail? + @opts[:report_type] == 'detailed' + end + + def tax_type(taxable) + taxable.has_tax? ? 'GST on Income' : 'GST Free Income' + end + end +end diff --git a/lib/spree/core/controller_helpers/order_decorator.rb b/lib/spree/core/controller_helpers/order_decorator.rb index 88ae412ec2..dc38e861e6 100644 --- a/lib/spree/core/controller_helpers/order_decorator.rb +++ b/lib/spree/core/controller_helpers/order_decorator.rb @@ -3,8 +3,9 @@ Spree::Core::ControllerHelpers::Order.class_eval do order = current_order_without_scoped_variants(create_order_if_necessary) if order + scoper = OpenFoodNetwork::ScopeVariantToHub.new(order.distributor) order.line_items.each do |li| - li.variant.scope_to_hub order.distributor + scoper.scope(li.variant) end end diff --git a/lib/spree/money_decorator.rb b/lib/spree/money_decorator.rb index fed92b8210..3479bbd9a2 100644 --- a/lib/spree/money_decorator.rb +++ b/lib/spree/money_decorator.rb @@ -4,4 +4,9 @@ Spree::Money.class_eval do def self.currency_symbol Money.new(0, Spree::Config[:currency]).symbol end + + def rounded + @options[:no_cents] = true if @money.amount % 1 == 0 + to_s + end end diff --git a/lib/tasks/billing.rake b/lib/tasks/billing.rake new file mode 100644 index 0000000000..6bfd0a53e7 --- /dev/null +++ b/lib/tasks/billing.rake @@ -0,0 +1,13 @@ +namespace :openfoodnetwork do + namespace :billing do + desc 'Update enterprise user invoices' + task update_account_invoices: :environment do + Delayed::Job.enqueue(UpdateAccountInvoices.new) if Spree::Config[:auto_update_invoices] + end + + desc 'Finalize enterprise user invoices' + task finalize_account_invoices: :environment do + Delayed::Job.enqueue(FinalizeAccountInvoices.new) if Spree::Config[:auto_finalize_invoices] + end + end +end diff --git a/lib/tasks/data.rake b/lib/tasks/data.rake index 23db3e6049..643886943c 100644 --- a/lib/tasks/data.rake +++ b/lib/tasks/data.rake @@ -5,7 +5,7 @@ namespace :openfoodnetwork do input = request_months # For each order cycle which was modified within the past 3 months - OrderCycle.where('updated_at > ?', Date.today - input.months).each do |order_cycle| + OrderCycle.where('updated_at > ?', Date.current - input.months).each do |order_cycle| # Cycle through the incoming exchanges order_cycle.exchanges.incoming.each do |exchange| unless exchange.sender == exchange.receiver diff --git a/lib/tasks/karma.rake b/lib/tasks/karma.rake index 4ebd4ae278..ec994d37fc 100644 --- a/lib/tasks/karma.rake +++ b/lib/tasks/karma.rake @@ -11,7 +11,7 @@ namespace :karma do def with_tmp_config(command, args = nil) Tempfile.open('karma_unit.js', Rails.root.join('tmp') ) do |f| - f.write unit_js(application_spec_files) + f.write unit_js(application_spec_files << i18n_file) f.flush trap('SIGINT') { puts "Killing Karma"; exit } exec "karma #{command} #{f.path} #{args}" @@ -29,4 +29,13 @@ namespace :karma do unit_js = File.open('config/ng-test.conf.js', 'r').read unit_js.gsub "APPLICATION_SPEC", "\"#{files.join("\",\n\"")}\"" end + + def i18n_file + I18n.backend.send(:init_translations) unless I18n.backend.initialized? + f = Tempfile.open('i18n.js', Rails.root.join('tmp') ) + f.write 'window.I18n = ' + f.write I18n.backend.send(:translations)[I18n.locale].with_indifferent_access.to_json.html_safe + f.flush + f.path + end end diff --git a/public/AveniBla.eot b/public/AveniBla.eot deleted file mode 100644 index 1ae59a3381..0000000000 Binary files a/public/AveniBla.eot and /dev/null differ diff --git a/public/AveniMed.eot b/public/AveniMed.eot deleted file mode 100644 index 61c35da54b..0000000000 Binary files a/public/AveniMed.eot and /dev/null differ diff --git a/public/AvenirLTStd-Black.otf b/public/AvenirLTStd-Black.otf deleted file mode 100644 index 1a934a2af3..0000000000 Binary files a/public/AvenirLTStd-Black.otf and /dev/null differ diff --git a/public/AvenirLTStd-Medium.otf b/public/AvenirLTStd-Medium.otf deleted file mode 100644 index e902718152..0000000000 Binary files a/public/AvenirLTStd-Medium.otf and /dev/null differ diff --git a/public/favicon-staging.ico b/public/favicon-staging.ico index c54a7c4528..91eac8cce8 100644 Binary files a/public/favicon-staging.ico and b/public/favicon-staging.ico differ diff --git a/public/favicon.ico b/public/favicon.ico index 9e0f9a2681..c62c6eeb38 100644 Binary files a/public/favicon.ico and b/public/favicon.ico differ diff --git a/script/archive_branch.sh b/script/archive_branch.sh new file mode 100755 index 0000000000..0177592a54 --- /dev/null +++ b/script/archive_branch.sh @@ -0,0 +1,10 @@ +#!/bin/bash + +# Archive an old branch as a tagged named archive/branch-name to declutter the branch list + +BRANCH_NAME=$1 + +git tag archive/$BRANCH_NAME $BRANCH_NAME +git checkout -q archive/$BRANCH_NAME +git branch -d $BRANCH_NAME +git checkout -q master diff --git a/script/ci/check_github_status.sh b/script/ci/check_github_status.sh new file mode 100755 index 0000000000..0a4775d268 --- /dev/null +++ b/script/ci/check_github_status.sh @@ -0,0 +1,25 @@ +#!/bin/bash + +set -e +source "`dirname $0`/includes.sh" + +OFN_COMMIT=$(get_ofn_commit) +if [ "$OFN_COMMIT" = 'OFN_COMMIT_NOT_FOUND' ]; then + OFN_COMMIT=$(git rev-parse $BUILDKITE_COMMIT) +fi + +GITHUB_REPO="$(echo $BUILDKITE_REPO | sed 's/git@github.com:\(.*\).git/\1/')" +GITHUB_API_URL="https://api.github.com/repos/$GITHUB_REPO/commits/$OFN_COMMIT/status" + +echo "--- Checking environment variables" +require_env_vars OFN_COMMIT BUILDKITE_REPO + +echo "--- Checking GitHub status" +if [ -n "$1" ]; then + REQUIRED_STATUS="$1" +else + REQUIRED_STATUS="success" +fi +echo "Require status '$REQUIRED_STATUS'" +echo "Visiting $GITHUB_API_URL" +curl -s "$GITHUB_API_URL" | head -n 2 | grep '^ *"state":' | egrep "\"$REQUIRED_STATUS\",\$" diff --git a/script/ci/includes.sh b/script/ci/includes.sh index d7619f7d23..bcb5662469 100644 --- a/script/ci/includes.sh +++ b/script/ci/includes.sh @@ -1,34 +1,89 @@ function load_environment { - source /var/lib/jenkins/.rvm/environments/ruby-1.9.3-p392 + source /var/lib/jenkins/.rvm/environments/ruby-2.1.5 if [ ! -f config/application.yml ]; then ln -s application.yml.example config/application.yml fi } +function require_env_vars { + for var in "$@"; do + eval value=\$$var + echo "$var=$value" + if [ -z "$value" ]; then + echo "Environment variable $var missing." + exit 1 + fi + done +} + +function master_merged { + if [[ `git tag -l "$BUILDKITE_BRANCH"` != '' ]]; then + echo "'$BUILDKITE_BRANCH' is a tag." + if [[ `git tag -l --contains origin/master "$BUILDKITE_BRANCH"` != '' ]]; then + echo "This tag contains the current master." + return 0 + else + echo "This tag does not contain the current master." + return 1 + fi + fi + if [[ `git branch -r --merged origin/$BUILDKITE_BRANCH` == *origin/master* ]]; then + echo "This branch already has the current master merged." + return 0 + fi + return 1 +} + function exit_unless_master_merged { - if [[ `git branch -a --merged origin/$BUILDKITE_BRANCH` != *origin/master* ]]; then + if ! master_merged; then echo "This branch does not have the current master merged. Please merge master and push again." exit 1 fi } function succeed_if_master_merged { - if [[ `git branch -a --merged origin/$BUILDKITE_BRANCH` == *origin/master* ]]; then - echo "This branch already has the current master merged." - exit 0 + if master_merged; then + exit 0 fi } +function set_ofn_commit { + echo "Setting commit to $1" + buildkite-agent meta-data set "openfoodnetwork:git:commit" $1 +} + +function get_ofn_commit { + OFN_COMMIT=`buildkite-agent meta-data get "openfoodnetwork:git:commit"` + + # If we don't catch this failure case, push will execute: + # git push remote :master --force + # Which will delete the master branch on the server + + if [[ `expr length "$OFN_COMMIT"` == 0 ]]; then + echo 'OFN_COMMIT_NOT_FOUND' + else + echo $OFN_COMMIT + fi +} + +function checkout_ofn_commit { + OFN_COMMIT=`buildkite-agent meta-data get "openfoodnetwork:git:commit"` + echo "Checking out stored commit $OFN_COMMIT" + git checkout -qf "$OFN_COMMIT" +} + function drop_and_recreate_database { # Adapted from: http://stackoverflow.com/questions/12924466/capistrano-with-postgresql-error-database-is-being-accessed-by-other-users - psql -U openfoodweb postgres < pg_backend_pid() -AND datname='$1'; -DROP DATABASE $1; -CREATE DATABASE $1; +AND datname='$DB'; +DROP DATABASE $DB; +CREATE DATABASE $DB; EOF } diff --git a/script/ci/load_staging_baseline.sh b/script/ci/load_staging_baseline.sh index ac7d88569f..45a57cf5c0 100755 --- a/script/ci/load_staging_baseline.sh +++ b/script/ci/load_staging_baseline.sh @@ -5,23 +5,30 @@ # current database. set -e -cd /home/openfoodweb/apps/openfoodweb/current -source ./script/ci/includes.sh +source "`dirname $0`/includes.sh" + +# We need ruby to call script/delayed_job +export PATH="$HOME/.rbenv/shims:$PATH" + +echo "Checking environment variables" +require_env_vars CURRENT_PATH SERVICE DB_HOST DB_USER DB + +cd "$CURRENT_PATH" echo "Stopping unicorn and delayed job..." -service unicorn_openfoodweb stop +service "$SERVICE" stop RAILS_ENV=staging script/delayed_job -i 0 stop echo "Backing up current data..." mkdir -p db/backup -pg_dump -h localhost -U openfoodweb openfoodweb_production |gzip > db/backup/staging-`date +%Y%m%d%H%M%S`.sql.gz +pg_dump -h "$DB_HOST" -U "$DB_USER" "$DB" |gzip > db/backup/staging-`date +%Y%m%d%H%M%S`.sql.gz echo "Loading baseline data..." -drop_and_recreate_database "openfoodweb_production" -gunzip -c db/backup/staging-baseline.sql.gz |psql -h localhost -U openfoodweb openfoodweb_production +drop_and_recreate_database "$DB" -U "$DB_USER" +gunzip -c db/backup/staging-baseline.sql.gz |psql -h "$DB_HOST" -U "$DB_USER" "$DB" echo "Restarting unicorn..." -service unicorn_openfoodweb start +service "$SERVICE" start # Delayed job is restarted by monit echo "Done!" diff --git a/script/ci/merge_branch_to_master.sh b/script/ci/merge_branch_to_master.sh index 3eaae17d56..fd9c602802 100755 --- a/script/ci/merge_branch_to_master.sh +++ b/script/ci/merge_branch_to_master.sh @@ -6,5 +6,9 @@ source ./script/ci/includes.sh echo "--- Verifying branch is based on current master" exit_unless_master_merged -echo "--- Pushing branch" -git push origin $BUILDKITE_COMMIT:master +echo "--- Merging and pushing branch" +git checkout master +git merge origin/master +git merge origin/$BUILDKITE_BRANCH +git push origin master +git checkout origin/$BUILDKITE_BRANCH diff --git a/script/ci/merge_master_into_branch.sh b/script/ci/merge_master_into_branch.sh index a5e05c7759..94705604d1 100755 --- a/script/ci/merge_master_into_branch.sh +++ b/script/ci/merge_master_into_branch.sh @@ -4,6 +4,7 @@ set -e source ./script/ci/includes.sh echo "--- Checking if master has already been merged" +set_ofn_commit $BUILDKITE_COMMIT succeed_if_master_merged echo "--- Merging master into this branch" @@ -12,3 +13,5 @@ git merge origin/$BUILDKITE_BRANCH git merge origin/master -m "Auto-merge from CI [skip ci]" git push origin $BUILDKITE_BRANCH git checkout origin/$BUILDKITE_BRANCH + +set_ofn_commit `git rev-parse $BUILDKITE_BRANCH` diff --git a/script/ci/push_to_production.sh b/script/ci/push_to_production.sh index 00ade0af07..413aeeabf8 100755 --- a/script/ci/push_to_production.sh +++ b/script/ci/push_to_production.sh @@ -1,15 +1,21 @@ #!/bin/bash -set -ex +set -e +source "`dirname $0`/includes.sh" -# Add production git remote if required -PROD_TEST=`git remote | grep -s 'production' || true` -if [[ "$PROD_TEST" != *production* ]]; then - git remote add production ubuntu@ofn-prod:apps/openfoodweb/current +OFN_COMMIT=$(get_ofn_commit) +if [ "$OFN_COMMIT" = 'OFN_COMMIT_NOT_FOUND' ]; then + OFN_COMMIT=$(git rev-parse $BUILDKITE_COMMIT) fi +echo "--- Checking environment variables" +require_env_vars OFN_COMMIT STAGING_SSH_HOST STAGING_CURRENT_PATH STAGING_SERVICE STAGING_DB_HOST STAGING_DB_USER STAGING_DB PRODUCTION_REMOTE + echo "--- Saving baseline data for staging" -ssh ofn-staging2 "/home/openfoodweb/apps/openfoodweb/current/script/ci/save_staging_baseline.sh $BUILDKITE_COMMIT" +VARS="CURRENT_PATH='$STAGING_CURRENT_PATH' SERVICE='$STAGING_SERVICE' DB_HOST='$STAGING_DB_HOST' DB_USER='$STAGING_DB_USER' DB='$STAGING_DB'" +ssh "$STAGING_SSH_HOST" "$VARS $STAGING_CURRENT_PATH/script/ci/save_staging_baseline.sh $OFN_COMMIT" echo "--- Pushing to production" -[[ $(git push production $BUILDKITE_COMMIT:master --force 2>&1) =~ "Done" ]] +exec 5>&1 +OUTPUT=$(git push "$PRODUCTION_REMOTE" "$OFN_COMMIT":master --force 2>&1 |tee /dev/fd/5) +[[ $OUTPUT =~ "Done" ]] diff --git a/script/ci/push_to_staging.sh b/script/ci/push_to_staging.sh index c634ca53a4..c2ba9827da 100755 --- a/script/ci/push_to_staging.sh +++ b/script/ci/push_to_staging.sh @@ -1,19 +1,32 @@ #!/bin/bash -set -ex -source ./script/ci/includes.sh +set -e +source "`dirname $0`/includes.sh" -# Add staging git remote if required -ST2_TEST=`git remote | grep -s 'staging2' || true` -if [[ "$ST2_TEST" != *staging2* ]]; then - git remote add staging2 openfoodweb@ofn-staging2:apps/openfoodweb/current +OFN_COMMIT=$(get_ofn_commit) +if [ "$OFN_COMMIT" = 'OFN_COMMIT_NOT_FOUND' ]; then + OFN_COMMIT=$(git rev-parse $BUILDKITE_COMMIT) +fi +STAGING_REMOTE="${STAGING_REMOTE:-$STAGING_SSH_HOST:$STAGING_CURRENT_PATH}" + +echo "--- Checking environment variables" +require_env_vars OFN_COMMIT STAGING_SSH_HOST STAGING_CURRENT_PATH STAGING_REMOTE STAGING_SERVICE STAGING_DB_HOST STAGING_DB_USER STAGING_DB + +if [ "$REQUIRE_MASTER_MERGED" = false ]; then + echo "--- NOT verifying branch is based on current master" +else + echo "--- Verifying branch is based on current master" + exit_unless_master_merged fi -echo "--- Verifying branch is based on current master" -exit_unless_master_merged - +# TODO: Optimise staging deployment +# This is stopping and re-starting unicorn and delayed job. echo "--- Loading baseline data" -ssh ofn-staging2 "/home/openfoodweb/apps/openfoodweb/current/script/ci/load_staging_baseline.sh" +VARS="CURRENT_PATH='$STAGING_CURRENT_PATH' SERVICE='$STAGING_SERVICE' DB_HOST='$STAGING_DB_HOST' DB_USER='$STAGING_DB_USER' DB='$STAGING_DB'" +ssh "$STAGING_SSH_HOST" "$VARS $STAGING_CURRENT_PATH/script/ci/load_staging_baseline.sh" +# This is stopping and re-starting unicorn and delayed job again. echo "--- Pushing to staging" -[[ $(git push staging2 $BUILDKITE_COMMIT:master --force 2>&1) =~ "Done" ]] +exec 5>&1 +OUTPUT=$(git push "$STAGING_REMOTE" "$OFN_COMMIT":master --force 2>&1 |tee /dev/fd/5) +[[ $OUTPUT =~ "Done" ]] diff --git a/script/ci/run_js_tests.sh b/script/ci/run_js_tests.sh index 91a4be09d9..c4a03403b9 100755 --- a/script/ci/run_js_tests.sh +++ b/script/ci/run_js_tests.sh @@ -5,6 +5,7 @@ set -e echo "--- Loading environment" source ./script/ci/includes.sh load_environment +checkout_ofn_commit echo "--- Verifying branch is based on current master" exit_unless_master_merged diff --git a/script/ci/run_tests.sh b/script/ci/run_tests.sh index 5539935a8b..efae0805a6 100755 --- a/script/ci/run_tests.sh +++ b/script/ci/run_tests.sh @@ -5,6 +5,7 @@ set -e echo "--- Loading environment" source ./script/ci/includes.sh load_environment +checkout_ofn_commit echo "--- Verifying branch is based on current master" exit_unless_master_merged @@ -13,7 +14,8 @@ echo "--- Bundling" bundle install echo "--- Loading test database" -bundle exec rake db:test:load +bundle exec rake db:drop db:create db:schema:load +bundle exec rake parallel:drop parallel:create parallel:load_schema echo "--- Running tests" -bundle exec rspec spec +bundle exec rake parallel:spec diff --git a/script/ci/save_staging_baseline.sh b/script/ci/save_staging_baseline.sh index 9fcc6c373b..37ff60bf17 100755 --- a/script/ci/save_staging_baseline.sh +++ b/script/ci/save_staging_baseline.sh @@ -9,12 +9,17 @@ # current code checked out. set -e +source "`dirname $0`/includes.sh" -cd /home/openfoodweb/apps/openfoodweb/current +echo "Checking environment variables" +require_env_vars CURRENT_PATH SERVICE DB_HOST DB_USER DB + +cd "$CURRENT_PATH" if [[ `git rev-parse HEAD` == $1 ]]; then mkdir -p db/backup - pg_dump -h localhost -U openfoodweb openfoodweb_production |gzip > db/backup/staging-baseline.sql.gz + pg_dump -h "$DB_HOST" -U "$DB_USER" "$DB" |gzip > db/backup/staging-baseline.sql.gz echo "Staging baseline data saved." else echo "Staging SHA does not match production, we will not save staging baseline data." + echo "'`git rev-parse HEAD`' is not '$1'" fi diff --git a/script/delayed_job.sh b/script/delayed_job.sh deleted file mode 100755 index a1498d3ce6..0000000000 --- a/script/delayed_job.sh +++ /dev/null @@ -1,6 +0,0 @@ -#!/usr/bin/env bash - -export HOME="/home/openfoodweb" -export PATH="$HOME/.rbenv/bin:$HOME/.rbenv/shims:$PATH" - -$HOME/apps/openfoodweb/current/script/delayed_job $@ diff --git a/script/mirror_db.sh b/script/mirror_db.sh index 608f20eb75..4c6142b701 100755 --- a/script/mirror_db.sh +++ b/script/mirror_db.sh @@ -7,7 +7,15 @@ set -e if hash zeus 2>/dev/null && [ -e .zeus.sock ]; then RAILS_RUN='zeus r' else - RAILS_RUN='rails runner' + RAILS_RUN='bundle exec rails runner' +fi + +if [[ $1 != 'ofn-no' ]]; then + DB_USER='openfoodweb' + DB_DATABASE='openfoodweb_production' +else + DB_USER='ofn_user' + DB_DATABASE='openfoodnetwork' fi @@ -15,7 +23,7 @@ fi echo "Mirroring database..." echo "drop database open_food_network_dev" | psql -h localhost -U ofn open_food_network_test echo "create database open_food_network_dev" | psql -h localhost -U ofn open_food_network_test -ssh $1 "pg_dump -h localhost -U openfoodweb openfoodweb_production |gzip" |gunzip |psql -h localhost -U ofn open_food_network_dev +ssh $1 "pg_dump -h localhost -U $DB_USER $DB_DATABASE |gzip" |gunzip |psql -h localhost -U ofn open_food_network_dev # -- Disable S3 diff --git a/script/rails b/script/rails index a444686425..f8da2cffd4 100755 --- a/script/rails +++ b/script/rails @@ -1,11 +1,6 @@ #!/usr/bin/env ruby # This command will automatically be run when you run "rails" with Rails 3 gems installed from the root of your application. -if ENV['RAILS_ENV'] == 'test' - require 'simplecov' - SimpleCov.start 'rails' -end - APP_PATH = File.expand_path('../../config/application', __FILE__) require File.expand_path('../../config/boot', __FILE__) require 'rails/commands' diff --git a/spec/controllers/admin/accounts_and_billing_settings_controller_spec.rb b/spec/controllers/admin/accounts_and_billing_settings_controller_spec.rb new file mode 100644 index 0000000000..7ec9eb1ca0 --- /dev/null +++ b/spec/controllers/admin/accounts_and_billing_settings_controller_spec.rb @@ -0,0 +1,241 @@ +require 'spec_helper' + +describe Admin::AccountsAndBillingSettingsController, type: :controller do + let!(:pm1) { create(:payment_method) } + let!(:sm1) { create(:shipping_method) } + let!(:pm2) { create(:payment_method) } + let!(:sm2) { create(:shipping_method) } + let!(:accounts_distributor) { create(:distributor_enterprise, payment_methods: [pm1], shipping_methods: [sm1]) } + let!(:new_distributor) { create(:distributor_enterprise, payment_methods: [pm2], shipping_methods: [sm2]) } + let(:user) { create(:user) } + let(:admin) { create(:admin_user) } + + before do + Spree::Config.set({ + accounts_distributor_id: accounts_distributor.id, + default_accounts_payment_method_id: pm1.id, + default_accounts_shipping_method_id: sm1.id, + auto_update_invoices: true, + auto_finalize_invoices: false + }) + end + + describe "edit" do + context "as an enterprise user" do + before { allow(controller).to receive(:spree_current_user) { user } } + + it "does not allow access" do + spree_get :edit + expect(response).to redirect_to spree.unauthorized_path + end + end + + context "as super admin" do + before { allow(controller).to receive(:spree_current_user) { admin } } + + it "loads relevant global settings into a locally dummy class" do + spree_get :edit + settings = assigns(:settings) + + expect(settings.accounts_distributor_id).to eq accounts_distributor.id + expect(settings.default_accounts_payment_method_id).to eq pm1.id + expect(settings.default_accounts_shipping_method_id).to eq sm1.id + expect(settings.auto_update_invoices).to eq true + expect(settings.auto_finalize_invoices).to eq false + end + end + end + + describe "update" do + context "as an enterprise user" do + before { allow(controller).to receive(:spree_current_user) { user } } + + it "does not allow access" do + spree_get :update + expect(response).to redirect_to spree.unauthorized_path + end + end + + context "as super admin" do + before {allow(controller).to receive(:spree_current_user) { admin } } + let(:params) { { settings: { } } } + + context "when required settings have no values" do + before do + params[:settings][:accounts_distributor_id] = '' + params[:settings][:default_accounts_payment_method_id] = '0' + params[:settings][:default_accounts_shipping_method_id] = '0' + params[:settings][:auto_update_invoices] = '0' + params[:settings][:auto_finalize_invoices] = '0' + spree_get :update, params + end + + it "does not allow them to be empty/false" do + expect(response).to render_template :edit + expect(assigns(:settings).errors.count).to be 3 + expect(Spree::Config.accounts_distributor_id).to eq accounts_distributor.id + expect(Spree::Config.default_accounts_payment_method_id).to eq pm1.id + expect(Spree::Config.default_accounts_shipping_method_id).to eq sm1.id + expect(Spree::Config.auto_update_invoices).to be true + expect(Spree::Config.auto_finalize_invoices).to be false + end + end + + context "when required settings have values" do + before do + params[:settings][:accounts_distributor_id] = new_distributor.id + params[:settings][:default_accounts_payment_method_id] = pm2.id + params[:settings][:default_accounts_shipping_method_id] = sm2.id + params[:settings][:auto_update_invoices] = '0' + params[:settings][:auto_finalize_invoices] = '0' + end + + it "sets global config to the specified values" do + spree_get :update, params + expect(Spree::Config.accounts_distributor_id).to eq new_distributor.id + expect(Spree::Config.default_accounts_payment_method_id).to eq pm2.id + expect(Spree::Config.default_accounts_shipping_method_id).to eq sm2.id + expect(Spree::Config.auto_update_invoices).to be false + expect(Spree::Config.auto_finalize_invoices).to be false + end + end + end + end + + describe "start_job" do + context "as an enterprise user" do + before do + allow(controller).to receive(:spree_current_user) { user } + spree_post :start_job, enterprise_id: accounts_distributor.id + end + + it "does not allow access" do + expect(response).to redirect_to spree.unauthorized_path + end + end + + context "as super admin" do + before do + allow(controller).to receive(:spree_current_user) { admin } + end + + context "when settings are not valid" do + before do + Spree::Config.set({ accounts_distributor_id: "" }) + Spree::Config.set({ default_accounts_payment_method_id: "" }) + Spree::Config.set({ default_accounts_shipping_method_id: "" }) + spree_post :start_job, job: { name: "" } + end + + it "returns immediately and renders :edit" do + expect(assigns(:settings).errors.count).to eq 3 + expect(response).to render_template :edit + end + end + + context "when settings are valid" do + before do + Spree::Config.set({ accounts_distributor_id: accounts_distributor.id }) + Spree::Config.set({ default_accounts_payment_method_id: pm1.id }) + Spree::Config.set({ default_accounts_shipping_method_id: sm1.id }) + end + + context "and job_name is not on the known_jobs list" do + before do + spree_post :start_job, job: { name: "" } + end + + it "returns immediately with an error" do + expect(flash[:error]).to eq "Unknown Task: " + expect(response).to redirect_to edit_admin_accounts_and_billing_settings_path + end + end + + context "and job_name is update_account_invoices" do + let!(:params) { { job: { name: "update_account_invoices" } } } + + context "and no jobs are currently running" do + before do + allow(controller).to receive(:load_jobs) + end + + it "runs the job" do + expect{spree_post :start_job, params}.to enqueue_job UpdateAccountInvoices + expect(flash[:success]).to eq "Task Queued" + expect(response).to redirect_to edit_admin_accounts_and_billing_settings_path + end + end + + context "and there are jobs currently running" do + before do + allow(controller).to receive(:load_jobs) + controller.instance_variable_set("@update_account_invoices_job", double(:update_account_invoices_job)) + end + + it "does not run the job" do + expect{spree_post :start_job, params}.to_not enqueue_job UpdateAccountInvoices + expect(flash[:error]).to eq "A task is already running, please wait until it has finished" + expect(response).to redirect_to edit_admin_accounts_and_billing_settings_path + end + end + end + + context "and job_name is finalize_account_invoices" do + let!(:params) { { job: { name: "finalize_account_invoices" } } } + + context "and no jobs are currently running" do + before do + allow(controller).to receive(:load_jobs) + end + + it "runs the job" do + expect{spree_post :start_job, params}.to enqueue_job FinalizeAccountInvoices + expect(flash[:success]).to eq "Task Queued" + expect(response).to redirect_to edit_admin_accounts_and_billing_settings_path + end + end + + context "and there are jobs currently running" do + before do + allow(controller).to receive(:load_jobs) + controller.instance_variable_set("@finalize_account_invoices_job", double(:finalize_account_invoices_job)) + end + + it "does not run the job" do + expect{spree_post :start_job, params}.to_not enqueue_job FinalizeAccountInvoices + expect(flash[:error]).to eq "A task is already running, please wait until it has finished" + expect(response).to redirect_to edit_admin_accounts_and_billing_settings_path + end + end + end + end + end + end + + describe "show_methods" do + context "as an enterprise user" do + before do + allow(controller).to receive(:spree_current_user) { user } + spree_get :show_methods, enterprise_id: accounts_distributor.id + end + + it "does not allow access" do + expect(response).to redirect_to spree.unauthorized_path + end + end + + context "as super admin" do + before do + allow(controller).to receive(:spree_current_user) { admin } + spree_get :show_methods, enterprise_id: accounts_distributor.id + end + + it "renders the method_settings template" do + expect(assigns(:payment_methods)).to eq [pm1] + expect(assigns(:shipping_methods)).to eq [sm1] + expect(assigns(:enterprise)).to eq accounts_distributor + expect(response).to render_template :method_settings + end + end + end +end diff --git a/spec/controllers/admin/business_model_configuration_controller_spec.rb b/spec/controllers/admin/business_model_configuration_controller_spec.rb new file mode 100644 index 0000000000..b0ae86d4ac --- /dev/null +++ b/spec/controllers/admin/business_model_configuration_controller_spec.rb @@ -0,0 +1,88 @@ +require 'spec_helper' + +describe Admin::BusinessModelConfigurationController, type: :controller do + let(:user) { create(:user) } + let(:admin) { create(:admin_user) } + + before do + Spree::Config.set({ + account_invoices_monthly_fixed: 5, + account_invoices_monthly_rate: 0.02, + account_invoices_monthly_cap: 50, + account_invoices_tax_rate: 0.1 + }) + end + + describe "edit" do + context "as an enterprise user" do + before { allow(controller).to receive(:spree_current_user) { user } } + + it "does not allow access" do + spree_get :edit + expect(response).to redirect_to spree.unauthorized_path + end + end + + context "as super admin" do + before { allow(controller).to receive(:spree_current_user) { admin } } + + it "allows access" do + spree_get :edit + expect(response).to_not redirect_to spree.unauthorized_path + end + end + end + + describe "update" do + context "as an enterprise user" do + before { allow(controller).to receive(:spree_current_user) { user } } + + it "does not allow access" do + spree_get :update + expect(response).to redirect_to spree.unauthorized_path + end + end + + context "as super admin" do + before {allow(controller).to receive(:spree_current_user) { admin } } + let(:params) { { settings: { } } } + + context "when settings are invalid" do + before do + params[:settings][:account_invoices_monthly_fixed] = '' + params[:settings][:account_invoices_monthly_rate] = '2' + params[:settings][:account_invoices_monthly_cap] = '-1' + params[:settings][:account_invoices_tax_rate] = '4' + spree_get :update, params + end + + it "does not allow them to be set" do + expect(response).to render_template :edit + expect(assigns(:settings).errors.count).to be 5 + expect(Spree::Config.account_invoices_monthly_fixed).to eq 5 + expect(Spree::Config.account_invoices_monthly_rate).to eq 0.02 + expect(Spree::Config.account_invoices_monthly_cap).to eq 50 + expect(Spree::Config.account_invoices_tax_rate).to eq 0.1 + end + end + + context "when required settings are valid" do + before do + params[:settings][:account_invoices_monthly_fixed] = '10' + params[:settings][:account_invoices_monthly_rate] = '0.05' + params[:settings][:account_invoices_monthly_cap] = '30' + params[:settings][:account_invoices_tax_rate] = '0.15' + end + + it "sets global config to the specified values" do + spree_get :update, params + expect(assigns(:settings).errors.count).to be 0 + expect(Spree::Config.account_invoices_monthly_fixed).to eq 10 + expect(Spree::Config.account_invoices_monthly_rate).to eq 0.05 + expect(Spree::Config.account_invoices_monthly_cap).to eq 30 + expect(Spree::Config.account_invoices_tax_rate).to eq 0.15 + end + end + end + end +end diff --git a/spec/controllers/admin/customers_controller_spec.rb b/spec/controllers/admin/customers_controller_spec.rb new file mode 100644 index 0000000000..bb2e4888c2 --- /dev/null +++ b/spec/controllers/admin/customers_controller_spec.rb @@ -0,0 +1,97 @@ +require 'spec_helper' + +describe Admin::CustomersController, type: :controller do + include AuthenticationWorkflow + + describe "index" do + let(:enterprise) { create(:distributor_enterprise) } + let(:another_enterprise) { create(:distributor_enterprise) } + + context "html" do + before do + controller.stub spree_current_user: enterprise.owner + end + + it "returns an empty @collection" do + spree_get :index, format: :html + expect(assigns(:collection)).to eq [] + end + end + + context "json" do + let!(:customer) { create(:customer, enterprise: enterprise) } + + context "where I manage the enterprise" do + before do + controller.stub spree_current_user: enterprise.owner + end + + context "and enterprise_id is given in params" do + let(:params) { { format: :json, enterprise_id: enterprise.id } } + + it "scopes @collection to customers of that enterprise" do + spree_get :index, params + expect(assigns(:collection)).to eq [customer] + end + + it "serializes the data" do + expect(ActiveModel::ArraySerializer).to receive(:new) + spree_get :index, params + end + end + + context "and enterprise_id is not given in params" do + it "returns an empty collection" do + spree_get :index, format: :json + expect(assigns(:collection)).to eq [] + end + end + end + + context "and I do not manage the enterprise" do + before do + controller.stub spree_current_user: another_enterprise.owner + end + + it "returns an empty collection" do + spree_get :index, format: :json + expect(assigns(:collection)).to eq [] + end + end + end + end + + describe "update" do + let(:enterprise) { create(:distributor_enterprise) } + let(:another_enterprise) { create(:distributor_enterprise) } + + context "json" do + let!(:customer) { create(:customer, enterprise: enterprise) } + + context "where I manage the customer's enterprise" do + before do + controller.stub spree_current_user: enterprise.owner + end + + it "allows me to update the customer" do + spree_put :update, format: :json, id: customer.id, customer: { email: 'new.email@gmail.com' } + expect(assigns(:customer)).to eq customer + expect(customer.reload.email).to eq 'new.email@gmail.com' + end + end + + context "where I don't manage the customer's enterprise" do + before do + controller.stub spree_current_user: another_enterprise.owner + end + + it "prevents me from updating the customer" do + spree_put :update, format: :json, id: customer.id, customer: { email: 'new.email@gmail.com' } + expect(response).to redirect_to spree.unauthorized_path + expect(assigns(:customer)).to eq nil + expect(customer.email).to_not eq 'new.email@gmail.com' + end + end + end + end +end diff --git a/spec/controllers/admin/enterprises_controller_spec.rb b/spec/controllers/admin/enterprises_controller_spec.rb index 79f72cfac3..8b972a3321 100644 --- a/spec/controllers/admin/enterprises_controller_spec.rb +++ b/spec/controllers/admin/enterprises_controller_spec.rb @@ -184,6 +184,15 @@ module Admin end context "as owner" do + it "allows 'sells' to be changed" do + controller.stub spree_current_user: profile_enterprise.owner + enterprise_params = { id: profile_enterprise, enterprise: { sells: 'any' } } + + spree_put :update, enterprise_params + profile_enterprise.reload + expect(profile_enterprise.sells).to eq 'any' + end + it "allows owner to be changed" do controller.stub spree_current_user: distributor_owner update_params = { id: distributor, enterprise: { owner_id: distributor_manager } } @@ -234,39 +243,51 @@ module Admin end end - describe "set_sells" do + describe "register" do let(:enterprise) { create(:enterprise, sells: 'none') } - before do - controller.stub spree_current_user: distributor_manager - end - context "as a normal user" do - it "does not allow 'sells' to be set" do - spree_post :set_sells, { id: enterprise.id, sells: 'none' } + before do + controller.stub spree_current_user: distributor_manager + end + + it "does not allow access" do + spree_post :register, { id: enterprise.id, sells: 'none' } expect(response).to redirect_to spree.unauthorized_path end end context "as a manager" do before do + controller.stub spree_current_user: distributor_manager enterprise.enterprise_roles.build(user: distributor_manager).save end - context "allows setting 'sells' to 'none'" do + it "does not allow access" do + spree_post :register, { id: enterprise.id, sells: 'none' } + expect(response).to redirect_to spree.unauthorized_path + end + end + + context "as an owner" do + before do + controller.stub spree_current_user: enterprise.owner + end + + context "setting 'sells' to 'none'" do it "is allowed" do - spree_post :set_sells, { id: enterprise, sells: 'none' } + spree_post :register, { id: enterprise, sells: 'none' } expect(response).to redirect_to spree.admin_path expect(flash[:success]).to eq "Congratulations! Registration for #{enterprise.name} is complete!" expect(enterprise.reload.sells).to eq 'none' end + end - context "setting producer_profile_only to true" do - it "is allowed" do - spree_post :set_sells, { id: enterprise, sells: 'none', producer_profile_only: true } - expect(response).to redirect_to spree.admin_path - expect(enterprise.reload.producer_profile_only).to eq true - end + context "setting producer_profile_only" do + it "is ignored" do + spree_post :register, { id: enterprise, sells: 'none', producer_profile_only: true } + expect(response).to redirect_to spree.admin_path + expect(enterprise.reload.producer_profile_only).to be false end end @@ -277,68 +298,97 @@ module Admin end context "if the trial has finished" do - it "is disallowed" do + let(:trial_start) { 30.days.ago.beginning_of_day } + + before do + enterprise.update_attribute(:shop_trial_start_date, trial_start) + end + + it "is allowed" do Timecop.freeze(Time.zone.local(2015, 4, 16, 14, 0, 0)) do - enterprise.update_attribute(:shop_trial_start_date, 30.days.ago.beginning_of_day) - spree_post :set_sells, { id: enterprise, sells: 'own' } + spree_post :register, { id: enterprise, sells: 'own' } expect(response).to redirect_to spree.admin_path - trial_expiry = Date.today.strftime("%Y-%m-%d") - expect(flash[:error]).to eq "Sorry, but you've already had a trial. Expired on: #{trial_expiry}" - expect(enterprise.reload.sells).to eq 'none' + expect(enterprise.reload.sells).to eq 'own' + expect(enterprise.shop_trial_start_date).to eq trial_start end end end context "if the trial has not finished" do + let(:trial_start) { Date.current.to_time } + before do - enterprise.shop_trial_start_date = Date.today.to_time - enterprise.save! + enterprise.update_attribute(:shop_trial_start_date, trial_start) end it "is allowed, but trial start date is not reset" do - spree_post :set_sells, { id: enterprise, sells: 'own' } + spree_post :register, { id: enterprise, sells: 'own' } expect(response).to redirect_to spree.admin_path - trial_expiry = (Date.today + 30.days).strftime("%Y-%m-%d") - expect(flash[:notice]).to eq "Welcome back! Your trial expires on: #{trial_expiry}" expect(enterprise.reload.sells).to eq 'own' - expect(enterprise.reload.shop_trial_start_date).to eq Date.today.to_time + expect(enterprise.shop_trial_start_date).to eq trial_start end end context "if a trial has not started" do it "is allowed" do - spree_post :set_sells, { id: enterprise, sells: 'own' } + spree_post :register, { id: enterprise, sells: 'own' } expect(response).to redirect_to spree.admin_path expect(flash[:success]).to eq "Congratulations! Registration for #{enterprise.name} is complete!" expect(enterprise.reload.sells).to eq 'own' - expect(enterprise.reload.shop_trial_start_date).to be > Time.now-(1.minute) - end - end - - context "setting producer_profile_only to true" do - it "is ignored" do - spree_post :set_sells, { id: enterprise, sells: 'own', producer_profile_only: true } - expect(response).to redirect_to spree.admin_path - expect(enterprise.reload.producer_profile_only).to be false + expect(enterprise.reload.shop_trial_start_date).to be > Time.zone.now-(1.minute) end end end context "setting 'sells' to any" do - it "is not allowed" do - spree_post :set_sells, { id: enterprise, sells: 'any' } - expect(response).to redirect_to spree.admin_path - expect(flash[:error]).to eq "Unauthorised" - expect(enterprise.reload.sells).to eq 'none' + context "if the trial has finished" do + let(:trial_start) { 30.days.ago.beginning_of_day } + + before do + enterprise.update_attribute(:shop_trial_start_date, trial_start) + end + + it "is allowed" do + Timecop.freeze(Time.zone.local(2015, 4, 16, 14, 0, 0)) do + spree_post :register, { id: enterprise, sells: 'any' } + expect(response).to redirect_to spree.admin_path + expect(enterprise.reload.sells).to eq 'any' + expect(enterprise.shop_trial_start_date).to eq trial_start + end + end + end + + context "if the trial has not finished" do + let(:trial_start) { Date.current.to_time } + + before do + enterprise.update_attribute(:shop_trial_start_date, trial_start) + end + + it "is allowed, but trial start date is not reset" do + spree_post :register, { id: enterprise, sells: 'any' } + expect(response).to redirect_to spree.admin_path + expect(enterprise.reload.sells).to eq 'any' + expect(enterprise.shop_trial_start_date).to eq trial_start + end + end + + context "if a trial has not started" do + it "is allowed" do + spree_post :register, { id: enterprise, sells: 'any' } + expect(response).to redirect_to spree.admin_path + expect(flash[:success]).to eq "Congratulations! Registration for #{enterprise.name} is complete!" + expect(enterprise.reload.sells).to eq 'any' + expect(enterprise.reload.shop_trial_start_date).to be > Time.zone.now-(1.minute) + end end end context "settiing 'sells' to 'unspecified'" do it "is not allowed" do - spree_post :set_sells, { id: enterprise, sells: 'unspecified' } - expect(response).to redirect_to spree.admin_path - expect(flash[:error]).to eq "Unauthorised" - expect(enterprise.reload.sells).to eq 'none' + spree_post :register, { id: enterprise, sells: 'unspecified' } + expect(response).to render_template :welcome + expect(flash[:error]).to eq "Please select a package" end end end @@ -386,6 +436,21 @@ module Admin end end + context "as the owner of an enterprise" do + it "allows 'sells' and 'owner' to be changed" do + controller.stub spree_current_user: original_owner + bulk_enterprise_params = { enterprise_set: { collection_attributes: { '0' => { id: profile_enterprise1.id, sells: 'any', owner_id: new_owner.id }, '1' => { id: profile_enterprise2.id, sells: 'any', owner_id: new_owner.id } } } } + + spree_put :bulk_update, bulk_enterprise_params + profile_enterprise1.reload + profile_enterprise2.reload + expect(profile_enterprise1.sells).to eq 'any' + expect(profile_enterprise2.sells).to eq 'any' + expect(profile_enterprise1.owner).to eq original_owner + expect(profile_enterprise2.owner).to eq original_owner + end + end + context "as super admin" do it "allows 'sells' and 'owner' to be changed" do profile_enterprise1.enterprise_roles.build(user: new_owner).save @@ -449,5 +514,74 @@ module Admin end end end + + describe "for_line_items" do + let!(:user) { create(:user) } + let!(:enterprise) { create(:enterprise, sells: 'any', owner: user) } + + before do + # As a user with permission + controller.stub spree_current_user: user + end + + it "initializes permissions with the existing OrderCycle" do + # expect(controller).to receive(:render_as_json).with([enterprise], {ams_prefix: 'basic', spree_current_user: user}) + spree_get :for_line_items, format: :json + end + end + + describe "index" do + context "as super admin" do + let(:super_admin) { create(:admin_user) } + let!(:user) { create_enterprise_user(enterprise_limit: 10) } + let!(:enterprise1) { create(:enterprise, sells: 'any', owner: user) } + let!(:enterprise2) { create(:enterprise, sells: 'own', owner: user) } + let!(:enterprise3) { create(:enterprise, sells: 'any', owner: create_enterprise_user ) } + + before do + controller.stub spree_current_user: super_admin + end + + context "html" do + it "returns all enterprises" do + spree_get :index, format: :html + expect(assigns(:collection)).to include enterprise1, enterprise2, enterprise3 + end + end + + context "json" do + it "returns all enterprises" do + spree_get :index, format: :json + expect(assigns(:collection)).to include enterprise1, enterprise2, enterprise3 + end + end + end + + context "as an enterprise user" do + let!(:user) { create_enterprise_user(enterprise_limit: 10) } + let!(:enterprise1) { create(:enterprise, sells: 'any', owner: user) } + let!(:enterprise2) { create(:enterprise, sells: 'own', owner: user) } + let!(:enterprise3) { create(:enterprise, sells: 'any', owner: create_enterprise_user ) } + + before do + controller.stub spree_current_user: user + end + + context "html" do + it "returns an empty @collection" do + spree_get :index, format: :html + expect(assigns(:collection)).to eq [] + end + end + + context "json" do + it "scopes @collection to enterprises editable by the user" do + spree_get :index, format: :json + expect(assigns(:collection)).to include enterprise1, enterprise2 + expect(assigns(:collection)).to_not include enterprise3 + end + end + end + end end end diff --git a/spec/controllers/admin/order_cycles_controller_spec.rb b/spec/controllers/admin/order_cycles_controller_spec.rb index 9e996f0da7..9fc9ad9096 100644 --- a/spec/controllers/admin/order_cycles_controller_spec.rb +++ b/spec/controllers/admin/order_cycles_controller_spec.rb @@ -3,12 +3,47 @@ require 'spec_helper' module Admin describe OrderCyclesController do include AuthenticationWorkflow + let!(:distributor_owner) { create_enterprise_user enterprise_limit: 2 } before do controller.stub spree_current_user: distributor_owner end + describe "#index" do + describe "when the user manages a coordinator" do + let!(:coordinator) { create(:distributor_enterprise, owner: distributor_owner) } + let!(:oc1) { create(:simple_order_cycle, orders_close_at: 60.days.ago ) } + let!(:oc2) { create(:simple_order_cycle, orders_close_at: 40.days.ago ) } + let!(:oc3) { create(:simple_order_cycle, orders_close_at: 20.days.ago ) } + + context "where show_more is set to true" do + it "loads all order cycles" do + spree_get :index, show_more: true + expect(assigns(:collection)).to include oc1, oc2, oc3 + end + end + + context "where show_more is not set" do + context "and q[orders_close_at_gt] is set" do + it "loads order cycles that closed within the past month" do + spree_get :index, q: { orders_close_at_gt: 45.days.ago } + expect(assigns(:collection)).to_not include oc1 + expect(assigns(:collection)).to include oc2, oc3 + end + end + + context "and q[orders_close_at_gt] is not set" do + it "loads order cycles that closed within the past month" do + spree_get :index + expect(assigns(:collection)).to_not include oc1, oc2 + expect(assigns(:collection)).to include oc3 + end + end + end + end + end + describe "new" do describe "when the user manages no distributor enterprises suitable for coordinator" do let!(:distributor) { create(:distributor_enterprise, owner: distributor_owner, confirmed_at: nil) } @@ -57,6 +92,48 @@ module Admin end end + describe "update" do + let(:order_cycle) { create(:simple_order_cycle) } + + before { login_as_admin } + + it "sets flash message when page is reloading" do + spree_put :update, id: order_cycle.id, reloading: '1', order_cycle: {} + flash[:notice].should == 'Your order cycle has been updated.' + end + + it "does not set flash message otherwise" do + spree_put :update, id: order_cycle.id, reloading: '0', order_cycle: {} + flash[:notice].should be_nil + end + + context "as a producer supplying to an order cycle" do + let(:producer) { create(:supplier_enterprise) } + let(:coordinator) { order_cycle.coordinator } + let(:hub) { create(:distributor_enterprise) } + + before { login_as_enterprise_user [producer] } + + describe "removing a variant from incoming" do + let(:v) { create(:variant) } + let!(:ex_i) { create(:exchange, order_cycle: order_cycle, sender: producer, receiver: coordinator, incoming: true, variants: [v]) } + let!(:ex_o) { create(:exchange, order_cycle: order_cycle, sender: coordinator, receiver: hub, incoming: false, variants: [v]) } + + let(:params) do + {order_cycle: { + incoming_exchanges: [{id: ex_i.id, enterprise_id: producer.id, sender_id: producer.id, variants: {v.id => false}}], + outgoing_exchanges: [{id: ex_o.id, enterprise_id: hub.id, receiver_id: hub.id, variants: {v.id => false}}] } + } + end + + it "removes the variant from outgoing also" do + spree_put :update, {id: order_cycle.id}.merge(params) + Exchange.where(order_cycle_id: order_cycle).with_variant(v).should be_empty + end + end + end + end + describe "bulk_update" do let(:oc) { create(:simple_order_cycle) } let!(:coordinator) { oc.coordinator } @@ -67,13 +144,19 @@ module Admin it "updates order cycle properties" do spree_put :bulk_update, order_cycle_set: { collection_attributes: { '0' => { id: oc.id, - orders_open_at: Date.today - 21.days, - orders_close_at: Date.today + 21.days, + orders_open_at: Date.current - 21.days, + orders_close_at: Date.current + 21.days, } } } oc.reload - expect(oc.orders_open_at.to_date).to eq Date.today - 21.days - expect(oc.orders_close_at.to_date).to eq Date.today + 21.days + expect(oc.orders_open_at.to_date).to eq Date.current - 21.days + expect(oc.orders_close_at.to_date).to eq Date.current + 21.days + end + + it "does nothing when no data is supplied" do + expect do + spree_put :bulk_update + end.to change(oc, :orders_open_at).by(0) end end @@ -84,17 +167,45 @@ module Admin it "doesn't update order cycle properties" do spree_put :bulk_update, order_cycle_set: { collection_attributes: { '0' => { id: oc.id, - orders_open_at: Date.today - 21.days, - orders_close_at: Date.today + 21.days, + orders_open_at: Date.current - 21.days, + orders_close_at: Date.current + 21.days, } } } oc.reload - expect(oc.orders_open_at.to_date).to_not eq Date.today - 21.days - expect(oc.orders_close_at.to_date).to_not eq Date.today + 21.days + expect(oc.orders_open_at.to_date).to_not eq Date.current - 21.days + expect(oc.orders_close_at.to_date).to_not eq Date.current + 21.days end end end + + describe "notifying producers" do + let(:user) { create_enterprise_user } + let(:admin_user) do + user = create(:user) + user.spree_roles << Spree::Role.find_or_create_by_name!('admin') + user + end + let(:order_cycle) { create(:simple_order_cycle) } + + before do + controller.stub spree_current_user: admin_user + end + + it "enqueues a job" do + expect do + spree_post :notify_producers, {id: order_cycle.id} + end.to enqueue_job OrderCycleNotificationJob + end + + it "redirects back to the order cycles path with a success message" do + spree_post :notify_producers, {id: order_cycle.id} + expect(response).to redirect_to admin_order_cycles_path + flash[:notice].should == 'Emails to be sent to producers have been queued for sending.' + end + end + + describe "destroy" do let!(:distributor) { create(:distributor_enterprise, owner: distributor_owner) } diff --git a/spec/controllers/admin/variant_overrides_controller_spec.rb b/spec/controllers/admin/variant_overrides_controller_spec.rb new file mode 100644 index 0000000000..d796f2d52f --- /dev/null +++ b/spec/controllers/admin/variant_overrides_controller_spec.rb @@ -0,0 +1,135 @@ +require 'spec_helper' + +describe Admin::VariantOverridesController, type: :controller do + # include AuthenticationWorkflow + + describe "bulk_update" do + context "json" do + let(:format) { :json } + + let(:hub) { create(:distributor_enterprise) } + let(:variant) { create(:variant) } + let!(:variant_override) { create(:variant_override, hub: hub, variant: variant) } + let(:variant_override_params) { [ { id: variant_override.id, price: 123.45, count_on_hand: 321, sku: "MySKU", on_demand: false } ] } + + context "where I don't manage the variant override hub" do + before do + user = create(:user) + user.owned_enterprises << create(:enterprise) + allow(controller).to receive(:spree_current_user) { user } + end + + it "redirects to unauthorized" do + spree_put :bulk_update, format: format, variant_overrides: variant_override_params + expect(response).to redirect_to spree.unauthorized_path + end + end + + context "where I manage the variant override hub" do + before do + allow(controller).to receive(:spree_current_user) { hub.owner } + end + + context "but the producer has not granted VO permission" do + it "redirects to unauthorized" do + spree_put :bulk_update, format: format, variant_overrides: variant_override_params + expect(response).to redirect_to spree.unauthorized_path + end + end + + context "and the producer has granted VO permission" do + before do + create(:enterprise_relationship, parent: variant.product.supplier, child: hub, permissions_list: [:create_variant_overrides]) + end + + it "allows me to update the variant override" do + spree_put :bulk_update, format: format, variant_overrides: variant_override_params + variant_override.reload + expect(variant_override.price).to eq 123.45 + expect(variant_override.count_on_hand).to eq 321 + expect(variant_override.sku).to eq "MySKU" + expect(variant_override.on_demand).to eq false + end + + context "where params for a variant override are blank" do + let(:variant_override_params) { [ { id: variant_override.id, price: "", count_on_hand: "", default_stock: nil, resettable: nil, sku: nil, on_demand: nil } ] } + + it "destroys the variant override" do + spree_put :bulk_update, format: format, variant_overrides: variant_override_params + expect(VariantOverride.find_by_id(variant_override.id)).to be_nil + end + end + end + end + end + end + + describe "bulk_reset" do + context "json" do + let(:format) { :json } + + let(:hub) { create(:distributor_enterprise) } + let(:producer) { create(:supplier_enterprise) } + let(:product) { create(:product, supplier: producer) } + let(:variant1) { create(:variant, product: product) } + let(:variant2) { create(:variant, product: product) } + let!(:variant_override1) { create(:variant_override, hub: hub, variant: variant1, count_on_hand: 5, default_stock: 7, resettable: true) } + let!(:variant_override2) { create(:variant_override, hub: hub, variant: variant2, count_on_hand: 2, default_stock: 1, resettable: false) } + + let(:params) { { format: format, hub_id: hub.id } } + + context "where I don't manage the variant override hub" do + before do + user = create(:user) + user.owned_enterprises << create(:enterprise) + allow(controller).to receive(:spree_current_user) { user } + end + + it "redirects to unauthorized" do + spree_put :bulk_reset, params + expect(response).to redirect_to spree.unauthorized_path + end + end + + context "where I manage the variant override hub" do + before do + allow(controller).to receive(:spree_current_user) { hub.owner } + end + + context "where the producer has not granted create_variant_overrides permission to the hub" do + it "restricts access" do + spree_put :bulk_reset, params + expect(response).to redirect_to spree.unauthorized_path + end + end + + context "where the producer has granted create_variant_overrides permission to the hub" do + let!(:er1) { create(:enterprise_relationship, parent: producer, child: hub, permissions_list: [:create_variant_overrides]) } + + it "updates stock to default values where reset is enabled" do + expect(variant_override1.reload.count_on_hand).to eq 5 # reset enabled + expect(variant_override2.reload.count_on_hand).to eq 2 # reset disabled + spree_put :bulk_reset, params + expect(variant_override1.reload.count_on_hand).to eq 7 # reset enabled + expect(variant_override2.reload.count_on_hand).to eq 2 # reset disabled + end + + context "and the producer has granted create_variant_overrides permission to another hub I manage" do + before { hub.owner.update_attribute(:enterprise_limit, 2) } + let(:hub2) { create(:distributor_enterprise, owner: hub.owner) } + let(:product) { create(:product, supplier: producer) } + let(:variant3) { create(:variant, product: product) } + let!(:variant_override3) { create(:variant_override, hub: hub2, variant: variant3, count_on_hand: 1, default_stock: 13, resettable: true) } + let!(:er2) { create(:enterprise_relationship, parent: producer, child: hub2, permissions_list: [:create_variant_overrides]) } + + it "does not reset count_on_hand for variant_overrides not in params" do + expect { + spree_put :bulk_reset, params + }.to_not change{variant_override3.reload.count_on_hand} + end + end + end + end + end + end +end diff --git a/spec/controllers/api/enterprises_controller_spec.rb b/spec/controllers/api/enterprises_controller_spec.rb index 77e16368d9..0e8f2ef606 100644 --- a/spec/controllers/api/enterprises_controller_spec.rb +++ b/spec/controllers/api/enterprises_controller_spec.rb @@ -1,7 +1,7 @@ require 'spec_helper' module Api - describe EnterprisesController do + describe EnterprisesController, :type => :controller do include AuthenticationWorkflow render_views diff --git a/spec/controllers/api/order_cycles_controller_spec.rb b/spec/controllers/api/order_cycles_controller_spec.rb index 3bb9a76602..a9a86608e7 100644 --- a/spec/controllers/api/order_cycles_controller_spec.rb +++ b/spec/controllers/api/order_cycles_controller_spec.rb @@ -2,7 +2,7 @@ require 'spec_helper' require 'spree/api/testing_support/helpers' module Api - describe OrderCyclesController do + describe OrderCyclesController, :type => :controller do include Spree::Api::TestingSupport::Helpers include AuthenticationWorkflow render_views diff --git a/spec/controllers/base_controller_spec.rb b/spec/controllers/base_controller_spec.rb index 1040b0594c..4ea2e31ff2 100644 --- a/spec/controllers/base_controller_spec.rb +++ b/spec/controllers/base_controller_spec.rb @@ -1,6 +1,6 @@ require 'spec_helper' -describe BaseController do +describe BaseController, :type => :controller do let(:oc) { mock_model(OrderCycle) } let(:hub) { mock_model(Enterprise, ready_for_checkout?: true) } let(:order) { mock_model(Spree::Order, distributor: hub) } @@ -24,9 +24,4 @@ describe BaseController do response.should redirect_to root_url flash[:info].should == "The order cycle you've selected has just closed. Please try again!" end - - it "loads active_distributors" do - Enterprise.stub(:distributors_with_active_order_cycles) { 'active distributors' } - controller.load_active_distributors.should == 'active distributors' - end end diff --git a/spec/controllers/checkout_controller_spec.rb b/spec/controllers/checkout_controller_spec.rb index 0499febfc9..ddacd0fdeb 100644 --- a/spec/controllers/checkout_controller_spec.rb +++ b/spec/controllers/checkout_controller_spec.rb @@ -52,12 +52,6 @@ describe CheckoutController do response.should be_success end - it "doesn't copy the previous shipping address from a pickup order" do - old_order = create(:order, bill_address: create(:address), ship_address: create(:address)) - Spree::Order.stub_chain(:order, :where, :where, :limit, :detect).and_return(old_order) - controller.send(:find_last_used_addresses, "email").last.should == nil - end - describe "building the order" do before do controller.stub(:current_distributor).and_return(distributor) @@ -69,7 +63,7 @@ describe CheckoutController do get :edit assigns[:order].ship_address.address1.should be_nil end - + it "clears the ship address when re-rendering edit" do controller.should_receive(:clear_ship_address).and_return true order.stub(:update_attributes).and_return false @@ -119,6 +113,32 @@ describe CheckoutController do response.status.should == 200 response.body.should == {path: spree.order_path(order)}.to_json end + + describe "stale object handling" do + it "retries when a stale object error is encountered" do + order.stub(:update_attributes).and_return true + controller.stub(:state_callback) + + # The first time, raise a StaleObjectError. The second time, succeed. + order.stub(:next).once. + and_raise(ActiveRecord::StaleObjectError.new(Spree::Variant.new, 'update')) + order.stub(:next).once do + order.update_column :state, 'complete' + true + end + + xhr :post, :update, order: {}, use_route: :spree + response.status.should == 200 + end + + it "tries a maximum of 3 times before giving up and returning an error" do + order.stub(:update_attributes).and_return true + order.stub(:next) { raise ActiveRecord::StaleObjectError.new(Spree::Variant.new, 'update') } + + xhr :post, :update, order: {}, use_route: :spree + response.status.should == 400 + end + end end describe "Paypal routing" do diff --git a/spec/controllers/enterprise_confirmations_controller_spec.rb b/spec/controllers/enterprise_confirmations_controller_spec.rb index 6fdab61be2..49e7ffe6e1 100644 --- a/spec/controllers/enterprise_confirmations_controller_spec.rb +++ b/spec/controllers/enterprise_confirmations_controller_spec.rb @@ -50,6 +50,7 @@ describe EnterpriseConfirmationsController do end it "redirects to the user to reset their password" do + expect(new_user).to receive(:send_reset_password_instructions_without_delay).and_call_original spree_get :show, confirmation_token: unconfirmed_enterprise.confirmation_token expect(response).to redirect_to spree.edit_spree_user_password_path(new_user, :reset_password_token => "token", return_to: spree.admin_path) expect(flash[:success]).to eq I18n.t('devise.enterprise_confirmations.enterprise.confirmed') diff --git a/spec/controllers/enterprises_controller_spec.rb b/spec/controllers/enterprises_controller_spec.rb index 34d095c29a..b3cb6b5e32 100644 --- a/spec/controllers/enterprises_controller_spec.rb +++ b/spec/controllers/enterprises_controller_spec.rb @@ -1,63 +1,13 @@ require 'spec_helper' describe EnterprisesController do - it "displays suppliers" do - s = create(:supplier_enterprise) - d = create(:distributor_enterprise) - - spree_get :suppliers - - assigns(:suppliers).should == [s] - end - - describe "displaying an enterprise and its products" do - let(:p) { create(:simple_product, supplier: s) } - let(:s) { create(:supplier_enterprise) } - let!(:c) { create(:distributor_enterprise) } - let(:d1) { create(:distributor_enterprise) } - let(:d2) { create(:distributor_enterprise) } - let(:oc1) { create(:simple_order_cycle) } - let(:oc2) { create(:simple_order_cycle) } - - it "displays products for the selected (order_cycle -> outgoing exchange)" do - create(:exchange, order_cycle: oc1, sender: s, receiver: c, incoming: true, variants: [p.master]) - create(:exchange, order_cycle: oc1, sender: c, receiver: d1, incoming: false, variants: [p.master]) - - controller.stub(:current_distributor) { d1 } - controller.stub(:current_order_cycle) { oc1 } - - spree_get :show, {id: d1} - - assigns(:products).should include p - end - - it "does not display other products in the order cycle or in the distributor" do - # Given a product that is in this order cycle on a different distributor - create(:exchange, order_cycle: oc1, sender: s, receiver: c, incoming: true, variants: [p.master]) - create(:exchange, order_cycle: oc1, sender: c, receiver: d2, incoming: false, variants: [p.master]) - - # And is also in this distributor in a different order cycle - create(:exchange, order_cycle: oc2, sender: s, receiver: c, incoming: true, variants: [p.master]) - create(:exchange, order_cycle: oc2, sender: c, receiver: d1, incoming: false, variants: [p.master]) - - # When I view the enterprise page for d1 x oc1 - controller.stub(:current_distributor) { d1 } - controller.stub(:current_order_cycle) { oc1 } - spree_get :show, {id: d1} - - # Then I should not see the product - assigns(:products).should_not include p - end - - end - describe "shopping for a distributor" do before(:each) do @current_distributor = create(:distributor_enterprise, with_payment_and_shipping: true) @distributor = create(:distributor_enterprise, with_payment_and_shipping: true) - @order_cycle1 = create(:simple_order_cycle, distributors: [@distributor]) - @order_cycle2 = create(:simple_order_cycle, distributors: [@distributor]) + @order_cycle1 = create(:simple_order_cycle, distributors: [@distributor], orders_open_at: 2.days.ago, orders_close_at: 3.days.from_now ) + @order_cycle2 = create(:simple_order_cycle, distributors: [@distributor], orders_open_at: 3.days.ago, orders_close_at: 4.days.from_now ) controller.current_order(true).distributor = @current_distributor end @@ -68,6 +18,16 @@ describe EnterprisesController do controller.current_order.order_cycle.should be_nil end + it "sorts order cycles by the distributor's preferred ordering attr" do + @distributor.update_attribute(:preferred_shopfront_order_cycle_order, 'orders_close_at') + spree_get :shop, {id: @distributor} + assigns(:order_cycles).should == [@order_cycle1, @order_cycle2].sort_by(&:orders_close_at) + + @distributor.update_attribute(:preferred_shopfront_order_cycle_order, 'orders_open_at') + spree_get :shop, {id: @distributor} + assigns(:order_cycles).should == [@order_cycle1, @order_cycle2].sort_by(&:orders_open_at) + end + it "empties an order that was set for a previous distributor, when shopping at a new distributor" do line_item = create(:line_item) controller.current_order.line_items << line_item @@ -102,14 +62,6 @@ describe EnterprisesController do end end - context "when a distributor has not been chosen" do - it "redirects #show to distributor selection" do - @distributor = create(:distributor_enterprise) - spree_get :show, {id: @distributor} - response.should redirect_to spree.root_path - end - end - context "checking permalink availability" do # let(:enterprise) { create(:enterprise, permalink: 'enterprise_permalink') } diff --git a/spec/controllers/map_controller_spec.rb b/spec/controllers/map_controller_spec.rb deleted file mode 100644 index fab9b7ac22..0000000000 --- a/spec/controllers/map_controller_spec.rb +++ /dev/null @@ -1,13 +0,0 @@ -require 'spec_helper' - -describe MapController do - it "loads active distributors" do - active_distributors = double(:distributors) - - Enterprise.stub(:distributors_with_active_order_cycles) { active_distributors } - - get :index - - assigns(:active_distributors).should == active_distributors - end -end diff --git a/spec/controllers/producers_controller_spec.rb b/spec/controllers/producers_controller_spec.rb deleted file mode 100644 index ec3c39036c..0000000000 --- a/spec/controllers/producers_controller_spec.rb +++ /dev/null @@ -1,15 +0,0 @@ -require 'spec_helper' - -describe ProducersController do - let!(:distributor) { create(:distributor_enterprise) } - - before do - Enterprise.stub(:distributors_with_active_order_cycles) { [distributor] } - Enterprise.stub(:all).and_return([distributor]) - end - - it "sets active distributors" do - get :index - assigns[:active_distributors].should == [distributor] - end -end diff --git a/spec/controllers/shop_controller_spec.rb b/spec/controllers/shop_controller_spec.rb index 3fdc1a7ea9..2c7105b356 100644 --- a/spec/controllers/shop_controller_spec.rb +++ b/spec/controllers/shop_controller_spec.rb @@ -162,7 +162,10 @@ describe ShopController do end it "returns price including fees" do - Spree::Variant.any_instance.stub(:price_with_fees).and_return 998.00 + # Price is 19.99 + OpenFoodNetwork::EnterpriseFeeCalculator.any_instance. + stub(:indexed_fees_for).and_return 978.01 + xhr :get, :products response.body.should have_content "998.0" end @@ -176,4 +179,18 @@ describe ShopController do end end end + + describe "loading variants" do + let(:hub) { create(:distributor_enterprise) } + let(:oc) { create(:simple_order_cycle, distributors: [hub], variants: [v1]) } + let(:p) { create(:simple_product) } + let!(:v1) { create(:variant, product: p, unit_value: 3) } + let!(:v2) { create(:variant, product: p, unit_value: 5) } + + it "scopes variants to distribution" do + controller.stub(:current_order_cycle) { oc } + controller.stub(:current_distributor) { hub } + controller.send(:variants_for_shop_by_id).should == {p.id => [v1]} + end + end end diff --git a/spec/controllers/home_controller_spec.rb b/spec/controllers/shops_controller_spec.rb similarity index 59% rename from spec/controllers/home_controller_spec.rb rename to spec/controllers/shops_controller_spec.rb index bbbff9a7b1..a4c66ea3a7 100644 --- a/spec/controllers/home_controller_spec.rb +++ b/spec/controllers/shops_controller_spec.rb @@ -1,6 +1,6 @@ require 'spec_helper' -describe HomeController do +describe ShopsController do render_views let!(:distributor) { create(:distributor_enterprise) } let!(:invisible_distributor) { create(:distributor_enterprise, visible: false) } @@ -9,21 +9,9 @@ describe HomeController do Enterprise.stub(:distributors_with_active_order_cycles) { [distributor] } end - it "sets active distributors" do - get :index - assigns[:active_distributors].should == [distributor] - end - # Exclusion from actual rendered view handled in features/consumer/home it "shows JSON for invisible hubs" do get :index response.body.should have_content invisible_distributor.name end - - # This is done inside the json/hubs Serializer - it "gets the next order cycle for each hub" do - OrderCycle.should_receive(:first_closing_for).twice - get :index - end end - diff --git a/spec/controllers/spree/admin/adjustments_controller_spec.rb b/spec/controllers/spree/admin/adjustments_controller_spec.rb new file mode 100644 index 0000000000..bb20fb792b --- /dev/null +++ b/spec/controllers/spree/admin/adjustments_controller_spec.rb @@ -0,0 +1,60 @@ +require 'spec_helper' + +module Spree + describe Admin::AdjustmentsController do + include AuthenticationWorkflow + + before { login_as_admin } + + describe "setting included tax" do + let(:order) { create(:order) } + let(:tax_rate) { create(:tax_rate, amount: 0.1, calculator: Spree::Calculator::DefaultTax.new) } + + describe "creating an adjustment" do + it "sets included tax to zero when no tax rate is specified" do + spree_post :create, {order_id: order.number, adjustment: {label: 'Testing included tax', amount: '110'}, tax_rate_id: ''} + response.should redirect_to spree.admin_order_adjustments_path(order) + + a = Adjustment.last + a.label.should == 'Testing included tax' + a.amount.should == 110 + a.included_tax.should == 0 + end + + it "calculates included tax when a tax rate is provided" do + spree_post :create, {order_id: order.number, adjustment: {label: 'Testing included tax', amount: '110'}, tax_rate_id: tax_rate.id.to_s} + response.should redirect_to spree.admin_order_adjustments_path(order) + + a = Adjustment.last + a.label.should == 'Testing included tax' + a.amount.should == 110 + a.included_tax.should == 10 + end + end + + describe "updating an adjustment" do + let(:adjustment) { create(:adjustment, adjustable: order, amount: 1100, included_tax: 100) } + + it "sets included tax to zero when no tax rate is specified" do + spree_put :update, {order_id: order.number, id: adjustment.id, adjustment: {label: 'Testing included tax', amount: '110'}, tax_rate_id: ''} + response.should redirect_to spree.admin_order_adjustments_path(order) + + a = Adjustment.last + a.label.should == 'Testing included tax' + a.amount.should == 110 + a.included_tax.should == 0 + end + + it "calculates included tax when a tax rate is provided" do + spree_put :update, {order_id: order.number, id: adjustment.id, adjustment: {label: 'Testing included tax', amount: '110'}, tax_rate_id: tax_rate.id.to_s} + response.should redirect_to spree.admin_order_adjustments_path(order) + + a = Adjustment.last + a.label.should == 'Testing included tax' + a.amount.should == 110 + a.included_tax.should == 10 + end + end + end + end +end diff --git a/spec/controllers/spree/admin/base_controller_spec.rb b/spec/controllers/spree/admin/base_controller_spec.rb index c56b3ae0db..e1b1993e8e 100644 --- a/spec/controllers/spree/admin/base_controller_spec.rb +++ b/spec/controllers/spree/admin/base_controller_spec.rb @@ -35,4 +35,82 @@ describe Spree::Admin::BaseController do "Until you set these up, customers will not be able to shop at these hubs." end end + + describe "rendering as json ActiveModelSerializer" do + context "when data is an object" do + let(:data) { { attr: 'value' } } + + context "when an ams prefix is passed" do + let(:prefix) { "prefix" } + + it "passes a prefix to the serializer method and renders with serializer" do + expect(controller).to receive(:serializer).with(prefix) { "SerializerClass" } + expect(controller).to receive(:render).with({ json: data, serializer: "SerializerClass" }) + controller.send(:render_as_json, data, ams_prefix: prefix) + end + end + + context "when no ams prefix is passed" do + let(:prefix) { "prefix" } + + it "does not pass a prefix to the serializer method and renders with serializer" do + expect(controller).to receive(:serializer).with(prefix) { "SerializerClass" } + expect(controller).to receive(:render).with({ json: data, serializer: "SerializerClass" }) + controller.send(:render_as_json, data, ams_prefix: prefix) + end + end + end + + context "when data is an array" do + let(:data) { [{ attr: 'value' }] } + + context "when an ams prefix is passed" do + let(:prefix) { "prefix" } + + it "passes a prefix to the serializer method and renders with each_serializer" do + expect(controller).to receive(:serializer).with(prefix) { "SerializerClass" } + expect(controller).to receive(:render).with({ json: data, each_serializer: "SerializerClass" }) + controller.send(:render_as_json, data, ams_prefix: prefix) + end + end + + context "when no ams prefix is passed" do + let(:prefix) { "prefix" } + + it "does not pass a prefix to the serializer method and renders with each_serializer" do + expect(controller).to receive(:serializer).with(prefix) { "SerializerClass" } + expect(controller).to receive(:render).with({ json: data, each_serializer: "SerializerClass" }) + controller.send(:render_as_json, data, ams_prefix: prefix) + end + end + end + end + + describe "determining the name of the serializer to be used" do + before do + class Api::Admin::AllowedPrefixAnonymouSerializer;end; + class Api::Admin::AnonymouSerializer;end; + allow(controller).to receive(:ams_prefix_whitelist) { [:allowed_prefix] } + end + + context "when a prefix is passed in" do + context "and the prefix appears in the whitelist" do + it "returns the requested serializer" do + expect(controller.send(:serializer, 'allowed_prefix')).to eq Api::Admin::AllowedPrefixAnonymouSerializer + end + end + + context "and the prefix does not appear in the whitelist" do + it "raises an error" do + expect{controller.send(:serializer, 'other_prefix')}.to raise_error RuntimeError + end + end + end + + context "when no prefix is passed in" do + it "returns the default serializer" do + expect(controller.send(:serializer, nil)).to eq Api::Admin::AnonymouSerializer + end + end + end end diff --git a/spec/controllers/spree/admin/line_items_controller_spec.rb b/spec/controllers/spree/admin/line_items_controller_spec.rb new file mode 100644 index 0000000000..907bec2bbb --- /dev/null +++ b/spec/controllers/spree/admin/line_items_controller_spec.rb @@ -0,0 +1,196 @@ +require 'spec_helper' + +describe Spree::Admin::LineItemsController do + include AuthenticationWorkflow + + describe "#index" do + render_views + + let(:line_item_attributes) { [:id, :quantity, :max_quantity, :price, :supplier, :final_weight_volume, :units_product, :units_variant, :order] } + let!(:dist1) { FactoryGirl.create(:distributor_enterprise) } + let!(:order1) { FactoryGirl.create(:order, state: 'complete', completed_at: 1.day.ago, distributor: dist1, billing_address: FactoryGirl.create(:address) ) } + let!(:order2) { FactoryGirl.create(:order, state: 'complete', completed_at: Time.zone.now, distributor: dist1, billing_address: FactoryGirl.create(:address) ) } + let!(:order3) { FactoryGirl.create(:order, state: 'complete', completed_at: Time.zone.now, distributor: dist1, billing_address: FactoryGirl.create(:address) ) } + let!(:line_item1) { FactoryGirl.create(:line_item, order: order1) } + let!(:line_item2) { FactoryGirl.create(:line_item, order: order2) } + let!(:line_item3) { FactoryGirl.create(:line_item, order: order2) } + let!(:line_item4) { FactoryGirl.create(:line_item, order: order3) } + + context "as a normal user" do + before { controller.stub spree_current_user: create_enterprise_user } + + it "should deny me access to the index action" do + spree_get :index, :format => :json + expect(response).to redirect_to spree.unauthorized_path + end + end + + context "as an administrator" do + + before do + controller.stub spree_current_user: quick_login_as_admin + end + + context "when no ransack params are passed in" do + before do + spree_get :index, :format => :json + end + + it "retrieves a list of line_items with appropriate attributes, including line items with appropriate attributes" do + keys = json_response.first.keys.map{ |key| key.to_sym } + line_item_attributes.all?{ |attr| keys.include? attr }.should == true + end + + it "sorts line_items in ascending id line_item" do + ids = json_response.map{ |line_item| line_item['id'] } + ids[0].should < ids[1] + ids[1].should < ids[2] + end + + it "formats final_weight_volume as a float" do + json_response.map{ |line_item| line_item['final_weight_volume'] }.all?{ |fwv| fwv.is_a?(Float) }.should == true + end + + it "returns distributor object with id key" do + json_response.map{ |line_item| line_item['supplier'] }.all?{ |d| d.has_key?('id') }.should == true + end + end + + context "when ransack params are passed in for line items" do + before do + spree_get :index, :format => :json, q: { order_id_eq: order2.id } + end + + it "retrives a list of line items which match the criteria" do + expect(json_response.map{ |line_item| line_item['id'] }).to eq [line_item2.id, line_item3.id] + end + end + + context "when ransack params are passed in for orders" do + before do + spree_get :index, :format => :json, q: { order: { completed_at_gt: 2.hours.ago } } + end + + it "retrives a list of line items whose orders match the criteria" do + expect(json_response.map{ |line_item| line_item['id'] }).to eq [line_item2.id, line_item3.id, line_item4.id] + end + end + end + + context "as an enterprise user" do + let(:supplier) { create(:supplier_enterprise) } + let(:distributor1) { create(:distributor_enterprise) } + let(:distributor2) { create(:distributor_enterprise) } + let(:coordinator) { create(:distributor_enterprise) } + let(:order_cycle) { create(:simple_order_cycle, coordinator: coordinator) } + let!(:order1) { FactoryGirl.create(:order, order_cycle: order_cycle, state: 'complete', completed_at: Time.zone.now, distributor: distributor1, billing_address: FactoryGirl.create(:address) ) } + let!(:line_item1) { FactoryGirl.create(:line_item, order: order1, product: FactoryGirl.create(:product, supplier: supplier)) } + let!(:line_item2) { FactoryGirl.create(:line_item, order: order1, product: FactoryGirl.create(:product, supplier: supplier)) } + let!(:order2) { FactoryGirl.create(:order, order_cycle: order_cycle, state: 'complete', completed_at: Time.zone.now, distributor: distributor2, billing_address: FactoryGirl.create(:address) ) } + let!(:line_item3) { FactoryGirl.create(:line_item, order: order2, product: FactoryGirl.create(:product, supplier: supplier)) } + + context "producer enterprise" do + before do + controller.stub spree_current_user: supplier.owner + spree_get :index, :format => :json + end + + it "does not display line items for which my enterprise is a supplier" do + expect(response).to redirect_to spree.unauthorized_path + end + end + + context "coordinator enterprise" do + before do + controller.stub spree_current_user: coordinator.owner + spree_get :index, :format => :json + end + + it "retrieves a list of line_items" do + keys = json_response.first.keys.map{ |key| key.to_sym } + line_item_attributes.all?{ |attr| keys.include? attr }.should == true + end + end + + context "hub enterprise" do + before do + controller.stub spree_current_user: distributor1.owner + spree_get :index, :format => :json + end + + it "retrieves a list of line_items" do + keys = json_response.first.keys.map{ |key| key.to_sym } + line_item_attributes.all?{ |attr| keys.include? attr }.should == true + end + end + end + end + + describe "#create" do + let!(:variant) { create(:variant, price: 88) } + let!(:vo) { create(:variant_override, hub: distributor, variant: variant, price: 11.11) } + let!(:distributor) { create(:distributor_enterprise) } + let!(:order_cycle) { create(:simple_order_cycle, distributors: [distributor], variants: [variant]) } + let!(:order) { create(:order, distributor: distributor, order_cycle: order_cycle) } + let(:params) { { order_id: order.number, line_item: { variant_id: variant.id, quantity: 1 } } } + + before { login_as_admin } + + it "takes variant overrides into account for price" do + spree_post :create, params + + order.line_items(:reload).last.price.should == 11.11 + end + end + + describe "#update" do + let(:supplier) { create(:supplier_enterprise) } + let(:distributor1) { create(:distributor_enterprise) } + let(:coordinator) { create(:distributor_enterprise) } + let(:order_cycle) { create(:simple_order_cycle, coordinator: coordinator) } + let!(:order1) { FactoryGirl.create(:order, order_cycle: order_cycle, state: 'complete', completed_at: Time.zone.now, distributor: distributor1, billing_address: FactoryGirl.create(:address) ) } + let!(:line_item1) { FactoryGirl.create(:line_item, order: order1, product: FactoryGirl.create(:product, supplier: supplier)) } + let(:params) { { format: :json, id: line_item1.id, order_id: order1.number, line_item: { quantity: 3, final_weight_volume: 3000, price: 3.00 } } } + + context "as an enterprise user" do + context "producer enterprise" do + before do + controller.stub spree_current_user: supplier.owner + spree_put :update, params + end + + it "does not allow access" do + expect(response).to redirect_to spree.unauthorized_path + end + end + + context "coordinator enterprise" do + before do + controller.stub spree_current_user: coordinator.owner + spree_put :update, params + end + + it "updates the line_item" do + line_item1.reload + expect(line_item1.quantity).to eq 3 + expect(line_item1.final_weight_volume).to eq 3000 + expect(line_item1.price).to eq 3.00 + end + end + + context "hub enterprise" do + before do + controller.stub spree_current_user: distributor1.owner + spree_put :update, params + end + + it "retrieves a list of line_items" do + line_item1.reload + expect(line_item1.quantity).to eq 3 + expect(line_item1.final_weight_volume).to eq 3000 + expect(line_item1.price).to eq 3.00 + end + end + end + end +end diff --git a/spec/controllers/spree/admin/orders_controller_spec.rb b/spec/controllers/spree/admin/orders_controller_spec.rb index dae1edcf29..5ecc393dab 100644 --- a/spec/controllers/spree/admin/orders_controller_spec.rb +++ b/spec/controllers/spree/admin/orders_controller_spec.rb @@ -29,16 +29,16 @@ describe Spree::Admin::OrdersController do end end - describe "managed" do + describe "#index" do render_views - let(:order_attributes) { [:id, :full_name, :email, :phone, :completed_at, :line_items, :distributor, :order_cycle, :number] } + let(:order_attributes) { [:id, :full_name, :email, :phone, :completed_at, :distributor, :order_cycle, :number] } def self.make_simple_data! let!(:dist1) { FactoryGirl.create(:distributor_enterprise) } - let!(:order1) { FactoryGirl.create(:order, state: 'complete', completed_at: Time.now, distributor: dist1, billing_address: FactoryGirl.create(:address) ) } - let!(:order2) { FactoryGirl.create(:order, state: 'complete', completed_at: Time.now, distributor: dist1, billing_address: FactoryGirl.create(:address) ) } - let!(:order3) { FactoryGirl.create(:order, state: 'complete', completed_at: Time.now, distributor: dist1, billing_address: FactoryGirl.create(:address) ) } + let!(:order1) { FactoryGirl.create(:order, state: 'complete', completed_at: Time.zone.now, distributor: dist1, billing_address: FactoryGirl.create(:address) ) } + let!(:order2) { FactoryGirl.create(:order, state: 'complete', completed_at: Time.zone.now, distributor: dist1, billing_address: FactoryGirl.create(:address) ) } + let!(:order3) { FactoryGirl.create(:order, state: 'complete', completed_at: Time.zone.now, distributor: dist1, billing_address: FactoryGirl.create(:address) ) } let!(:line_item1) { FactoryGirl.create(:line_item, order: order1) } let!(:line_item2) { FactoryGirl.create(:line_item, order: order2) } let!(:line_item3) { FactoryGirl.create(:line_item, order: order2) } @@ -51,8 +51,8 @@ describe Spree::Admin::OrdersController do make_simple_data! - it "should deny me access to managed orders" do - spree_get :managed, { :template => 'bulk_index', :format => :json } + it "should deny me access to the index action" do + spree_get :index, :format => :json expect(response).to redirect_to spree.unauthorized_path end end @@ -62,7 +62,7 @@ describe Spree::Admin::OrdersController do before do controller.stub spree_current_user: quick_login_as_admin - spree_get :managed, { :template => 'bulk_index', :format => :json } + spree_get :index, :format => :json end it "retrieves a list of orders with appropriate attributes, including line items with appropriate attributes" do @@ -70,11 +70,6 @@ describe Spree::Admin::OrdersController do order_attributes.all?{ |attr| keys.include? attr }.should == true end - it "retrieves a list of line items with appropriate attributes" do - li_keys = json_response.first['line_items'].first.keys.map{ |key| key.to_sym } - line_item_attributes.all?{ |attr| li_keys.include? attr }.should == true - end - it "sorts orders in ascending id order" do ids = json_response.map{ |order| order['id'] } ids[0].should < ids[1] @@ -85,21 +80,8 @@ describe Spree::Admin::OrdersController do json_response.map{ |order| order['completed_at'] }.all?{ |a| a.match("^\\d{4}-\\d{2}-\\d{2} \\d{2}:\\d{2}:\\d{2}$") }.should == true end - it "returns an array for line_items" do - json_response.map{ |order| order['line_items'] }.all?{ |a| a.is_a? Array }.should == true - end - - it "returns quantity and max quantity at integers" do - json_response.map{ |order| order['line_items'] }.flatten.map{ |li| li['quantity'] }.all?{ |q| q.is_a? Fixnum }.should == true - json_response.map{ |order| order['line_items'] }.flatten.map{ |li| li['max_quantity'] }.all?{ |mq| mq.nil? || mq.is_a?( Fixnum ) }.should == true - end - - it "returns supplier object with id and name keys" do - json_response.map{ |order| order['line_items'] }.flatten.map{ |li| li['supplier'] }.all?{ |s| s.has_key?('id') && s.has_key?('name') }.should == true - end - - it "returns distributor object with id and name keys" do - json_response.map{ |order| order['distributor'] }.all?{ |d| d.has_key?('id') && d.has_key?('name') }.should == true + it "returns distributor object with id key" do + json_response.map{ |order| order['distributor'] }.all?{ |d| d.has_key?('id') }.should == true end it "retrieves the order number" do @@ -113,17 +95,17 @@ describe Spree::Admin::OrdersController do let(:distributor2) { create(:distributor_enterprise) } let(:coordinator) { create(:distributor_enterprise) } let(:order_cycle) { create(:simple_order_cycle, coordinator: coordinator) } - let!(:order1) { FactoryGirl.create(:order, order_cycle: order_cycle, state: 'complete', completed_at: Time.now, distributor: distributor1, billing_address: FactoryGirl.create(:address) ) } + let!(:order1) { FactoryGirl.create(:order, order_cycle: order_cycle, state: 'complete', completed_at: Time.zone.now, distributor: distributor1, billing_address: FactoryGirl.create(:address) ) } let!(:line_item1) { FactoryGirl.create(:line_item, order: order1, product: FactoryGirl.create(:product, supplier: supplier)) } let!(:line_item2) { FactoryGirl.create(:line_item, order: order1, product: FactoryGirl.create(:product, supplier: supplier)) } - let!(:order2) { FactoryGirl.create(:order, order_cycle: order_cycle, state: 'complete', completed_at: Time.now, distributor: distributor2, billing_address: FactoryGirl.create(:address) ) } + let!(:order2) { FactoryGirl.create(:order, order_cycle: order_cycle, state: 'complete', completed_at: Time.zone.now, distributor: distributor2, billing_address: FactoryGirl.create(:address) ) } let!(:line_item3) { FactoryGirl.create(:line_item, order: order2, product: FactoryGirl.create(:product, supplier: supplier)) } context "producer enterprise" do before do controller.stub spree_current_user: supplier.owner - spree_get :managed, { :format => :json } + spree_get :index, :format => :json end it "does not display line items for which my enterprise is a supplier" do @@ -134,32 +116,110 @@ describe Spree::Admin::OrdersController do context "coordinator enterprise" do before do controller.stub spree_current_user: coordinator.owner - spree_get :managed, { :format => :json } + spree_get :index, :format => :json end it "retrieves a list of orders" do keys = json_response.first.keys.map{ |key| key.to_sym } order_attributes.all?{ |attr| keys.include? attr }.should == true end - - it "only displays line items from orders for which my enterprise is the order_cycle coorinator" do - json_response.map{ |order| order['line_items'] }.flatten.map{ |line_item| line_item["id"] }.should match_array [line_item1.id, line_item2.id, line_item3.id] - end end context "hub enterprise" do before do controller.stub spree_current_user: distributor1.owner - spree_get :managed, { :format => :json } + spree_get :index, :format => :json end it "retrieves a list of orders" do keys = json_response.first.keys.map{ |key| key.to_sym } order_attributes.all?{ |attr| keys.include? attr }.should == true end + end + end + end - it "only displays line items from orders for which my enterprise is a distributor" do - json_response.map{ |order| order['line_items'] }.flatten.map{ |line_item| line_item["id"] }.should match_array [line_item1.id, line_item2.id] + describe "#invoice" do + let!(:user) { create(:user) } + let!(:enterprise_user) { create(:user) } + let!(:order) { create(:order_with_distributor, bill_address: create(:address), ship_address: create(:address)) } + let!(:distributor) { order.distributor } + let(:params) { { id: order.number } } + + context "as a normal user" do + before { controller.stub spree_current_user: user } + + it "should prevent me from sending order invoices" do + spree_get :invoice, params + expect(response).to redirect_to spree.unauthorized_path + end + end + + context "as an enterprise user" do + context "which is not a manager of the distributor for an order" do + before { controller.stub spree_current_user: user } + it "should prevent me from sending order invoices" do + spree_get :invoice, params + expect(response).to redirect_to spree.unauthorized_path + end + end + + context "which is a manager of the distributor for an order" do + before { controller.stub spree_current_user: distributor.owner } + context "when the distributor's ABN has not been set" do + before { distributor.update_attribute(:abn, "") } + it "should allow me to send order invoices" do + expect do + spree_get :invoice, params + end.to_not change{Spree::OrderMailer.deliveries.count} + expect(response).to redirect_to spree.edit_admin_order_path(order) + expect(flash[:error]).to eq "#{distributor.name} must have a valid ABN before invoices can be sent." + end + end + + context "when the distributor's ABN has been set" do + before { distributor.update_attribute(:abn, "123") } + it "should allow me to send order invoices" do + expect do + spree_get :invoice, params + end.to change{Spree::OrderMailer.deliveries.count}.by(1) + expect(response).to redirect_to spree.edit_admin_order_path(order) + end + end + end + end + end + + describe "#print" do + let!(:user) { create(:user) } + let!(:enterprise_user) { create(:user) } + let!(:order) { create(:order_with_distributor, bill_address: create(:address), ship_address: create(:address)) } + let!(:distributor) { order.distributor } + let(:params) { { id: order.number } } + + context "as a normal user" do + before { controller.stub spree_current_user: user } + + it "should prevent me from sending order invoices" do + spree_get :print, params + expect(response).to redirect_to spree.unauthorized_path + end + end + + context "as an enterprise user" do + context "which is not a manager of the distributor for an order" do + before { controller.stub spree_current_user: user } + it "should prevent me from sending order invoices" do + spree_get :print, params + expect(response).to redirect_to spree.unauthorized_path + end + end + + context "which is a manager of the distributor for an order" do + before { controller.stub spree_current_user: distributor.owner } + it "should allow me to send order invoices" do + spree_get :print, params + expect(response).to render_template :invoice end end end diff --git a/spec/controllers/spree/admin/overview_controller_spec.rb b/spec/controllers/spree/admin/overview_controller_spec.rb index d38d4b3b2e..868a779d4a 100644 --- a/spec/controllers/spree/admin/overview_controller_spec.rb +++ b/spec/controllers/spree/admin/overview_controller_spec.rb @@ -9,23 +9,37 @@ describe Spree::Admin::OverviewController do controller.stub spree_current_user: user end - context "when user own only one enterprise" do + context "when user owns only one enterprise" do let!(:enterprise) { create(:distributor_enterprise, owner: user) } - it "renders the single enterprise dashboard" do - spree_get :index - response.should render_template "single_enterprise_dashboard" - end + context "when the referer is not an admin page" do + before { @request.env['HTTP_REFERER'] = 'http://test.com/some_other_path' } - context "when the enterprise sells property has not been set" do - before do - enterprise.sells = "unspecified" - enterprise.save + context "and the enterprise has sells='unspecified'" do + before do + enterprise.update_attribute(:sells, "unspecified") + end + + it "redirects to the welcome page for the enterprise" do + spree_get :index + response.should redirect_to welcome_admin_enterprise_path(enterprise) + end end - it "renders the welcome page" do + context "and the enterprise does not have sells='unspecified'" do + it "renders the single enterprise dashboard" do + spree_get :index + response.should render_template "single_enterprise_dashboard" + end + end + end + + context "when the refer is an admin page" do + before { @request.env['HTTP_REFERER'] = 'http://test.com/admin' } + + it "renders the single enterprise dashboard" do spree_get :index - response.should render_template "welcome" + response.should render_template "single_enterprise_dashboard" end end end @@ -34,10 +48,36 @@ describe Spree::Admin::OverviewController do let!(:enterprise1) { create(:distributor_enterprise, owner: user) } let!(:enterprise2) { create(:distributor_enterprise, owner: user) } - it "renders the multi enterprise dashboard" do - spree_get :index - response.should render_template "multi_enterprise_dashboard" + context "when the referer is not an admin page" do + before { @request.env['HTTP_REFERER'] = 'http://test.com/some_other_path' } + + context "and at least one owned enterprise has sells='unspecified'" do + before do + enterprise1.update_attribute(:sells, "unspecified") + end + + it "redirects to the enterprises index" do + spree_get :index + response.should redirect_to admin_enterprises_path + end + end + + context "and no owned enterprises have sells='unspecified'" do + it "renders the multiple enterprise dashboard" do + spree_get :index + response.should render_template "multi_enterprise_dashboard" + end + end + end + + context "when the refer is an admin page" do + before { @request.env['HTTP_REFERER'] = 'http://test.com/admin' } + + it "renders the multiple enterprise dashboard" do + spree_get :index + response.should render_template "multi_enterprise_dashboard" + end end end end -end \ No newline at end of file +end diff --git a/spec/controllers/spree/admin/reports_controller_spec.rb b/spec/controllers/spree/admin/reports_controller_spec.rb index e930c1e339..c388bed39e 100644 --- a/spec/controllers/spree/admin/reports_controller_spec.rb +++ b/spec/controllers/spree/admin/reports_controller_spec.rb @@ -4,6 +4,7 @@ describe Spree::Admin::ReportsController do # Given two distributors and two suppliers let(:ba) { create(:address) } + let(:sa) { create(:address) } let(:si) { "pick up on thursday please" } let(:c1) { create(:distributor_enterprise) } let(:c2) { create(:distributor_enterprise) } @@ -23,7 +24,7 @@ describe Spree::Admin::ReportsController do # orderA1 can only be accessed by s1, s3 and d1 let!(:orderA1) do - order = create(:order, distributor: d1, bill_address: ba, special_instructions: si, order_cycle: ocA) + order = create(:order, distributor: d1, bill_address: ba, ship_address: sa, special_instructions: si, order_cycle: ocA) order.line_items << create(:line_item, variant: p1.master) order.line_items << create(:line_item, variant: p3.master) order.finalize! @@ -32,7 +33,7 @@ describe Spree::Admin::ReportsController do end # orderA2 can only be accessed by s2 and d2 let!(:orderA2) do - order = create(:order, distributor: d2, bill_address: ba, special_instructions: si, order_cycle: ocA) + order = create(:order, distributor: d2, bill_address: ba, ship_address: sa, special_instructions: si, order_cycle: ocA) order.line_items << create(:line_item, variant: p2.master) order.finalize! order.save @@ -40,7 +41,7 @@ describe Spree::Admin::ReportsController do end # orderB1 can only be accessed by s1, s3 and d1 let!(:orderB1) do - order = create(:order, distributor: d1, bill_address: ba, special_instructions: si, order_cycle: ocB) + order = create(:order, distributor: d1, bill_address: ba, ship_address: sa, special_instructions: si, order_cycle: ocB) order.line_items << create(:line_item, variant: p1.master) order.line_items << create(:line_item, variant: p3.master) order.finalize! @@ -49,13 +50,18 @@ describe Spree::Admin::ReportsController do end # orderB2 can only be accessed by s2 and d2 let!(:orderB2) do - order = create(:order, distributor: d2, bill_address: ba, special_instructions: si, order_cycle: ocB) + order = create(:order, distributor: d2, bill_address: ba, ship_address: sa, special_instructions: si, order_cycle: ocB) order.line_items << create(:line_item, variant: p2.master) order.finalize! order.save order end + # Results + let(:resulting_orders_prelim) { assigns(:report).search.result } + let(:resulting_orders) { assigns(:report).table_items.map(&:order) } + let(:resulting_products) { assigns(:report).table_items.map(&:product) } + # As manager of a coordinator (c1) context "Coordinator Enterprise User" do before { login_as_enterprise_user [c1] } @@ -64,8 +70,8 @@ describe Spree::Admin::ReportsController do it "shows all orders in order cycles I coordinate" do spree_get :orders_and_fulfillment - assigns(:line_items).map(&:order).should include orderA1, orderA2 - assigns(:line_items).map(&:order).should_not include orderB1, orderB2 + resulting_orders.should include orderA1, orderA2 + resulting_orders.should_not include orderB1, orderB2 end end end @@ -88,9 +94,9 @@ describe Spree::Admin::ReportsController do it "only shows orders that I have access to" do spree_get :bulk_coop - assigns(:search).result.should include(orderA1, orderB1) - assigns(:search).result.should_not include(orderA2) - assigns(:search).result.should_not include(orderB2) + resulting_orders.should include(orderA1, orderB1) + resulting_orders.should_not include(orderA2) + resulting_orders.should_not include(orderB2) end end @@ -98,9 +104,9 @@ describe Spree::Admin::ReportsController do it "only shows orders that I have access to" do spree_get :payments - assigns(:search).result.should include(orderA1, orderB1) - assigns(:search).result.should_not include(orderA2) - assigns(:search).result.should_not include(orderB2) + resulting_orders_prelim.should include(orderA1, orderB1) + resulting_orders_prelim.should_not include(orderA2) + resulting_orders_prelim.should_not include(orderB2) end end @@ -108,15 +114,15 @@ describe Spree::Admin::ReportsController do it "only shows orders that I distribute" do spree_get :orders_and_fulfillment - assigns(:line_items).map(&:order).should include orderA1, orderB1 - assigns(:line_items).map(&:order).should_not include orderA2, orderB2 + resulting_orders.should include orderA1, orderB1 + resulting_orders.should_not include orderA2, orderB2 end it "only shows the selected order cycle" do spree_get :orders_and_fulfillment, q: {order_cycle_id_in: [ocA.id.to_s]} - assigns(:search).result.should include(orderA1) - assigns(:search).result.should_not include(orderB1) + resulting_orders.should include(orderA1) + resulting_orders.should_not include(orderB1) end end end @@ -126,11 +132,25 @@ describe Spree::Admin::ReportsController do before { login_as_enterprise_user [s1] } describe 'Bulk Coop' do - it "only shows product line items that I am supplying" do - spree_get :bulk_coop + context "where I have granted P-OC to the distributor" do + before do + create(:enterprise_relationship, parent: s1, child: d1, permissions_list: [:add_to_order_cycle]) + end - assigns(:line_items).map(&:product).should include p1 - assigns(:line_items).map(&:product).should_not include p2, p3 + it "only shows product line items that I am supplying" do + spree_get :bulk_coop + + resulting_products.should include p1 + resulting_products.should_not include p2, p3 + end + end + + context "where I have not granted P-OC to the distributor" do + it "shows product line items that I am supplying" do + spree_get :bulk_coop + + resulting_products.should_not include p1, p2, p3 + end end end @@ -143,8 +163,15 @@ describe Spree::Admin::ReportsController do it "only shows product line items that I am supplying" do spree_get :orders_and_fulfillment - assigns(:line_items).map(&:product).should include p1 - assigns(:line_items).map(&:product).should_not include p2, p3 + resulting_products.should include p1 + resulting_products.should_not include p2, p3 + end + + it "only shows the selected order cycle" do + spree_get :orders_and_fulfillment, q: {order_cycle_id_eq: ocA.id} + + resulting_orders_prelim.should include(orderA1) + resulting_orders_prelim.should_not include(orderB1) end end @@ -152,16 +179,9 @@ describe Spree::Admin::ReportsController do it "does not show me line_items I supply" do spree_get :orders_and_fulfillment - assigns(:line_items).map(&:product).should_not include p1, p2, p3 + resulting_products.should_not include p1, p2, p3 end end - - it "only shows the selected order cycle" do - spree_get :orders_and_fulfillment, q: {order_cycle_id_eq: ocA.id} - - assigns(:search).result.should include(orderA1) - assigns(:search).result.should_not include(orderB1) - end end end diff --git a/spec/controllers/spree/api/line_items_controller_spec.rb b/spec/controllers/spree/api/line_items_controller_spec.rb index 37ec50eb7e..119f9fb188 100644 --- a/spec/controllers/spree/api/line_items_controller_spec.rb +++ b/spec/controllers/spree/api/line_items_controller_spec.rb @@ -10,8 +10,8 @@ module Spree end def self.make_simple_data! - let!(:order) { FactoryGirl.create(:order, state: 'complete', completed_at: Time.now) } - let!(:line_item) { FactoryGirl.create(:line_item, order: order, unit_value: 500) } + let!(:order) { FactoryGirl.create(:order, state: 'complete', completed_at: Time.zone.now) } + let!(:line_item) { FactoryGirl.create(:line_item, order: order, final_weight_volume: 500) } end #test that when a line item is updated, an order's fees are updated too @@ -21,7 +21,7 @@ module Spree context "as a line item is updated" do it "update distribution charge on the order" do - line_item_params = { order_id: order.number, id: line_item.id, line_item: { id: line_item.id, unit_value: 520 }, format: :json} + line_item_params = { order_id: order.number, id: line_item.id, line_item: { id: line_item.id, final_weight_volume: 520 }, format: :json} allow(controller).to receive(:order) { order } expect(order).to receive(:update_distribution_charge!) spree_post :update, line_item_params diff --git a/spec/controllers/spree/orders_controller_spec.rb b/spec/controllers/spree/orders_controller_spec.rb index 52bd21b500..73c72f3386 100644 --- a/spec/controllers/spree/orders_controller_spec.rb +++ b/spec/controllers/spree/orders_controller_spec.rb @@ -27,6 +27,8 @@ describe Spree::OrdersController do end it "redirects home with message if hub is not ready for checkout" do + VariantOverride.stub(:indexed).and_return({}) + order = subject.current_order(true) distributor.stub(:ready_for_checkout?) { false } order.stub(distributor: distributor, order_cycle: order_cycle) @@ -56,21 +58,21 @@ describe Spree::OrdersController do end it "returns HTTP success when successful" do - Spree::OrderPopulator.stub(:new).and_return(populator = mock()) + Spree::OrderPopulator.stub(:new).and_return(populator = double()) populator.stub(:populate).and_return true xhr :post, :populate, use_route: :spree, format: :json response.status.should == 200 end it "returns failure when unsuccessful" do - Spree::OrderPopulator.stub(:new).and_return(populator = mock()) + Spree::OrderPopulator.stub(:new).and_return(populator = double()) populator.stub(:populate).and_return false xhr :post, :populate, use_route: :spree, format: :json response.status.should == 402 end it "tells populator to overwrite" do - Spree::OrderPopulator.stub(:new).and_return(populator = mock()) + Spree::OrderPopulator.stub(:new).and_return(populator = double()) populator.should_receive(:populate).with({}, true) xhr :post, :populate, use_route: :spree, format: :json end diff --git a/spec/factories.rb b/spec/factories.rb index f820d04da5..dd8d02e7bf 100644 --- a/spec/factories.rb +++ b/spec/factories.rb @@ -15,15 +15,17 @@ FactoryGirl.define do # Incoming Exchanges ex1 = create(:exchange, :order_cycle => oc, :incoming => true, - :sender => supplier1, :receiver => oc.coordinator) + :sender => supplier1, :receiver => oc.coordinator, + :receival_instructions => 'instructions 0') ex2 = create(:exchange, :order_cycle => oc, :incoming => true, - :sender => supplier2, :receiver => oc.coordinator) + :sender => supplier2, :receiver => oc.coordinator, + :receival_instructions => 'instructions 1') ExchangeFee.create!(exchange: ex1, enterprise_fee: create(:enterprise_fee, enterprise: ex1.sender)) ExchangeFee.create!(exchange: ex2, enterprise_fee: create(:enterprise_fee, enterprise: ex2.sender)) - #Distributors + # Distributors distributor1 = create(:distributor_enterprise) distributor2 = create(:distributor_enterprise) @@ -42,7 +44,7 @@ FactoryGirl.define do # Products with images [ex1, ex2].each do |exchange| product = create(:product, supplier: exchange.sender) - image = File.open(File.expand_path('../../app/assets/images/logo.jpg', __FILE__)) + image = File.open(File.expand_path('../../app/assets/images/logo-white.png', __FILE__)) Spree::Image.create({:viewable_id => product.master.id, :viewable_type => 'Spree::Variant', :alt => "position 1", :attachment => image, :position => 1}) exchange.variants << product.variants.first @@ -71,7 +73,7 @@ FactoryGirl.define do after(:create) do |oc, proxy| proxy.suppliers.each do |supplier| - ex = create(:exchange, :order_cycle => oc, :sender => supplier, :receiver => oc.coordinator, :incoming => true, :pickup_time => 'time', :pickup_instructions => 'instructions') + ex = create(:exchange, :order_cycle => oc, :sender => supplier, :receiver => oc.coordinator, :incoming => true, :receival_instructions => 'instructions') proxy.variants.each { |v| ex.variants << v } end @@ -92,6 +94,8 @@ FactoryGirl.define do factory :variant_override, :class => VariantOverride do price 77.77 count_on_hand 11111 + default_stock 2000 + resettable false end factory :enterprise, :class => Enterprise do @@ -102,7 +106,7 @@ FactoryGirl.define do long_description '

Hello, world!

This is a paragraph.

' email 'enterprise@example.com' address { FactoryGirl.create(:address) } - confirmed_at { Time.now } + confirmed_at { Time.zone.now } end factory :supplier_enterprise, :parent => :enterprise do @@ -212,9 +216,29 @@ FactoryGirl.define do factory :customer, :class => Customer do email { Faker::Internet.email } enterprise - code 'abc123' + code { SecureRandom.base64(150) } user end + + factory :billable_period do + begins_at { Time.zone.now.beginning_of_month } + ends_at { Time.zone.now.beginning_of_month + 1.month } + sells { 'any' } + trial { false } + enterprise + owner { enterprise.owner } + turnover { rand(100000).to_f/100 } + account_invoice do + AccountInvoice.where(user_id: owner_id, year: begins_at.year, month: begins_at.month).first || + FactoryGirl.create(:account_invoice, user: owner, year: begins_at.year, month: begins_at.month) + end + end + + factory :account_invoice do + user { FactoryGirl.create :user } + year { 2000 + rand(100) } + month { 1 + rand(12) } + end end diff --git a/spec/features/admin/account_spec.rb b/spec/features/admin/account_spec.rb new file mode 100644 index 0000000000..f2be99c3a3 --- /dev/null +++ b/spec/features/admin/account_spec.rb @@ -0,0 +1,21 @@ +require 'spec_helper' + +feature 'Account Page' do + include AuthenticationWorkflow + + describe "updating" do + let!(:user) { create(:user) } + let!(:enterprise) { create(:distributor_enterprise, owner: user) } + + before do + quick_login_as user + end + + context "as an enterprise user" do + it "loads the page" do + visit admin_account_path + expect(page).to have_content "Account" + end + end + end +end diff --git a/spec/features/admin/accounts_and_billing_settings_spec.rb b/spec/features/admin/accounts_and_billing_settings_spec.rb new file mode 100644 index 0000000000..b5316f6b21 --- /dev/null +++ b/spec/features/admin/accounts_and_billing_settings_spec.rb @@ -0,0 +1,60 @@ +require 'spec_helper' + +feature 'Account and Billing Settings' do + include AuthenticationWorkflow + include WebHelper + + describe "updating" do + let!(:admin) { create(:admin_user) } + let!(:pm1) { create(:payment_method) } + let!(:sm1) { create(:shipping_method) } + let!(:accounts_distributor) { create(:distributor_enterprise, payment_methods: [pm1], shipping_methods: [sm1]) } + + before do + Spree::Config.set({ + accounts_distributor_id: 0, + default_accounts_payment_method_id: 0, + default_accounts_shipping_method_id: 0, + auto_update_invoices: false, + auto_finalize_invoices: false + }) + end + + before do + quick_login_as_admin + end + + context "as an admin user", js: true do + it "loads the page" do + visit spree.admin_path + click_link "Configuration" + click_link "Accounts & Billing" + + expect(page).to have_select2 "settings_accounts_distributor_id" + select2_select accounts_distributor.name, from: "settings_accounts_distributor_id" + expect(page).to have_select "settings_default_accounts_payment_method_id" + expect(page).to have_select "settings_default_accounts_shipping_method_id" + expect(page).to have_link "Update User Invoices", href: start_job_admin_accounts_and_billing_settings_path(job: { name: 'update_account_invoices'}) + expect(page).to have_link "Finalise User Invoices", href: start_job_admin_accounts_and_billing_settings_path(job: { name: 'finalize_account_invoices'}) + end + + it "attributes can be changed", js: true do + visit edit_admin_accounts_and_billing_settings_path + + select2_select accounts_distributor.name, from: "settings_accounts_distributor_id" + select pm1.name, from: "settings_default_accounts_payment_method_id" + select sm1.name, from: "settings_default_accounts_shipping_method_id" + check "settings_auto_update_invoices" + check "settings_auto_finalize_invoices" + + click_button "Update" + + expect(Spree::Config.accounts_distributor_id).to eq accounts_distributor.id + expect(Spree::Config.default_accounts_payment_method_id).to eq pm1.id + expect(Spree::Config.default_accounts_shipping_method_id).to eq sm1.id + expect(Spree::Config.auto_update_invoices).to be true + expect(Spree::Config.auto_finalize_invoices).to be true + end + end + end +end diff --git a/spec/features/admin/adjustments_spec.rb b/spec/features/admin/adjustments_spec.rb new file mode 100644 index 0000000000..b9a6ee3465 --- /dev/null +++ b/spec/features/admin/adjustments_spec.rb @@ -0,0 +1,89 @@ +require "spec_helper" + +feature %q{ + As an administrator + I want to manage adjustments on orders +} do + include AuthenticationWorkflow + include WebHelper + + let!(:user) { create(:user) } + let!(:distributor) { create(:distributor_enterprise, charges_sales_tax: true) } + let!(:order_cycle) { create(:simple_order_cycle, distributors: [distributor]) } + + let!(:order) { create(:order_with_totals_and_distribution, user: user, distributor: distributor, order_cycle: order_cycle, state: 'complete', payment_state: 'balance_due') } + let!(:tax_rate) { create(:tax_rate, name: 'GST', calculator: build(:calculator, preferred_amount: 10), zone: create(:zone_with_member)) } + + before do + order.finalize! + create(:check_payment, order: order, amount: order.total) + end + + scenario "adding taxed adjustments to an order" do + # When I go to the adjustments page for the order + login_to_admin_section + visit spree.admin_orders_path + page.find('td.actions a.icon-edit').click + click_link 'Adjustments' + + # And I create a new adjustment with tax + click_link 'New Adjustment' + fill_in 'adjustment_amount', with: 110 + fill_in 'adjustment_label', with: 'Late fee' + select 'GST', from: 'tax_rate_id' + click_button 'Continue' + + # Then I should see the adjustment, with the correct tax + page.should have_selector 'td.label', text: 'Late fee' + page.should have_selector 'td.amount', text: '110' + page.should have_selector 'td.included-tax', text: '10' + end + + scenario "modifying taxed adjustments on an order" do + # Given a taxed adjustment + adjustment = create(:adjustment, adjustable: order, amount: 110, included_tax: 10) + + # When I go to the adjustments page for the order + login_to_admin_section + visit spree.admin_orders_path + page.find('td.actions a.icon-edit').click + click_link 'Adjustments' + page.find('td.actions a.icon-edit').click + + # Then I should see the uneditable included tax and our tax rate as the default + page.should have_field :adjustment_included_tax, with: '10.00', disabled: true + page.should have_select :tax_rate_id, selected: 'GST' + + # When I edit the adjustment, removing the tax + select 'Remove tax', from: :tax_rate_id + click_button 'Continue' + + # Then the adjustment tax should be cleared + page.should have_selector 'td.amount', text: '110' + page.should have_selector 'td.included-tax', text: '0' + end + + scenario "modifying an untaxed adjustment on an order" do + # Given an untaxed adjustment + adjustment = create(:adjustment, adjustable: order, amount: 110, included_tax: 0) + + # When I go to the adjustments page for the order + login_to_admin_section + visit spree.admin_orders_path + page.find('td.actions a.icon-edit').click + click_link 'Adjustments' + page.find('td.actions a.icon-edit').click + + # Then I should see the uneditable included tax and 'Remove tax' as the default tax rate + page.should have_field :adjustment_included_tax, with: '0.00', disabled: true + page.should have_select :tax_rate_id, selected: [] + + # When I edit the adjustment, setting a tax rate + select 'GST', from: :tax_rate_id + click_button 'Continue' + + # Then the adjustment tax should be recalculated + page.should have_selector 'td.amount', text: '110' + page.should have_selector 'td.included-tax', text: '10' + end +end diff --git a/spec/features/admin/bulk_order_management_spec.rb b/spec/features/admin/bulk_order_management_spec.rb index 3bbb23bb8a..d28a20d6f9 100644 --- a/spec/features/admin/bulk_order_management_spec.rb +++ b/spec/features/admin/bulk_order_management_spec.rb @@ -14,12 +14,12 @@ feature %q{ it "displays a message when number of line items is zero" do visit '/admin/orders/bulk_management' - page.should have_text "No orders found." + expect(page).to have_text 'No orders found.' end context "displaying the list of line items" do - let!(:o1) { FactoryGirl.create(:order_with_distributor, state: 'complete', completed_at: Time.now ) } - let!(:o2) { FactoryGirl.create(:order_with_distributor, state: 'complete', completed_at: Time.now ) } + let!(:o1) { FactoryGirl.create(:order_with_distributor, state: 'complete', completed_at: Time.zone.now ) } + let!(:o2) { FactoryGirl.create(:order_with_distributor, state: 'complete', completed_at: Time.zone.now ) } let!(:o3) { FactoryGirl.create(:order_with_distributor, state: 'address', completed_at: nil ) } let!(:li1) { FactoryGirl.create(:line_item, order: o1 ) } let!(:li2) { FactoryGirl.create(:line_item, order: o2 ) } @@ -29,20 +29,16 @@ feature %q{ visit '/admin/orders/bulk_management' end - #it "displays a 'loading' splash for line items" do - # page.should have_selector "div.loading", :text => "Loading Line Items..." - #end - it "displays a list of line items" do - page.should have_selector "tr#li_#{li1.id}" - page.should have_selector "tr#li_#{li2.id}" - page.should_not have_selector "tr#li_#{li3.id}" + expect(page).to have_selector "tr#li_#{li1.id}" + expect(page).to have_selector "tr#li_#{li2.id}" + expect(page).to_not have_selector "tr#li_#{li3.id}" end end context "displaying individual columns" do - let!(:o1) { FactoryGirl.create(:order_with_distributor, state: 'complete', completed_at: Time.now, bill_address: FactoryGirl.create(:address) ) } - let!(:o2) { FactoryGirl.create(:order_with_distributor, state: 'complete', completed_at: Time.now, bill_address: nil ) } + let!(:o1) { FactoryGirl.create(:order_with_distributor, state: 'complete', completed_at: Time.zone.now, bill_address: FactoryGirl.create(:address) ) } + let!(:o2) { FactoryGirl.create(:order_with_distributor, state: 'complete', completed_at: Time.zone.now, bill_address: nil ) } let!(:li1) { FactoryGirl.create(:line_item, order: o1 ) } let!(:li2) { FactoryGirl.create(:line_item, order: o2, product: FactoryGirl.create(:product_with_option_types) ) } @@ -51,39 +47,39 @@ feature %q{ end it "displays a column for user's full name" do - page.should have_selector "th.full_name", text: "NAME", :visible => true - page.should have_selector "td.full_name", text: o1.bill_address.full_name, :visible => true - page.should have_selector "td.full_name", text: "", :visible => true + expect(page).to have_selector "th.full_name", text: "NAME", :visible => true + expect(page).to have_selector "td.full_name", text: o1.bill_address.full_name, :visible => true + expect(page).to have_selector "td.full_name", text: "", :visible => true end it "displays a column for order date" do - page.should have_selector "th.date", text: "ORDER DATE", :visible => true - page.should have_selector "td.date", text: o1.completed_at.strftime("%F %T"), :visible => true - page.should have_selector "td.date", text: o2.completed_at.strftime("%F %T"), :visible => true + expect(page).to have_selector "th.date", text: "ORDER DATE", :visible => true + expect(page).to have_selector "td.date", text: o1.completed_at.strftime("%F %T"), :visible => true + expect(page).to have_selector "td.date", text: o2.completed_at.strftime("%F %T"), :visible => true end it "displays a column for producer" do - page.should have_selector "th.producer", text: "PRODUCER", :visible => true - page.should have_selector "td.producer", text: li1.product.supplier.name, :visible => true - page.should have_selector "td.producer", text: li2.product.supplier.name, :visible => true + expect(page).to have_selector "th.producer", text: "PRODUCER", :visible => true + expect(page).to have_selector "td.producer", text: li1.product.supplier.name, :visible => true + expect(page).to have_selector "td.producer", text: li2.product.supplier.name, :visible => true end it "displays a column for variant description, which shows only product name when options text is blank" do - page.should have_selector "th.variant", text: "PRODUCT: UNIT", :visible => true - page.should have_selector "td.variant", text: li1.product.name, :visible => true - page.should have_selector "td.variant", text: (li2.product.name + ": " + li2.variant.options_text), :visible => true + expect(page).to have_selector "th.variant", text: "PRODUCT: UNIT", :visible => true + expect(page).to have_selector "td.variant", text: li1.product.name, :visible => true + expect(page).to have_selector "td.variant", text: (li2.product.name + ": " + li2.variant.options_text), :visible => true end it "displays a field for quantity" do - page.should have_selector "th.quantity", text: "QUANTITY", :visible => true - page.should have_field "quantity", with: li1.quantity.to_s, :visible => true - page.should have_field "quantity", with: li2.quantity.to_s, :visible => true + expect(page).to have_selector "th.quantity", text: "QUANTITY", :visible => true + expect(page).to have_field "quantity", with: li1.quantity.to_s, :visible => true + expect(page).to have_field "quantity", with: li2.quantity.to_s, :visible => true end it "displays a column for max quantity" do - page.should have_selector "th.max", text: "MAX", :visible => true - page.should have_selector "td.max", text: li1.max_quantity.to_s, :visible => true - page.should have_selector "td.max", text: li2.max_quantity.to_s, :visible => true + expect(page).to have_selector "th.max", text: "MAX", :visible => true + expect(page).to have_selector "td.max", text: li1.max_quantity.to_s, :visible => true + expect(page).to have_selector "td.max", text: li2.max_quantity.to_s, :visible => true end end end @@ -94,44 +90,53 @@ feature %q{ end context "tracking changes" do - let!(:o1) { FactoryGirl.create(:order_with_distributor, state: 'complete', completed_at: Time.now ) } + let!(:o1) { FactoryGirl.create(:order_with_distributor, state: 'complete', completed_at: Time.zone.now ) } let!(:li1) { FactoryGirl.create(:line_item, order: o1, :quantity => 5 ) } before :each do visit '/admin/orders/bulk_management' end - it "adds the class 'update-pending' to input elements when value is altered" do - page.should_not have_css "input[name='quantity'].update-pending" + it "adds the class 'ng-dirty' to input elements when value is altered" do + expect(page).to_not have_css "input[name='quantity'].ng-dirty" fill_in "quantity", :with => 2 - page.should have_css "input[name='quantity'].update-pending" - end - - it "removes the class 'update-pending' from input elements when initial (DB) value is entered" do - page.should_not have_css "input[name='quantity'].update-pending" - fill_in "quantity", :with => 2 - page.should have_css "input[name='quantity'].update-pending" - fill_in "quantity", :with => 5 - page.should_not have_css "input[name='quantity'].update-pending" + expect(page).to have_css "input[name='quantity'].ng-dirty" end end context "submitting data to the server" do - let!(:o1) { FactoryGirl.create(:order_with_distributor, state: 'complete', completed_at: Time.now ) } + let!(:o1) { FactoryGirl.create(:order_with_distributor, state: 'complete', completed_at: Time.zone.now ) } let!(:li1) { FactoryGirl.create(:line_item, order: o1, :quantity => 5 ) } before :each do + Spree::Config.set(allow_backorders: false) + li1.variant.update_attributes(on_hand: 1, on_demand: false) visit '/admin/orders/bulk_management' end - it "displays an update button which submits pending changes" do - fill_in "quantity", :with => 2 - page.should have_selector "input[name='quantity'].update-pending" - page.should_not have_selector "input[name='quantity'].update-success" - page.should have_button "Update" - click_button "Update" - page.should_not have_selector "input[name='quantity'].update-pending" - page.should have_selector "input[name='quantity'].update-success" + context "when acceptable data is sent to the server" do + it "displays an update button which submits pending changes" do + expect(page).to_not have_selector "#save-bar" + fill_in "quantity", :with => 2 + expect(page).to have_selector "input[name='quantity'].ng-dirty" + expect(page).to have_selector "#save-bar", text: "You have unsaved changes" + click_button "Save Changes" + expect(page).to have_selector "#save-bar", text: "All changes saved" + expect(page).to_not have_selector "input[name='quantity'].ng-dirty" + end + end + + context "when unacceptable data is sent to the server" do + it "displays an update button which submits pending changes" do + expect(page).to_not have_selector "#save-bar" + fill_in "quantity", :with => li1.variant.on_hand + li1.quantity + 10 + expect(page).to have_selector "input[name='quantity'].ng-dirty" + expect(page).to have_selector "#save-bar", text: "You have unsaved changes" + click_button "Save Changes" + expect(page).to have_selector "#save-bar", text: "Fields with red borders contain errors." + expect(page).to have_selector "input[name='quantity'].ng-dirty.update-error" + expect(page).to have_content "exceeds available stock. Please ensure line items have a valid quantity." + end end end end @@ -143,18 +148,53 @@ feature %q{ let!(:p1) { FactoryGirl.create(:product_with_option_types, group_buy: true, group_buy_unit_size: 5000, variant_unit: "weight", variants: [FactoryGirl.create(:variant, unit_value: 1000)] ) } let!(:v1) { p1.variants.first } - let!(:o1) { FactoryGirl.create(:order_with_distributor, state: 'complete', completed_at: Time.now ) } - let!(:li1) { FactoryGirl.create(:line_item, order: o1, variant: v1, :quantity => 5, :unit_value => 1000 ) } + let!(:o1) { FactoryGirl.create(:order_with_distributor, state: 'complete', completed_at: Time.zone.now ) } + let!(:li1) { FactoryGirl.create(:line_item, order: o1, variant: v1, :quantity => 5, :final_weight_volume => 1000, price: 10.00 ) } + + before { v1.update_attribute(:on_hand, 100)} context "modifying the weight/volume of a line item" do - it "update-pending is added to variable 'price'" do + it "price is altered" do visit '/admin/orders/bulk_management' - first("div#columns_dropdown", :text => "COLUMNS").click - first("div#columns_dropdown div.menu div.menu_item", text: "Weight/Volume").click - page.should_not have_css "input[name='price'].update-pending" - li1_unit_value_column = find("tr#li_#{li1.id} td.unit_value") - li1_unit_value_column.fill_in "unit_value", :with => 1200 - page.should have_css "input[name='price'].update-pending", :visible => false + first("div#columns-dropdown", :text => "COLUMNS").click + first("div#columns-dropdown div.menu div.menu_item", text: "Weight/Volume").click + first("div#columns-dropdown div.menu div.menu_item", text: "Price").click + within "tr#li_#{li1.id}" do + expect(page).to have_field "price", with: "$50.00" + fill_in "final_weight_volume", :with => 2000 + expect(page).to have_field "price", with: "$100.00" + end + click_button "Save Changes" + expect(page).to_not have_selector "#save-bar" + li1.reload + expect(li1.final_weight_volume).to eq 2000 + expect(li1.price).to eq 20.00 + end + end + + context "modifying the quantity of a line item" do + it "price is altered" do + visit '/admin/orders/bulk_management' + first("div#columns-dropdown", :text => "COLUMNS").click + first("div#columns-dropdown div.menu div.menu_item", text: "Price").click + within "tr#li_#{li1.id}" do + expect(page).to have_field "price", with: "$#{format("%.2f",li1.price * 5)}" + fill_in "quantity", :with => 6 + expect(page).to have_field "price", with: "$#{format("%.2f",li1.price * 6)}" + end + end + end + + context "modifying the quantity of a line item" do + it "weight/volume is altered" do + visit '/admin/orders/bulk_management' + first("div#columns-dropdown", :text => "COLUMNS").click + first("div#columns-dropdown div.menu div.menu_item", text: "Weight/Volume").click + within "tr#li_#{li1.id}" do + expect(page).to have_field "final_weight_volume", with: "#{li1.final_weight_volume.round}" + fill_in "quantity", :with => 6 + expect(page).to have_field "final_weight_volume", with: "#{((li1.final_weight_volume*6)/5).round}" + end end end @@ -162,22 +202,22 @@ feature %q{ it "shows a column display toggle button, which shows a list of columns when clicked" do visit '/admin/orders/bulk_management' - page.should have_selector "th", :text => "NAME" - page.should have_selector "th", :text => "ORDER DATE" - page.should have_selector "th", :text => "PRODUCER" - page.should have_selector "th", :text => "PRODUCT: UNIT" - page.should have_selector "th", :text => "QUANTITY" - page.should have_selector "th", :text => "MAX" + expect(page).to have_selector "th", :text => "NAME" + expect(page).to have_selector "th", :text => "ORDER DATE" + expect(page).to have_selector "th", :text => "PRODUCER" + expect(page).to have_selector "th", :text => "PRODUCT: UNIT" + expect(page).to have_selector "th", :text => "QUANTITY" + expect(page).to have_selector "th", :text => "MAX" - first("div#columns_dropdown", :text => "COLUMNS").click - first("div#columns_dropdown div.menu div.menu_item", text: "Producer").click + first("div#columns-dropdown", :text => "COLUMNS").click + first("div#columns-dropdown div.menu div.menu_item", text: "Producer").click - page.should_not have_selector "th", :text => "PRODUCER" - page.should have_selector "th", :text => "NAME" - page.should have_selector "th", :text => "ORDER DATE" - page.should have_selector "th", :text => "PRODUCT: UNIT" - page.should have_selector "th", :text => "QUANTITY" - page.should have_selector "th", :text => "MAX" + expect(page).to_not have_selector "th", :text => "PRODUCER" + expect(page).to have_selector "th", :text => "NAME" + expect(page).to have_selector "th", :text => "ORDER DATE" + expect(page).to have_selector "th", :text => "PRODUCT: UNIT" + expect(page).to have_selector "th", :text => "QUANTITY" + expect(page).to have_selector "th", :text => "MAX" end end @@ -185,7 +225,7 @@ feature %q{ context "supplier filter" do let!(:s1) { create(:supplier_enterprise) } let!(:s2) { create(:supplier_enterprise) } - let!(:o1) { FactoryGirl.create(:order_with_distributor, state: 'complete', completed_at: Time.now ) } + let!(:o1) { FactoryGirl.create(:order_with_distributor, state: 'complete', completed_at: Time.zone.now, order_cycle: create(:simple_order_cycle) ) } let!(:li1) { FactoryGirl.create(:line_item, order: o1, product: create(:product, supplier: s1) ) } let!(:li2) { FactoryGirl.create(:line_item, order: o1, product: create(:product, supplier: s2) ) } @@ -196,31 +236,31 @@ feature %q{ it "displays a select box for producers, which filters line items by the selected supplier" do supplier_names = ["All"] Enterprise.is_primary_producer.each{ |e| supplier_names << e.name } - find("div.select2-container#s2id_supplier_filter").click - supplier_names.each { |sn| page.should have_selector "div.select2-drop-active ul.select2-results li", text: sn } - find("div.select2-container#s2id_supplier_filter").click - page.should have_selector "tr#li_#{li1.id}", visible: true - page.should have_selector "tr#li_#{li2.id}", visible: true + open_select2 "div.select2-container#s2id_supplier_filter" + supplier_names.each { |sn| expect(page).to have_selector "div.select2-drop-active ul.select2-results li", text: sn } + close_select2 "div.select2-container#s2id_supplier_filter" + expect(page).to have_selector "tr#li_#{li1.id}", visible: true + expect(page).to have_selector "tr#li_#{li2.id}", visible: true select2_select s1.name, from: "supplier_filter" - page.should have_selector "tr#li_#{li1.id}", visible: true - page.should_not have_selector "tr#li_#{li2.id}" + expect(page).to have_selector "tr#li_#{li1.id}", visible: true + expect(page).to_not have_selector "tr#li_#{li2.id}" end it "displays all line items when 'All' is selected from supplier filter" do select2_select s1.name, from: "supplier_filter" - page.should have_selector "tr#li_#{li1.id}", visible: true - page.should_not have_selector "tr#li_#{li2.id}", visible: true + expect(page).to have_selector "tr#li_#{li1.id}", visible: true + expect(page).to_not have_selector "tr#li_#{li2.id}", visible: true select2_select "All", from: "supplier_filter" - page.should have_selector "tr#li_#{li1.id}", visible: true - page.should have_selector "tr#li_#{li2.id}", visible: true + expect(page).to have_selector "tr#li_#{li1.id}", visible: true + expect(page).to have_selector "tr#li_#{li2.id}", visible: true end end context "distributor filter" do let!(:d1) { create(:distributor_enterprise) } let!(:d2) { create(:distributor_enterprise) } - let!(:o1) { FactoryGirl.create(:order_with_distributor, state: 'complete', completed_at: Time.now, distributor: d1 ) } - let!(:o2) { FactoryGirl.create(:order_with_distributor, state: 'complete', completed_at: Time.now, distributor: d2 ) } + let!(:o1) { FactoryGirl.create(:order_with_distributor, state: 'complete', completed_at: Time.zone.now, distributor: d1, order_cycle: create(:simple_order_cycle) ) } + let!(:o2) { FactoryGirl.create(:order_with_distributor, state: 'complete', completed_at: Time.zone.now, distributor: d2, order_cycle: create(:simple_order_cycle) ) } let!(:li1) { FactoryGirl.create(:line_item, order: o1 ) } let!(:li2) { FactoryGirl.create(:line_item, order: o2 ) } @@ -231,23 +271,25 @@ feature %q{ it "displays a select box for distributors, which filters line items by the selected distributor" do distributor_names = ["All"] Enterprise.is_distributor.each{ |e| distributor_names << e.name } - find("div.select2-container#s2id_distributor_filter").click - distributor_names.each { |dn| page.should have_selector "div.select2-drop-active ul.select2-results li", text: dn } - find("div.select2-container#s2id_distributor_filter").click - page.should have_selector "tr#li_#{li1.id}", visible: true - page.should have_selector "tr#li_#{li2.id}", visible: true + open_select2 "div.select2-container#s2id_distributor_filter" + distributor_names.each { |dn| expect(page).to have_selector "div.select2-drop-active ul.select2-results li", text: dn } + close_select2 "div.select2-container#s2id_distributor_filter" + expect(page).to have_selector "tr#li_#{li1.id}", visible: true + expect(page).to have_selector "tr#li_#{li2.id}", visible: true select2_select d1.name, from: "distributor_filter" - page.should have_selector "tr#li_#{li1.id}", visible: true - page.should_not have_selector "tr#li_#{li2.id}", visible: true + expect(page).to have_selector "tr#li_#{li1.id}", visible: true + expect(page).to_not have_selector "tr#li_#{li2.id}", visible: true end it "displays all line items when 'All' is selected from distributor filter" do + expect(page).to have_selector "tr#li_#{li1.id}" + expect(page).to have_selector "tr#li_#{li2.id}" select2_select d1.name, from: "distributor_filter" - page.should have_selector "tr#li_#{li1.id}", visible: true - page.should_not have_selector "tr#li_#{li2.id}", visible: true + expect(page).to have_selector "tr#li_#{li1.id}" + expect(page).to_not have_selector "tr#li_#{li2.id}" select2_select "All", from: "distributor_filter" - page.should have_selector "tr#li_#{li1.id}", visible: true - page.should have_selector "tr#li_#{li2.id}", visible: true + expect(page).to have_selector "tr#li_#{li1.id}" + expect(page).to have_selector "tr#li_#{li2.id}" end end @@ -255,37 +297,34 @@ feature %q{ let!(:distributor) { create(:distributor_enterprise) } let!(:oc1) { FactoryGirl.create(:simple_order_cycle, distributors: [distributor]) } let!(:oc2) { FactoryGirl.create(:simple_order_cycle, distributors: [distributor]) } - let!(:o1) { FactoryGirl.create(:order_with_distributor, state: 'complete', completed_at: Time.now, order_cycle: oc1 ) } - let!(:o2) { FactoryGirl.create(:order_with_distributor, state: 'complete', completed_at: Time.now, order_cycle: oc2 ) } + let!(:o1) { FactoryGirl.create(:order_with_distributor, state: 'complete', completed_at: Time.zone.now, order_cycle: oc1 ) } + let!(:o2) { FactoryGirl.create(:order_with_distributor, state: 'complete', completed_at: Time.zone.now, order_cycle: oc2 ) } let!(:li1) { FactoryGirl.create(:line_item, order: o1 ) } let!(:li2) { FactoryGirl.create(:line_item, order: o2 ) } - before :each do + before do visit '/admin/orders/bulk_management' end it "displays a select box for order cycles, which filters line items by the selected order cycle" do - order_cycle_names = ["All"] - OrderCycle.all.each{ |oc| order_cycle_names << oc.name } - find("div.select2-container#s2id_order_cycle_filter").click - order_cycle_names.each { |ocn| page.should have_selector "div.select2-drop-active ul.select2-results li", text: ocn } - find("div.select2-container#s2id_order_cycle_filter").click - page.should have_selector "tr#li_#{li1.id}" - page.should have_selector "tr#li_#{li2.id}" + expect(page).to have_select2 'order_cycle_filter', with_options: OrderCycle.pluck(:name).unshift("All") + expect(page).to have_selector "tr#li_#{li1.id}" + expect(page).to have_selector "tr#li_#{li2.id}" select2_select oc1.name, from: "order_cycle_filter" - page.should have_selector "#loading img.spinner" - page.should_not have_selector "#loading img.spinner" - page.should have_selector "tr#li_#{li1.id}" - page.should_not have_selector "tr#li_#{li2.id}" + expect(page).to_not have_selector "#loading img.spinner" + expect(page).to have_selector "tr#li_#{li1.id}" + expect(page).to_not have_selector "tr#li_#{li2.id}" end it "displays all line items when 'All' is selected from order_cycle filter" do + expect(page).to have_selector "tr#li_#{li1.id}" + expect(page).to have_selector "tr#li_#{li2.id}" select2_select oc1.name, from: "order_cycle_filter" - page.should have_selector "tr#li_#{li1.id}" - page.should_not have_selector "tr#li_#{li2.id}" + expect(page).to have_selector "tr#li_#{li1.id}" + expect(page).to_not have_selector "tr#li_#{li2.id}" select2_select "All", from: "order_cycle_filter" - page.should have_selector "tr#li_#{li1.id}" - page.should have_selector "tr#li_#{li2.id}" + expect(page).to have_selector "tr#li_#{li1.id}" + expect(page).to have_selector "tr#li_#{li2.id}" end end @@ -298,8 +337,8 @@ feature %q{ let!(:oc2) { FactoryGirl.create(:simple_order_cycle, suppliers: [s2], distributors: [d2] ) } let!(:p1) { FactoryGirl.create(:product, supplier: s1) } let!(:p2) { FactoryGirl.create(:product, supplier: s2) } - let!(:o1) { FactoryGirl.create(:order_with_distributor, state: 'complete', completed_at: Time.now, distributor: d1, order_cycle: oc1 ) } - let!(:o2) { FactoryGirl.create(:order_with_distributor, state: 'complete', completed_at: Time.now, distributor: d2, order_cycle: oc2 ) } + let!(:o1) { FactoryGirl.create(:order_with_distributor, state: 'complete', completed_at: Time.zone.now, distributor: d1, order_cycle: oc1 ) } + let!(:o2) { FactoryGirl.create(:order_with_distributor, state: 'complete', completed_at: Time.zone.now, distributor: d2, order_cycle: oc2 ) } let!(:li1) { FactoryGirl.create(:line_item, order: o1, product: p1 ) } let!(:li2) { FactoryGirl.create(:line_item, order: o2, product: p2 ) } @@ -309,42 +348,42 @@ feature %q{ it "allows filters to be used in combination" do select2_select oc1.name, from: "order_cycle_filter" - page.should have_selector "tr#li_#{li1.id}", visible: true - page.should_not have_selector "tr#li_#{li2.id}", visible: true + expect(page).to have_selector "tr#li_#{li1.id}", visible: true + expect(page).to_not have_selector "tr#li_#{li2.id}", visible: true select2_select d1.name, from: "distributor_filter" select2_select s1.name, from: "supplier_filter" - page.should have_selector "tr#li_#{li1.id}", visible: true - page.should_not have_selector "tr#li_#{li2.id}", visible: true + expect(page).to have_selector "tr#li_#{li1.id}", visible: true + expect(page).to_not have_selector "tr#li_#{li2.id}", visible: true select2_select d2.name, from: "distributor_filter" select2_select s2.name, from: "supplier_filter" - page.should_not have_selector "tr#li_#{li1.id}", visible: true - page.should_not have_selector "tr#li_#{li2.id}", visible: true + expect(page).to_not have_selector "tr#li_#{li1.id}", visible: true + expect(page).to_not have_selector "tr#li_#{li2.id}", visible: true select2_select oc2.name, from: "order_cycle_filter" - page.should_not have_selector "tr#li_#{li1.id}", visible: true - page.should have_selector "tr#li_#{li2.id}", visible: true + expect(page).to_not have_selector "tr#li_#{li1.id}", visible: true + expect(page).to have_selector "tr#li_#{li2.id}", visible: true end it "displays a 'Clear All' button which sets all select filters to 'All'" do select2_select oc1.name, from: "order_cycle_filter" select2_select d1.name, from: "distributor_filter" select2_select s1.name, from: "supplier_filter" - page.should have_selector "tr#li_#{li1.id}", visible: true - page.should_not have_selector "tr#li_#{li2.id}", visible: true - page.should have_button "Clear All" + expect(page).to have_selector "tr#li_#{li1.id}" + expect(page).to_not have_selector "tr#li_#{li2.id}" + expect(page).to have_button "Clear All" click_button "Clear All" - page.should have_selector "div#s2id_order_cycle_filter a.select2-choice", text: "All" - page.should have_selector "div#s2id_supplier_filter a.select2-choice", text: "All" - page.should have_selector "div#s2id_distributor_filter a.select2-choice", text: "All" - page.should have_selector "tr#li_#{li1.id}", visible: true - page.should have_selector "tr#li_#{li2.id}", visible: true + expect(page).to have_selector "div#s2id_order_cycle_filter a.select2-choice", text: "All" + expect(page).to have_selector "div#s2id_supplier_filter a.select2-choice", text: "All" + expect(page).to have_selector "div#s2id_distributor_filter a.select2-choice", text: "All" + expect(page).to have_selector "tr#li_#{li1.id}" + expect(page).to have_selector "tr#li_#{li2.id}" end end end context "using quick search" do - let!(:o1) { FactoryGirl.create(:order_with_distributor, state: 'complete', completed_at: Time.now ) } - let!(:o2) { FactoryGirl.create(:order_with_distributor, state: 'complete', completed_at: Time.now ) } - let!(:o3) { FactoryGirl.create(:order_with_distributor, state: 'complete', completed_at: Time.now ) } + let!(:o1) { FactoryGirl.create(:order_with_distributor, state: 'complete', completed_at: Time.zone.now ) } + let!(:o2) { FactoryGirl.create(:order_with_distributor, state: 'complete', completed_at: Time.zone.now ) } + let!(:o3) { FactoryGirl.create(:order_with_distributor, state: 'complete', completed_at: Time.zone.now ) } let!(:li1) { FactoryGirl.create(:line_item, order: o1 ) } let!(:li2) { FactoryGirl.create(:line_item, order: o2 ) } let!(:li3) { FactoryGirl.create(:line_item, order: o3 ) } @@ -354,24 +393,24 @@ feature %q{ end it "displays a quick search input" do - page.should have_field "quick_search" + expect(page).to have_field "quick_search" end it "filters line items based on their attributes and the contents of the quick search input" do - page.should have_selector "tr#li_#{li1.id}", visible: true - page.should have_selector "tr#li_#{li2.id}", visible: true - page.should have_selector "tr#li_#{li3.id}", visible: true + expect(page).to have_selector "tr#li_#{li1.id}", visible: true + expect(page).to have_selector "tr#li_#{li2.id}", visible: true + expect(page).to have_selector "tr#li_#{li3.id}", visible: true fill_in "quick_search", :with => o1.email - page.should have_selector "tr#li_#{li1.id}", visible: true - page.should_not have_selector "tr#li_#{li2.id}", visible: true - page.should_not have_selector "tr#li_#{li3.id}", visible: true + expect(page).to have_selector "tr#li_#{li1.id}", visible: true + expect(page).to_not have_selector "tr#li_#{li2.id}", visible: true + expect(page).to_not have_selector "tr#li_#{li3.id}", visible: true end end context "using date restriction controls" do - let!(:o1) { FactoryGirl.create(:order_with_distributor, state: 'complete', completed_at: (Date.today - 8).strftime("%F %T") ) } - let!(:o2) { FactoryGirl.create(:order_with_distributor, state: 'complete', completed_at: Time.now ) } - let!(:o3) { FactoryGirl.create(:order_with_distributor, state: 'complete', completed_at: (Date.today + 2).strftime("%F %T") ) } + let!(:o1) { FactoryGirl.create(:order_with_distributor, state: 'complete', completed_at: (Date.current - 8).strftime("%F %T") ) } + let!(:o2) { FactoryGirl.create(:order_with_distributor, state: 'complete', completed_at: Time.zone.now ) } + let!(:o3) { FactoryGirl.create(:order_with_distributor, state: 'complete', completed_at: (Date.current + 2).strftime("%F %T") ) } let!(:li1) { FactoryGirl.create(:line_item, order: o1, :quantity => 1 ) } let!(:li2) { FactoryGirl.create(:line_item, order: o2, :quantity => 2 ) } let!(:li3) { FactoryGirl.create(:line_item, order: o3, :quantity => 3 ) } @@ -381,70 +420,64 @@ feature %q{ end it "displays date fields for filtering orders, with default values set" do - one_week_ago = Date.today.prev_day(7).strftime("%F") - tonight = Date.tomorrow.strftime("%F") - page.should have_field "start_date_filter", with: one_week_ago - page.should have_field "end_date_filter", with: tonight + # use Date.current since Date.today is without timezone + today = Date.current + one_week_ago = today.prev_day(7).strftime("%F") + tonight = today.next_day.strftime("%F") + expect(page).to have_field "start_date_filter", with: one_week_ago + expect(page).to have_field "end_date_filter", with: tonight end it "only loads line items whose orders meet the date restriction criteria" do - page.should_not have_selector "tr#li_#{li1.id}", visible: true - page.should have_selector "tr#li_#{li2.id}", visible: true - page.should_not have_selector "tr#li_#{li3.id}", visible: true + expect(page).to_not have_selector "tr#li_#{li1.id}", visible: true + expect(page).to have_selector "tr#li_#{li2.id}", visible: true + expect(page).to_not have_selector "tr#li_#{li3.id}", visible: true end it "displays only line items whose orders meet the date restriction criteria, when changed" do - fill_in "start_date_filter", :with => (Date.today - 9).strftime("%F") - page.should have_selector "tr#li_#{li1.id}", visible: true - page.should have_selector "tr#li_#{li2.id}", visible: true - page.should_not have_selector "tr#li_#{li3.id}", visible: true + fill_in "start_date_filter", :with => (Date.current - 9).strftime("%F") + expect(page).to have_selector "tr#li_#{li1.id}", visible: true + expect(page).to have_selector "tr#li_#{li2.id}", visible: true + expect(page).to_not have_selector "tr#li_#{li3.id}", visible: true - fill_in "end_date_filter", :with => (Date.today + 3).strftime("%F") - page.should have_selector "tr#li_#{li1.id}", visible: true - page.should have_selector "tr#li_#{li2.id}", visible: true - page.should have_selector "tr#li_#{li3.id}", visible: true + fill_in "end_date_filter", :with => (Date.current + 3).strftime("%F") + expect(page).to have_selector "tr#li_#{li1.id}", visible: true + expect(page).to have_selector "tr#li_#{li2.id}", visible: true + expect(page).to have_selector "tr#li_#{li3.id}", visible: true end - context "when pending changes exist" do - it "alerts the user when dates are altered" do - li2_quantity_column = find("tr#li_#{li2.id} td.quantity") - li2_quantity_column.fill_in "quantity", :with => li2.quantity + 1 - page.should_not have_button "IGNORE" - page.should_not have_button "SAVE" - fill_in "start_date_filter", :with => (Date.today - 9).strftime("%F %T") - page.should have_button "IGNORE" - page.should have_button "SAVE" - end - - it "saves pendings changes when 'SAVE' button is clicked" do + context "when the form is dirty" do + before do within("tr#li_#{li2.id} td.quantity") do page.fill_in "quantity", :with => (li2.quantity + 1).to_s end - fill_in "start_date_filter", :with => (Date.today - 9).strftime("%F %T") - click_button "SAVE" - page.should_not have_selector "input[name='quantity'].update-pending" + end + + it "shows a dialog and ignores changes when confirm dialog is accepted" do + page.driver.accept_modal :confirm, text: "Unsaved changes exist and will be lost if you continue." do + fill_in "start_date_filter", :with => (Date.current - 9).strftime("%F %T") + end + expect(page).to have_no_selector "#save-bar" within("tr#li_#{li2.id} td.quantity") do - page.should have_field "quantity", :with => ( li2.quantity + 1 ).to_s + expect(page).to have_no_selector "input[name=quantity].ng-dirty" end end - it "ignores pending changes when 'IGNORE' button is clicked" do - within("tr#li_#{li2.id} td.quantity") do - page.fill_in "quantity", :with => (li2.quantity + 1).to_s + it "shows a dialog and keeps changes when confirm dialog is rejected" do + page.driver.dismiss_modal :confirm, text: "Unsaved changes exist and will be lost if you continue." do + fill_in "start_date_filter", :with => (Date.current - 9).strftime("%F %T") end - fill_in "start_date_filter", :with => (Date.today - 9).strftime("%F %T") - click_button "IGNORE" - page.should_not have_selector "input[name='quantity'].update-pending" + expect(page).to have_selector "#save-bar" within("tr#li_#{li2.id} td.quantity") do - page.should have_field "quantity", :with => ( li2.quantity ).to_s + expect(page).to have_selector "input[name=quantity].ng-dirty" end end end end context "bulk action controls" do - let!(:o1) { FactoryGirl.create(:order_with_distributor, state: 'complete', completed_at: Time.now ) } - let!(:o2) { FactoryGirl.create(:order_with_distributor, state: 'complete', completed_at: Time.now ) } + let!(:o1) { FactoryGirl.create(:order_with_distributor, state: 'complete', completed_at: Time.zone.now ) } + let!(:o2) { FactoryGirl.create(:order_with_distributor, state: 'complete', completed_at: Time.zone.now ) } let!(:li1) { FactoryGirl.create(:line_item, order: o1 ) } let!(:li2) { FactoryGirl.create(:line_item, order: o2 ) } @@ -453,36 +486,36 @@ feature %q{ end it "displays a checkbox for each line item in the list" do - page.should have_selector "tr#li_#{li1.id} input[type='checkbox'][name='bulk']" - page.should have_selector "tr#li_#{li2.id} input[type='checkbox'][name='bulk']" + expect(page).to have_selector "tr#li_#{li1.id} input[type='checkbox'][name='bulk']" + expect(page).to have_selector "tr#li_#{li2.id} input[type='checkbox'][name='bulk']" end it "displays a checkbox to which toggles the 'checked' state of all checkboxes" do check "toggle_bulk" - page.all("input[type='checkbox'][name='bulk']").each{ |checkbox| checkbox.checked?.should == true } + page.all("input[type='checkbox'][name='bulk']").each{ |checkbox| expect(checkbox.checked?).to be true } uncheck "toggle_bulk" - page.all("input[type='checkbox'][name='bulk']").each{ |checkbox| checkbox.checked?.should == false } + page.all("input[type='checkbox'][name='bulk']").each{ |checkbox| expect(checkbox.checked?).to be false } end it "displays a bulk action select box with a list of actions" do list_of_actions = ['Delete Selected'] - find("div#bulk_actions_dropdown").click - within("div#bulk_actions_dropdown") do - list_of_actions.each { |action_name| page.should have_selector "div.menu_item", text: action_name } + find("div#bulk-actions-dropdown").click + within("div#bulk-actions-dropdown") do + list_of_actions.each { |action_name| expect(page).to have_selector "div.menu_item", text: action_name } end end context "performing actions" do it "deletes selected items" do - page.should have_selector "tr#li_#{li1.id}", visible: true - page.should have_selector "tr#li_#{li2.id}", visible: true + expect(page).to have_selector "tr#li_#{li1.id}", visible: true + expect(page).to have_selector "tr#li_#{li2.id}", visible: true within("tr#li_#{li2.id} td.bulk") do check "bulk" end - find("div#bulk_actions_dropdown").click - find("div#bulk_actions_dropdown div.menu_item", :text => "Delete Selected" ).click - page.should have_selector "tr#li_#{li1.id}", visible: true - page.should_not have_selector "tr#li_#{li2.id}", visible: true + find("div#bulk-actions-dropdown").click + find("div#bulk-actions-dropdown div.menu_item", :text => "Delete Selected" ).click + expect(page).to have_selector "tr#li_#{li1.id}", visible: true + expect(page).to_not have_selector "tr#li_#{li2.id}", visible: true end end @@ -491,27 +524,27 @@ feature %q{ fill_in "quick_search", with: o1.number check "toggle_bulk" fill_in "quick_search", with: '' - find("tr#li_#{li1.id} input[type='checkbox'][name='bulk']").checked?.should == true - find("tr#li_#{li2.id} input[type='checkbox'][name='bulk']").checked?.should == false - find("input[type='checkbox'][name='toggle_bulk']").checked?.should == false + expect(find("tr#li_#{li1.id} input[type='checkbox'][name='bulk']").checked?).to be true + expect(find("tr#li_#{li2.id} input[type='checkbox'][name='bulk']").checked?).to be false + expect(find("input[type='checkbox'][name='toggle_bulk']").checked?).to be false end it "only applies the delete action to filteredLineItems" do check "toggle_bulk" fill_in "quick_search", with: o1.number - find("div#bulk_actions_dropdown").click - find("div#bulk_actions_dropdown div.menu_item", :text => "Delete Selected" ).click + find("div#bulk-actions-dropdown").click + find("div#bulk-actions-dropdown div.menu_item", :text => "Delete Selected" ).click fill_in "quick_search", with: '' - page.should_not have_selector "tr#li_#{li1.id}", visible: true - page.should have_selector "tr#li_#{li2.id}", visible: true + expect(page).to_not have_selector "tr#li_#{li1.id}", visible: true + expect(page).to have_selector "tr#li_#{li2.id}", visible: true end end end context "using action buttons" do context "using edit buttons" do - let!(:o1) { FactoryGirl.create(:order_with_distributor, state: 'complete', completed_at: Time.now ) } - let!(:o2) { FactoryGirl.create(:order_with_distributor, state: 'complete', completed_at: Time.now ) } + let!(:o1) { FactoryGirl.create(:order_with_distributor, state: 'complete', completed_at: Time.zone.now ) } + let!(:o2) { FactoryGirl.create(:order_with_distributor, state: 'complete', completed_at: Time.zone.now ) } let!(:li1) { FactoryGirl.create(:line_item, order: o1 ) } let!(:li2) { FactoryGirl.create(:line_item, order: o2 ) } @@ -520,17 +553,31 @@ feature %q{ end it "shows an edit button for line_items, which takes the user to the standard edit page for the order" do - page.should have_selector "a.edit-order", :count => 2 + expect(page).to have_selector "a.edit-order", :count => 2 - first("a.edit-order").click + # Shows a confirm dialog when unsaved changes exist + page.driver.dismiss_modal :confirm, text: "Unsaved changes exist and will be lost if you continue." do + within "tr#li_#{li1.id}" do + fill_in "quantity", with: (li1.quantity + 1) + first("a.edit-order").click + end + end - URI.parse(current_url).path.should == "/admin/orders/#{o1.number}/edit" + # So we save the changes + expect(URI.parse(current_url).path).to eq "/admin/orders/bulk_management" + click_button "Save Changes" + + # And try again + within "tr#li_#{li1.id}" do + first("a.edit-order").click + end + expect(URI.parse(current_url).path).to eq "/admin/orders/#{o1.number}/edit" end end context "using delete buttons" do - let!(:o1) { FactoryGirl.create(:order_with_distributor, state: 'complete', completed_at: Time.now ) } - let!(:o2) { FactoryGirl.create(:order_with_distributor, state: 'complete', completed_at: Time.now ) } + let!(:o1) { FactoryGirl.create(:order_with_distributor, state: 'complete', completed_at: Time.zone.now ) } + let!(:o2) { FactoryGirl.create(:order_with_distributor, state: 'complete', completed_at: Time.zone.now ) } let!(:li1) { FactoryGirl.create(:line_item, order: o1 ) } let!(:li2) { FactoryGirl.create(:line_item, order: o2 ) } @@ -538,28 +585,25 @@ feature %q{ visit '/admin/orders/bulk_management' end - it "shows a delete button for each line item" do - page.should have_selector "a.delete-line-item", :count => 2 - end - it "removes a line item when the relevant delete button is clicked" do + expect(page).to have_selector "a.delete-line-item", :count => 2 first("a.delete-line-item").click - page.should_not have_selector "a.delete-line-item", :count => 2 - page.should have_selector "a.delete-line-item", :count => 1 + expect(page).to_not have_selector "a.delete-line-item", :count => 2 + expect(page).to have_selector "a.delete-line-item", :count => 1 visit '/admin/orders/bulk_management' - page.should have_selector "a.delete-line-item", :count => 1 + expect(page).to have_selector "a.delete-line-item", :count => 1 end end end context "clicking the link on variant name" do - let!(:o1) { FactoryGirl.create(:order_with_distributor, state: 'complete', completed_at: Time.now ) } - let!(:o2) { FactoryGirl.create(:order_with_distributor, state: 'complete', completed_at: Time.now ) } + let!(:o1) { FactoryGirl.create(:order_with_distributor, state: 'complete', completed_at: Time.zone.now ) } + let!(:o2) { FactoryGirl.create(:order_with_distributor, state: 'complete', completed_at: Time.zone.now ) } let!(:li1) { FactoryGirl.create(:line_item, order: o1 ) } let!(:li2) { FactoryGirl.create(:line_item, order: o2 ) } let!(:p3) { FactoryGirl.create(:product_with_option_types, group_buy: true, group_buy_unit_size: 5000, variant_unit: "weight", variants: [FactoryGirl.create(:variant, unit_value: 1000)] ) } let!(:v3) { p3.variants.first } - let!(:o3) { FactoryGirl.create(:order_with_distributor, state: 'complete', completed_at: Time.now ) } + let!(:o3) { FactoryGirl.create(:order_with_distributor, state: 'complete', completed_at: Time.zone.now ) } let!(:li3) { FactoryGirl.create(:line_item, order: o3, variant: v3, quantity: 3, max_quantity: 6 ) } let!(:li4) { FactoryGirl.create(:line_item, order: o2, variant: v3, quantity: 1, max_quantity: 3 ) } @@ -571,32 +615,32 @@ feature %q{ end it "displays group buy calc box" do - page.should have_selector "div#group_buy_calculation", :visible => true + expect(page).to have_selector "div#group_buy_calculation", :visible => true within "div#group_buy_calculation" do - page.should have_text "Group Buy Unit Size" - page.should have_text "5 kg" - page.should have_text "Total Quantity Ordered" - page.should have_text "4 kg" - page.should have_text "Max Quantity Ordered" - page.should have_text "9 kg" - page.should have_text "Current Fulfilled Units" - page.should have_text "0.8" - page.should have_text "Max Fulfilled Units" - page.should have_text "1.8" - page.should have_selector "div.shared_resource", :visible => true + expect(page).to have_text "Group Buy Unit Size" + expect(page).to have_text "5 kg" + expect(page).to have_text "Total Quantity Ordered" + expect(page).to have_text "4 kg" + expect(page).to have_text "Max Quantity Ordered" + expect(page).to have_text "9 kg" + expect(page).to have_text "Current Fulfilled Units" + expect(page).to have_text "0.8" + expect(page).to have_text "Max Fulfilled Units" + expect(page).to have_text "1.8" + expect(page).to have_selector "div.shared_resource", :visible => true within "div.shared_resource" do - page.should have_selector "span", :text => "Shared Resource?" - page.should have_selector "input#shared_resource" + expect(page).to have_selector "span", :text => "Shared Resource?" + expect(page).to have_selector "input#shared_resource" end end end it "all line items of the same variant" do - page.should_not have_selector "tr#li_#{li1.id}", :visible => true - page.should_not have_selector "tr#li_#{li2.id}", :visible => true - page.should have_selector "tr#li_#{li3.id}", :visible => true - page.should have_selector "tr#li_#{li4.id}", :visible => true + expect(page).to_not have_selector "tr#li_#{li1.id}", :visible => true + expect(page).to_not have_selector "tr#li_#{li2.id}", :visible => true + expect(page).to have_selector "tr#li_#{li3.id}", :visible => true + expect(page).to have_selector "tr#li_#{li4.id}", :visible => true end context "clicking 'Clear' in group buy box" do @@ -605,11 +649,11 @@ feature %q{ end it "shows all products and clears group buy box" do - page.should_not have_selector "div#group_buy_calculation", :visible => true - page.should have_selector "tr#li_#{li1.id}", :visible => true - page.should have_selector "tr#li_#{li2.id}", :visible => true - page.should have_selector "tr#li_#{li3.id}", :visible => true - page.should have_selector "tr#li_#{li4.id}", :visible => true + expect(page).to_not have_selector "div#group_buy_calculation", :visible => true + expect(page).to have_selector "tr#li_#{li1.id}", :visible => true + expect(page).to have_selector "tr#li_#{li2.id}", :visible => true + expect(page).to have_selector "tr#li_#{li3.id}", :visible => true + expect(page).to have_selector "tr#li_#{li4.id}", :visible => true end end end @@ -619,8 +663,8 @@ feature %q{ let(:s1) { create(:supplier_enterprise, name: 'First Supplier') } let(:d1) { create(:distributor_enterprise, name: 'First Distributor') } let(:d2) { create(:distributor_enterprise, name: 'Another Distributor') } - let!(:o1) { FactoryGirl.create(:order_with_distributor, state: 'complete', completed_at: Time.now, distributor: d1 ) } - let!(:o2) { FactoryGirl.create(:order_with_distributor, state: 'complete', completed_at: Time.now, distributor: d2 ) } + let!(:o1) { FactoryGirl.create(:order_with_distributor, state: 'complete', completed_at: Time.zone.now, distributor: d1 ) } + let!(:o2) { FactoryGirl.create(:order_with_distributor, state: 'complete', completed_at: Time.zone.now, distributor: d2 ) } let!(:line_item_distributed) { FactoryGirl.create(:line_item, order: o1, product: create(:product, supplier: s1) ) } let!(:line_item_not_distributed) { FactoryGirl.create(:line_item, order: o2, product: create(:product, supplier: s1) ) } @@ -634,16 +678,16 @@ feature %q{ it "displays a Bulk Management Tab under the Orders item" do visit '/admin/orders' - page.should have_link "Bulk Order Management" + expect(page).to have_link "Bulk Order Management" click_link "Bulk Order Management" - page.should have_selector "h1.page-title", text: "Bulk Order Management" + expect(page).to have_selector "h1.page-title", text: "Bulk Order Management" end it "shows only line item from orders that I distribute, and not those that I supply" do visit '/admin/orders/bulk_management' - page.should have_selector "tr#li_#{line_item_distributed.id}", :visible => true - page.should_not have_selector "tr#li_#{line_item_not_distributed.id}", :visible => true + expect(page).to have_selector "tr#li_#{line_item_distributed.id}", :visible => true + expect(page).to_not have_selector "tr#li_#{line_item_not_distributed.id}", :visible => true end end end diff --git a/spec/features/admin/bulk_product_update_spec.rb b/spec/features/admin/bulk_product_update_spec.rb index 4c52ec2256..9dabf93028 100644 --- a/spec/features/admin/bulk_product_update_spec.rb +++ b/spec/features/admin/bulk_product_update_spec.rb @@ -42,12 +42,12 @@ feature %q{ end it "displays a date input for available_on for each product, formatted to yyyy-mm-dd hh:mm:ss" do - p1 = FactoryGirl.create(:product, available_on: Date.today) - p2 = FactoryGirl.create(:product, available_on: Date.today-1) + p1 = FactoryGirl.create(:product, available_on: Date.current) + p2 = FactoryGirl.create(:product, available_on: Date.current-1) visit '/admin/products/bulk_edit' - first("div#columns_dropdown", :text => "COLUMNS").click - first("div#columns_dropdown div.menu div.menu_item", text: "Available On").click + first("div#columns-dropdown", :text => "COLUMNS").click + first("div#columns-dropdown div.menu div.menu_item", text: "Available On").click expect(page).to have_field "available_on", with: p1.available_on.strftime("%F %T") expect(page).to have_field "available_on", with: p2.available_on.strftime("%F %T") @@ -205,8 +205,9 @@ feature %q{ expect(page).to have_selector "a.edit-variant", count: 1 # When I remove two, they should be removed - page.all('a.delete-variant').first.click - page.all('a.delete-variant').first.click + page.all('a.delete-variant', visible: true).first.click + expect(page).to have_selector "tr.variant", count: 2 + page.all('a.delete-variant', visible: true).first.click expect(page).to have_selector "tr.variant", count: 1 # When I fill out variant details and hit update @@ -236,17 +237,17 @@ feature %q{ s2 = FactoryGirl.create(:supplier_enterprise) t1 = FactoryGirl.create(:taxon) t2 = FactoryGirl.create(:taxon) - p = FactoryGirl.create(:product, supplier: s1, available_on: Date.today, variant_unit: 'volume', variant_unit_scale: 1, primary_taxon: t2, sku: "OLD SKU") + p = FactoryGirl.create(:product, supplier: s1, available_on: Date.current, variant_unit: 'volume', variant_unit_scale: 1, primary_taxon: t2, sku: "OLD SKU") login_to_admin_section visit '/admin/products/bulk_edit' - first("div#columns_dropdown", :text => "COLUMNS").click - first("div#columns_dropdown div.menu div.menu_item", text: "Available On").click - first("div#columns_dropdown div.menu div.menu_item", text: "Category").click - first("div#columns_dropdown div.menu div.menu_item", text: "Inherits Properties?").click - first("div#columns_dropdown div.menu div.menu_item", text: "SKU").click + first("div#columns-dropdown", :text => "COLUMNS").click + first("div#columns-dropdown div.menu div.menu_item", text: "Available On").click + first("div#columns-dropdown div.menu div.menu_item", text: "Category").click + first("div#columns-dropdown div.menu div.menu_item", text: "Inherits Properties?").click + first("div#columns-dropdown div.menu div.menu_item", text: "SKU").click within "tr#p_#{p.id}" do expect(page).to have_field "product_name", with: p.name @@ -304,9 +305,10 @@ feature %q{ scenario "updating a product with variants" do s1 = FactoryGirl.create(:supplier_enterprise) s2 = FactoryGirl.create(:supplier_enterprise) - p = FactoryGirl.create(:product, supplier: s1, available_on: Date.today, variant_unit: 'volume', variant_unit_scale: 0.001, + p = FactoryGirl.create(:product, supplier: s1, available_on: Date.current, variant_unit: 'volume', variant_unit_scale: 0.001, price: 3.0, on_hand: 9, unit_value: 0.25, unit_description: '(bottle)' ) v = p.variants.first + v.update_column(:sku, "VARIANTSKU") login_to_admin_section @@ -314,12 +316,18 @@ feature %q{ expect(page).to have_selector "a.view-variants" first("a.view-variants").trigger('click') + first("div#columns-dropdown", :text => "COLUMNS").click + first("div#columns-dropdown div.menu div.menu_item", text: "SKU").click + first("div#columns-dropdown", :text => "COLUMNS").click + + expect(page).to have_field "variant_sku", with: "VARIANTSKU" expect(page).to have_field "variant_price", with: "3.0" expect(page).to have_field "variant_unit_value_with_description", with: "250 (bottle)" expect(page).to have_field "variant_on_hand", with: "9" expect(page).to have_selector "span[name='on_hand']", "9" select "Volume (L)", from: "variant_unit_with_scale" + fill_in "variant_sku", with: "NEWSKU" fill_in "variant_price", with: "4.0" fill_in "variant_on_hand", with: "10" fill_in "variant_unit_value_with_description", with: "2 (8x250 mL bottles)" @@ -330,6 +338,7 @@ feature %q{ expect(page.find("#status-message")).to have_content "Changes saved." v.reload + expect(v.sku).to eq "NEWSKU" expect(v.price).to eq 4.0 expect(v.on_hand).to eq 10 expect(v.unit_value).to eq 2 # 2L in L @@ -555,8 +564,8 @@ feature %q{ visit '/admin/products/bulk_edit' - first("div#columns_dropdown", :text => "COLUMNS").click - first("div#columns_dropdown div.menu div.menu_item", text: "Available On").click + first("div#columns-dropdown", :text => "COLUMNS").click + first("div#columns-dropdown div.menu div.menu_item", text: "Available On").click expect(page).to have_selector "th", :text => "NAME" expect(page).to have_selector "th", :text => "PRODUCER" @@ -564,7 +573,7 @@ feature %q{ expect(page).to have_selector "th", :text => "ON HAND" expect(page).to have_selector "th", :text => "AV. ON" - first("div#columns_dropdown div.menu div.menu_item", text: /^.{0,1}Producer$/).click + first("div#columns-dropdown div.menu div.menu_item", text: /^.{0,1}Producer$/).click expect(page).to have_no_selector "th", :text => "PRODUCER" expect(page).to have_selector "th", :text => "NAME" @@ -687,8 +696,8 @@ feature %q{ v = p.variants.first visit '/admin/products/bulk_edit' - first("div#columns_dropdown", :text => "COLUMNS").click - first("div#columns_dropdown div.menu div.menu_item", text: "Available On").click + first("div#columns-dropdown", :text => "COLUMNS").click + first("div#columns-dropdown div.menu div.menu_item", text: "Available On").click within "tr#p_#{p.id}" do expect(page).to have_field "product_name", with: p.name diff --git a/spec/features/admin/business_model_configuration_spec.rb b/spec/features/admin/business_model_configuration_spec.rb new file mode 100644 index 0000000000..05d5367437 --- /dev/null +++ b/spec/features/admin/business_model_configuration_spec.rb @@ -0,0 +1,52 @@ +require 'spec_helper' + +feature 'Business Model Configuration' do + include AuthenticationWorkflow + include WebHelper + + describe "updating" do + let!(:admin) { create(:admin_user) } + + before do + Spree::Config.set({ + account_invoices_monthly_fixed: 5, + account_invoices_monthly_rate: 0.02, + account_invoices_monthly_cap: 50, + account_invoices_tax_rate: 0.1 + }) + end + + before do + quick_login_as_admin + end + + context "as an admin user", js: true do + it "loads the page" do + visit spree.admin_path + click_link "Configuration" + click_link "Business Model" + + expect(page).to have_field "settings_account_invoices_monthly_fixed", with: 5.0 + expect(page).to have_field "settings_account_invoices_monthly_rate", with: 0.02 + expect(page).to have_field "settings_account_invoices_monthly_cap", with: 50.0 + expect(page).to have_field "settings_account_invoices_tax_rate", with: 0.1 + end + + it "attributes can be changed", js: true do + visit edit_admin_business_model_configuration_path + + fill_in "settings_account_invoices_monthly_fixed", with: 10 + fill_in "settings_account_invoices_monthly_rate", with: 0.05 + fill_in "settings_account_invoices_monthly_cap", with: 30 + fill_in "settings_account_invoices_tax_rate", with: 0.15 + + click_button "Update" + + expect(Spree::Config.account_invoices_monthly_fixed).to eq 10 + expect(Spree::Config.account_invoices_monthly_rate).to eq 0.05 + expect(Spree::Config.account_invoices_monthly_cap).to eq 30 + expect(Spree::Config.account_invoices_tax_rate).to eq 0.15 + end + end + end +end diff --git a/spec/features/admin/cms_spec.rb b/spec/features/admin/cms_spec.rb index e3dfcf19b9..2d4056b792 100644 --- a/spec/features/admin/cms_spec.rb +++ b/spec/features/admin/cms_spec.rb @@ -21,7 +21,7 @@ feature %q{ scenario "anonymous user can't access CMS admin", js: true do visit cms_admin_path page.should_not have_content "ComfortableMexicanSofa" - page.should have_content "Log in" + page.should have_content "Login" end scenario "non-admin user can't access CMS admin", js: true do diff --git a/spec/features/admin/content_spec.rb b/spec/features/admin/content_spec.rb new file mode 100644 index 0000000000..073ae35e3c --- /dev/null +++ b/spec/features/admin/content_spec.rb @@ -0,0 +1,40 @@ +require 'spec_helper' + +feature %q{ + As a site administrator + I want to configure the site content +} do + include AuthenticationWorkflow + include WebHelper + + before do + login_to_admin_section + click_link 'Configuration' + click_link 'Content' + end + + scenario "filling in a setting shows the result on the home page" do + fill_in 'footer_facebook_url', with: '' + fill_in 'footer_twitter_url', with: 'http://twitter.com/me' + fill_in 'footer_links_md', with: '[markdown link](/)' + click_button 'Update' + page.should have_content 'Your content has been successfully updated!' + + visit root_path + + # Then social media icons are only shown if they have a value + page.should_not have_selector 'i.ofn-i_044-facebook' + page.should have_selector 'i.ofn-i_041-twitter' + + # And markdown is rendered + page.should have_link 'markdown link' + end + + scenario "uploading logos" do + attach_file 'logo', "#{Rails.root}/app/assets/images/logo-white.png" + click_button 'Update' + page.should have_content 'Your content has been successfully updated!' + + ContentConfig.logo.to_s.should include "logo-white" + end +end diff --git a/spec/features/admin/customers_spec.rb b/spec/features/admin/customers_spec.rb new file mode 100644 index 0000000000..5d2ffedb4e --- /dev/null +++ b/spec/features/admin/customers_spec.rb @@ -0,0 +1,71 @@ +require 'spec_helper' + +feature 'Customers' do + include AuthenticationWorkflow + include WebHelper + + context "as an enterprise user" do + let(:user) { create_enterprise_user } + let(:managed_distributor) { create(:distributor_enterprise, owner: user) } + let(:unmanaged_distributor) { create(:distributor_enterprise) } + + describe "using the customers index" do + let!(:customer1) { create(:customer, enterprise: managed_distributor) } + let!(:customer2) { create(:customer, enterprise: managed_distributor) } + let!(:customer3) { create(:customer, enterprise: unmanaged_distributor) } + + before do + quick_login_as user + visit admin_customers_path + end + + it "passes the smoke test", js: true do + # Prompts for a hub for a list of my managed enterprises + expect(page).to have_select2 "shop_id", with_options: [managed_distributor.name], without_options: [unmanaged_distributor.name] + + select2_select managed_distributor.name, from: "shop_id" + + # Loads the right customers + expect(page).to have_selector "tr#c_#{customer1.id}" + expect(page).to have_selector "tr#c_#{customer2.id}" + expect(page).to_not have_selector "tr#c_#{customer3.id}" + + # Searching + fill_in "quick_search", with: customer2.email + expect(page).to_not have_selector "tr#c_#{customer1.id}" + expect(page).to have_selector "tr#c_#{customer2.id}" + fill_in "quick_search", with: "" + + # Toggling columns + expect(page).to have_selector "th.email" + expect(page).to have_content customer1.email + first("div#columns-dropdown", :text => "COLUMNS").click + first("div#columns-dropdown div.menu div.menu_item", text: "Email").click + expect(page).to_not have_selector "th.email" + expect(page).to_not have_content customer1.email + end + + it "allows updating of attributes", js: true do + select2_select managed_distributor.name, from: "shop_id" + + within "tr#c_#{customer1.id}" do + fill_in "code", with: "new-customer-code" + expect(page).to have_css "input#code.update-pending" + end + within "tr#c_#{customer1.id}" do + find(:css, "tags-input .tags input").set "awesome\n" + expect(page).to have_css ".tag_watcher.update-pending" + end + click_button "Update" + + # Every says it updated + expect(page).to have_css "input#code.update-success" + expect(page).to have_css ".tag_watcher.update-success" + + # And it actually did + expect(customer1.reload.code).to eq "new-customer-code" + expect(customer1.tag_list).to eq ["awesome"] + end + end + end +end diff --git a/spec/features/admin/enterprise_fees_spec.rb b/spec/features/admin/enterprise_fees_spec.rb index fe55d8e90b..d462a3d809 100644 --- a/spec/features/admin/enterprise_fees_spec.rb +++ b/spec/features/admin/enterprise_fees_spec.rb @@ -139,7 +139,9 @@ feature %q{ ef2 click_link 'Enterprises' - within(".enterprise-#{distributor1.id}") { click_link 'Enterprise Fees' } + within("#e_#{distributor1.id}") { click_link 'Manage' } + within(".side_menu") { click_link 'Enterprise Fees' } + click_link "Create One Now" select distributor1.name, :from => 'enterprise_fee_set_collection_attributes_0_enterprise_id' fill_in 'enterprise_fee_set_collection_attributes_0_name', :with => 'foo' @@ -156,17 +158,21 @@ feature %q{ enterprise_fee.enterprise.should == distributor1 end - it "shows me only enterprise fees for the enterprise I select" do + pending "shows me only enterprise fees for the enterprise I select" do ef1 ef2 click_link 'Enterprises' - within(".enterprise-#{distributor1.id}") { click_link 'Enterprise Fees' } + within("#e_#{distributor1.id}") { click_link 'Manage' } + within(".side_menu") { click_link 'Enterprise Fees' } + click_link "Manage Enterprise Fees" page.should have_field 'enterprise_fee_set_collection_attributes_0_name', with: 'One' page.should_not have_field 'enterprise_fee_set_collection_attributes_1_name', with: 'Two' click_link 'Enterprises' - within(".enterprise-#{distributor2.id}") { click_link 'Enterprise Fees' } + within("#e_#{distributor2.id}") { click_link 'Manage' } + within(".side_menu") { click_link 'Enterprise Fees' } + click_link "Manage Enterprise Fees" page.should_not have_field 'enterprise_fee_set_collection_attributes_0_name', with: 'One' page.should have_field 'enterprise_fee_set_collection_attributes_0_name', with: 'Two' end @@ -177,7 +183,9 @@ feature %q{ distributor3 click_link 'Enterprises' - within(".enterprise-#{distributor2.id}") { click_link 'Enterprise Fees' } + within("#e_#{distributor2.id}") { click_link 'Manage' } + within(".side_menu") { click_link 'Enterprise Fees' } + click_link "Manage Enterprise Fees" page.should have_select('enterprise_fee_set_collection_attributes_1_enterprise_id', selected: 'Second Distributor', options: ['', 'First Distributor', 'Second Distributor']) diff --git a/spec/features/admin/enterprises/index_spec.rb b/spec/features/admin/enterprises/index_spec.rb new file mode 100644 index 0000000000..5786e7a75b --- /dev/null +++ b/spec/features/admin/enterprises/index_spec.rb @@ -0,0 +1,214 @@ +require 'spec_helper' + +feature 'Enterprises Index' do + include AuthenticationWorkflow + include WebHelper + + context "as an admin user" do + scenario "listing enterprises" do + s = create(:supplier_enterprise) + d = create(:distributor_enterprise) + + login_to_admin_section + click_link 'Enterprises' + + within("tr.enterprise-#{s.id}") do + expect(page).to have_content s.name + expect(page).to have_select "enterprise_set_collection_attributes_1_sells" + expect(page).to have_content "Edit Profile" + expect(page).to have_content "Delete" + expect(page).to_not have_content "Payment Methods" + expect(page).to_not have_content "Shipping Methods" + expect(page).to have_content "Enterprise Fees" + end + + within("tr.enterprise-#{d.id}") do + expect(page).to have_content d.name + expect(page).to have_select "enterprise_set_collection_attributes_0_sells" + expect(page).to have_content "Edit Profile" + expect(page).to have_content "Delete" + expect(page).to have_content "Payment Methods" + expect(page).to have_content "Shipping Methods" + expect(page).to have_content "Enterprise Fees" + end + end + + context "editing enterprises in bulk" do + let!(:s){ create(:supplier_enterprise) } + let!(:d){ create(:distributor_enterprise, sells: 'none') } + let!(:d_manager) { create_enterprise_user(enterprise_limit: 1) } + + before do + d_manager.enterprise_roles.build(enterprise: d).save + expect(d.owner).to_not eq d_manager + end + + context "without violating rules" do + before do + login_to_admin_section + click_link 'Enterprises' + end + + it "updates the enterprises" do + within("tr.enterprise-#{d.id}") do + expect(page).to have_checked_field "enterprise_set_collection_attributes_0_visible" + uncheck "enterprise_set_collection_attributes_0_visible" + select 'any', from: "enterprise_set_collection_attributes_0_sells" + select d_manager.email, from: 'enterprise_set_collection_attributes_0_owner_id' + end + click_button "Update" + flash_message.should == 'Enterprises updated successfully' + distributor = Enterprise.find(d.id) + expect(distributor.visible).to eq false + expect(distributor.sells).to eq 'any' + expect(distributor.owner).to eq d_manager + end + end + + context "with data that violates rules" do + let!(:second_distributor) { create(:distributor_enterprise, sells: 'none') } + + before do + d_manager.enterprise_roles.build(enterprise: second_distributor).save + expect(d.owner).to_not eq d_manager + + login_to_admin_section + click_link 'Enterprises' + end + + it "does not update the enterprises and displays errors" do + within("tr.enterprise-#{d.id}") do + select d_manager.email, from: 'enterprise_set_collection_attributes_0_owner_id' + end + within("tr.enterprise-#{second_distributor.id}") do + select d_manager.email, from: 'enterprise_set_collection_attributes_1_owner_id' + end + click_button "Update" + flash_message.should == 'Update failed' + expect(page).to have_content "#{d_manager.email} is not permitted to own any more enterprises (limit is 1)." + second_distributor.reload + expect(second_distributor.owner).to_not eq d_manager + end + end + end + end + + describe "as the manager of an enterprise" do + let(:supplier1) { create(:supplier_enterprise, name: 'First Supplier') } + let(:supplier2) { create(:supplier_enterprise, name: 'Another Supplier') } + let(:distributor1) { create(:distributor_enterprise, name: 'First Distributor') } + let(:distributor2) { create(:distributor_enterprise, name: 'Another Distributor') } + let(:distributor3) { create(:distributor_enterprise, name: 'Yet Another Distributor') } + let(:enterprise_manager) { create_enterprise_user } + let!(:er) { create(:enterprise_relationship, parent: distributor3, child: distributor1, permissions_list: [:edit_profile]) } + + before(:each) do + enterprise_manager.enterprise_roles.build(enterprise: supplier1).save + enterprise_manager.enterprise_roles.build(enterprise: distributor1).save + + login_to_admin_as enterprise_manager + end + + context "listing enterprises", js: true do + it "displays enterprises I have permission to manage" do + click_link "Enterprises" + + within("tbody#e_#{distributor1.id}") do + expect(page).to have_content distributor1.name + expect(page).to have_selector "td.producer", text: 'Non-Producer' + expect(page).to have_selector "td.package", text: 'Hub' + end + + within("tbody#e_#{distributor3.id}") do + expect(page).to have_content distributor3.name + expect(page).to have_selector "td.producer", text: 'Non-Producer' + expect(page).to have_selector "td.package", text: 'Hub' + end + + within("tbody#e_#{supplier1.id}") do + expect(page).to have_content supplier1.name + expect(page).to have_selector "td.producer", text: 'Producer' + expect(page).to have_selector "td.package", text: 'Profile' + end + + expect(page).to_not have_content "supplier2.name" + expect(page).to_not have_content "distributor2.name" + + expect(find("#content-header")).to have_link "New Enterprise" + end + + + it "does not give me an option to change or update the package and producer properties of enterprises I manage" do + click_link "Enterprises" + + within("tbody#e_#{distributor1.id}") do + find("td.producer").click + expect(page).to have_selector "a.selector.producer.disabled" + find("a.selector.producer.disabled").click + expect(page).to have_selector "a.selector.non-producer.selected.disabled" + expect(page).to_not have_selector "a.update" + find("td.package").click + expect(page).to have_selector "a.selector.hub-profile.disabled" + find("a.selector.hub-profile.disabled").click + expect(page).to have_selector "a.selector.hub.selected.disabled" + expect(page).to_not have_selector "a.update" + end + end + end + end + + describe "as the owner of an enterprise" do + let!(:user) { create_enterprise_user } + let!(:owned_distributor) { create(:distributor_enterprise, name: 'Owned Distributor', owner: user) } + + before do + login_to_admin_as user + end + + context "listing enterprises", js: true do + it "allows me to change or update the package and producer properties of enterprises I manage" do + click_link "Enterprises" + + within("tbody#e_#{owned_distributor.id}") do + # Open the producer panel + find("td.producer").click + + expect(page).to_not have_selector "a.selector.producer.selected" + expect(page).to have_selector "a.selector.non-producer.selected" + + # Change to a producer + find("a.selector.producer").click + + expect(page).to_not have_selector "a.selector.non-producer.selected" + expect(page).to have_selector "a.selector.producer.selected" + expect(page).to have_selector "a.update", text: "SAVE" + + # Save selection + find('a.update').click + expect(page).to have_selector "a.update", text: "SAVED" + expect(owned_distributor.reload.is_primary_producer).to eq true + + # Open the package panel + find("td.package").click + + expect(page).to_not have_selector "a.selector.producer-profile.selected" + expect(page).to_not have_selector "a.selector.producer-shop.selected" + expect(page).to have_selector "a.selector.producer-hub.selected" + + # Change to a producer-shop + find("a.selector.producer-shop").click + + expect(page).to_not have_selector "a.selector.producer-profile.selected" + expect(page).to have_selector "a.selector.producer-shop.selected" + expect(page).to_not have_selector "a.selector.producer-hub.selected" + expect(page).to have_selector "a.update", text: "SAVE" + + # Save selection + find('a.update').click + expect(page).to have_selector "a.update", text: "SAVED" + expect(owned_distributor.reload.sells).to eq "own" + end + end + end + end +end diff --git a/spec/features/admin/enterprises_spec.rb b/spec/features/admin/enterprises_spec.rb index 395f882b51..aed96b5a91 100644 --- a/spec/features/admin/enterprises_spec.rb +++ b/spec/features/admin/enterprises_spec.rb @@ -7,93 +7,6 @@ feature %q{ include AuthenticationWorkflow include WebHelper - scenario "listing enterprises" do - s = create(:supplier_enterprise) - d = create(:distributor_enterprise) - - login_to_admin_section - click_link 'Enterprises' - - within("tr.enterprise-#{s.id}") do - expect(page).to have_content s.name - expect(page).to have_select "enterprise_set_collection_attributes_1_sells" - expect(page).to have_content "Edit Profile" - expect(page).to have_content "Delete" - expect(page).to_not have_content "Payment Methods" - expect(page).to_not have_content "Shipping Methods" - expect(page).to have_content "Enterprise Fees" - end - - within("tr.enterprise-#{d.id}") do - expect(page).to have_content d.name - expect(page).to have_select "enterprise_set_collection_attributes_0_sells" - expect(page).to have_content "Edit Profile" - expect(page).to have_content "Delete" - expect(page).to have_content "Payment Methods" - expect(page).to have_content "Shipping Methods" - expect(page).to have_content "Enterprise Fees" - end - end - - context "editing enterprises in bulk" do - let!(:s){ create(:supplier_enterprise) } - let!(:d){ create(:distributor_enterprise, sells: 'none') } - let!(:d_manager) { create_enterprise_user(enterprise_limit: 1) } - - before do - d_manager.enterprise_roles.build(enterprise: d).save - expect(d.owner).to_not eq d_manager - end - - context "without violating rules" do - before do - login_to_admin_section - click_link 'Enterprises' - end - - it "updates the enterprises" do - within("tr.enterprise-#{d.id}") do - expect(page).to have_checked_field "enterprise_set_collection_attributes_0_visible" - uncheck "enterprise_set_collection_attributes_0_visible" - select 'any', from: "enterprise_set_collection_attributes_0_sells" - select d_manager.email, from: 'enterprise_set_collection_attributes_0_owner_id' - end - click_button "Update" - flash_message.should == 'Enterprises updated successfully' - distributor = Enterprise.find(d.id) - expect(distributor.visible).to eq false - expect(distributor.sells).to eq 'any' - expect(distributor.owner).to eq d_manager - end - end - - context "with data that violates rules" do - let!(:second_distributor) { create(:distributor_enterprise, sells: 'none') } - - before do - d_manager.enterprise_roles.build(enterprise: second_distributor).save - expect(d.owner).to_not eq d_manager - - login_to_admin_section - click_link 'Enterprises' - end - - it "does not update the enterprises and displays errors" do - within("tr.enterprise-#{d.id}") do - select d_manager.email, from: 'enterprise_set_collection_attributes_0_owner_id' - end - within("tr.enterprise-#{second_distributor.id}") do - select d_manager.email, from: 'enterprise_set_collection_attributes_1_owner_id' - end - click_button "Update" - flash_message.should == 'Update failed' - expect(page).to have_content "#{d_manager.email} is not permitted to own any more enterprises (limit is 1)." - second_distributor.reload - expect(second_distributor.owner).to_not eq d_manager - end - end - end - scenario "viewing an enterprise" do e = create(:enterprise) @@ -165,6 +78,8 @@ feature %q{ end fill_in 'enterprise_name', :with => 'Eaterprises' + fill_in 'enterprise_permalink', with: 'eaterprises-permalink' + page.should have_selector '.available' choose 'Own' within (".side_menu") { click_link "Users" } @@ -172,8 +87,14 @@ feature %q{ click_link "About" fill_in 'enterprise_description', :with => 'Connecting farmers and eaters' - long_description = find :css, "text-angular#enterprise_long_description div.ta-scroll-window div.ta-bind" - long_description.set 'This is an interesting long description' + + # TODO: Directly altering the text in the contenteditable div like this started breaking with the upgrade + # of Poltergeist from 1.5 to 1.7. Probably requires an upgrade of AngularJS and/or TextAngular + # long_description = find :css, "text-angular#enterprise_long_description div.ta-scroll-window div.ta-bind" + # long_description.set 'This is an interesting long description' + # long_description.native.send_keys(:Enter) # Sets the value + + page.first("input[name='enterprise\[long_description\]']", visible: false).set('This is an interesting long description') # Check Angularjs switching of sidebar elements click_link "Primary Details" @@ -228,8 +149,12 @@ feature %q{ select2_search 'Victoria', :from => 'State' click_link "Shop Preferences" - shopfront_message = find :css, "text-angular#enterprise_preferred_shopfront_message div.ta-scroll-window div.ta-bind" - shopfront_message.set 'This is my shopfront message.' + # TODO: Same as above + # shopfront_message = find :css, "text-angular#enterprise_preferred_shopfront_message div.ta-scroll-window div.ta-bind" + # shopfront_message.set 'This is my shopfront message.' + page.first("input[name='enterprise\[preferred_shopfront_message\]']", visible: false).set('This is my shopfront message.') + page.should have_checked_field "enterprise_preferred_shopfront_order_cycle_order_orders_close_at" + choose "enterprise_preferred_shopfront_order_cycle_order_orders_open_at" click_button 'Update' @@ -255,6 +180,7 @@ feature %q{ click_link "Shop Preferences" page.should have_content 'This is my shopfront message.' + page.should have_checked_field "enterprise_preferred_shopfront_order_cycle_order_orders_open_at" end describe "producer properties" do @@ -325,56 +251,23 @@ feature %q{ end - context "as an Enterprise user" do + context "as an Enterprise user", js: true do let(:supplier1) { create(:supplier_enterprise, name: 'First Supplier') } let(:supplier2) { create(:supplier_enterprise, name: 'Another Supplier') } let(:distributor1) { create(:distributor_enterprise, name: 'First Distributor') } let(:distributor2) { create(:distributor_enterprise, name: 'Another Distributor') } let(:distributor3) { create(:distributor_enterprise, name: 'Yet Another Distributor') } let(:enterprise_user) { create_enterprise_user } - let(:er) { create(:enterprise_relationship, parent: distributor3, child: distributor1, permissions_list: [:edit_profile]) } + let!(:er) { create(:enterprise_relationship, parent: distributor3, child: distributor1, permissions_list: [:edit_profile]) } before(:each) do enterprise_user.enterprise_roles.build(enterprise: supplier1).save enterprise_user.enterprise_roles.build(enterprise: distributor1).save - er login_to_admin_as enterprise_user end - context "listing enterprises" do - scenario "displays enterprises I have permission to manage" do - oc_user_coordinating = create(:simple_order_cycle, { coordinator: supplier1, name: 'Order Cycle 1' } ) - oc_for_other_user = create(:simple_order_cycle, { coordinator: supplier2, name: 'Order Cycle 2' } ) - - click_link "Enterprises" - - within("tr.enterprise-#{distributor1.id}") do - expect(page).to have_content distributor1.name - expect(page).to have_unchecked_field "enterprise_set_collection_attributes_0_is_primary_producer" - expect(page).to_not have_select "enterprise_set_collection_attributes_0_sells" - end - - within("tr.enterprise-#{distributor3.id}") do - expect(page).to have_content distributor3.name - expect(page).to have_unchecked_field "enterprise_set_collection_attributes_1_is_primary_producer" - expect(page).to_not have_select "enterprise_set_collection_attributes_1_sells" - end - - within("tr.enterprise-#{supplier1.id}") do - expect(page).to have_content supplier1.name - expect(page).to have_checked_field "enterprise_set_collection_attributes_2_is_primary_producer" - expect(page).to_not have_select "enterprise_set_collection_attributes_2_sells" - end - - expect(page).to_not have_content "supplier2.name" - expect(page).to_not have_content "distributor2.name" - - expect(find("#content-header")).to have_link "New Enterprise" - end - end - - context "when I have reached my enterprise ownership limit", js: true do + context "when I have reached my enterprise ownership limit" do it "does not display the link to create a new enterprise" do supplier1.reload enterprise_user.owned_enterprises.push [supplier1] @@ -428,7 +321,7 @@ feature %q{ scenario "editing enterprises I manage" do click_link 'Enterprises' - within("#listing_enterprises tr.enterprise-#{distributor1.id}") { click_link 'Edit Profile' } + within("tbody#e_#{distributor1.id}") { click_link 'Manage' } fill_in 'enterprise_name', :with => 'Eaterprises' click_button 'Update' @@ -440,7 +333,7 @@ feature %q{ describe "enterprises I have edit permission for, but do not manage" do it "allows me to edit them" do click_link 'Enterprises' - within("#listing_enterprises tr.enterprise-#{distributor3.id}") { click_link 'Edit Profile' } + within("tbody#e_#{distributor3.id}") { click_link 'Manage' } fill_in 'enterprise_name', :with => 'Eaterprises' click_button 'Update' @@ -449,18 +342,9 @@ feature %q{ distributor3.reload.name.should == 'Eaterprises' end - it "does not show links to manage shipping methods, payment methods or enterprise fees" do + it "does not show links to manage shipping methods, payment methods or enterprise fees on the edit page" do click_link 'Enterprises' - within("#listing_enterprises tr.enterprise-#{distributor3.id}") do - page.should_not have_link 'Shipping Methods' - page.should_not have_link 'Payment Methods' - page.should_not have_link 'Enterprise Fees' - end - end - - it "does not show links to manage shipping methods, payment methods or enterprise fees on the edit page", js: true do - click_link 'Enterprises' - within("#listing_enterprises tr.enterprise-#{distributor3.id}") { click_link 'Edit Profile' } + within("tbody#e_#{distributor3.id}") { click_link 'Manage' } within(".side_menu") do page.should_not have_link 'Shipping Methods' @@ -472,26 +356,35 @@ feature %q{ scenario "editing images for an enterprise" do click_link 'Enterprises' - first(".edit").click - page.should have_content "Logo" - page.should have_content "Promo" + within("tbody#e_#{distributor1.id}") { click_link 'Manage' } + + within(".side_menu") do + click_link "Images" + end + + page.should have_content "LOGO" + page.should have_content "PROMO" end - scenario "managing producer properties", js: true do + scenario "managing producer properties" do create(:property, name: "Certified Organic") click_link 'Enterprises' - within(".enterprise-#{supplier1.id}") { click_link 'Properties' } + within("#e_#{supplier1.id}") { click_link 'Manage' } + within(".side_menu") do + click_link "Properties" + end # -- Update only select2_select "Certified Organic", from: 'enterprise_producer_properties_attributes_0_property_name' fill_in 'enterprise_producer_properties_attributes_0_value', with: "NASAA 12345" click_button 'Update' - page.should have_selector '#listing_enterprises a', text: supplier1.name supplier1.producer_properties(true).count.should == 1 # -- Destroy pp = supplier1.producer_properties.first - within(".enterprise-#{supplier1.id}") { click_link 'Properties' } + within(".side_menu") do + click_link "Properties" + end within("#spree_producer_property_#{pp.id}") { page.find('a.remove_fields').click } page.should_not have_selector '#progress' diff --git a/spec/features/admin/order_cycles_spec.rb b/spec/features/admin/order_cycles_spec.rb index 52b00a4865..aafaa5ee77 100644 --- a/spec/features/admin/order_cycles_spec.rb +++ b/spec/features/admin/order_cycles_spec.rb @@ -95,13 +95,23 @@ feature %q{ click_button 'Add coordinator fee' select 'Coord fee', from: 'order_cycle_coordinator_fee_0_id' + # I should not be able to add a blank supplier + page.should have_select 'new_supplier_id', selected: '' + page.should have_button 'Add supplier', disabled: true + # And I add a supplier and some products select 'My supplier', from: 'new_supplier_id' click_button 'Add supplier' + fill_in 'order_cycle_incoming_exchange_0_receival_instructions', with: 'receival instructions' page.find('table.exchanges tr.supplier td.products input').click check "order_cycle_incoming_exchange_0_variants_#{v1.id}" check "order_cycle_incoming_exchange_0_variants_#{v2.id}" + # I should not be able to re-add the supplier + page.should_not have_select 'new_supplier_id', with_options: ['My supplier'] + page.should have_button 'Add supplier', disabled: true + page.all("td.supplier_name").map(&:text).should == ['My supplier'] + # And I add a supplier fee within("tr.supplier-#{supplier.id}") { click_button 'Add fee' } select 'My supplier', from: 'order_cycle_incoming_exchange_0_enterprise_fees_0_enterprise_id' @@ -148,8 +158,11 @@ feature %q{ oc.exchanges.first.variants.count.should == 2 oc.exchanges.last.variants.count.should == 2 - # And my pickup time and instructions should have been saved - exchange = oc.exchanges.where(:sender_id => oc.coordinator_id).first + # And my receival and pickup time and instructions should have been saved + exchange = oc.exchanges.incoming.first + exchange.receival_instructions.should == 'receival instructions' + + exchange = oc.exchanges.outgoing.first exchange.pickup_time.should == 'pickup time' exchange.pickup_instructions.should == 'pickup instructions' end @@ -158,6 +171,10 @@ feature %q{ scenario "editing an order cycle" do # Given an order cycle with all the settings oc = create(:order_cycle) + oc.suppliers.first.update_attribute :name, 'AAA' + oc.suppliers.last.update_attribute :name, 'ZZZ' + oc.distributors.first.update_attribute :name, 'AAAA' + oc.distributors.last.update_attribute :name, 'ZZZZ' # When I edit it login_to_admin_section @@ -175,6 +192,9 @@ feature %q{ page.should have_selector 'td.supplier_name', :text => oc.suppliers.first.name page.should have_selector 'td.supplier_name', :text => oc.suppliers.last.name + page.should have_field 'order_cycle_incoming_exchange_0_receival_instructions', with: 'instructions 0' + page.should have_field 'order_cycle_incoming_exchange_1_receival_instructions', with: 'instructions 1' + # And the suppliers should have products page.all('table.exchanges tbody tr.supplier').each do |row| row.find('td.products input').click @@ -339,7 +359,7 @@ feature %q{ select 'Distributor fee 2', from: 'order_cycle_outgoing_exchange_2_enterprise_fees_0_enterprise_fee_id' # And I click Update - click_button 'Update' + click_button 'Update and Close' # Then my order cycle should have been updated page.should have_content 'Your order cycle has been updated.' @@ -879,7 +899,10 @@ feature %q{ click_button 'Add coordinator fee' select 'that fee', from: 'order_cycle_coordinator_fee_0_id' + # When I update, or update and close, both work click_button 'Update' + page.should have_content 'Your order cycle has been updated.' + click_button 'Update and Close' # Then my order cycle should have been updated page.should have_content 'Your order cycle has been updated.' diff --git a/spec/features/admin/orders_spec.rb b/spec/features/admin/orders_spec.rb index c2a302fcbb..725495b74b 100644 --- a/spec/features/admin/orders_spec.rb +++ b/spec/features/admin/orders_spec.rb @@ -1,17 +1,17 @@ require "spec_helper" feature %q{ - As a payment administrator - I want to capture multiple payments quickly from the one page -} do + As an administrator + I want to manage orders +}, js: true do include AuthenticationWorkflow include WebHelper background do @user = create(:user) @product = create(:simple_product) - @distributor = create(:distributor_enterprise) - @order_cycle = create(:simple_order_cycle, distributors: [@distributor], variants: [@product.variants.first]) + @distributor = create(:distributor_enterprise, charges_sales_tax: true) + @order_cycle = create(:simple_order_cycle, name: 'One', distributors: [@distributor], variants: [@product.variants.first]) @order = create(:order_with_totals_and_distribution, user: @user, distributor: @distributor, order_cycle: @order_cycle, state: 'complete', payment_state: 'balance_due') @@ -21,46 +21,75 @@ feature %q{ create :check_payment, order: @order, amount: @order.total end - scenario "creating an order with distributor and order cycle", js: true do - order_cycle = create(:order_cycle) - distributor = order_cycle.distributors.first - product = order_cycle.products.first + scenario "creating an order with distributor and order cycle", retry: 3 do + distributor_disabled = create(:distributor_enterprise) + create(:simple_order_cycle, name: 'Two') login_to_admin_section visit '/admin/orders' click_link 'New Order' + # Distributors without an order cycle should be shown as disabled + page.should have_selector "option[value='#{distributor_disabled.id}'][disabled='disabled']" + + # When we select a distributor, it should limit order cycle selection to those for that distributor + page.should_not have_select2 'order_order_cycle_id' + select @distributor.name, from: 'order_distributor_id' + page.should have_select2 'order_order_cycle_id', options: ['', 'One (open)'] + select2_select @order_cycle.name, from: 'order_order_cycle_id' + page.should have_content 'ADD PRODUCT' - targetted_select2_search product.name, from: '#add_variant_id', dropdown_css: '.select2-drop' + targetted_select2_search @product.name, from: '#add_variant_id', dropdown_css: '.select2-drop' click_link 'Add' page.has_selector? "table.index tbody[data-hook='admin_order_form_line_items'] tr" # Wait for JS - page.should have_selector 'td', text: product.name + page.should have_selector 'td', text: @product.name - select distributor.name, from: 'order_distributor_id' - select order_cycle.name, from: 'order_order_cycle_id' click_button 'Update' page.should have_selector 'h1', text: 'Customer Details' o = Spree::Order.last - o.distributor.should == distributor - o.order_cycle.should == order_cycle + o.distributor.should == @distributor + o.order_cycle.should == @order_cycle end - scenario "can add a product to an existing order", js: true do + scenario "can add a product to an existing order", retry: 3 do login_to_admin_section visit '/admin/orders' - page.find('td.actions a.icon-edit').click - targetted_select2_search @product.name, from: ".variant_autocomplete", dropdown_css: ".select2-search" + click_edit - click_icon :plus + targetted_select2_search @product.name, from: '#add_variant_id', dropdown_css: '.select2-drop' + + click_link 'Add' page.should have_selector 'td', text: @product.name @order.line_items(true).map(&:product).should include @product end - scenario "can't add products to an order outside the order's hub and order cycle", js: true do + scenario "displays error when incorrect distribution for products is chosen" do + d = create(:distributor_enterprise) + oc = create(:simple_order_cycle, distributors: [d]) + + @order.state = 'cart'; @order.completed_at = nil; @order.save + + login_to_admin_section + visit '/admin/orders' + uncheck 'Only show complete orders' + click_button 'Filter Results' + + click_edit + + select d.name, from: 'order_distributor_id' + select2_select oc.name, from: 'order_order_cycle_id' + + click_button 'Update And Recalculate Fees' + + page.should have_content "Distributor or order cycle cannot supply the products in your cart" + end + + + scenario "can't add products to an order outside the order's hub and order cycle" do product = create(:simple_product) login_to_admin_section @@ -78,12 +107,42 @@ feature %q{ page.find('td.actions a.icon-edit').click page.should have_no_select 'order_distributor_id' - page.should have_no_select 'order_order_cycle_id' + page.should_not have_select2 'order_order_cycle_id' page.should have_selector 'p', text: "Distributor: #{@order.distributor.name}" page.should have_selector 'p', text: "Order cycle: None" end + scenario "filling customer details" do + # Given a customer with an order, which includes their shipping and billing address + @order.ship_address = create(:address, lastname: 'Ship') + @order.bill_address = create(:address, lastname: 'Bill') + @order.shipping_method = create(:shipping_method, require_ship_address: true) + @order.save! + + # When I create a new order + login_to_admin_section + visit '/admin/orders' + click_link 'New Order' + select @distributor.name, from: 'order_distributor_id' + select2_select @order_cycle.name, from: 'order_order_cycle_id' + targetted_select2_search @product.name, from: '#add_variant_id', dropdown_css: '.select2-drop' + click_link 'Add' + page.has_selector? "table.index tbody[data-hook='admin_order_form_line_items'] tr" # Wait for JS + click_button 'Update' + within('h1.page-title') { page.should have_content "Customer Details" } + + # And I select that customer's email address and save the order + targetted_select2_search @order.user.email, from: '#customer_search', dropdown_css: '.select2-drop' + click_button 'Continue' + within('h1.page-title') { page.should have_content "Shipments" } + + # Then their addresses should be associated with the order + order = Spree::Order.last + order.ship_address.lastname.should == 'Ship' + order.bill_address.lastname.should == 'Bill' + end + scenario "capture multiple payments from the orders index page" do # d.cook: could also test for an order that has had payment voided, then a new check payment created but not yet captured. But it's not critical and I know it works anyway. @@ -105,6 +164,7 @@ feature %q{ current_path.should == spree.admin_orders_path end + context "as an enterprise manager" do let(:coordinator1) { create(:distributor_enterprise) } let(:coordinator2) { create(:distributor_enterprise) } @@ -125,10 +185,32 @@ feature %q{ login_to_admin_as @enterprise_user end - scenario "creating an order with distributor and order cycle", js: true do + context "viewing the edit page" do + before { Rails.application.routes.default_url_options[:host] = "test.host" } + it "shows the dropdown menu" do + distributor1.update_attribute(:abn, '12345678') + order = create(:completed_order_with_totals, distributor: distributor1) + visit spree.admin_order_path(order) + + find("#links-dropdown .ofn-drop-down").click + within "#links-dropdown" do + expect(page).to have_link "Edit", href: spree.edit_admin_order_path(order) + expect(page).to have_link "Resend Confirmation", href: spree.resend_admin_order_path(order) + expect(page).to have_link "Send Invoice", href: spree.invoice_admin_order_path(order) + expect(page).to have_link "Print Invoice", href: spree.print_admin_order_path(order) + # expect(page).to have_link "Ship Order", href: spree.fire_admin_order_path(order, :e => 'ship') + expect(page).to have_link "Cancel Order", href: spree.fire_admin_order_path(order, :e => 'cancel') + end + end + end + + scenario "creating an order with distributor and order cycle" do visit '/admin/orders' click_link 'New Order' + select distributor1.name, from: 'order_distributor_id' + select2_select order_cycle1.name, from: 'order_order_cycle_id' + expect(page).to have_content 'ADD PRODUCT' targetted_select2_search product.name, from: '#add_variant_id', dropdown_css: '.select2-drop' @@ -139,11 +221,9 @@ feature %q{ expect(page).to have_select 'order_distributor_id', with_options: [distributor1.name] expect(page).to_not have_select 'order_distributor_id', with_options: [distributor2.name] - expect(page).to have_select 'order_order_cycle_id', with_options: [order_cycle1.name] - expect(page).to_not have_select 'order_order_cycle_id', with_options: [order_cycle2.name] + expect(page).to have_select2 'order_order_cycle_id', with_options: ["#{order_cycle1.name} (open)"] + expect(page).to_not have_select2 'order_order_cycle_id', with_options: ["#{order_cycle2.name} (open)"] - select distributor1.name, from: 'order_distributor_id' - select order_cycle1.name, from: 'order_order_cycle_id' click_button 'Update' expect(page).to have_selector 'h1', text: 'Customer Details' @@ -153,4 +233,21 @@ feature %q{ end end + + # Working around intermittent click failing + # Possible causes of failure: + # - the link moves + # - the missing content (font icon only) + # - the screen is not big enough + # However, some operations before the click or a second click on failure work. + # + # A lot of people had similar problems: + # https://github.com/teampoltergeist/poltergeist/issues/520 + # https://github.com/thoughtbot/capybara-webkit/issues/494 + def click_edit + click_result = click_icon :edit + unless click_result['status'] == 'success' + click_icon :edit + end + end end diff --git a/spec/features/admin/payment_method_spec.rb b/spec/features/admin/payment_method_spec.rb index 36fe344930..aa7caa2e0b 100644 --- a/spec/features/admin/payment_method_spec.rb +++ b/spec/features/admin/payment_method_spec.rb @@ -20,12 +20,12 @@ feature %q{ click_link 'New Payment Method' fill_in 'payment_method_name', :with => 'Cheque payment method' - + check "payment_method_distributor_ids_#{@distributors[0].id}" click_button 'Create' flash_message.should == 'Payment Method has been successfully created!' - + payment_method = Spree::PaymentMethod.find_by_name('Cheque payment method') payment_method.distributors.should == [@distributors[0]] end @@ -53,7 +53,7 @@ feature %q{ end end - context "as an enterprise user" do + context "as an enterprise user", js: true do let(:enterprise_user) { create_enterprise_user } let(:distributor1) { create(:distributor_enterprise, name: 'First Distributor') } let(:distributor2) { create(:distributor_enterprise, name: 'Second Distributor') } @@ -70,8 +70,11 @@ feature %q{ it "I can get to the new enterprise page" do click_link 'Enterprises' - within(".enterprise-#{distributor1.id}") { click_link 'Payment Methods' } - click_link 'New Payment Method' + within("#e_#{distributor1.id}") { click_link 'Manage' } + within(".side_menu") do + click_link "Payment Methods" + end + click_link 'Create One Now' current_path.should == spree.new_admin_payment_method_path end @@ -109,17 +112,25 @@ feature %q{ end - it "shows me only payment methods for the enterprise I select" do + pending "shows me only payment methods for the enterprise I select" do pm1 pm2 click_link 'Enterprises' - within(".enterprise-#{distributor1.id}") { click_link 'Payment Methods' } + within("#e_#{distributor1.id}") { click_link 'Manage' } + within(".side_menu") do + click_link "Payment Methods" + end + page.should have_content pm1.name page.should have_content pm2.name click_link 'Enterprises' - within(".enterprise-#{distributor2.id}") { click_link 'Payment Methods' } + within("#e_#{distributor2.id}") { click_link 'Manage' } + within(".side_menu") do + click_link "Payment Methods" + end + page.should_not have_content pm1.name page.should have_content pm2.name end diff --git a/spec/features/admin/products_spec.rb b/spec/features/admin/products_spec.rb index a82674ddaf..2aafc82b02 100644 --- a/spec/features/admin/products_spec.rb +++ b/spec/features/admin/products_spec.rb @@ -82,7 +82,7 @@ feature %q{ visit spree.edit_admin_product_path(product) choose 'product_group_buy_1' - fill_in 'Group buy unit size', :with => '10' + fill_in 'Bulk unit size', :with => '10' click_button 'Update' @@ -117,31 +117,34 @@ feature %q{ end end - scenario "creating a new product", js: true do - Spree::Config.products_require_tax_category = false - click_link 'Products' - click_link 'New Product' + context "products do not require a tax category" do + scenario "creating a new product", js: true do + with_products_require_tax_category(false) do + click_link 'Products' + click_link 'New Product' - fill_in 'product_name', :with => 'A new product !!!' - fill_in 'product_price', :with => '19.99' + fill_in 'product_name', :with => 'A new product !!!' + fill_in 'product_price', :with => '19.99' - page.should have_selector('#product_supplier_id') - select 'Another Supplier', :from => 'product_supplier_id' - select 'Weight (g)', from: 'product_variant_unit_with_scale' - fill_in 'product_unit_value_with_description', with: '500' - select taxon.name, from: "product_primary_taxon_id" - select 'None', from: "product_tax_category_id" + page.should have_selector('#product_supplier_id') + select 'Another Supplier', :from => 'product_supplier_id' + select 'Weight (g)', from: 'product_variant_unit_with_scale' + fill_in 'product_unit_value_with_description', with: '500' + select taxon.name, from: "product_primary_taxon_id" + select 'None', from: "product_tax_category_id" - # Should only have suppliers listed which the user can manage - page.should have_select 'product_supplier_id', with_options: [@supplier2.name, @supplier_permitted.name] - page.should_not have_select 'product_supplier_id', with_options: [@supplier.name] + # Should only have suppliers listed which the user can manage + page.should have_select 'product_supplier_id', with_options: [@supplier2.name, @supplier_permitted.name] + page.should_not have_select 'product_supplier_id', with_options: [@supplier.name] - click_button 'Create' + click_button 'Create' - flash_message.should == 'Product "A new product !!!" has been successfully created!' - product = Spree::Product.find_by_name('A new product !!!') - product.supplier.should == @supplier2 - product.tax_category.should be_nil + flash_message.should == 'Product "A new product !!!" has been successfully created!' + product = Spree::Product.find_by_name('A new product !!!') + product.supplier.should == @supplier2 + product.tax_category.should be_nil + end + end end scenario "editing a product" do @@ -202,7 +205,7 @@ feature %q{ scenario "deleting product images", js: true do product = create(:simple_product, supplier: @supplier2) - image = File.open(File.expand_path('../../../../app/assets/images/logo.jpg', __FILE__)) + image = File.open(File.expand_path('../../../../app/assets/images/logo-white.png', __FILE__)) Spree::Image.create({:viewable_id => product.master.id, :viewable_type => 'Spree::Variant', :alt => "position 1", :attachment => image, :position => 1}) visit spree.admin_product_images_path(product) diff --git a/spec/features/admin/reports_spec.rb b/spec/features/admin/reports_spec.rb index 5d72705d8a..2bd39a3e38 100644 --- a/spec/features/admin/reports_spec.rb +++ b/spec/features/admin/reports_spec.rb @@ -67,7 +67,7 @@ feature %q{ scenario "payment method report" do click_link "Payment Methods Report" - rows = find("table#listing_order_payment_methods").all("thead tr") + rows = find("table#listing_ocm_orders").all("thead tr") table = rows.map { |r| r.all("th").map { |c| c.text.strip } } table.sort.should == [ ["First Name", "Last Name", "Hub", "Hub Code", "Email", "Phone", "Shipping Method", "Payment Method", "Amount", "Balance"] @@ -76,7 +76,7 @@ feature %q{ scenario "delivery report" do click_link "Delivery Report" - rows = find("table#listing_order_payment_methods").all("thead tr") + rows = find("table#listing_ocm_orders").all("thead tr") table = rows.map { |r| r.all("th").map { |c| c.text.strip } } table.sort.should == [ ["First Name", "Last Name", "Hub", "Hub Code", "Delivery Address", "Delivery Postcode", "Phone", "Shipping Method", "Payment Method", "Amount", "Balance", "Temp Controlled Items?", "Special Instructions"] @@ -84,6 +84,66 @@ feature %q{ end end + describe "Packing reports" do + before do + login_to_admin_section + click_link "Reports" + end + + let(:bill_address1) { create(:address, lastname: "Aman") } + let(:bill_address2) { create(:address, lastname: "Bman") } + let(:distributor_address) { create(:address, :address1 => "distributor address", :city => 'The Shire', :zipcode => "1234") } + let(:distributor) { create(:distributor_enterprise, :address => distributor_address) } + let(:order1) { create(:order, distributor: distributor, bill_address: bill_address1) } + let(:order2) { create(:order, distributor: distributor, bill_address: bill_address2) } + let(:supplier) { create(:supplier_enterprise, name: "Supplier") } + let(:product_1) { create(:simple_product, name: "Product 1", supplier: supplier ) } + let(:variant_1) { create(:variant, product: product_1, unit_description: "Big") } + let(:variant_2) { create(:variant, product: product_1, unit_description: "Small") } + let(:product_2) { create(:simple_product, name: "Product 2", supplier: supplier) } + + before do + Timecop.travel(Time.zone.local(2013, 4, 25, 14, 0, 0)) { order1.finalize! } + Timecop.travel(Time.zone.local(2013, 4, 25, 15, 0, 0)) { order2.finalize! } + + create(:line_item, variant: variant_1, quantity: 1, order: order1) + create(:line_item, variant: variant_2, quantity: 3, order: order1) + create(:line_item, variant: product_2.master, quantity: 3, order: order2) + + end + + scenario "Pack By Customer" do + click_link "Pack By Customer" + fill_in 'q_completed_at_gt', with: '2013-04-25 13:00:00' + fill_in 'q_completed_at_lt', with: '2013-04-25 16:00:00' + #select 'Pack By Customer', from: 'report_type' + click_button 'Search' + + rows = find("table#listing_orders.index").all("thead tr") + table = rows.map { |r| r.all("th").map { |c| c.text.strip } } + table.sort.should == [ + ["Hub", "Code", "First Name", "Last Name", "Supplier", "Product", "Variant", "Quantity", "TempControlled?"] + ].sort + all('table#listing_orders tbody tr').count.should == 5 # Totals row per order + end + + scenario "Pack By Supplier" do + click_link "Pack By Supplier" + fill_in 'q_completed_at_gt', with: '2013-04-25 13:00:00' + fill_in 'q_completed_at_lt', with: '2013-04-25 16:00:00' + #select 'Pack By Customer', from: 'report_type' + click_button 'Search' + + rows = find("table#listing_orders").all("thead tr") + table = rows.map { |r| r.all("th").map { |c| c.text.strip } } + table.sort.should == [ + ["Hub", "Supplier", "Code", "First Name", "Last Name", "Product", "Variant", "Quantity", "TempControlled?"] + ].sort + all('table#listing_orders tbody tr').count.should == 4 # Totals row per supplier + end + end + + scenario "orders and distributors report" do login_to_admin_section click_link 'Reports' @@ -212,7 +272,7 @@ feature %q{ it "handles order cycles with nil opening or closing times" do distributor = create(:distributor_enterprise) - oc = create(:simple_order_cycle, name: "My Order Cycle", distributors: [distributor], orders_open_at: Time.now, orders_close_at: nil) + oc = create(:simple_order_cycle, name: "My Order Cycle", distributors: [distributor], orders_open_at: Time.zone.now, orders_close_at: nil) o = create(:order, order_cycle: oc, distributor: distributor) login_to_admin_section @@ -222,19 +282,31 @@ feature %q{ end end - describe "products and inventory report" do - it "shows products and inventory report" do - product1 = create(:simple_product, name: "Product Name", price: 100) - variant1 = product1.variants.first - variant2 = create(:variant, product: product1, price: 80.0) - product2 = create(:simple_product, name: "Product 2", price: 99.0, variant_unit: 'weight', variant_unit_scale: 1, unit_value: '100') - variant3 = product2.variants.first + describe "products and inventory report", js: true do + let(:supplier) { create(:supplier_enterprise, name: 'Supplier Name') } + let(:taxon) { create(:taxon, name: 'Taxon Name') } + let(:product1) { create(:simple_product, name: "Product Name", price: 100, supplier: supplier, primary_taxon: taxon) } + let(:product2) { create(:simple_product, name: "Product 2", price: 99.0, variant_unit: 'weight', variant_unit_scale: 1, unit_value: '100', supplier: supplier, primary_taxon: taxon, sku: "product_sku") } + let(:variant1) { product1.variants.first } + let(:variant2) { create(:variant, product: product1, price: 80.0) } + let(:variant3) { product2.variants.first } + + before do + product1.set_property 'Organic', 'NASAA 12345' + product2.set_property 'Organic', 'NASAA 12345' + product1.taxons = [taxon] + product2.taxons = [taxon] variant1.update_column(:count_on_hand, 10) + variant1.update_column(:sku, "sku1") variant2.update_column(:count_on_hand, 20) + variant2.update_column(:sku, "sku2") variant3.update_column(:count_on_hand, 9) + variant3.update_column(:sku, "") variant1.option_values = [create(:option_value, :presentation => "Test")] variant2.option_values = [create(:option_value, :presentation => "Something")] + end + it "shows products and inventory report" do login_to_admin_section click_link 'Reports' @@ -243,15 +315,19 @@ feature %q{ click_link 'Products & Inventory' page.should have_content "Supplier" - rows = find("table#listing_products").all("tr") - table = rows.map { |r| r.all("th,td").map { |c| c.text.strip } } + page.should have_table_row ["Supplier", "Producer Suburb", "Product", "Product Properties", "Taxons", "Variant Value", "Price", "Group Buy Unit Quantity", "Amount", "SKU"].map(&:upcase) + page.should have_table_row [product1.supplier.name, product1.supplier.address.city, "Product Name", product1.properties.map(&:presentation).join(", "), product1.primary_taxon.name, "Test", "100.0", product1.group_buy_unit_size.to_s, "", "sku1"] + page.should have_table_row [product1.supplier.name, product1.supplier.address.city, "Product Name", product1.properties.map(&:presentation).join(", "), product1.primary_taxon.name, "Something", "80.0", product1.group_buy_unit_size.to_s, "", "sku2"] + page.should have_table_row [product2.supplier.name, product1.supplier.address.city, "Product 2", product1.properties.map(&:presentation).join(", "), product2.primary_taxon.name, "100g", "99.0", product1.group_buy_unit_size.to_s, "", "product_sku"] + end - table.sort.should == [ - ["Supplier", "Producer Suburb", "Product", "Product Properties", "Taxons", "Variant Value", "Price", "Group Buy Unit Quantity", "Amount"], - [product1.supplier.name, product1.supplier.address.city, "Product Name", product1.properties.join(", "), product1.primary_taxon.name, "Test", "100.0", product1.group_buy_unit_size.to_s, ""], - [product1.supplier.name, product1.supplier.address.city, "Product Name", product1.properties.join(", "), product1.primary_taxon.name, "Something", "80.0", product1.group_buy_unit_size.to_s, ""], - [product2.supplier.name, product1.supplier.address.city, "Product 2", product1.properties.join(", "), product2.primary_taxon.name, "100g", "99.0", product1.group_buy_unit_size.to_s, ""] - ].sort + it "shows the LettuceShare report" do + login_to_admin_section + click_link 'Reports' + click_link 'LettuceShare' + + page.should have_table_row ['PRODUCT', 'Description', 'Qty', 'Pack Size', 'Unit', 'Unit Price', 'Total', 'GST incl.', 'Grower and growing method', 'Taxon'].map(&:upcase) + page.should have_table_row ['Product 2', '100g', '', '100', 'g', '99.0', '', '0', 'Supplier Name (Organic - NASAA 12345)', 'Taxon Name'] end end @@ -300,4 +376,171 @@ feature %q{ ].sort end end + + describe "Xero invoices report" do + let(:distributor1) { create(:distributor_enterprise, with_payment_and_shipping: true, charges_sales_tax: true) } + let(:distributor2) { create(:distributor_enterprise, with_payment_and_shipping: true, charges_sales_tax: true) } + let(:user1) { create_enterprise_user enterprises: [distributor1] } + let(:user2) { create_enterprise_user enterprises: [distributor2] } + let(:shipping_method) { create(:shipping_method, name: "Shipping", description: "Expensive", calculator: Spree::Calculator::FlatRate.new(preferred_amount: 100.55)) } + let(:enterprise_fee1) { create(:enterprise_fee, enterprise: user1.enterprises.first, tax_category: product2.tax_category, calculator: Spree::Calculator::FlatRate.new(preferred_amount: 10)) } + let(:enterprise_fee2) { create(:enterprise_fee, enterprise: user1.enterprises.first, tax_category: product2.tax_category, calculator: Spree::Calculator::FlatRate.new(preferred_amount: 20)) } + let(:order_cycle) { create(:simple_order_cycle, coordinator: distributor1, coordinator_fees: [enterprise_fee1, enterprise_fee2], distributors: [distributor1], variants: [product1.master]) } + + let!(:zone) { create(:zone_with_member) } + let(:country) { Spree::Country.find Spree::Config.default_country_id } + let(:bill_address) { create(:address, firstname: 'Customer', lastname: 'Name', address1: 'customer l1', address2: '', city: 'customer city', zipcode: 1234, country: country) } + let(:order1) { create(:order, order_cycle: order_cycle, distributor: user1.enterprises.first, shipping_method: shipping_method, bill_address: bill_address) } + let(:product1) { create(:taxed_product, zone: zone, price: 12.54, tax_rate_amount: 0, sku: 'sku1') } + let(:product2) { create(:taxed_product, zone: zone, price: 500.15, tax_rate_amount: 0.2, sku: 'sku2') } + + let!(:line_item1) { create(:line_item, variant: product1.master, price: 12.54, quantity: 1, order: order1) } + let!(:line_item2) { create(:line_item, variant: product2.master, price: 500.15, quantity: 3, order: order1) } + + let!(:adj_shipping) { create(:adjustment, adjustable: order1, label: "Shipping", originator: shipping_method, amount: 100.55, included_tax: 10.06) } + let!(:adj_fee1) { create(:adjustment, adjustable: order1, originator: enterprise_fee1, label: "Enterprise fee untaxed", amount: 10, included_tax: 0) } + let!(:adj_fee2) { create(:adjustment, adjustable: order1, originator: enterprise_fee2, label: "Enterprise fee taxed", amount: 20, included_tax: 2) } + let!(:adj_manual1) { create(:adjustment, adjustable: order1, originator: nil, source: nil, label: "Manual adjustment", amount: 30, included_tax: 0) } + let!(:adj_manual2) { create(:adjustment, adjustable: order1, originator: nil, source: nil, label: "Manual adjustment", amount: 40, included_tax: 3) } + + + before do + order1.update_attribute :email, 'customer@email.com' + Timecop.travel(Time.zone.local(2015, 4, 25, 14, 0, 0)) { order1.finalize! } + + login_to_admin_section + click_link 'Reports' + + click_link 'Xero Invoices' + end + + around do |example| + Timecop.travel(Time.zone.local(2015, 4, 26, 14, 0, 0)) do + example.yield + end + end + + it "shows Xero invoices report" do + xero_invoice_table.should match_table [ + xero_invoice_header, + xero_invoice_summary_row('Total untaxable produce (no tax)', 12.54, 'GST Free Income'), + xero_invoice_summary_row('Total taxable produce (tax inclusive)', 1500.45, 'GST on Income'), + xero_invoice_summary_row('Total untaxable fees (no tax)', 10.0, 'GST Free Income'), + xero_invoice_summary_row('Total taxable fees (tax inclusive)', 20.0, 'GST on Income'), + xero_invoice_summary_row('Delivery Shipping Cost (tax inclusive)', 100.55, 'GST on Income'), + xero_invoice_summary_row('Total untaxable admin adjustments (no tax)', 30.0, 'GST Free Income'), + xero_invoice_summary_row('Total taxable admin adjustments (tax inclusive)', 40.0, 'GST on Income') + ] + end + + it "can customise a number of fields" do + fill_in 'initial_invoice_number', with: '5' + fill_in 'invoice_date', with: '2015-02-12' + fill_in 'due_date', with: '2015-03-12' + fill_in 'account_code', with: 'abc123' + click_button 'Search' + + opts = {invoice_number: '5', invoice_date: '2015-02-12', due_date: '2015-03-12', account_code: 'abc123'} + + xero_invoice_table.should match_table [ + xero_invoice_header, + xero_invoice_summary_row('Total untaxable produce (no tax)', 12.54, 'GST Free Income', opts), + xero_invoice_summary_row('Total taxable produce (tax inclusive)', 1500.45, 'GST on Income', opts), + xero_invoice_summary_row('Total untaxable fees (no tax)', 10.0, 'GST Free Income', opts), + xero_invoice_summary_row('Total taxable fees (tax inclusive)', 20.0, 'GST on Income', opts), + xero_invoice_summary_row('Delivery Shipping Cost (tax inclusive)', 100.55, 'GST on Income', opts), + xero_invoice_summary_row('Total untaxable admin adjustments (no tax)', 30.0, 'GST Free Income', opts), + xero_invoice_summary_row('Total taxable admin adjustments (tax inclusive)', 40.0, 'GST on Income', opts) + ] + end + + it "generates a detailed report" do + select 'Detailed', from: 'report_type' + click_button 'Search' + + opts = {} + + xero_invoice_table.should match_table [ + xero_invoice_header, + xero_invoice_li_row(line_item1), + xero_invoice_li_row(line_item2), + xero_invoice_adjustment_row(adj_manual1), + xero_invoice_adjustment_row(adj_manual2), + xero_invoice_summary_row('Total untaxable fees (no tax)', 10.0, 'GST Free Income', opts), + xero_invoice_summary_row('Total taxable fees (tax inclusive)', 20.0, 'GST on Income', opts), + xero_invoice_summary_row('Delivery Shipping Cost (tax inclusive)', 100.55, 'GST on Income', opts) + ] + end + + describe "account invoices" do + let(:accounts_distributor) { create(:distributor_enterprise) } + let(:billable_period) { create(:billable_period, account_invoice: account_invoice) } + let(:account_invoice) { create(:account_invoice, order: account_invoice_order) } + let!(:account_invoice_order) { create(:order, order_cycle: order_cycle, distributor: accounts_distributor) } + let!(:adjustment) { create(:adjustment, adjustable: account_invoice_order, source: billable_period, label: 'Account invoice item', amount: 12.34) } # Tax? + + before do + Spree::Config.accounts_distributor_id = accounts_distributor.id + + account_invoice_order.update_attribute :email, 'customer@email.com' + Timecop.travel(Time.zone.local(2015, 4, 25, 14, 0, 0)) { account_invoice_order.finalize! } + + visit current_path + end + + it "generates a detailed report for account invoices" do + select 'Detailed', from: 'report_type' + select accounts_distributor.name, from: 'q_distributor_id_eq' + click_button 'Search' + + opts = {} + + xero_invoice_table.should match_table [ + xero_invoice_header, + xero_invoice_account_invoice_row(adjustment) + ] + end + end + + + private + + def xero_invoice_table + find("table#listing_invoices") + end + + def xero_invoice_header + %w(*ContactName EmailAddress POAddressLine1 POAddressLine2 POAddressLine3 POAddressLine4 POCity PORegion POPostalCode POCountry *InvoiceNumber Reference *InvoiceDate *DueDate InventoryItemCode *Description *Quantity *UnitAmount Discount *AccountCode *TaxType TrackingName1 TrackingOption1 TrackingName2 TrackingOption2 Currency BrandingTheme Paid?) + end + + def xero_invoice_summary_row(description, amount, tax_type, opts={}) + xero_invoice_row '', description, amount, '1', tax_type, opts + end + + def xero_invoice_li_row(line_item, opts={}) + tax_type = line_item.has_tax? ? 'GST on Income' : 'GST Free Income' + xero_invoice_row line_item.product.sku, line_item.product_and_full_name, line_item.price.to_s, line_item.quantity.to_s, tax_type, opts + end + + def xero_invoice_adjustment_row(adjustment, opts={}) + tax_type = adjustment.has_tax? ? 'GST on Income' : 'GST Free Income' + xero_invoice_row('', adjustment.label, adjustment.amount, '1', tax_type, opts) + end + + def xero_invoice_account_invoice_row(adjustment, opts={}) + opts.reverse_merge!({customer_name: '', address1: '', city: '', state: '', zipcode: '', country: '', invoice_number: account_invoice_order.number, order_number: account_invoice_order.number}) + xero_invoice_adjustment_row(adjustment, opts) + end + + def xero_invoice_row(sku, description, amount, quantity, tax_type, opts={}) + opts.reverse_merge!({customer_name: 'Customer Name', address1: 'customer l1', city: 'customer city', state: 'Victoria', zipcode: '1234', country: country.name, invoice_number: order1.number, order_number: order1.number, invoice_date: '2015-04-26', due_date: '2015-05-26', account_code: 'food sales'}) + + [opts[:customer_name], 'customer@email.com', opts[:address1], '', '', '', opts[:city], opts[:state], opts[:zipcode], opts[:country], opts[:invoice_number], opts[:order_number], opts[:invoice_date], opts[:due_date], + + sku, + description, + quantity, + amount.to_s, '', opts[:account_code], tax_type, '', '', '', '', Spree::Config.currency, '', 'N'] + end + end end diff --git a/spec/features/admin/shipping_methods_spec.rb b/spec/features/admin/shipping_methods_spec.rb index 2857fa6502..9f04b6707b 100644 --- a/spec/features/admin/shipping_methods_spec.rb +++ b/spec/features/admin/shipping_methods_spec.rb @@ -61,7 +61,7 @@ feature 'shipping methods' do end end - context "as an enterprise user" do + context "as an enterprise user", js: true do let(:enterprise_user) { create_enterprise_user } let(:distributor1) { create(:distributor_enterprise, name: 'First Distributor') } let(:distributor2) { create(:distributor_enterprise, name: 'Second Distributor') } @@ -78,8 +78,11 @@ feature 'shipping methods' do it "creating a shipping method" do click_link 'Enterprises' - within(".enterprise-#{distributor1.id}") { click_link 'Shipping Methods' } - click_link 'New Shipping Method' + within("#e_#{distributor1.id}") { click_link 'Manage' } + within(".side_menu") do + click_link "Shipping Methods" + end + click_link 'Create One Now' # Show the correct fields page.should have_field 'shipping_method_name' @@ -119,17 +122,24 @@ feature 'shipping methods' do page.all('td', text: 'Two').count.should == 1 end - it "shows me only shipping methods for the enterprise I select" do + pending "shows me only shipping methods for the enterprise I select" do sm1 sm2 click_link 'Enterprises' - within(".enterprise-#{distributor1.id}") { click_link 'Shipping Methods' } + within("#e_#{distributor1.id}") { click_link 'Manage' } + within(".side_menu") do + click_link "Shipping Methods" + end page.should have_content sm1.name page.should have_content sm2.name click_link 'Enterprises' - within(".enterprise-#{distributor2.id}") { click_link 'Shipping Methods' } + within("#e_#{distributor2.id}") { click_link 'Manage' } + within(".side_menu") do + click_link "Shipping Methods" + end + page.should_not have_content sm1.name page.should have_content sm2.name end diff --git a/spec/features/admin/tax_settings_spec.rb b/spec/features/admin/tax_settings_spec.rb new file mode 100644 index 0000000000..4b2dd4486c --- /dev/null +++ b/spec/features/admin/tax_settings_spec.rb @@ -0,0 +1,48 @@ +require 'spec_helper' + +feature 'Account and Billing Settings' do + include AuthenticationWorkflow + include WebHelper + + describe "updating" do + let!(:admin) { create(:admin_user) } + + before do + Spree::Config.set({ + products_require_tax_category: false, + shipment_inc_vat: false, + shipping_tax_rate: 0 + }) + end + + before do + quick_login_as_admin + end + + context "as an admin user" do + it "loads the page" do + visit spree.admin_path + click_link "Configuration" + click_link "Tax Settings" + + expect(page).to have_unchecked_field 'preferences_products_require_tax_category' + expect(page).to have_unchecked_field 'preferences_shipment_inc_vat' + expect(page).to have_field 'preferences_shipping_tax_rate' + end + + it "attributes can be changed" do + visit spree.edit_admin_tax_settings_path + + check 'preferences_products_require_tax_category' + check 'preferences_shipment_inc_vat' + fill_in 'preferences_shipping_tax_rate', with: '0.12' + + click_button "Update" + + expect(Spree::Config.products_require_tax_category).to be true + expect(Spree::Config.shipment_inc_vat).to be true + expect(Spree::Config.shipping_tax_rate).to eq 0.12 + end + end + end +end diff --git a/spec/features/admin/variant_overrides_spec.rb b/spec/features/admin/variant_overrides_spec.rb index ee615f44f5..378afa3626 100644 --- a/spec/features/admin/variant_overrides_spec.rb +++ b/spec/features/admin/variant_overrides_spec.rb @@ -26,14 +26,6 @@ feature %q{ page.should have_select2 'hub_id', options: ['', hub.name, hub2.name] end - - it "displays the hub" do - visit '/admin/variant_overrides' - select2_select hub.name, from: 'hub_id' - click_button 'Go' - - page.should have_selector 'h2', text: hub.name - end end context "when a hub is selected" do @@ -59,29 +51,60 @@ feature %q{ before do visit '/admin/variant_overrides' select2_select hub.name, from: 'hub_id' - click_button 'Go' end it "displays the list of products with variants" do page.should have_table_row ['PRODUCER', 'PRODUCT', 'PRICE', 'ON HAND'] - page.should have_table_row [producer.name, product.name, '', ''] page.should have_input "variant-overrides-#{variant.id}-price", placeholder: '1.23' - page.should have_input "variant-overrides-#{variant.id}-count-on-hand", placeholder: '12' + page.should have_input "variant-overrides-#{variant.id}-count_on_hand", placeholder: '12' page.should have_table_row [producer_related.name, product_related.name, '', ''] page.should have_input "variant-overrides-#{variant_related.id}-price", placeholder: '2.34' - page.should have_input "variant-overrides-#{variant_related.id}-count-on-hand", placeholder: '23' - end + page.should have_input "variant-overrides-#{variant_related.id}-count_on_hand", placeholder: '23' - it "filters the products to those the hub can override" do + # filters the products to those the hub can override page.should_not have_content producer_unrelated.name page.should_not have_content product_unrelated.name + + # Filters based on the producer select filter + expect(page).to have_selector "#v_#{variant.id}" + expect(page).to have_selector "#v_#{variant_related.id}" + select2_select producer.name, from: 'producer_filter' + expect(page).to have_selector "#v_#{variant.id}" + expect(page).to_not have_selector "#v_#{variant_related.id}" + select2_select 'All', from: 'producer_filter' + + # Filters based on the quick search box + expect(page).to have_selector "#v_#{variant.id}" + expect(page).to have_selector "#v_#{variant_related.id}" + fill_in 'query', with: product.name + expect(page).to have_selector "#v_#{variant.id}" + expect(page).to_not have_selector "#v_#{variant_related.id}" + fill_in 'query', with: '' + + # Clears the filters + expect(page).to have_selector "#v_#{variant.id}" + expect(page).to have_selector "#v_#{variant_related.id}" + select2_select producer.name, from: 'producer_filter' + fill_in 'query', with: product_related.name + expect(page).to_not have_selector "#v_#{variant.id}" + expect(page).to_not have_selector "#v_#{variant_related.id}" + click_button 'Clear All' + expect(page).to have_selector "#v_#{variant.id}" + expect(page).to have_selector "#v_#{variant_related.id}" end it "creates new overrides" do + first("div#columns-dropdown", :text => "COLUMNS").click + first("div#columns-dropdown div.menu div.menu_item", text: "SKU").click + first("div#columns-dropdown div.menu div.menu_item", text: "On Demand").click + first("div#columns-dropdown", :text => "COLUMNS").click + + fill_in "variant-overrides-#{variant.id}-sku", with: 'NEWSKU' fill_in "variant-overrides-#{variant.id}-price", with: '777.77' - fill_in "variant-overrides-#{variant.id}-count-on-hand", with: '123' + fill_in "variant-overrides-#{variant.id}-count_on_hand", with: '123' + check "variant-overrides-#{variant.id}-on_demand" page.should have_content "Changes to one override remain unsaved." expect do @@ -92,15 +115,17 @@ feature %q{ vo = VariantOverride.last vo.variant_id.should == variant.id vo.hub_id.should == hub.id + vo.sku.should == "NEWSKU" vo.price.should == 777.77 vo.count_on_hand.should == 123 + vo.on_demand.should == true end describe "creating and then updating the new override" do it "updates the same override instead of creating a duplicate" do # When I create a new override fill_in "variant-overrides-#{variant.id}-price", with: '777.77' - fill_in "variant-overrides-#{variant.id}-count-on-hand", with: '123' + fill_in "variant-overrides-#{variant.id}-count_on_hand", with: '123' page.should have_content "Changes to one override remain unsaved." expect do @@ -110,7 +135,7 @@ feature %q{ # And I update its settings without reloading the page fill_in "variant-overrides-#{variant.id}-price", with: '111.11' - fill_in "variant-overrides-#{variant.id}-count-on-hand", with: '111' + fill_in "variant-overrides-#{variant.id}-count_on_hand", with: '111' page.should have_content "Changes to one override remain unsaved." # Then I shouldn't see a new override @@ -130,7 +155,7 @@ feature %q{ it "displays an error when unauthorised to access the page" do fill_in "variant-overrides-#{variant.id}-price", with: '777.77' - fill_in "variant-overrides-#{variant.id}-count-on-hand", with: '123' + fill_in "variant-overrides-#{variant.id}-count_on_hand", with: '123' page.should have_content "Changes to one override remain unsaved." user.enterprises.clear @@ -143,7 +168,7 @@ feature %q{ it "displays an error when unauthorised to update a particular override" do fill_in "variant-overrides-#{variant_related.id}-price", with: '777.77' - fill_in "variant-overrides-#{variant_related.id}-count-on-hand", with: '123' + fill_in "variant-overrides-#{variant_related.id}-count_on_hand", with: '123' page.should have_content "Changes to one override remain unsaved." er2.destroy @@ -156,23 +181,27 @@ feature %q{ end context "with overrides" do - let!(:vo) { create(:variant_override, variant: variant, hub: hub, price: 77.77, count_on_hand: 11111) } + let!(:vo) { create(:variant_override, variant: variant, hub: hub, price: 77.77, count_on_hand: 11111, default_stock: 1000, resettable: true) } let!(:vo_no_auth) { create(:variant_override, variant: variant, hub: hub3, price: 1, count_on_hand: 2) } + let!(:product2) { create(:simple_product, supplier: producer, variant_unit: 'weight', variant_unit_scale: 1) } + let!(:variant2) { create(:variant, product: product2, unit_value: 8, price: 1.00, on_hand: 12) } + let!(:vo_no_reset) { create(:variant_override, variant: variant2, hub: hub, price: 3.99, count_on_hand: 40, default_stock: 100, resettable: false) } + let!(:variant3) { create(:variant, product: product, unit_value: 2, price: 5.00, on_hand: 6) } + let!(:vo3) { create(:variant_override, variant: variant3, hub: hub, price: 6, count_on_hand: 7, sku: "SOMESKU", default_stock: 100, resettable: false) } before do visit '/admin/variant_overrides' select2_select hub.name, from: 'hub_id' - click_button 'Go' end it "product values are affected by overrides" do page.should have_input "variant-overrides-#{variant.id}-price", with: '77.77', placeholder: '1.23' - page.should have_input "variant-overrides-#{variant.id}-count-on-hand", with: '11111', placeholder: '12' + page.should have_input "variant-overrides-#{variant.id}-count_on_hand", with: '11111', placeholder: '12' end it "updates existing overrides" do fill_in "variant-overrides-#{variant.id}-price", with: '22.22' - fill_in "variant-overrides-#{variant.id}-count-on-hand", with: '8888' + fill_in "variant-overrides-#{variant.id}-count_on_hand", with: '8888' page.should have_content "Changes to one override remain unsaved." expect do @@ -187,17 +216,54 @@ feature %q{ vo.count_on_hand.should == 8888 end + # Any new fields added to the VO model need to be added to this test it "deletes overrides when values are cleared" do + first("div#columns-dropdown", :text => "COLUMNS").click + first("div#columns-dropdown div.menu div.menu_item", text: "On Demand").click + first("div#columns-dropdown div.menu div.menu_item", text: "Reset Stock Level").click + first("div#columns-dropdown", :text => "COLUMNS").click + + # Clearing values manually fill_in "variant-overrides-#{variant.id}-price", with: '' - fill_in "variant-overrides-#{variant.id}-count-on-hand", with: '' + fill_in "variant-overrides-#{variant.id}-count_on_hand", with: '' + fill_in "variant-overrides-#{variant.id}-default_stock", with: '' + page.uncheck "variant-overrides-#{variant.id}-resettable" page.should have_content "Changes to one override remain unsaved." + # Clearing values by 'inheriting' + first("div#columns-dropdown", :text => "COLUMNS").click + first("div#columns-dropdown div.menu div.menu_item", text: "Inheritance").click + first("div#columns-dropdown", :text => "COLUMNS").click + page.check "variant-overrides-#{variant3.id}-inherit" + expect do click_button 'Save Changes' page.should have_content "Changes saved." - end.to change(VariantOverride, :count).by(-1) + end.to change(VariantOverride, :count).by(-2) VariantOverride.where(id: vo.id).should be_empty + VariantOverride.where(id: vo3.id).should be_empty + end + + it "resets stock to defaults" do + click_button 'Reset Stock to Defaults' + page.should have_content 'Stocks reset to defaults.' + vo.reload + page.should have_input "variant-overrides-#{variant.id}-count_on_hand", with: '1000', placeholder: '12' + vo.count_on_hand.should == 1000 + end + + it "doesn't reset stock levels if the behaviour is disabled" do + click_button 'Reset Stock to Defaults' + vo_no_reset.reload + page.should have_input "variant-overrides-#{variant2.id}-count_on_hand", with: '40', placeholder: '12' + vo_no_reset.count_on_hand.should == 40 + end + + it "prompts to save changes before reset if any are pending" do + fill_in "variant-overrides-#{variant.id}-price", with: '200' + click_button 'Reset Stock to Defaults' + page.should have_content "Save changes first" end end end diff --git a/spec/features/admin/variants_spec.rb b/spec/features/admin/variants_spec.rb index 910f299386..7303e81766 100644 --- a/spec/features/admin/variants_spec.rb +++ b/spec/features/admin/variants_spec.rb @@ -68,7 +68,9 @@ feature %q{ within "tr#spree_variant_#{v.id}" do page.find('a.delete-resource').click end - page.should_not have_content v.options_text + + page.should_not have_selector "tr#spree_variant_#{v.id}" + v.reload v.deleted_at.should_not be_nil diff --git a/spec/features/consumer/authentication_spec.rb b/spec/features/consumer/authentication_spec.rb index bfa9f141fe..b01b04fe84 100644 --- a/spec/features/consumer/authentication_spec.rb +++ b/spec/features/consumer/authentication_spec.rb @@ -1,6 +1,6 @@ require 'spec_helper' -feature "Authentication", js: true do +feature "Authentication", js: true, retry: 3 do include UIComponentHelper # Attempt to address intermittent failures in these specs diff --git a/spec/features/consumer/producers_spec.rb b/spec/features/consumer/producers_spec.rb index 6e09365195..8a26eaf026 100644 --- a/spec/features/consumer/producers_spec.rb +++ b/spec/features/consumer/producers_spec.rb @@ -10,6 +10,8 @@ feature %q{ let!(:invisible_producer) { create(:supplier_enterprise, visible: false) } let(:taxon) { create(:taxon) } let!(:product) { create(:simple_product, supplier: producer, taxons: [taxon]) } + let(:shop) { create(:distributor_enterprise) } + let!(:er) { create(:enterprise_relationship, parent: shop, child: producer) } before do visit producers_path @@ -24,4 +26,9 @@ feature %q{ it "doesn't show invisible producers" do page.should_not have_content invisible_producer.name end + + it "links to places to buy produce" do + expand_active_table_node producer.name + page.should have_link shop.name + end end diff --git a/spec/features/consumer/registration_spec.rb b/spec/features/consumer/registration_spec.rb index 25a91ae160..d36cd48f7d 100644 --- a/spec/features/consumer/registration_spec.rb +++ b/spec/features/consumer/registration_spec.rb @@ -11,7 +11,7 @@ feature "Registration", js: true do expect(URI.parse(current_url).path).to eq registration_auth_path - page.has_selector? "dd", text: "Log in" + page.has_selector? "dd", text: "Login" switch_to_login_tab # Enter Login details @@ -45,7 +45,7 @@ feature "Registration", js: true do # Choosing a type expect(page).to have_content 'Last step to add My Awesome Enterprise!' click_link 'producer-panel' - click_button 'Continue' + click_button 'Create Profile' # Enterprise should be created expect(page).to have_content 'Nice one!' @@ -102,8 +102,8 @@ feature "Registration", js: true do # Link appears to be unresponsive for a while, so keep clicking it until it works using_wait_time 0.5 do 10.times do - click_link "Log in" - break if page.has_selector? "dd.active", text: "Log in" + find("a", text: "Login").click() + break if page.has_selector? "dd.active", text: "Login" end end end @@ -112,7 +112,7 @@ feature "Registration", js: true do # Buttons appear to be unresponsive for a while, so keep clicking them until content appears using_wait_time 1 do 3.times do - click_button "Log in" + click_button "Login" break if page.has_selector? "div#loading", text: "Hold on a moment, we're logging you in" end end diff --git a/spec/features/consumer/shopping/cart_spec.rb b/spec/features/consumer/shopping/cart_spec.rb index 7ec4c75004..58c570735d 100644 --- a/spec/features/consumer/shopping/cart_spec.rb +++ b/spec/features/consumer/shopping/cart_spec.rb @@ -24,7 +24,6 @@ feature "full-page cart", js: true do end it "shows the total tax for the order, including product tax and tax on fees" do - save_screenshot '/home/rohan/ss.png', full: true page.should have_selector '.tax-total', text: '11.00' # 10 + 1 end end diff --git a/spec/features/consumer/shopping/checkout_auth_spec.rb b/spec/features/consumer/shopping/checkout_auth_spec.rb index 8b4f3bb977..a19776f7a9 100644 --- a/spec/features/consumer/shopping/checkout_auth_spec.rb +++ b/spec/features/consumer/shopping/checkout_auth_spec.rb @@ -25,7 +25,7 @@ feature "As a consumer I want to check out my cart", js: true do quick_login_as user visit checkout_path within "section[role='main']" do - page.should_not have_content "Log in" + page.should_not have_content "Login" page.should have_checkout_details end end @@ -33,19 +33,19 @@ feature "As a consumer I want to check out my cart", js: true do it "renders the login buttons when logged out" do visit checkout_path within "section[role='main']" do - page.should have_content "Log in" - click_button "Log in" + page.should have_content "Login" + click_button "Login" end page.should have_login_modal end it "populates user details once logged in" do visit checkout_path - within("section[role='main']") { click_button "Log in" } + within("section[role='main']") { click_button "Login" } page.should have_login_modal fill_in "Email", with: user.email fill_in "Password", with: user.password - within(".login-modal") { click_button 'Log in' } + within(".login-modal") { click_button 'Login' } toggle_details page.should have_field 'First Name', with: 'Foo' diff --git a/spec/features/consumer/shopping/checkout_spec.rb b/spec/features/consumer/shopping/checkout_spec.rb index bf85db2a75..1df700a1fc 100644 --- a/spec/features/consumer/shopping/checkout_spec.rb +++ b/spec/features/consumer/shopping/checkout_spec.rb @@ -198,7 +198,7 @@ feature "As a consumer I want to check out my cart", js: true do toggle_payment fill_in 'Card Number', with: "4111111111111111" select 'February', from: 'secrets.card_month' - select (Date.today.year+1).to_s, from: 'secrets.card_year' + select (Date.current.year+1).to_s, from: 'secrets.card_year' fill_in 'Security Code', with: '123' place_order @@ -213,7 +213,7 @@ feature "As a consumer I want to check out my cart", js: true do toggle_payment fill_in 'Card Number', with: "9999999988887777" select 'February', from: 'secrets.card_month' - select (Date.today.year+1).to_s, from: 'secrets.card_year' + select (Date.current.year+1).to_s, from: 'secrets.card_year' fill_in 'Security Code', with: '123' place_order diff --git a/spec/features/consumer/shopping/shopping_spec.rb b/spec/features/consumer/shopping/shopping_spec.rb index 248826ea5a..e81a792881 100644 --- a/spec/features/consumer/shopping/shopping_spec.rb +++ b/spec/features/consumer/shopping/shopping_spec.rb @@ -22,7 +22,7 @@ feature "As a consumer I want to shop with a distributor", js: true do it "shows a distributor with images" do # Given the distributor has a logo - distributor.logo = File.new(Rails.root + 'app/assets/images/logo.jpg') + distributor.logo = File.new(Rails.root + 'app/assets/images/logo-white.png') distributor.save! # Then we should see the distributor and its logo @@ -42,7 +42,7 @@ feature "As a consumer I want to shop with a distributor", js: true do end describe "selecting an order cycle" do - let(:exchange1) { Exchange.find(oc1.exchanges.to_enterprises(distributor).outgoing.first.id) } + let(:exchange1) { oc1.exchanges.to_enterprises(distributor).outgoing.first } it "selects an order cycle if only one is open" do exchange1.update_attribute :pickup_time, "turtles" @@ -51,7 +51,8 @@ feature "As a consumer I want to shop with a distributor", js: true do end describe "with multiple order cycles" do - let(:exchange2) { Exchange.find(oc2.exchanges.to_enterprises(distributor).outgoing.first.id) } + let(:exchange2) { oc2.exchanges.to_enterprises(distributor).outgoing.first } + before do exchange1.update_attribute :pickup_time, "frogs" exchange2.update_attribute :pickup_time, "turtles" @@ -83,6 +84,59 @@ feature "As a consumer I want to shop with a distributor", js: true do open_product_modal product modal_should_be_open_for product end + + describe "changing order cycle" do + it "shows the correct fees after selecting and changing an order cycle" do + enterprise_fee = create(:enterprise_fee, amount: 1001) + exchange2.enterprise_fees << enterprise_fee + exchange2.variants << variant + exchange1.variants << variant + + # -- Selecting an order cycle + visit shop_path + select "turtles", from: "order_cycle_id" + page.should have_content "$1020.99" + + # -- Cart shows correct price + fill_in "variants[#{variant.id}]", with: 1 + show_cart + within("li.cart") { page.should have_content "$1020.99" } + + # -- Changing order cycle + select "frogs", from: "order_cycle_id" + page.should have_content "$19.99" + + # -- Cart should be cleared + # ng-animate means that the old product row is likely to be present, so we explicitly + # fill in the quantity in the incoming row + page.should_not have_selector "tr.product-cart" + within('product.ng-enter') { fill_in "variants[#{variant.id}]", with: 1 } + within("li.cart") { page.should have_content "$19.99" } + end + + describe "declining to clear the cart" do + before do + exchange2.variants << variant + exchange1.variants << variant + + visit shop_path + select "turtles", from: "order_cycle_id" + fill_in "variants[#{variant.id}]", with: 1 + end + + it "leaves the cart untouched when the user declines" do + handle_js_confirm(false) do + select "frogs", from: "order_cycle_id" + show_cart + page.should have_selector "tr.product-cart" + page.should have_selector 'li.cart', text: '1 item' + + # The order cycle choice should not have changed + page.should have_select 'order_cycle_id', selected: 'turtles' + end + end + end + end end end @@ -133,32 +187,53 @@ feature "As a consumer I want to shop with a distributor", js: true do visit shop_path end - it "should save group buy data to the cart" do + it "should save group buy data to the cart and display it on shopfront reload" do + # -- Quantity fill_in "variants[#{variant.id}]", with: 6 - fill_in "variant_attributes[#{variant.id}][max_quantity]", with: 7 page.should have_in_cart product.name + wait_until { !cart_dirty } + li = Spree::Order.order(:created_at).last.line_items.order(:created_at).last + li.quantity.should == 6 + + # -- Max quantity + fill_in "variant_attributes[#{variant.id}][max_quantity]", with: 7 wait_until { !cart_dirty } li = Spree::Order.order(:created_at).last.line_items.order(:created_at).last li.max_quantity.should == 7 - li.quantity.should == 6 + + # -- Reload + visit shop_path + page.should have_field "variants[#{variant.id}]", with: 6 + page.should have_field "variant_attributes[#{variant.id}][max_quantity]", with: 7 end end end - describe "adding products to cart" do + describe "adding and removing products from cart" do let(:exchange) { Exchange.find(oc1.exchanges.to_enterprises(distributor).outgoing.first.id) } let(:product) { create(:simple_product) } let(:variant) { create(:variant, product: product) } + before do add_product_and_variant_to_order_cycle(exchange, product, variant) set_order_cycle(order, oc1) visit shop_path end - it "should let us add products to our cart" do - fill_in "variants[#{variant.id}]", with: "1" + + it "lets us add and remove products from our cart" do + fill_in "variants[#{variant.id}]", with: '1' page.should have_in_cart product.name + wait_until { !cart_dirty } + li = Spree::Order.order(:created_at).last.line_items.order(:created_at).last + li.quantity.should == 1 + + fill_in "variants[#{variant.id}]", with: '0' + within('li.cart') { page.should_not have_content product.name } + wait_until { !cart_dirty } + + Spree::LineItem.where(id: li).should be_empty end end diff --git a/spec/features/consumer/shopping/variant_overrides_spec.rb b/spec/features/consumer/shopping/variant_overrides_spec.rb index 98d58e174f..92945a60be 100644 --- a/spec/features/consumer/shopping/variant_overrides_spec.rb +++ b/spec/features/consumer/shopping/variant_overrides_spec.rb @@ -15,21 +15,26 @@ feature "shopping with variant overrides defined", js: true do let(:pm) { hub.payment_methods.first } let(:p1) { create(:simple_product, supplier: producer) } let(:p2) { create(:simple_product, supplier: producer) } + let(:p3) { create(:simple_product, supplier: producer, on_demand: true) } let(:v1) { create(:variant, product: p1, price: 11.11, unit_value: 1) } let(:v2) { create(:variant, product: p1, price: 22.22, unit_value: 2) } let(:v3) { create(:variant, product: p2, price: 33.33, unit_value: 3) } let(:v4) { create(:variant, product: p1, price: 44.44, unit_value: 4) } - let!(:vo1) { create(:variant_override, hub: hub, variant: v1, price: 55.55, count_on_hand: nil) } - let!(:vo2) { create(:variant_override, hub: hub, variant: v2, count_on_hand: 0) } - let!(:vo3) { create(:variant_override, hub: hub, variant: v3, count_on_hand: 0) } - let!(:vo4) { create(:variant_override, hub: hub, variant: v4, count_on_hand: 3) } + let(:v5) { create(:variant, product: p3, price: 55.55, unit_value: 5, on_demand: true) } + let(:v6) { create(:variant, product: p3, price: 66.66, unit_value: 6, on_demand: true) } + let!(:vo1) { create(:variant_override, hub: hub, variant: v1, price: 55.55, count_on_hand: nil, default_stock: nil, resettable: false) } + let!(:vo2) { create(:variant_override, hub: hub, variant: v2, count_on_hand: 0, default_stock: nil, resettable: false) } + let!(:vo3) { create(:variant_override, hub: hub, variant: v3, count_on_hand: 0, default_stock: nil, resettable: false) } + let!(:vo4) { create(:variant_override, hub: hub, variant: v4, count_on_hand: 3, default_stock: nil, resettable: false) } + let!(:vo5) { create(:variant_override, hub: hub, variant: v5, count_on_hand: 0, default_stock: nil, resettable: false) } + let!(:vo6) { create(:variant_override, hub: hub, variant: v6, count_on_hand: 6, default_stock: nil, resettable: false) } let(:ef) { create(:enterprise_fee, enterprise: hub, fee_type: 'packing', calculator: Spree::Calculator::FlatPercentItemTotal.new(preferred_flat_percent: 10)) } before do - outgoing_exchange.variants = [v1, v2, v3, v4] + outgoing_exchange.variants = [v1, v2, v3, v4, v5, v6] outgoing_exchange.enterprise_fees << ef sm.calculator.preferred_amount = 0 - visit shop_path + visit shops_path click_link hub.name end @@ -46,6 +51,9 @@ feature "shopping with variant overrides defined", js: true do # Entire product should not appear - no stock page.should_not have_content p2.name page.should_not have_content v3.options_text + + # On-demand product with VO of no stock should NOT appear + page.should_not have_content v5.options_text end it "calculates fees correctly" do @@ -89,9 +97,7 @@ feature "shopping with variant overrides defined", js: true do it "shows the correct prices in the checkout" do fill_in "variants[#{v1.id}]", with: "2" - show_cart - wait_until_enabled 'li.cart a.button' - click_link 'Checkout now' + click_checkout page.should have_selector 'form.edit_order .cart-total', text: '$122.21' page.should have_selector 'form.edit_order .shipping', text: '$0.00' @@ -103,9 +109,7 @@ feature "shopping with variant overrides defined", js: true do describe "creating orders" do it "creates the order with the correct prices" do fill_in "variants[#{v1.id}]", with: "2" - show_cart - wait_until_enabled 'li.cart a.button' - click_link 'Checkout now' + click_checkout complete_checkout @@ -116,9 +120,7 @@ feature "shopping with variant overrides defined", js: true do it "subtracts stock from the override" do fill_in "variants[#{v4.id}]", with: "2" - show_cart - wait_until_enabled 'li.cart a.button' - click_link 'Checkout now' + click_checkout expect do expect do @@ -127,12 +129,20 @@ feature "shopping with variant overrides defined", js: true do end.to change { vo4.reload.count_on_hand }.by(-2) end + it "subtracts stock from stock-overridden on_demand variants" do + fill_in "variants[#{v6.id}]", with: "2" + click_checkout + + expect do + expect do + complete_checkout + end.to change { v6.reload.count_on_hand }.by(0) + end.to change { vo6.reload.count_on_hand }.by(-2) + end + it "does not subtract stock from overrides that do not override count_on_hand" do fill_in "variants[#{v1.id}]", with: "2" - show_cart - wait_until_enabled 'li.cart a.button' - click_link 'Checkout now' - + click_checkout expect do complete_checkout end.to change { v1.reload.count_on_hand }.by(-2) @@ -142,9 +152,7 @@ feature "shopping with variant overrides defined", js: true do it "does not show out of stock flags on order confirmation page" do v4.update_attribute :count_on_hand, 0 fill_in "variants[#{v4.id}]", with: "2" - show_cart - wait_until_enabled 'li.cart a.button' - click_link 'Checkout now' + click_checkout complete_checkout @@ -183,8 +191,15 @@ feature "shopping with variant overrides defined", js: true do within "#payment" do choose pm.name end - + place_order page.should have_content "Your order has been processed successfully" end + + def click_checkout + show_cart + wait_until_enabled 'li.cart a.button' + click_link 'Checkout now', match: :first + end + end diff --git a/spec/features/consumer/home_spec.rb b/spec/features/consumer/shops_spec.rb similarity index 95% rename from spec/features/consumer/home_spec.rb rename to spec/features/consumer/shops_spec.rb index 128b1f734f..faeff9c37d 100644 --- a/spec/features/consumer/home_spec.rb +++ b/spec/features/consumer/shops_spec.rb @@ -1,6 +1,6 @@ require 'spec_helper' -feature 'Home', js: true do +feature 'Shops', js: true do include AuthenticationWorkflow include UIComponentHelper @@ -13,7 +13,7 @@ feature 'Home', js: true do let!(:er) { create(:enterprise_relationship, parent: distributor, child: producer) } before do - visit "/" + visit shops_path end it "shows hubs" do @@ -28,7 +28,7 @@ feature 'Home', js: true do it "should grey out hubs that are not in an order cycle" do create(:simple_product, distributors: [d1, d2]) - visit root_path + visit shops_path page.should have_selector 'hub.inactive' page.should have_selector 'hub.inactive', text: d2.name end diff --git a/spec/features/consumer/suppliers_spec.rb b/spec/features/consumer/suppliers_spec.rb deleted file mode 100644 index d110e89ca4..0000000000 --- a/spec/features/consumer/suppliers_spec.rb +++ /dev/null @@ -1,34 +0,0 @@ -require 'spec_helper' - -feature %q{ - As a consumer - I want to see a list of products from a supplier - So that I can connect with them (and maybe buy stuff too) -} do - include AuthenticationWorkflow - include WebHelper - - background do - create(:distributor_enterprise, :name => "Edible garden") - end - - scenario "entering the site via a supplier's page" do - # Given a supplier with some distributed products - s = create(:supplier_enterprise) - d = create(:distributor_enterprise, with_payment_and_shipping: true) - p = create(:simple_product, supplier: s) - oc = create(:simple_order_cycle, suppliers: [s], distributors: [d], variants: [p.master]) - - # When I visit a supplier page - visit enterprise_path(s) - - # Then I should see a list of hubs that distribute the suppliers products - page.should have_link d.name - - # When I click on a hub - click_link d.name - - # Then that hub should be selected - page.should have_content d.name - end -end diff --git a/spec/helpers/admin/business_model_configuration_helper_spec.rb b/spec/helpers/admin/business_model_configuration_helper_spec.rb new file mode 100644 index 0000000000..a10771cba7 --- /dev/null +++ b/spec/helpers/admin/business_model_configuration_helper_spec.rb @@ -0,0 +1,139 @@ +require 'spec_helper' + +describe Admin::BusinessModelConfigurationHelper do + describe "describing monthly bills for enterprises" do + context "when tax is applied to the service change" do + before { Spree::Config.set(:account_invoices_tax_rate, 0.1) } + context "when a fixed cost is included" do + before { Spree::Config.set(:account_invoices_monthly_fixed, 10) } + + context "when a percentage of turnover is included" do + before { Spree::Config.set(:account_invoices_monthly_rate, 0.05) } + + context "when the bill is capped" do + before { Spree::Config.set(:account_invoices_monthly_cap, 20) } + it { expect(helper.monthly_bill_description).to eq "$10 + 5.0% OF SALES{joiner}CAPPED AT $20 PER MONTH, PLUS GST" } + end + + context "when the bill is not capped" do + before { Spree::Config.set(:account_invoices_monthly_cap, 0) } + it { expect(helper.monthly_bill_description).to eq "$10 + 5.0% OF SALES PER MONTH, PLUS GST" } + end + end + + context "when a percentage of turnover is not included" do + before { Spree::Config.set(:account_invoices_monthly_rate, 0) } + + context "when the bill is capped" do + before { Spree::Config.set(:account_invoices_monthly_cap, 20) } + it { expect(helper.monthly_bill_description).to eq "$10 PER MONTH, PLUS GST" } + end + + context "when the bill is not capped" do + before { Spree::Config.set(:account_invoices_monthly_cap, 0) } + it { expect(helper.monthly_bill_description).to eq "$10 PER MONTH, PLUS GST" } + end + end + end + + context "when a fixed cost is not included" do + before { Spree::Config.set(:account_invoices_monthly_fixed, 0) } + + context "when a percentage of turnover is included" do + before { Spree::Config.set(:account_invoices_monthly_rate, 0.05) } + + context "when the bill is capped" do + before { Spree::Config.set(:account_invoices_monthly_cap, 20) } + it { expect(helper.monthly_bill_description).to eq "5.0% OF SALES{joiner}CAPPED AT $20 PER MONTH, PLUS GST" } + end + + context "when the bill is not capped" do + before { Spree::Config.set(:account_invoices_monthly_cap, 0) } + it { expect(helper.monthly_bill_description).to eq "5.0% OF SALES PER MONTH, PLUS GST" } + end + end + + context "when a percentage of turnover is not included" do + before { Spree::Config.set(:account_invoices_monthly_rate, 0) } + + context "when the bill is capped" do + before { Spree::Config.set(:account_invoices_monthly_cap, 20) } + it { expect(helper.monthly_bill_description).to eq "FREE" } + end + + context "when the bill is not capped" do + before { Spree::Config.set(:account_invoices_monthly_cap, 0) } + it { expect(helper.monthly_bill_description).to eq "FREE" } + end + end + end + end + + context "when tax is applied to the service change" do + before { Spree::Config.set(:account_invoices_tax_rate, 0.0) } + context "when a fixed cost is included" do + before { Spree::Config.set(:account_invoices_monthly_fixed, 10) } + + context "when a percentage of turnover is included" do + before { Spree::Config.set(:account_invoices_monthly_rate, 0.05) } + + context "when the bill is capped" do + before { Spree::Config.set(:account_invoices_monthly_cap, 20) } + it { expect(helper.monthly_bill_description).to eq "$10 + 5.0% OF SALES{joiner}CAPPED AT $20 PER MONTH" } + end + + context "when the bill is not capped" do + before { Spree::Config.set(:account_invoices_monthly_cap, 0) } + it { expect(helper.monthly_bill_description).to eq "$10 + 5.0% OF SALES PER MONTH" } + end + end + + context "when a percentage of turnover is not included" do + before { Spree::Config.set(:account_invoices_monthly_rate, 0) } + + context "when the bill is capped" do + before { Spree::Config.set(:account_invoices_monthly_cap, 20) } + it { expect(helper.monthly_bill_description).to eq "$10 PER MONTH" } + end + + context "when the bill is not capped" do + before { Spree::Config.set(:account_invoices_monthly_cap, 0) } + it { expect(helper.monthly_bill_description).to eq "$10 PER MONTH" } + end + end + end + + context "when a fixed cost is not included" do + before { Spree::Config.set(:account_invoices_monthly_fixed, 0) } + + context "when a percentage of turnover is included" do + before { Spree::Config.set(:account_invoices_monthly_rate, 0.05) } + + context "when the bill is capped" do + before { Spree::Config.set(:account_invoices_monthly_cap, 20) } + it { expect(helper.monthly_bill_description).to eq "5.0% OF SALES{joiner}CAPPED AT $20 PER MONTH" } + end + + context "when the bill is not capped" do + before { Spree::Config.set(:account_invoices_monthly_cap, 0) } + it { expect(helper.monthly_bill_description).to eq "5.0% OF SALES PER MONTH" } + end + end + + context "when a percentage of turnover is not included" do + before { Spree::Config.set(:account_invoices_monthly_rate, 0) } + + context "when the bill is capped" do + before { Spree::Config.set(:account_invoices_monthly_cap, 20) } + it { expect(helper.monthly_bill_description).to eq "FREE" } + end + + context "when the bill is not capped" do + before { Spree::Config.set(:account_invoices_monthly_cap, 0) } + it { expect(helper.monthly_bill_description).to eq "FREE" } + end + end + end + end + end +end diff --git a/spec/helpers/injection_helper_spec.rb b/spec/helpers/injection_helper_spec.rb index 96d9279ef5..362ca6479b 100644 --- a/spec/helpers/injection_helper_spec.rb +++ b/spec/helpers/injection_helper_spec.rb @@ -4,7 +4,7 @@ describe InjectionHelper do let!(:enterprise) { create(:distributor_enterprise, facebook: "roger") } it "will inject via AMS" do - helper.inject_json_ams("test", [enterprise], Api::EnterpriseSerializer).should match enterprise.name + helper.inject_json_ams("test", [enterprise], Api::IdSerializer).should match /#{enterprise.id}/ end it "injects enterprises" do diff --git a/spec/javascripts/application_spec.js b/spec/javascripts/application_spec.js index 10db2226a4..07202027b4 100644 --- a/spec/javascripts/application_spec.js +++ b/spec/javascripts/application_spec.js @@ -7,6 +7,7 @@ //= require angularjs-file-upload //= require lodash.underscore.js //= require angular-flash.min.js +//= require shared/ng-tags-input.min.js //= require shared/mm-foundation-tpls-0.2.2.min.js //= require textAngular.min.js //= require textAngular-sanitize.min.js diff --git a/spec/javascripts/unit/admin/controllers/variant_overrides_controller_spec.js.coffee b/spec/javascripts/unit/admin/controllers/variant_overrides_controller_spec.js.coffee index bdb62e8d37..699e1bc4ab 100644 --- a/spec/javascripts/unit/admin/controllers/variant_overrides_controller_spec.js.coffee +++ b/spec/javascripts/unit/admin/controllers/variant_overrides_controller_spec.js.coffee @@ -1,29 +1,39 @@ describe "VariantOverridesCtrl", -> ctrl = null - scope = null + scope = {} hubs = [{id: 1, name: 'Hub'}] producers = [{id: 2, name: 'Producer'}] products = [{id: 1, name: 'Product'}] hubPermissions = {} VariantOverrides = null variantOverrides = {} + DirtyVariantOverrides = null + dirtyVariantOverrides = {} + StatusMessage = null + statusMessage = {} beforeEach -> - module 'ofn.admin' + module 'admin.variantOverrides' module ($provide) -> $provide.value 'SpreeApiKey', 'API_KEY' $provide.value 'variantOverrides', variantOverrides + $provide.value 'dirtyVariantOverrides', dirtyVariantOverrides null - scope = {} - inject ($controller, Indexer, _VariantOverrides_) -> + inject ($controller, _VariantOverrides_, _DirtyVariantOverrides_, _StatusMessage_) -> VariantOverrides = _VariantOverrides_ - ctrl = $controller 'AdminVariantOverridesCtrl', {$scope: scope, Indexer: Indexer, hubs: hubs, producers: producers, products: products, hubPermissions: hubPermissions, VariantOverrides: _VariantOverrides_} + DirtyVariantOverrides = _DirtyVariantOverrides_ + StatusMessage = _StatusMessage_ + ctrl = $controller 'AdminVariantOverridesCtrl', { $scope: scope, hubs: hubs, producers: producers, products: products, hubPermissions: hubPermissions, VariantOverrides: VariantOverrides, DirtyVariantOverrides: DirtyVariantOverrides, StatusMessage: StatusMessage} it "initialises the hub list and the chosen hub", -> - expect(scope.hubs).toEqual hubs + expect(scope.hubs).toEqual { 1: {id: 1, name: 'Hub'} } expect(scope.hub).toBeNull() + it "initialises select filters", -> + expect(scope.producerFilter).toEqual 0 + expect(scope.query).toEqual '' + it "adds products", -> spyOn(VariantOverrides, "ensureDataFor") expect(scope.products).toEqual [] @@ -54,4 +64,23 @@ describe "VariantOverridesCtrl", -> expect(scope.updateError(data, 400)).toEqual "I had some trouble saving: Hub can't be blank, Variant can't be blank" it "returns a generic message otherwise", -> - expect(scope.updateError({}, 500)).toEqual "Oh no! I was unable to save your changes." \ No newline at end of file + expect(scope.updateError({}, 500)).toEqual "Oh no! I was unable to save your changes." + + describe "setting stock to defaults", -> + it "prompts to save changes if there are any pending", -> + spyOn(StatusMessage, "display") + DirtyVariantOverrides.add {hub_id: 1, variant_id: 1} + scope.resetStock() + expect(StatusMessage.display).toHaveBeenCalledWith 'alert', 'Save changes first.' + + it "updates and refreshes on hand value for variant overrides with a default stock level", inject ($httpBackend) -> + scope.hub_id = 123 + variant_overrides_mock = "mock object" + spyOn(StatusMessage, "display") + spyOn(VariantOverrides, "updateData") + $httpBackend.expectPOST("/admin/variant_overrides/bulk_reset", hub_id: 123).respond 200, variant_overrides_mock + scope.resetStock() + expect(StatusMessage.display).toHaveBeenCalledWith 'progress', 'Changing on hand stock levels...' + $httpBackend.flush() + expect(VariantOverrides.updateData).toHaveBeenCalledWith variant_overrides_mock + expect(StatusMessage.display).toHaveBeenCalledWith 'success', 'Stocks reset to defaults.' diff --git a/spec/javascripts/unit/admin/customers/controllers/customers_controller_spec.js.coffee b/spec/javascripts/unit/admin/customers/controllers/customers_controller_spec.js.coffee new file mode 100644 index 0000000000..22777a6528 --- /dev/null +++ b/spec/javascripts/unit/admin/customers/controllers/customers_controller_spec.js.coffee @@ -0,0 +1,25 @@ +describe "CustomersCtrl", -> + ctrl = null + scope = null + Customers = null + + beforeEach -> + shops = "list of shops" + + module('admin.customers') + inject ($controller, $rootScope, _Customers_) -> + scope = $rootScope + Customers = _Customers_ + ctrl = $controller 'customersCtrl', {$scope: scope, Customers: Customers, shops: shops} + + describe "setting the shop on scope", -> + beforeEach -> + spyOn(Customers, "index").andReturn "list of customers" + scope.$apply -> + scope.shop = {id: 1} + + it "calls Customers#index with the correct params", -> + expect(Customers.index).toHaveBeenCalledWith({enterprise_id: 1}) + + it "resets $scope.customers with the result of Customers#index", -> + expect(scope.customers).toEqual "list of customers" diff --git a/spec/javascripts/unit/admin/customers/services/customers_spec.js.coffee b/spec/javascripts/unit/admin/customers/services/customers_spec.js.coffee new file mode 100644 index 0000000000..7123055d63 --- /dev/null +++ b/spec/javascripts/unit/admin/customers/services/customers_spec.js.coffee @@ -0,0 +1,31 @@ +describe "Customers service", -> + Customers = CustomerResource = customers = $httpBackend = null + + beforeEach -> + module 'admin.customers' + + inject ($q, _$httpBackend_, _Customers_, _CustomerResource_) -> + Customers = _Customers_ + CustomerResource = _CustomerResource_ + $httpBackend = _$httpBackend_ + $httpBackend.expectGET('/admin/customers.json?enterprise_id=2').respond 200, [{ id: 5, email: 'someone@email.com'}] + + describe "#index", -> + result = null + + beforeEach -> + expect(Customers.loaded).toBe false + result = Customers.index(enterprise_id: 2) + $httpBackend.flush() + + it "stores returned data in @customers, with ids as keys", -> + # This is super weird and freaking annoying. I think resource results have extra + # properties ($then, $promise) that cause them to not be equal to the reponse object + # provided to the expectGET clause above. + expect(Customers.customers).toEqual [ new CustomerResource({ id: 5, email: 'someone@email.com'}) ] + + it "returns @customers", -> + expect(result).toEqual Customers.customers + + it "sets @loaded to true", -> + expect(Customers.loaded).toBe true diff --git a/spec/javascripts/unit/admin/enterprises/controllers/enterprise_controller_spec.js.coffee b/spec/javascripts/unit/admin/enterprises/controllers/enterprise_controller_spec.js.coffee index 71e4c37aee..b504ff8f90 100644 --- a/spec/javascripts/unit/admin/enterprises/controllers/enterprise_controller_spec.js.coffee +++ b/spec/javascripts/unit/admin/enterprises/controllers/enterprise_controller_spec.js.coffee @@ -1,16 +1,15 @@ describe "enterpriseCtrl", -> ctrl = null scope = null - Enterprise = null + enterprise = null PaymentMethods = null ShippingMethods = null beforeEach -> module('admin.enterprises') - Enterprise = - enterprise: - is_primary_producer: true - sells: "none" + enterprise = + is_primary_producer: true + sells: "none" PaymentMethods = paymentMethods: "payment methods" ShippingMethods = @@ -18,11 +17,11 @@ describe "enterpriseCtrl", -> inject ($rootScope, $controller) -> scope = $rootScope - ctrl = $controller 'enterpriseCtrl', {$scope: scope, Enterprise: Enterprise, EnterprisePaymentMethods: PaymentMethods, EnterpriseShippingMethods: ShippingMethods} + ctrl = $controller 'enterpriseCtrl', {$scope: scope, enterprise: enterprise, EnterprisePaymentMethods: PaymentMethods, EnterpriseShippingMethods: ShippingMethods} describe "initialisation", -> it "stores enterprise", -> - expect(scope.Enterprise).toEqual Enterprise.enterprise + expect(scope.Enterprise).toEqual enterprise it "stores payment methods", -> expect(scope.PaymentMethods).toBe PaymentMethods.paymentMethods @@ -36,28 +35,28 @@ describe "enterpriseCtrl", -> u1 = { id: 1, email: 'name1@email.com' } u2 = { id: 2, email: 'name2@email.com' } u3 = { id: 3, email: 'name3@email.com' } - Enterprise.enterprise.users = [u1, u2 ,u3] + enterprise.users = [u1, u2 ,u3] it "adds a user to the list", -> u4 = { id: 4, email: "name4@email.com" } scope.addManager u4 - expect(Enterprise.enterprise.users).toContain u4 + expect(enterprise.users).toContain u4 it "ignores object without an id", -> u4 = { not_id: 4, email: "name4@email.com" } scope.addManager u4 - expect(Enterprise.enterprise.users).not.toContain u4 + expect(enterprise.users).not.toContain u4 it "it ignores objects without an email", -> u4 = { id: 4, not_email: "name4@email.com" } scope.addManager u4 - expect(Enterprise.enterprise.users).not.toContain u4 + expect(enterprise.users).not.toContain u4 it "ignores objects that are already in the list, and alerts the user", -> spyOn(window, "alert").andCallThrough() u4 = { id: 3, email: "email-doesn't-matter.com" } scope.addManager u4 - expect(Enterprise.enterprise.users).not.toContain u4 + expect(enterprise.users).not.toContain u4 expect(window.alert).toHaveBeenCalledWith "email-doesn't-matter.com is already a manager!" @@ -67,13 +66,13 @@ describe "enterpriseCtrl", -> u1 = { id: 1, email: 'name1@email.com' } u2 = { id: 2, email: 'name2@email.com' } u3 = { id: 3, email: 'name3@email.com' } - Enterprise.enterprise.users = [u1, u2 ,u3] + enterprise.users = [u1, u2 ,u3] it "removes a user with the given id", -> scope.removeManager {id: 2} - expect(Enterprise.enterprise.users).not.toContain u2 + expect(enterprise.users).not.toContain u2 it "does nothing when given object has no id attribute", -> scope.removeManager {not_id: 2} - expect(Enterprise.enterprise.users).toEqual [u1,u2,u3] + expect(enterprise.users).toEqual [u1,u2,u3] diff --git a/spec/javascripts/unit/admin/enterprises/controllers/enterprises_controller_spec.js.coffee b/spec/javascripts/unit/admin/enterprises/controllers/enterprises_controller_spec.js.coffee new file mode 100644 index 0000000000..6e8bdd8040 --- /dev/null +++ b/spec/javascripts/unit/admin/enterprises/controllers/enterprises_controller_spec.js.coffee @@ -0,0 +1,19 @@ +describe "EnterprisesCtrl", -> + ctrl = null + scope = null + Enterprises = null + + beforeEach -> + module('admin.enterprises') + inject ($controller, $rootScope, _Enterprises_) -> + scope = $rootScope + Enterprises = _Enterprises_ + spyOn(Enterprises, "index").andReturn "list of enterprises" + ctrl = $controller 'enterprisesCtrl', {$scope: scope, Enterprises: Enterprises} + + describe "setting the shop on scope", -> + it "calls Enterprises#index with the correct params", -> + expect(Enterprises.index).toHaveBeenCalled() + + it "resets $scope.allEnterprises with the result of Enterprises#index", -> + expect(scope.allEnterprises).toEqual "list of enterprises" diff --git a/spec/javascripts/unit/admin/enterprises/controllers/index_panel_controller_spec.js.coffee b/spec/javascripts/unit/admin/enterprises/controllers/index_panel_controller_spec.js.coffee new file mode 100644 index 0000000000..b74595b775 --- /dev/null +++ b/spec/javascripts/unit/admin/enterprises/controllers/index_panel_controller_spec.js.coffee @@ -0,0 +1,52 @@ +describe "indexPanelCtrl", -> + ctrl = null + scope = null + Enterprises = null + + beforeEach -> + module('admin.enterprises') + inject ($controller, $rootScope, _Enterprises_) -> + scope = $rootScope.$new() + $rootScope.object = { some: "object" } + Enterprises = _Enterprises_ + ctrl = $controller 'indexPanelCtrl', {$scope: scope, Enterprises: Enterprises} + + describe "initialisation", -> + it "pulls object from the parent scope and points the 'enterprise' on the current scope to it", inject ($rootScope) -> + expect(scope.enterprise).toBe $rootScope.object + + describe "saving changes on an enterprise", -> + describe "when changes have been made", -> + deferred = null + + beforeEach inject ($q) -> + spyOn(scope, "saved").andReturn false + spyOn(scope, "$emit") + deferred = $q.defer() + spyOn(Enterprises, "save").andReturn(deferred.promise) + scope.save() + + it "sets scope.saving to true", -> + expect(scope.saving).toBe true + + describe "when the save is successful", -> + beforeEach inject ($rootScope) -> + deferred.resolve() + $rootScope.$digest() + + it "sets scope.saving to false", -> + expect(scope.saving).toBe false + + it "emits an 'enterprise:updated' event", -> + expect(scope.$emit).toHaveBeenCalledWith("enterprise:updated") + + describe "when the save is unsuccessful", -> + beforeEach inject ($rootScope) -> + deferred.reject({ status: 404 }) + $rootScope.$digest() + + it "sets scope.saving to false", -> + expect(scope.saving).toBe false + + it "does not emit an 'enterprise:updated' event", -> + expect(scope.$emit).not.toHaveBeenCalled() diff --git a/spec/javascripts/unit/admin/enterprises/controllers/side_menu_controller_spec.js.coffee b/spec/javascripts/unit/admin/enterprises/controllers/side_menu_controller_spec.js.coffee index ebed830c6b..e88aeb44f2 100644 --- a/spec/javascripts/unit/admin/enterprises/controllers/side_menu_controller_spec.js.coffee +++ b/spec/javascripts/unit/admin/enterprises/controllers/side_menu_controller_spec.js.coffee @@ -1,15 +1,14 @@ describe "menuCtrl", -> ctrl = null scope = null - Enterprise = null + enterprise = null SideMenu = SideMenu beforeEach -> module('admin.enterprises') - Enterprise = - enterprise: - payment_method_ids: [ 1, 3 ] - shipping_method_ids: [ 2, 4 ] + enterprise = + payment_method_ids: [ 1, 3 ] + shipping_method_ids: [ 2, 4 ] # PaymentMethods = # paymentMethods: [ { id: 1 }, { id: 2 }, { id: 3 }, { id: 4 } ] # ShippingMethods = @@ -20,11 +19,11 @@ describe "menuCtrl", -> SideMenu = _SideMenu_ spyOn(SideMenu, "select").andCallThrough() spyOn(SideMenu, "setItems").andCallThrough() - ctrl = $controller 'sideMenuCtrl', {$scope: scope, Enterprise: Enterprise, SideMenu: SideMenu, enterprisePermissions: {}} + ctrl = $controller 'sideMenuCtrl', {$scope: scope, enterprise: enterprise, SideMenu: SideMenu, enterprisePermissions: {}} describe "initialisation", -> it "stores enterprise", -> - expect(scope.Enterprise).toEqual Enterprise.enterprise + expect(scope.Enterprise).toEqual enterprise it "sets the item list", -> expect(SideMenu.setItems).toHaveBeenCalled diff --git a/spec/javascripts/unit/admin/enterprises/services/enterprise_payment_methods_spec.js.coffee b/spec/javascripts/unit/admin/enterprises/services/enterprise_payment_methods_spec.js.coffee index 28052c59f9..0a719b203a 100644 --- a/spec/javascripts/unit/admin/enterprises/services/enterprise_payment_methods_spec.js.coffee +++ b/spec/javascripts/unit/admin/enterprises/services/enterprise_payment_methods_spec.js.coffee @@ -1,19 +1,18 @@ describe "EnterprisePaymentMethods service", -> - Enterprise = null + enterprise = null PaymentMethods = null EnterprisePaymentMethods = null beforeEach -> - Enterprise = - enterprise: - payment_method_ids: [ 1, 3 ] + enterprise = + payment_method_ids: [ 1, 3 ] PaymentMethods = paymentMethods: [ { id: 1 }, { id: 2 }, { id: 3 }, { id: 4 } ] module 'admin.enterprises' module ($provide) -> $provide.value 'PaymentMethods', PaymentMethods - $provide.value 'Enterprise', Enterprise + $provide.value 'enterprise', enterprise null inject (_EnterprisePaymentMethods_) -> diff --git a/spec/javascripts/unit/admin/enterprises/services/enterprise_shipping_methods_spec.js.coffee b/spec/javascripts/unit/admin/enterprises/services/enterprise_shipping_methods_spec.js.coffee index 4cbcf9ab25..4b857023b8 100644 --- a/spec/javascripts/unit/admin/enterprises/services/enterprise_shipping_methods_spec.js.coffee +++ b/spec/javascripts/unit/admin/enterprises/services/enterprise_shipping_methods_spec.js.coffee @@ -1,19 +1,18 @@ describe "EnterpriseShippingMethods service", -> - Enterprise = null + enterprise = null ShippingMethods = null EnterpriseShippingMethods = null beforeEach -> - Enterprise = - enterprise: - shipping_method_ids: [ 1, 3 ] + enterprise = + shipping_method_ids: [ 1, 3 ] ShippingMethods = shippingMethods: [ { id: 1 }, { id: 2 }, { id: 3 }, { id: 4 } ] module 'admin.enterprises' module ($provide) -> $provide.value 'ShippingMethods', ShippingMethods - $provide.value 'Enterprise', Enterprise + $provide.value 'enterprise', enterprise null inject (_EnterpriseShippingMethods_) -> diff --git a/spec/javascripts/unit/admin/enterprises/services/enterprises_spec.js.coffee b/spec/javascripts/unit/admin/enterprises/services/enterprises_spec.js.coffee new file mode 100644 index 0000000000..fb653d2df4 --- /dev/null +++ b/spec/javascripts/unit/admin/enterprises/services/enterprises_spec.js.coffee @@ -0,0 +1,130 @@ +describe "Enterprises service", -> + Enterprises = EnterpriseResource = enterprises = $httpBackend = null + + beforeEach -> + module 'admin.enterprises' + + this.addMatchers + toDeepEqual: (expected) -> + return angular.equals(this.actual, expected) + + inject ($q, _$httpBackend_, _Enterprises_, _EnterpriseResource_) -> + Enterprises = _Enterprises_ + EnterpriseResource = _EnterpriseResource_ + $httpBackend = _$httpBackend_ + + describe "#index", -> + result = response = null + + beforeEach -> + response = [{ id: 5, name: 'Enterprise 1'}] + + describe "when no params are passed", -> + beforeEach -> + $httpBackend.expectGET('/admin/enterprises.json').respond 200, response + result = Enterprises.index() + $httpBackend.flush() + + it "stores returned data in @enterprisesByID, with ids as keys", -> + # EnterpriseResource returns instances of Resource rather than raw objects + expect(Enterprises.enterprisesByID).toDeepEqual { 5: response[0] } + + it "stores returned data in @pristineByID, with ids as keys", -> + expect(Enterprises.pristineByID).toDeepEqual { 5: response[0] } + + it "returns an array of enterprises", -> + expect(result).toDeepEqual response + + describe "when params are passed", -> + describe "where includeBlank param is truthy", -> + beforeEach -> + params = {includeBlank: true, someParam: 'someVal'} + $httpBackend.expectGET('/admin/enterprises.json?someParam=someVal').respond 200, response + result = Enterprises.index(params) + $httpBackend.flush() + + it "returns an array of enterprises, with a blank option appended to the beginning", -> + expect(result).toDeepEqual [{id: '0', name: 'All'} ,{ id: 5, name: 'Enterprise 1'}] + + describe "where includeBlank param is falsey", -> + beforeEach -> + params = {includeBlank: false, someParam: 'someVal'} + $httpBackend.expectGET('/admin/enterprises.json?someParam=someVal').respond 200, response + result = Enterprises.index(params) + $httpBackend.flush() + + it "returns an array of enterprises, with a blank option appended to the beginning", -> + expect(result).toDeepEqual response + + + describe "#save", -> + result = null + + describe "success", -> + enterprise = null + resolved = false + + beforeEach -> + enterprise = new EnterpriseResource({ id: 15, permalink: 'enterprise1', name: 'Enterprise 1' }) + $httpBackend.expectPUT('/admin/enterprises/enterprise1.json').respond 200, { id: 15, name: 'Enterprise 1'} + Enterprises.save(enterprise).then( -> resolved = true) + $httpBackend.flush() + + it "updates the pristine copy of the enterprise", -> + # Resource results have extra properties ($then, $promise) that cause them to not + # be exactly equal to the response object provided to the expectPUT clause above. + expect(Enterprises.pristineByID[15]).toEqual enterprise + + it "resolves the promise", -> + expect(resolved).toBe(true); + + + describe "failure", -> + enterprise = null + rejected = false + + beforeEach -> + enterprise = new EnterpriseResource( { id: 15, permalink: 'permalink', name: 'Enterprise 1' } ) + $httpBackend.expectPUT('/admin/enterprises/permalink.json').respond 422, { error: 'obj' } + Enterprises.save(enterprise).catch( -> rejected = true) + $httpBackend.flush() + + it "does not update the pristine copy of the enterprise", -> + expect(Enterprises.pristineByID[15]).toBeUndefined() + + it "rejects the promise", -> + expect(rejected).toBe(true); + + describe "#saved", -> + describe "when attributes of the object have been altered", -> + beforeEach -> + spyOn(Enterprises, "diff").andReturn ["attr1", "attr2"] + + it "returns false", -> + expect(Enterprises.saved({})).toBe false + + describe "when attributes of the object have not been altered", -> + beforeEach -> + spyOn(Enterprises, "diff").andReturn [] + + it "returns false", -> + expect(Enterprises.saved({})).toBe true + + + describe "diff", -> + beforeEach -> + Enterprises.pristineByID = { 23: { id: 23, name: "ent1", is_primary_producer: true } } + + it "returns a list of properties that have been altered", -> + expect(Enterprises.diff({ id: 23, name: "enterprise123", is_primary_producer: true })).toEqual ["name"] + + + describe "resetAttribute", -> + enterprise = { id: 23, name: "ent1", is_primary_producer: true } + + beforeEach -> + Enterprises.pristineByID = { 23: { id: 23, name: "enterprise1", is_primary_producer: true } } + + it "resets the specified value according to the pristine record", -> + Enterprises.resetAttribute(enterprise, "name") + expect(enterprise.name).toEqual "enterprise1" diff --git a/spec/javascripts/unit/admin/enterprises/services/permalink_checker_spec.js.coffee b/spec/javascripts/unit/admin/enterprises/services/permalink_checker_spec.js.coffee index 6eef847858..3814d74cbc 100644 --- a/spec/javascripts/unit/admin/enterprises/services/permalink_checker_spec.js.coffee +++ b/spec/javascripts/unit/admin/enterprises/services/permalink_checker_spec.js.coffee @@ -1,6 +1,10 @@ describe "Permalink Checker service", -> PermalinkChecker = null $httpBackend = null + permalink = "this-is-a-permalink" + permalink_too_long = "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa" + permalink_invalid_chars = "" + beforeEach -> module 'admin.enterprises' @@ -8,7 +12,31 @@ describe "Permalink Checker service", -> $httpBackend = _$httpBackend_ PermalinkChecker = $injector.get("PermalinkChecker") - it "sends an http request to check the permalink", -> - permalink = "this-is-a-permalink" - $httpBackend.expectGET "/enterprises/check_permalink?permalink=#{permalink}" - PermalinkChecker.check(permalink) \ No newline at end of file + it "responds to available permalinks", -> + $httpBackend.expectGET("/enterprises/check_permalink?permalink=#{permalink}").respond permalink + PermalinkChecker.check(permalink).then (data) -> + expect(data.permalink).toEqual permalink + expect(data.available).toEqual "Available" + $httpBackend.flush() + + it "responds to unavailable permalinks", -> + $httpBackend.expectGET("/enterprises/check_permalink?permalink=#{permalink}").respond 409, permalink + PermalinkChecker.check(permalink).then (data) -> + expect(data.permalink).toEqual permalink + expect(data.available).toEqual "Unavailable" + $httpBackend.flush() + + describe "invalid data", -> + it "errors for permalinks that are too long", -> + $httpBackend.expectGET("/enterprises/check_permalink?permalink=#{permalink}").respond permalink_too_long + PermalinkChecker.check(permalink).then (data) -> + expect(data.permalink).toEqual permalink + expect(data.available).toEqual "Error" + $httpBackend.flush() + + it "errors for permalinks that contain invalid characters", -> + $httpBackend.expectGET("/enterprises/check_permalink?permalink=#{permalink}").respond permalink_invalid_chars + PermalinkChecker.check(permalink).then (data) -> + expect(data.permalink).toEqual permalink + expect(data.available).toEqual "Error" + $httpBackend.flush() diff --git a/spec/javascripts/unit/admin/index_utils/controllers/columns_controller_spec.js.coffee b/spec/javascripts/unit/admin/index_utils/controllers/columns_controller_spec.js.coffee new file mode 100644 index 0000000000..5fd79e71bf --- /dev/null +++ b/spec/javascripts/unit/admin/index_utils/controllers/columns_controller_spec.js.coffee @@ -0,0 +1,17 @@ +describe "ColumnsCtrl", -> + ctrl = null + scope = null + Columns = null + + beforeEach -> + Columns = { columns: { name: { visible: true} } } + + module('admin.indexUtils') + inject ($controller, $rootScope) -> + scope = $rootScope + ctrl = $controller 'ColumnsCtrl', {$scope: scope, Columns: Columns} + + it "initialises data", -> + expect(scope.columns).toEqual Columns.columns + expect(scope.predicate).toEqual "" + expect(scope.reverse).toEqual false diff --git a/spec/javascripts/unit/admin/index_utils/directives/panel_row_spec.js.coffee b/spec/javascripts/unit/admin/index_utils/directives/panel_row_spec.js.coffee new file mode 100644 index 0000000000..2161afe7de --- /dev/null +++ b/spec/javascripts/unit/admin/index_utils/directives/panel_row_spec.js.coffee @@ -0,0 +1,33 @@ +describe "PanelRow directive", -> + Panels = null + element = null + directiveScope = null + + beforeEach -> + module 'admin.indexUtils' + + beforeEach inject ($rootScope, $compile, $injector, $templateCache, _Panels_) -> + Panels = _Panels_ + $templateCache.put 'admin/panel.html', '{{ template }}' + # Declare the directive HTML. + element = angular.element('
') + # Define the root scope. + scope = $rootScope + # Compile and digest the directive. + $compile(element) scope + scope.$digest() + + directiveScope = element.find('span').scope() + return + + describe "initialisation", -> + it "registers the scope with the panels service", -> + expect(Panels.panels[12]).toEqual directiveScope + + describe "setting the selected panel", -> + beforeEach -> + directiveScope.setSelected('panel1') + + it 'updates the active template on the scope', -> + expect(element.find('span').html()).toEqual "admin/panels/template.html" + return diff --git a/spec/javascripts/unit/admin/index_utils/services/columns_spec.js.coffee b/spec/javascripts/unit/admin/index_utils/services/columns_spec.js.coffee new file mode 100644 index 0000000000..2bff5e5a73 --- /dev/null +++ b/spec/javascripts/unit/admin/index_utils/services/columns_spec.js.coffee @@ -0,0 +1,40 @@ +describe "Columns service", -> + Columns = null + + beforeEach -> + module 'admin.indexUtils' + + inject (_Columns_) -> + Columns = _Columns_ + + describe "setting columns", -> + it "sets resets @columns and copies each column of the provided object across", -> + Columns.setColumns({ name: { visible: true } }) + expect(Columns.columns).toEqual { name: { visible: true } } + + it "calls calculateVisibleCount", -> + spyOn(Columns, "calculateVisibleCount") + Columns.setColumns({ name: { visible: true } }) + expect(Columns.calculateVisibleCount).toHaveBeenCalled() + + describe "toggling a column", -> + it "switches the visibility of the given column", -> + column = { visible: false } + Columns.toggleColumn(column) + expect(column.visible).toBe true + + it "calls calculateVisibleCount", -> + spyOn(Columns, "calculateVisibleCount") + Columns.toggleColumn({ visible: false }) + expect(Columns.calculateVisibleCount).toHaveBeenCalled() + + describe "calculating visibleCount", -> + it "counts the number of columns ", -> + Columns.columns = { col1: { visible: false }, col2: { visible: true }, col3: { visible: true }, col4: { visible: false } } + Columns.calculateVisibleCount() + expect(Columns.visibleCount).toBe 2 + + it "$broadcasts the updated visible count to $rootScope", inject ($rootScope) -> + spyOn($rootScope, "$broadcast") + Columns.calculateVisibleCount() + expect($rootScope.$broadcast).toHaveBeenCalled() diff --git a/spec/javascripts/unit/admin/index_utils/services/panels_spec.js.coffee b/spec/javascripts/unit/admin/index_utils/services/panels_spec.js.coffee new file mode 100644 index 0000000000..a55d9ffa61 --- /dev/null +++ b/spec/javascripts/unit/admin/index_utils/services/panels_spec.js.coffee @@ -0,0 +1,52 @@ +describe "Panels service", -> + Panels = null + + beforeEach -> + module 'admin.indexUtils' + + inject (_Panels_) -> + Panels = _Panels_ + + describe "registering panels", -> + it "adds the panel provided scope to @panelsm indexed by the provided id", -> + Panels.register(23, { some: 'scope'} ) + expect(Panels.panels[23]).toEqual { some: 'scope' } + + it "ignores the input if id or scope are null", -> + Panels.register(null, { some: 'scope'} ) + Panels.register(23, null) + expect(Panels.panels).toEqual { } + + describe "toggling a panel", -> + scopeMock = null + + beforeEach -> + scopeMock = + open: jasmine.createSpy('open') + close: jasmine.createSpy('close') + setSelected: jasmine.createSpy('setSelected') + Panels.panels = { '12': scopeMock } + + describe "when no panel is currently selected", -> + beforeEach -> + scopeMock.getSelected = jasmine.createSpy('getSelected').andReturn(null) + Panels.toggle(12, 'panel_name') + + it "calls #open on the scope", -> + expect(scopeMock.open).toHaveBeenCalledWith('panel_name') + + describe "when #toggle is called for the currently selected panel", -> + beforeEach -> + scopeMock.getSelected = jasmine.createSpy('getSelected').andReturn('panel_name') + Panels.toggle(12, 'panel_name') + + it "calls #close on the scope", -> + expect(scopeMock.close).toHaveBeenCalled() + + describe "when #toggle is called for a different panel", -> + beforeEach -> + scopeMock.getSelected = jasmine.createSpy('getSelected').andReturn('some_other_panel_name') + Panels.toggle(12, 'panel_name') + + it "calls #setSelected on the scope", -> + expect(scopeMock.setSelected).toHaveBeenCalledWith('panel_name') diff --git a/spec/javascripts/unit/admin/index_utils/services/pending_changes_spec.js.coffee b/spec/javascripts/unit/admin/index_utils/services/pending_changes_spec.js.coffee new file mode 100644 index 0000000000..31b85df217 --- /dev/null +++ b/spec/javascripts/unit/admin/index_utils/services/pending_changes_spec.js.coffee @@ -0,0 +1,149 @@ +describe "Pending Changes", -> + resourcesMock = pendingChanges = null + + beforeEach -> + + resourcesMock = + update: jasmine.createSpy('update').andCallFake (change) -> + $promise: + then: (successFn, errorFn) -> + return successFn({propertyName: "new_value"}) if change.success + errorFn("error") + + module 'admin.indexUtils', ($provide) -> + $provide.value 'resources', resourcesMock + return + + inject (_pendingChanges_) -> + pendingChanges = _pendingChanges_ + + + describe "adding a new change", -> + it "adds a new object with key of id if it does not already exist", -> + expect(pendingChanges.pendingChanges).toEqual {} + expect(pendingChanges.pendingChanges["1"]).not.toBeDefined() + pendingChanges.add 1, "propertyName", { a: 1 } + expect(pendingChanges.pendingChanges["1"]).toBeDefined() + + it "adds a new object with key of the altered attribute name if it does not already exist", -> + pendingChanges.add 1, "propertyName", { a: 1 } + expect(pendingChanges.pendingChanges["1"]).toBeDefined() + expect(pendingChanges.pendingChanges["1"]["propertyName"]).toEqual { a: 1 } + + it "replaces the existing object when adding a change to an attribute which already exists", -> + pendingChanges.add 1, "propertyName", { a: 1 } + expect(pendingChanges.pendingChanges["1"]).toBeDefined() + expect(pendingChanges.pendingChanges["1"]["propertyName"]).toEqual { a: 1 } + pendingChanges.add 1, "propertyName", { b: 2 } + expect(pendingChanges.pendingChanges["1"]["propertyName"]).toEqual { b: 2 } + + it "adds an attribute to key to a line item object when one already exists", -> + pendingChanges.add 1, "propertyName1", { a: 1 } + pendingChanges.add 1, "propertyName2", { b: 2 } + expect(pendingChanges.pendingChanges["1"]).toEqual { propertyName1: { a: 1}, propertyName2: { b: 2 } } + + describe "removing all existing changes", -> + it "resets pendingChanges object", -> + pendingChanges.pendingChanges = { 1: { "propertyName1": { a: 1 }, "propertyName2": { b: 2 } } } + expect(pendingChanges.pendingChanges["1"]["propertyName1"]).toBeDefined() + expect(pendingChanges.pendingChanges["1"]["propertyName2"]).toBeDefined() + pendingChanges.removeAll() + expect(pendingChanges.pendingChanges["1"]).not.toBeDefined() + expect(pendingChanges.pendingChanges).toEqual {} + + describe "removing an existing change", -> + it "deletes a change if it exists", -> + pendingChanges.pendingChanges = { 1: { "propertyName1": { a: 1 }, "propertyName2": { b: 2 } } } + expect(pendingChanges.pendingChanges["1"]["propertyName1"]).toBeDefined() + pendingChanges.remove 1, "propertyName1" + expect(pendingChanges.pendingChanges["1"]).toBeDefined() + expect(pendingChanges.pendingChanges["1"]["propertyName1"]).not.toBeDefined() + + it "deletes a line item object if it is empty", -> + pendingChanges.pendingChanges = { 1: { "propertyName1": { a: 1 } } } + expect(pendingChanges.pendingChanges["1"]["propertyName1"]).toBeDefined() + pendingChanges.remove 1, "propertyName1" + expect(pendingChanges.pendingChanges["1"]).not.toBeDefined() + + it "does nothing if key with specified attribute does not exist", -> + pendingChanges.pendingChanges = { 1: { "propertyName1": { a: 1 } } } + expect(pendingChanges.pendingChanges["1"]["propertyName1"]).toBeDefined() + pendingChanges.remove 1, "propertyName2" + expect(pendingChanges.pendingChanges["1"]["propertyName1"]).toEqual { a: 1 } + + it "does nothing if key with specified id does not exist", -> + pendingChanges.pendingChanges = { 1: { "propertyName1": { a: 1 } } } + expect(pendingChanges.pendingChanges["1"]["propertyName1"]).toBeDefined() + pendingChanges.remove 2, "propertyName1" + expect(pendingChanges.pendingChanges["1"]).toEqual { "propertyName1": { a: 1 } } + + describe "submitting an individual change to the server", -> + change = null + beforeEach -> + object = {id: 1} + scope = { reset: jasmine.createSpy('reset'), success: jasmine.createSpy('success'), error: jasmine.createSpy('error') }; + attr = "propertyName" + change = { object: object, scope: scope, attr: attr } + + + it "sends the correct object to dataSubmitter", -> + pendingChanges.submit change + expect(resourcesMock.update.calls.length).toEqual 1 + expect(resourcesMock.update).toHaveBeenCalledWith change + + describe "successful request", -> + beforeEach -> + change.success = true + + it "calls remove with id and attribute name", -> + spyOn(pendingChanges, "remove").andCallFake(->) + pendingChanges.submit change + expect(pendingChanges.remove.calls.length).toEqual 1 + expect(pendingChanges.remove).toHaveBeenCalledWith 1, "propertyName" + + it "calls reset on the relevant scope", -> + pendingChanges.submit change + expect(change.scope.reset).toHaveBeenCalledWith "new_value" + + it "calls success on the relevant scope", -> + pendingChanges.submit change + expect(change.scope.success).toHaveBeenCalled() + + describe "unsuccessful request", -> + beforeEach -> + change.success = false + + it "does not call remove", -> + spyOn(pendingChanges, "remove").andCallFake(->) + pendingChanges.submit change + expect(pendingChanges.remove).not.toHaveBeenCalled() + + it "does not call reset on the relevant scope", -> + pendingChanges.submit change + expect(change.scope.reset).not.toHaveBeenCalled() + + it "calls error on the relevant scope", -> + pendingChanges.submit change + expect(change.scope.error).toHaveBeenCalled() + + describe "cycling through all changes to submit to server", -> + it "sends the correct object to dataSubmitter", -> + spyOn(pendingChanges, "submit").andCallFake(->) + pendingChanges.pendingChanges = + 1: { "prop1": { attr: "prop1", value: 1 }, "prop2": { attr: "prop2", value: 2 } } + 2: { "prop1": { attr: "prop1", value: 2 }, "prop2": { attr: "prop2", value: 4 } } + 7: { "prop2": { attr: "prop2", value: 5 } } + pendingChanges.submitAll() + expect(pendingChanges.submit.calls.length).toEqual 5 + expect(pendingChanges.submit).toHaveBeenCalledWith { attr: "prop1", value: 1 } + expect(pendingChanges.submit).toHaveBeenCalledWith { attr: "prop2", value: 2 } + expect(pendingChanges.submit).toHaveBeenCalledWith { attr: "prop1", value: 2 } + expect(pendingChanges.submit).toHaveBeenCalledWith { attr: "prop2", value: 4 } + expect(pendingChanges.submit).toHaveBeenCalledWith { attr: "prop2", value: 5 } + + it "returns an array of promises representing all sumbit requests", -> + spyOn(pendingChanges, "submit").andCallFake (change) -> change.value + pendingChanges.pendingChanges = + 1: { "prop1": { attr: "prop1", value: 1 } } + 2: { "prop1": { attr: "prop1", value: 2 }, "prop2": { attr: "prop1", value: 4 } } + expect(pendingChanges.submitAll()).toEqual [ 1, 2, 4 ] diff --git a/spec/javascripts/unit/admin/index_utils/services/switch_class_spec.js.coffee b/spec/javascripts/unit/admin/index_utils/services/switch_class_spec.js.coffee new file mode 100644 index 0000000000..e7dedb4e92 --- /dev/null +++ b/spec/javascripts/unit/admin/index_utils/services/switch_class_spec.js.coffee @@ -0,0 +1,52 @@ +describe "switchClass service", -> + elementMock = timeoutMock = {} + removeClass = addClass = switchClassService = null + + beforeEach -> + addClass = jasmine.createSpy('addClass') + removeClass = jasmine.createSpy('removeClass') + elementMock = + addClass: addClass + removeClass: removeClass + timeoutMock = jasmine.createSpy('timeout').andReturn "new timeout" + timeoutMock.cancel = jasmine.createSpy('timeout.cancel') + + beforeEach -> + module "ofn.admin" , ($provide) -> + $provide.value '$timeout', timeoutMock + return + + beforeEach inject (switchClass) -> + switchClassService = switchClass + + it "calls addClass on the element once", -> + switchClassService elementMock, "addClass", [], false + expect(addClass).toHaveBeenCalledWith "addClass" + expect(addClass.calls.length).toEqual 1 + + it "calls removeClass on the element for ", -> + switchClassService elementMock, "", ["remClass1", "remClass2", "remClass3"], false + expect(removeClass).toHaveBeenCalledWith "remClass1" + expect(removeClass).toHaveBeenCalledWith "remClass2" + expect(removeClass).toHaveBeenCalledWith "remClass3" + expect(removeClass.calls.length).toEqual 3 + + it "call cancel on element.timout only if it exists", -> + switchClassService elementMock, "", [], false + expect(timeoutMock.cancel).not.toHaveBeenCalled() + elementMock.timeout = true + switchClassService elementMock, "", [], false + expect(timeoutMock.cancel).toHaveBeenCalled() + + it "doesn't set up a new timeout if 'timeout' is false", -> + switchClassService elementMock, "class1", ["class2"], false + expect(timeoutMock).not.toHaveBeenCalled() + + it "doesn't set up a new timeout if 'timeout' is a string", -> + switchClassService elementMock, "class1", ["class2"], "string" + expect(timeoutMock).not.toHaveBeenCalled() + + it "sets up a new timeout if 'timeout' parameter is an integer", -> + switchClassService elementMock, "class1", ["class2"], 1000 + expect(timeoutMock).toHaveBeenCalled() + expect(elementMock.timeout).toEqual "new timeout" diff --git a/spec/javascripts/unit/admin/line_items/controllers/line_items_controller_spec.js.coffee b/spec/javascripts/unit/admin/line_items/controllers/line_items_controller_spec.js.coffee new file mode 100644 index 0000000000..7a0b165e2b --- /dev/null +++ b/spec/javascripts/unit/admin/line_items/controllers/line_items_controller_spec.js.coffee @@ -0,0 +1,361 @@ +describe "LineItemsCtrl", -> + ctrl = scope = httpBackend = $timeout = VariantUnitManager = Enterprises = Orders = LineItems = OrderCycles = null + supplier = distributor = orderCycle = null + + beforeEach -> + module "admin.lineItems" + + this.addMatchers + toDeepEqual: (expected) -> + return angular.equals(this.actual, expected) + + beforeEach inject(($controller, $rootScope, $httpBackend, _$timeout_, _VariantUnitManager_, _Enterprises_, _Orders_, _LineItems_, _OrderCycles_) -> + scope = $rootScope.$new() + ctrl = $controller + $timeout = _$timeout_ + httpBackend = $httpBackend + Enterprises = _Enterprises_ + Orders = _Orders_ + LineItems = _LineItems_ + OrderCycles = _OrderCycles_ + VariantUnitManager = _VariantUnitManager_ + spyOn(window, "daysFromToday").andReturn "SomeDate" + spyOn(window, "formatDate").andReturn "SomeDate" + spyOn(window, "parseDate").andReturn "SomeDate" + + supplier = { id: 1, name: "Supplier" } + distributor = { id: 5, name: "Distributor" } + orderCycle = { id: 4, name: "OC1" } + order = { id: 9, order_cycle: { id: 4 }, distributor: { id: 5 }, number: "R123456" } + lineItem = { id: 7, quantity: 3, order: { id: 9 }, supplier: { id: 1 } } + + httpBackend.expectGET("/admin/orders.json?q%5Bcompleted_at_gt%5D=SomeDate&q%5Bcompleted_at_lt%5D=SomeDate&q%5Bcompleted_at_not_null%5D=true&q%5Bstate_not_eq%5D=canceled").respond [order] + httpBackend.expectGET("/admin/line_items.json?q%5Border%5D%5Bcompleted_at_gt%5D=SomeDate&q%5Border%5D%5Bcompleted_at_lt%5D=SomeDate&q%5Border%5D%5Bcompleted_at_not_null%5D=true&q%5Border%5D%5Bstate_not_eq%5D=canceled").respond [lineItem] + httpBackend.expectGET("/admin/enterprises/for_line_items.json?ams_prefix=basic&q%5Bsells_in%5D%5B%5D=own&q%5Bsells_in%5D%5B%5D=any").respond [distributor] + httpBackend.expectGET("/admin/order_cycles.json?ams_prefix=basic&as=distributor&q%5Borders_close_at_gt%5D=SomeDate").respond [orderCycle] + httpBackend.expectGET("/admin/enterprises/for_line_items.json?ams_prefix=basic&q%5Bis_primary_producer_eq%5D=true").respond [supplier] + + scope.bulk_order_form = jasmine.createSpyObj('bulk_order_form', ['$setPristine']) + + ctrl "LineItemsCtrl", {$scope: scope, $timeout: $timeout, Enterprises: Enterprises, Orders: Orders, LineItems: LineItems, OrderCycles: OrderCycles} + ) + + describe "before data is returned", -> + it "the RequestMonitor will have a state of loading", -> + expect(scope.RequestMonitor.loading).toBe true + + it "will not have reset the select filters", -> + expect(scope.distributorFilter).toBeUndefined() + expect(scope.supplierFilter).toBeUndefined() + expect(scope.orderCycleFilter).toBeUndefined() + expect(scope.quickSearch).toBeUndefined() + + it "will not have reset the form state to pristine", -> + expect(scope.bulk_order_form.$setPristine.calls.length).toEqual 0 + + describe "after data is returned", -> + beforeEach -> + httpBackend.flush() + $timeout.flush() + + describe "initialisation", -> + it "gets suppliers, adds a blank option as the first in the list", -> + expect(scope.suppliers).toDeepEqual [ { id : '0', name : 'All' }, supplier ] + + it "gets distributors, adds a blank option as the first in the list", -> + expect(scope.distributors).toDeepEqual [ { id : '0', name : 'All' }, distributor ] + + it "stores enterprises in an list that is accessible by id", -> + expect(Enterprises.enterprisesByID[1]).toDeepEqual supplier + + it "gets order cycles, adds a blank option as the first in the list", -> + expect(scope.orderCycles).toDeepEqual [ { id : '0', name : 'All' }, orderCycle ] + + it "gets orders, with dereferenced order cycles and distributors", -> + expect(scope.orders).toDeepEqual [ { id: 9, order_cycle: orderCycle, distributor: distributor, number: "R123456" } ] + + it "gets line_items, with dereferenced orders and suppliers", -> + expect(scope.lineItems).toDeepEqual [ { id: 7, quantity: 3, order: scope.orders[0], supplier: supplier } ] + + it "the RequestMonitor will have a state of loaded", -> + expect(scope.RequestMonitor.loading).toBe false + + it "resets the select filters", -> + expect(scope.distributorFilter).toBe '0' + expect(scope.supplierFilter).toBe '0' + expect(scope.orderCycleFilter).toBe '0' + expect(scope.quickSearch).toBe = "" + + it "resets the form state to pristine", -> + expect(scope.bulk_order_form.$setPristine.calls.length).toEqual 1 + + describe "deleting a line item", -> + order = line_item1 = line_item2 = null + + beforeEach inject((LineItemResource) -> + spyOn(window,"confirm").andReturn true + order = { number: "R12345678" } + line_item1 = new LineItemResource({ id: 1, order: order }) + line_item2 = new LineItemResource({ id: 2, order: order }) + scope.lineItems= [ line_item1, line_item2 ] + ) + + describe "where the request is successful", -> + beforeEach -> + httpBackend.expectDELETE("/admin/orders/R12345678/line_items/1.json").respond "nothing" + scope.deleteLineItem line_item1 + httpBackend.flush() + + it "removes the deleted item from the line_items array", -> + expect(scope.lineItems).toEqual [line_item2] + + describe "where the request is unsuccessful", -> + beforeEach -> + httpBackend.expectDELETE("/admin/orders/R12345678/line_items/1.json").respond 404, "NO CONTENT" + scope.deleteLineItem line_item1 + httpBackend.flush() + + it "does not remove line_item from the line_items array", -> + expect(scope.lineItems).toEqual [line_item1, line_item2] + + describe "deleting 'checked' line items", -> + line_item1 = line_item2 = line_item3 = line_item4 = null + + beforeEach -> + line_item1 = { name: "line item 1", checked: false } + line_item2 = { name: "line item 2", checked: true } + line_item3 = { name: "line item 3", checked: false } + line_item4 = { name: "line item 4", checked: true } + scope.lineItems = [ line_item1, line_item2, line_item3, line_item4 ] + + it "calls deletedLineItem for each 'checked' line item", -> + spyOn(scope, "deleteLineItem") + scope.deleteLineItems(scope.lineItems) + expect(scope.deleteLineItem).toHaveBeenCalledWith(line_item2) + expect(scope.deleteLineItem).toHaveBeenCalledWith(line_item4) + expect(scope.deleteLineItem).not.toHaveBeenCalledWith(line_item1) + expect(scope.deleteLineItem).not.toHaveBeenCalledWith(line_item3) + + describe "check boxes for line items", -> + line_item1 = line_item2 = null + + beforeEach -> + line_item1 = { name: "line item 1", checked: false } + line_item2 = { name: "line item 2", checked: false } + scope.filteredLineItems = [ line_item1, line_item2 ] + + it "keeps track of whether all filtered lines items are 'checked' or not", -> + expect(scope.allBoxesChecked()).toEqual false + line_item1.checked = true + expect(scope.allBoxesChecked()).toEqual false + line_item2.checked = true + expect(scope.allBoxesChecked()).toEqual true + line_item1.checked = false + expect(scope.allBoxesChecked()).toEqual false + + it "toggles the 'checked' attribute of all line items based to the value of allBoxesChecked", -> + scope.toggleAllCheckboxes() + expect(scope.allBoxesChecked()).toEqual true + line_item1.checked = false + expect(scope.allBoxesChecked()).toEqual false + scope.toggleAllCheckboxes() + expect(scope.allBoxesChecked()).toEqual true + scope.toggleAllCheckboxes() + expect(scope.allBoxesChecked()).toEqual false + + describe "unit calculations", -> + describe "fulfilled()", -> + it "returns '' if selectedUnitsVariant has no property 'variant_unit'", -> + expect(scope.fulfilled()).toEqual '' + + it "returns '' if selectedUnitsVariant has no property 'group_buy_unit_size' or group_buy_unit_size is 0", -> + scope.selectedUnitsProduct = { variant_unit: "weight", group_buy_unit_size: 0 } + expect(scope.fulfilled()).toEqual '' + scope.selectedUnitsProduct = { variant_unit: "weight" } + expect(scope.fulfilled()).toEqual '' + + it "returns '', and does not call Math.round if variant_unit is 'items'", -> + spyOn(Math,"round") + scope.selectedUnitsProduct = { variant_unit: "items", group_buy_unit_size: 10 } + expect(scope.fulfilled()).toEqual '' + expect(Math.round).not.toHaveBeenCalled() + + it "calls Math.round() if variant_unit is 'weight' or 'volume'", -> + spyOn(Math,"round") + scope.selectedUnitsProduct = { variant_unit: "weight", group_buy_unit_size: 10 } + scope.fulfilled() + expect(Math.round).toHaveBeenCalled() + scope.selectedUnitsProduct = { variant_unit: "volume", group_buy_unit_size: 10 } + scope.fulfilled() + expect(Math.round).toHaveBeenCalled() + + it "returns the quantity of fulfilled group buy units", -> + scope.selectedUnitsProduct = { variant_unit: "weight", group_buy_unit_size: 1000 } + expect(scope.fulfilled(1500)).toEqual 1.5 + + describe "allFinalWeightVolumesPresent()", -> + it "returns false if the unit_value of any item in filteredLineItems does not exist", -> + scope.filteredLineItems = [ + { final_weight_volume: 1000 } + { final_weight_volume: 3000 } + { final_weight_yayaya: 2000 } + ] + expect(scope.allFinalWeightVolumesPresent()).toEqual false + + it "returns false if the unit_value of any item in filteredLineItems is not a number greater than 0", -> + scope.filteredLineItems = [ + { final_weight_volume: 0 } + { final_weight_volume: 3000 } + { final_weight_volume: 2000 } + ] + expect(scope.allFinalWeightVolumesPresent()).toEqual false + scope.filteredLineItems = [ + { final_weight_volume: 'lalala' } + { final_weight_volume: 3000 } + { final_weight_volume: 2000 } + ] + expect(scope.allFinalWeightVolumesPresent()).toEqual false + + it "returns true if the unit_value of all items in filteredLineItems are numbers greater than 0", -> + scope.filteredLineItems = [ + { final_weight_volume: 1000 } + { final_weight_volume: 3000 } + { final_weight_volume: 2000 } + ] + expect(scope.allFinalWeightVolumesPresent()).toEqual true + + describe "sumUnitValues()", -> + it "returns the sum of the final_weight_volumes line_items", -> + scope.filteredLineItems = [ + { final_weight_volume: 2 } + { final_weight_volume: 7 } + { final_weight_volume: 21 } + ] + expect(scope.sumUnitValues()).toEqual 30 + + describe "sumMaxUnitValues()", -> + it "returns the sum of the product of unit_value and maxOf(max_quantity, pristine quantity) for specified line_items", -> + scope.filteredLineItems = [ + { id: 1, units_variant: { unit_value: 1 }, max_quantity: 5 } + { id: 2, units_variant: { unit_value: 2 }, max_quantity: 1 } + { id: 3, units_variant: { unit_value: 3 }, max_quantity: 10 } + ] + + expect(scope.sumMaxUnitValues()).toEqual 37 + + describe "formatting a value based upon the properties of a specified Units Variant", -> + # A Units Variant is an API object which holds unit properies of a variant + + beforeEach -> + spyOn(Math,"round").andCallThrough() + + it "returns '' if selectedUnitsVariant has no property 'variant_unit'", -> + expect(scope.formattedValueWithUnitName(1,{})).toEqual '' + + it "returns '', and does not call Math.round if variant_unit is 'items'", -> + unitsVariant = { variant_unit: "items" } + expect(scope.formattedValueWithUnitName(1,unitsVariant)).toEqual '' + expect(Math.round).not.toHaveBeenCalled() + + it "calls Math.round() if variant_unit is 'weight' or 'volume'", -> + unitsVariant = { variant_unit: "weight" } + scope.formattedValueWithUnitName(1,unitsVariant) + expect(Math.round).toHaveBeenCalled() + scope.selectedUnitsVariant = { variant_unit: "volume" } + scope.formattedValueWithUnitName(1,unitsVariant) + expect(Math.round).toHaveBeenCalled() + + it "calls Math.round with the quotient of scale and value, multiplied by 1000", -> + unitsVariant = { variant_unit: "weight" } + spyOn(VariantUnitManager, "getScale").andReturn 5 + scope.formattedValueWithUnitName(10, unitsVariant) + expect(Math.round).toHaveBeenCalledWith 10/5 * 1000 + + it "returns the result of Math.round divided by 1000, followed by the result of getUnitName", -> + unitsVariant = { variant_unit: "weight" } + spyOn(VariantUnitManager, "getScale").andReturn 1000 + spyOn(VariantUnitManager, "getUnitName").andReturn "kg" + expect(scope.formattedValueWithUnitName(2000,unitsVariant)).toEqual "2 kg" + + describe "updating the price upon updating the weight of a line item", -> + beforeEach -> + LineItems.pristineByID = { 1: { price: 2.00, quantity: 1, final_weight_volume: 2000 } } + + it "updates the price if the weight is changed", -> + scope.filteredLineItems = [ + { id: 1, price: 2.00, quantity: 1, final_weight_volume: 4000 } + ] + scope.weightAdjustedPrice(scope.filteredLineItems[0]) + expect(scope.filteredLineItems[0].price).toEqual 4.00 + + it "doesn't update the price if the weight <= 0", -> + scope.filteredLineItems = [ + { id: 1, price: 2.00, quantity: 1, final_weight_volume: 0 } + ] + scope.weightAdjustedPrice(scope.filteredLineItems[0]) + expect(scope.filteredLineItems[0].price).toEqual 2.00 + + it "doesn't update the price if the weight is an empty string", -> + scope.filteredLineItems = [ + { id: 1, price: 2.00, quantity: 1, final_weight_volume: "" } + ] + scope.weightAdjustedPrice(scope.filteredLineItems[0]) + expect(scope.filteredLineItems[0].price).toEqual 2.00 + + describe "updating final_weight_volume upon updating the quantity for a line_item", -> + beforeEach -> + LineItems.pristineByID = { 1: { price: 2.00, quantity: 1, final_weight_volume: 2000 } } + spyOn(scope, "weightAdjustedPrice") + + it "updates the weight if the quantity is changed, then calls weightAdjustedPrice()", -> + scope.filteredLineItems = [ + { id: 1, price: 2.00, quantity: 2, final_weight_volume: 0 } + ] + scope.updateOnQuantity(scope.filteredLineItems[0]) + expect(scope.filteredLineItems[0].final_weight_volume).toEqual 4000 + expect(scope.weightAdjustedPrice).toHaveBeenCalled() + + it "doesn't update the weight if the quantity <= 0", -> + scope.filteredLineItems = [ + { id: 1, price: 2.00, quantity: 0, final_weight_volume: 1000 } + ] + scope.updateOnQuantity(scope.filteredLineItems[0]) + expect(scope.filteredLineItems[0].final_weight_volume).toEqual 1000 + + it "doesn't update the weight if the quantity is an empty string", -> + scope.filteredLineItems = [ + { id: 1, price: 2.00, quantity: "", final_weight_volume: 1000 } + ] + scope.updateOnQuantity(scope.filteredLineItems[0]) + expect(scope.filteredLineItems[0].final_weight_volume).toEqual 1000 + + +describe "Auxiliary functions", -> + describe "getting a zero filled two digit number", -> + it "returns the number as a string if its value is greater than or equal to 10", -> + expect(twoDigitNumber(10)).toEqual "10" + expect(twoDigitNumber(15)).toEqual "15" + expect(twoDigitNumber(99)).toEqual "99" + + it "returns the number formatted as a zero filled string if its value is less than 10", -> + expect(twoDigitNumber(0)).toEqual "00" + expect(twoDigitNumber(1)).toEqual "01" + expect(twoDigitNumber(9)).toEqual "09" + + describe "formatting dates and times", -> + date = null + + beforeEach -> + date = new Date + date.setYear(2010) + date.setMonth(4) # Zero indexed, so 4 is May + date.setDate(15) + date.setHours(5) + date.setMinutes(10) + date.setSeconds(30) + + it "returns a date formatted as yyyy-mm-dd", -> + expect(formatDate(date)).toEqual "2010-05-15" + + it "returns a time formatted as hh-MM:ss", -> + expect(formatTime(date)).toEqual "05:10:30" diff --git a/spec/javascripts/unit/admin/line_items/services/line_items_spec.js.coffee b/spec/javascripts/unit/admin/line_items/services/line_items_spec.js.coffee new file mode 100644 index 0000000000..44948ecb37 --- /dev/null +++ b/spec/javascripts/unit/admin/line_items/services/line_items_spec.js.coffee @@ -0,0 +1,155 @@ +describe "LineItems service", -> + LineItems = LineItemResource = lineItems = $httpBackend = null + + beforeEach -> + module 'admin.lineItems' + + this.addMatchers + toDeepEqual: (expected) -> + return angular.equals(this.actual, expected) + + inject ($q, _$httpBackend_, _LineItems_, _LineItemResource_) -> + LineItems = _LineItems_ + LineItemResource = _LineItemResource_ + $httpBackend = _$httpBackend_ + + describe "#index", -> + result = response = null + + beforeEach -> + response = [{ id: 5, name: 'LineItem 1'}] + $httpBackend.expectGET('/admin/line_items.json').respond 200, response + result = LineItems.index() + $httpBackend.flush() + + it "stores returned data in @lineItemsByID, with ids as keys", -> + # LineItemResource returns instances of Resource rather than raw objects + expect(LineItems.lineItemsByID).toDeepEqual { 5: response[0] } + + it "stores returned data in @pristineByID, with ids as keys", -> + expect(LineItems.pristineByID).toDeepEqual { 5: response[0] } + + it "returns an array of line items", -> + expect(result).toDeepEqual response + + + describe "#save", -> + describe "success", -> + lineItem = null + resolved = false + + beforeEach -> + lineItem = new LineItemResource({ id: 15, order: { number: '12345678'} }) + $httpBackend.expectPUT('/admin/orders/12345678/line_items/15.json').respond 200, { id: 15, name: 'LineItem 1'} + LineItems.save(lineItem).then( -> resolved = true) + $httpBackend.flush() + + it "updates the pristine copy of the lineItem", -> + # Resource results have extra properties ($then, $promise) that cause them to not + # be exactly equal to the response object provided to the expectPUT clause above. + expect(LineItems.pristineByID[15]).toEqual lineItem + + it "resolves the promise", -> + expect(resolved).toBe(true); + + + describe "failure", -> + lineItem = null + rejected = false + + beforeEach -> + lineItem = new LineItemResource( { id: 15, order: { number: '12345678'} } ) + $httpBackend.expectPUT('/admin/orders/12345678/line_items/15.json').respond 422, { error: 'obj' } + LineItems.save(lineItem).catch( -> rejected = true) + $httpBackend.flush() + + it "does not update the pristine copy of the lineItem", -> + expect(LineItems.pristineByID[15]).toBeUndefined() + + it "rejects the promise", -> + expect(rejected).toBe(true); + + describe "#isSaved", -> + describe "when attributes of the object have been altered", -> + beforeEach -> + spyOn(LineItems, "diff").andReturn ["attr1", "attr2"] + + it "returns false", -> + expect(LineItems.isSaved({})).toBe false + + describe "when attributes of the object have not been altered", -> + beforeEach -> + spyOn(LineItems, "diff").andReturn [] + + it "returns false", -> + expect(LineItems.isSaved({})).toBe true + + + describe "diff", -> + beforeEach -> + LineItems.pristineByID = { 23: { id: 23, price: 15, quantity: 3, something: 3 } } + + it "returns a list of properties that have been altered and are in the list of updateable attrs", -> + expect(LineItems.diff({ id: 23, price: 12, quantity: 3 })).toEqual ["price"] + expect(LineItems.diff({ id: 23, price: 15, something: 1 })).toEqual [] + + + describe "resetAttribute", -> + lineItem = { id: 23, price: 15 } + + beforeEach -> + LineItems.pristineByID = { 23: { id: 23, price: 12, quantity: 3 } } + + it "resets the specified value according to the pristine record", -> + LineItems.resetAttribute(lineItem, "price") + expect(lineItem.price).toEqual 12 + + describe "#delete", -> + describe "success", -> + callback = jasmine.createSpy("callback") + lineItem = null + resolved = rejected = false + + beforeEach -> + lineItem = new LineItemResource({ id: 15, order: { number: '12345678'} }) + LineItems.pristineByID[15] = lineItem + LineItems.lineItemsByID[15] = lineItem + $httpBackend.expectDELETE('/admin/orders/12345678/line_items/15.json').respond 200, { id: 15, name: 'LineItem 1'} + LineItems.delete(lineItem, callback).then( -> resolved = true).catch( -> rejected = true) + $httpBackend.flush() + + it "updates the pristine copy of the lineItem", -> + expect(LineItems.pristineByID[15]).toBeUndefined() + expect(LineItems.lineItemsByID[15]).toBeUndefined() + + it "runs the callback", -> + expect(callback).toHaveBeenCalled() + + it "resolves the promise", -> + expect(resolved).toBe(true) + expect(rejected).toBe(false) + + + describe "failure", -> + callback = jasmine.createSpy("callback") + lineItem = null + resolved = rejected = false + + beforeEach -> + lineItem = new LineItemResource({ id: 15, order: { number: '12345678'} }) + LineItems.pristineByID[15] = lineItem + LineItems.lineItemsByID[15] = lineItem + $httpBackend.expectDELETE('/admin/orders/12345678/line_items/15.json').respond 422, { error: 'obj' } + LineItems.delete(lineItem, callback).then( -> resolved = true).catch( -> rejected = true) + $httpBackend.flush() + + it "does not update the pristine copy of the lineItem", -> + expect(LineItems.pristineByID[15]).toBeDefined() + expect(LineItems.lineItemsByID[15]).toBeDefined() + + it "does not run the callback", -> + expect(callback).not.toHaveBeenCalled() + + it "rejects the promise", -> + expect(resolved).toBe(false) + expect(rejected).toBe(true) diff --git a/spec/javascripts/unit/admin/order_cycles/controllers/simple_create.js.coffee b/spec/javascripts/unit/admin/order_cycles/controllers/simple_create.js.coffee index 0f1e043467..09d5524fac 100644 --- a/spec/javascripts/unit/admin/order_cycles/controllers/simple_create.js.coffee +++ b/spec/javascripts/unit/admin/order_cycles/controllers/simple_create.js.coffee @@ -27,7 +27,7 @@ describe "AdminSimpleCreateOrderCycleCtrl", -> index: jasmine.createSpy() ocInstance = {} - module('admin.order_cycles') + module('admin.orderCycles') inject ($controller) -> ctrl = $controller 'AdminSimpleCreateOrderCycleCtrl', {$scope: scope, OrderCycle: OrderCycle, Enterprise: Enterprise, EnterpriseFee: EnterpriseFee, ocInstance: ocInstance} diff --git a/spec/javascripts/unit/admin/order_cycles/controllers/simple_edit.js.coffee b/spec/javascripts/unit/admin/order_cycles/controllers/simple_edit.js.coffee index 0e3275d886..dbad5a9f05 100644 --- a/spec/javascripts/unit/admin/order_cycles/controllers/simple_edit.js.coffee +++ b/spec/javascripts/unit/admin/order_cycles/controllers/simple_edit.js.coffee @@ -3,6 +3,7 @@ describe "AdminSimpleEditOrderCycleCtrl", -> scope = {} location = {} OrderCycle = {} + StatusMessage = {} Enterprise = {} EnterpriseFee = {} incoming_exchange = {} @@ -23,9 +24,9 @@ describe "AdminSimpleEditOrderCycleCtrl", -> EnterpriseFee = index: jasmine.createSpy() - module('admin.order_cycles') + module('admin.orderCycles') inject ($controller) -> - ctrl = $controller 'AdminSimpleEditOrderCycleCtrl', {$scope: scope, $location: location, OrderCycle: OrderCycle, Enterprise: Enterprise, EnterpriseFee: EnterpriseFee} + ctrl = $controller 'AdminSimpleEditOrderCycleCtrl', {$scope: scope, $location: location, OrderCycle: OrderCycle, Enterprise: Enterprise, EnterpriseFee: EnterpriseFee, StatusMessage: StatusMessage} describe "initialisation", -> enterprise = {id: 123} diff --git a/spec/javascripts/unit/admin/order_cycles/services/order_cycles_spec.js.coffee b/spec/javascripts/unit/admin/order_cycles/services/order_cycles_spec.js.coffee new file mode 100644 index 0000000000..2ddfe92407 --- /dev/null +++ b/spec/javascripts/unit/admin/order_cycles/services/order_cycles_spec.js.coffee @@ -0,0 +1,130 @@ +describe "OrderCycles service", -> + OrderCycles = OrderCycleResource = orderCycles = $httpBackend = null + + beforeEach -> + module 'admin.orderCycles' + + this.addMatchers + toDeepEqual: (expected) -> + return angular.equals(this.actual, expected) + + inject ($q, _$httpBackend_, _OrderCycles_, _OrderCycleResource_) -> + OrderCycles = _OrderCycles_ + OrderCycleResource = _OrderCycleResource_ + $httpBackend = _$httpBackend_ + + describe "#index", -> + result = response = null + + beforeEach -> + response = [{ id: 5, name: 'OrderCycle 1'}] + + describe "when no params are passed", -> + beforeEach -> + $httpBackend.expectGET('/admin/order_cycles.json').respond 200, response + result = OrderCycles.index() + $httpBackend.flush() + + it "stores returned data in @orderCyclesByID, with ids as keys", -> + # OrderCycleResource returns instances of Resource rather than raw objects + expect(OrderCycles.orderCyclesByID).toDeepEqual { 5: response[0] } + + it "stores returned data in @pristineByID, with ids as keys", -> + expect(OrderCycles.pristineByID).toDeepEqual { 5: response[0] } + + it "returns an array of orderCycles", -> + expect(result).toDeepEqual response + + describe "when no params are passed", -> + describe "where includeBlank param is truthy", -> + beforeEach -> + params = {includeBlank: true, someParam: 'someVal'} + $httpBackend.expectGET('/admin/order_cycles.json?someParam=someVal').respond 200, response + result = OrderCycles.index(params) + $httpBackend.flush() + + it "returns an array of orderCycles", -> + expect(result).toDeepEqual [{id: '0', name: 'All'} ,{ id: 5, name: 'OrderCycle 1'}] + + describe "where includeBlank param is falsey", -> + beforeEach -> + params = {includeBlank: false, someParam: 'someVal'} + $httpBackend.expectGET('/admin/order_cycles.json?someParam=someVal').respond 200, response + result = OrderCycles.index(params) + $httpBackend.flush() + + it "returns an array of orderCycles", -> + expect(result).toDeepEqual response + + + describe "#save", -> + result = null + + describe "success", -> + orderCycle = null + resolved = false + + beforeEach -> + orderCycle = new OrderCycleResource({ id: 15, name: 'OrderCycle 1' }) + $httpBackend.expectPUT('/admin/order_cycles/15.json').respond 200, { id: 15, name: 'OrderCycle 1'} + OrderCycles.save(orderCycle).then( -> resolved = true) + $httpBackend.flush() + + it "updates the pristine copy of the orderCycle", -> + # Resource results have extra properties ($then, $promise) that cause them to not + # be exactly equal to the response object provided to the expectPUT clause above. + expect(OrderCycles.pristineByID[15]).toEqual orderCycle + + it "resolves the promise", -> + expect(resolved).toBe(true); + + + describe "failure", -> + orderCycle = null + rejected = false + + beforeEach -> + orderCycle = new OrderCycleResource( { id: 15, name: 'OrderCycle 1' } ) + $httpBackend.expectPUT('/admin/order_cycles/15.json').respond 422, { error: 'obj' } + OrderCycles.save(orderCycle).catch( -> rejected = true) + $httpBackend.flush() + + it "does not update the pristine copy of the orderCycle", -> + expect(OrderCycles.pristineByID[15]).toBeUndefined() + + it "rejects the promise", -> + expect(rejected).toBe(true); + + describe "#saved", -> + describe "when attributes of the object have been altered", -> + beforeEach -> + spyOn(OrderCycles, "diff").andReturn ["attr1", "attr2"] + + it "returns false", -> + expect(OrderCycles.saved({})).toBe false + + describe "when attributes of the object have not been altered", -> + beforeEach -> + spyOn(OrderCycles, "diff").andReturn [] + + it "returns false", -> + expect(OrderCycles.saved({})).toBe true + + + describe "diff", -> + beforeEach -> + OrderCycles.pristineByID = { 23: { id: 23, name: "ent1", is_primary_producer: true } } + + it "returns a list of properties that have been altered", -> + expect(OrderCycles.diff({ id: 23, name: "orderCycle123", is_primary_producer: true })).toEqual ["name"] + + + describe "resetAttribute", -> + orderCycle = { id: 23, name: "ent1", is_primary_producer: true } + + beforeEach -> + OrderCycles.pristineByID = { 23: { id: 23, name: "orderCycle1", is_primary_producer: true } } + + it "resets the specified value according to the pristine record", -> + OrderCycles.resetAttribute(orderCycle, "name") + expect(orderCycle.name).toEqual "orderCycle1" diff --git a/spec/javascripts/unit/admin/orders/controllers/orders_controller_spec.js.coffee b/spec/javascripts/unit/admin/orders/controllers/orders_controller_spec.js.coffee new file mode 100644 index 0000000000..4ceeea277b --- /dev/null +++ b/spec/javascripts/unit/admin/orders/controllers/orders_controller_spec.js.coffee @@ -0,0 +1,40 @@ +describe "ordersCtrl", -> + ctrl = null + scope = {} + attrs = {} + shops = [] + orderCycles = [ + {id: 10, name: 'Ten', status: 'open', distributors: [{id: 1, name: 'One'}]} + {id: 20, name: 'Twenty', status: 'closed', distributors: [{id: 2, name: 'Two', status: 'closed'}]} + ] + + beforeEach -> + scope = {} + + module('admin.orders') + inject ($controller) -> + ctrl = $controller 'ordersCtrl', {$scope: scope, $attrs: attrs, shops: shops, orderCycles: orderCycles} + + it "initialises name_and_status", -> + expect(scope.orderCycles[0].name_and_status).toEqual "Ten (open)" + expect(scope.orderCycles[1].name_and_status).toEqual "Twenty (closed)" + + describe "finding valid order cycles for a distributor", -> + order_cycle = {id: 10, distributors: [{id: 1, name: 'One'}]} + + it "returns true when the order cycle includes the distributor", -> + scope.distributor_id = '1' + expect(scope.validOrderCycle(order_cycle, 1, [order_cycle])).toBe true + + it "returns false otherwise", -> + scope.distributor_id = '2' + expect(scope.validOrderCycle(order_cycle, 1, [order_cycle])).toBe false + + describe "checking if a distributor has order cycles", -> + it "returns true when it does", -> + distributor = {id: 1} + expect(scope.distributorHasOrderCycles(distributor)).toBe true + + it "returns false otherwise", -> + distributor = {id: 3} + expect(scope.distributorHasOrderCycles(distributor)).toBe false diff --git a/spec/javascripts/unit/admin/orders/services/orders_spec.js.coffee b/spec/javascripts/unit/admin/orders/services/orders_spec.js.coffee new file mode 100644 index 0000000000..cefb1d7a10 --- /dev/null +++ b/spec/javascripts/unit/admin/orders/services/orders_spec.js.coffee @@ -0,0 +1,106 @@ +describe "Orders service", -> + Orders = OrderResource = orders = $httpBackend = null + + beforeEach -> + module 'admin.orders' + + this.addMatchers + toDeepEqual: (expected) -> + return angular.equals(this.actual, expected) + + inject ($q, _$httpBackend_, _Orders_, _OrderResource_) -> + Orders = _Orders_ + OrderResource = _OrderResource_ + $httpBackend = _$httpBackend_ + + describe "#index", -> + result = response = null + + beforeEach -> + response = [{ id: 5, name: 'Order 1'}] + $httpBackend.expectGET('/admin/orders.json').respond 200, response + result = Orders.index() + $httpBackend.flush() + + it "stores returned data in @ordersByID, with ids as keys", -> + # OrderResource returns instances of Resource rather than raw objects + expect(Orders.ordersByID).toDeepEqual { 5: response[0] } + + it "stores returned data in @pristineByID, with ids as keys", -> + expect(Orders.pristineByID).toDeepEqual { 5: response[0] } + + it "returns an array of orders", -> + expect(result).toDeepEqual response + + + describe "#save", -> + result = null + + describe "success", -> + order = null + resolved = false + + beforeEach -> + order = new OrderResource({ id: 15, number: "R12345", name: 'Order 1' }) + $httpBackend.expectPUT('/admin/orders/R12345.json').respond 200, { id: 15, name: 'Order 1'} + Orders.save(order).then( -> resolved = true) + $httpBackend.flush() + + it "updates the pristine copy of the order", -> + # Resource results have extra properties ($then, $promise) that cause them to not + # be exactly equal to the response object provided to the expectPUT clause above. + expect(Orders.pristineByID[15]).toEqual order + + it "resolves the promise", -> + expect(resolved).toBe(true); + + + describe "failure", -> + order = null + rejected = false + + beforeEach -> + order = new OrderResource( { id: 15, number: 'R12345', name: 'Order 1' } ) + $httpBackend.expectPUT('/admin/orders/R12345.json').respond 422, { error: 'obj' } + Orders.save(order).catch( -> rejected = true) + $httpBackend.flush() + + it "does not update the pristine copy of the order", -> + expect(Orders.pristineByID[15]).toBeUndefined() + + it "rejects the promise", -> + expect(rejected).toBe(true); + + describe "#saved", -> + describe "when attributes of the object have been altered", -> + beforeEach -> + spyOn(Orders, "diff").andReturn ["attr1", "attr2"] + + it "returns false", -> + expect(Orders.saved({})).toBe false + + describe "when attributes of the object have not been altered", -> + beforeEach -> + spyOn(Orders, "diff").andReturn [] + + it "returns false", -> + expect(Orders.saved({})).toBe true + + + describe "diff", -> + beforeEach -> + Orders.pristineByID = { 23: { id: 23, name: "ent1", is_primary_producer: true } } + + it "returns a list of properties that have been altered", -> + expect(Orders.diff({ id: 23, name: "order123", is_primary_producer: true })).toEqual ["name"] + + + describe "resetAttribute", -> + order = { id: 23, name: "ent1", is_primary_producer: true } + + beforeEach -> + Orders.pristineByID = { 23: { id: 23, name: "order1", is_primary_producer: true } } + + it "resets the specified value according to the pristine record", -> + Orders.resetAttribute(order, "name") + expect(order.name).toEqual "order1" diff --git a/spec/javascripts/unit/admin/services/bulk_products_spec.js.coffee b/spec/javascripts/unit/admin/services/bulk_products_spec.js.coffee index 54b57aed48..7d4c5d1d7c 100644 --- a/spec/javascripts/unit/admin/services/bulk_products_spec.js.coffee +++ b/spec/javascripts/unit/admin/services/bulk_products_spec.js.coffee @@ -62,12 +62,15 @@ describe "BulkProducts service", -> id: 17 spyOn(BulkProducts, "insertProductAfter") + spyOn(BulkProducts, "unpackProduct") BulkProducts.products = [originalProduct] $httpBackend.expectGET("/admin/products/oranges/clone.json").respond 200, product: clonedProduct $httpBackend.expectGET("/api/products/17?template=bulk_show").respond 200, clonedProduct BulkProducts.cloneProduct BulkProducts.products[0] $httpBackend.flush() + expect(BulkProducts.unpackProduct).toHaveBeenCalledWith clonedProduct + BulkProducts.unpackProduct(clonedProduct) expect(BulkProducts.insertProductAfter).toHaveBeenCalledWith originalProduct, clonedProduct diff --git a/spec/javascripts/unit/admin/services/dirty_variant_overrides_spec.js.coffee b/spec/javascripts/unit/admin/services/dirty_variant_overrides_spec.js.coffee index 653560a989..553bba5713 100644 --- a/spec/javascripts/unit/admin/services/dirty_variant_overrides_spec.js.coffee +++ b/spec/javascripts/unit/admin/services/dirty_variant_overrides_spec.js.coffee @@ -7,7 +7,7 @@ describe "maintaining a list of dirty variant overrides", -> count_on_hand: 4 beforeEach -> - module "ofn.admin" + module "admin.variantOverrides" beforeEach inject (_DirtyVariantOverrides_) -> DirtyVariantOverrides = _DirtyVariantOverrides_ diff --git a/spec/javascripts/unit/admin/services/enterprise_relationships_spec.js.coffee b/spec/javascripts/unit/admin/services/enterprise_relationships_spec.js.coffee index 0d8e50fc5a..0d0a01215d 100644 --- a/spec/javascripts/unit/admin/services/enterprise_relationships_spec.js.coffee +++ b/spec/javascripts/unit/admin/services/enterprise_relationships_spec.js.coffee @@ -12,7 +12,7 @@ describe "enterprise relationships", -> EnterpriseRelationships = _EnterpriseRelationships_ it "presents permission names", -> - expect(EnterpriseRelationships.permission_presentation("add_to_order_cycle")).toEqual "to add to order cycle" - expect(EnterpriseRelationships.permission_presentation("manage_products")).toEqual "to manage products" - expect(EnterpriseRelationships.permission_presentation("edit_profile")).toEqual "to edit profile" - expect(EnterpriseRelationships.permission_presentation("create_variant_overrides")).toEqual "to override variant details" + expect(EnterpriseRelationships.permission_presentation("add_to_order_cycle")).toEqual "add to order cycle" + expect(EnterpriseRelationships.permission_presentation("manage_products")).toEqual "manage products" + expect(EnterpriseRelationships.permission_presentation("edit_profile")).toEqual "edit profile" + expect(EnterpriseRelationships.permission_presentation("create_variant_overrides")).toEqual "override variant details" diff --git a/spec/javascripts/unit/admin/services/indexer_spec.js.coffee b/spec/javascripts/unit/admin/services/indexer_spec.js.coffee index f17f8bd83c..22f263e02b 100644 --- a/spec/javascripts/unit/admin/services/indexer_spec.js.coffee +++ b/spec/javascripts/unit/admin/services/indexer_spec.js.coffee @@ -2,7 +2,7 @@ describe "indexer", -> Indexer = null beforeEach -> - module "ofn.admin" + module "admin.indexUtils" beforeEach inject (_Indexer_) -> Indexer = _Indexer_ diff --git a/spec/javascripts/unit/admin/services/variant_overrides_spec.js.coffee b/spec/javascripts/unit/admin/services/variant_overrides_spec.js.coffee index 532bb1d65c..73dbdabee8 100644 --- a/spec/javascripts/unit/admin/services/variant_overrides_spec.js.coffee +++ b/spec/javascripts/unit/admin/services/variant_overrides_spec.js.coffee @@ -1,27 +1,28 @@ describe "VariantOverrides service", -> - VariantOverrides = null + VariantOverrides = $httpBackend = null variantOverrides = [ - {id: 1, hub_id: 10, variant_id: 100, price: 1, count_on_hand: 1} - {id: 2, hub_id: 10, variant_id: 200, price: 2, count_on_hand: 2} - {id: 3, hub_id: 20, variant_id: 300, price: 3, count_on_hand: 3} + {id: 1, hub_id: 10, variant_id: 100, sku: "V100", price: 1, count_on_hand: 1, on_demand: null, default_stock: null, resettable: false } + {id: 2, hub_id: 10, variant_id: 200, sku: "V200", price: 2, count_on_hand: 2, on_demand: null, default_stock: null, resettable: false} + {id: 3, hub_id: 20, variant_id: 300, sku: "V300", price: 3, count_on_hand: 3, on_demand: null, default_stock: null, resettable: false} ] beforeEach -> - module "ofn.admin" + module "admin.variantOverrides" module ($provide) -> $provide.value "variantOverrides", variantOverrides null - beforeEach inject (_VariantOverrides_) -> + beforeEach inject (_VariantOverrides_, _$httpBackend_) -> VariantOverrides = _VariantOverrides_ + $httpBackend = _$httpBackend_ it "indexes variant overrides by hub_id -> variant_id", -> expect(VariantOverrides.variantOverrides).toEqual 10: - 100: {id: 1, hub_id: 10, variant_id: 100, price: 1, count_on_hand: 1} - 200: {id: 2, hub_id: 10, variant_id: 200, price: 2, count_on_hand: 2} + 100: {id: 1, hub_id: 10, variant_id: 100, sku: "V100", price: 1, count_on_hand: 1, on_demand: null, default_stock: null, resettable: false } + 200: {id: 2, hub_id: 10, variant_id: 200, sku: "V200", price: 2, count_on_hand: 2, on_demand: null, default_stock: null, resettable: false } 20: - 300: {id: 3, hub_id: 20, variant_id: 300, price: 3, count_on_hand: 3} + 300: {id: 3, hub_id: 20, variant_id: 300, sku: "V300", price: 3, count_on_hand: 3, on_demand: null, default_stock: null, resettable: false } it "ensures blank data available for some products", -> hubs = [{id: 10}, {id: 20}, {id: 30}] @@ -32,37 +33,50 @@ describe "VariantOverrides service", -> } ] VariantOverrides.ensureDataFor hubs, products - expect(VariantOverrides.variantOverrides).toEqual - 10: - 100: {id: 1, hub_id: 10, variant_id: 100, price: 1, count_on_hand: 1} - 200: {id: 2, hub_id: 10, variant_id: 200, price: 2, count_on_hand: 2} - 300: { hub_id: 10, variant_id: 300, price: '', count_on_hand: ''} - 400: { hub_id: 10, variant_id: 400, price: '', count_on_hand: ''} - 500: { hub_id: 10, variant_id: 500, price: '', count_on_hand: ''} - 20: - 100: { hub_id: 20, variant_id: 100, price: '', count_on_hand: ''} - 200: { hub_id: 20, variant_id: 200, price: '', count_on_hand: ''} - 300: {id: 3, hub_id: 20, variant_id: 300, price: 3, count_on_hand: 3} - 400: { hub_id: 20, variant_id: 400, price: '', count_on_hand: ''} - 500: { hub_id: 20, variant_id: 500, price: '', count_on_hand: ''} - 30: - 100: { hub_id: 30, variant_id: 100, price: '', count_on_hand: ''} - 200: { hub_id: 30, variant_id: 200, price: '', count_on_hand: ''} - 300: { hub_id: 30, variant_id: 300, price: '', count_on_hand: ''} - 400: { hub_id: 30, variant_id: 400, price: '', count_on_hand: ''} - 500: { hub_id: 30, variant_id: 500, price: '', count_on_hand: ''} + expect(VariantOverrides.variantOverrides[10]).toEqual + 100: { id: 1, hub_id: 10, variant_id: 100, sku: "V100", price: 1, count_on_hand: 1, on_demand: null, default_stock: null, resettable: false } + 200: { id: 2, hub_id: 10, variant_id: 200, sku: "V200", price: 2, count_on_hand: 2, on_demand: null, default_stock: null, resettable: false } + 300: { hub_id: 10, variant_id: 300, sku: null, price: null, count_on_hand: null, on_demand: null, default_stock: null, resettable: false } + 400: { hub_id: 10, variant_id: 400, sku: null, price: null, count_on_hand: null, on_demand: null, default_stock: null, resettable: false } + 500: { hub_id: 10, variant_id: 500, sku: null, price: null, count_on_hand: null, on_demand: null, default_stock: null, resettable: false } + expect(VariantOverrides.variantOverrides[20]).toEqual + 100: { hub_id: 20, variant_id: 100, sku: null, price: null, count_on_hand: null, on_demand: null, default_stock: null, resettable: false } + 200: { hub_id: 20, variant_id: 200, sku: null, price: null, count_on_hand: null, on_demand: null, default_stock: null, resettable: false } + 300: { id: 3, hub_id: 20, variant_id: 300, sku: "V300", price: 3, count_on_hand: 3, on_demand: null, default_stock: null, resettable: false } + 400: { hub_id: 20, variant_id: 400, sku: null, price: null, count_on_hand: null, on_demand: null, default_stock: null, resettable: false } + 500: { hub_id: 20, variant_id: 500, sku: null, price: null, count_on_hand: null, on_demand: null, default_stock: null, resettable: false } + expect(VariantOverrides.variantOverrides[30]).toEqual + 100: { hub_id: 30, variant_id: 100, sku: null, price: null, count_on_hand: null, on_demand: null, default_stock: null, resettable: false } + 200: { hub_id: 30, variant_id: 200, sku: null, price: null, count_on_hand: null, on_demand: null, default_stock: null, resettable: false } + 300: { hub_id: 30, variant_id: 300, sku: null, price: null, count_on_hand: null, on_demand: null, default_stock: null, resettable: false } + 400: { hub_id: 30, variant_id: 400, sku: null, price: null, count_on_hand: null, on_demand: null, default_stock: null, resettable: false } + 500: { hub_id: 30, variant_id: 500, sku: null, price: null, count_on_hand: null, on_demand: null, default_stock: null, resettable: false } it "updates the IDs of variant overrides", -> VariantOverrides.variantOverrides[2] = {} - VariantOverrides.variantOverrides[2][3] = {hub_id: 2, variant_id: 3, price: "4.0", count_on_hand: 5} - VariantOverrides.variantOverrides[2][8] = {hub_id: 2, variant_id: 8, price: "9.0", count_on_hand: 10} + VariantOverrides.variantOverrides[2][3] = {hub_id: 2, variant_id: 3, price: "4.0", count_on_hand: 5, default_stock: '', resettable: false} + VariantOverrides.variantOverrides[2][8] = {hub_id: 2, variant_id: 8, price: "9.0", count_on_hand: 10, default_stock: '', resettable: false} updatedVos = [ - {id: 1, hub_id: 2, variant_id: 3, price: "4.0", count_on_hand: 5} - {id: 6, hub_id: 2, variant_id: 8, price: "9.0", count_on_hand: 10} + {id: 1, hub_id: 2, variant_id: 3, price: "4.0", count_on_hand: 5, default_stock: '', resettable: false} + {id: 6, hub_id: 2, variant_id: 8, price: "9.0", count_on_hand: 10, default_stock: '', resettable: false} ] VariantOverrides.updateIds updatedVos expect(VariantOverrides.variantOverrides[2][3].id).toEqual 1 expect(VariantOverrides.variantOverrides[2][8].id).toEqual 6 + + it "updates the variant overrides on the page with new data", -> + VariantOverrides.variantOverrides[1] = + 3: {id: 1, hub_id: 1, variant_id: 3, price: "4.0", count_on_hand: 5, default_stock: 3, resettable: true} + 8: {id: 2, hub_id: 1, variant_id: 8, price: "9.0", count_on_hand: 10, default_stock: '', resettable: false} + # Updated count on hand to 3 + updatedVos = [ + {id: 1, hub_id: 1, variant_id: 3, price: "4.0", count_on_hand: 3, default_stock: 3, resettable: true} + ] + + VariantOverrides.updateData(updatedVos) + expect(VariantOverrides.variantOverrides[1]).toEqual + 3: {id: 1, hub_id: 1, variant_id: 3, price: "4.0", count_on_hand: 3, default_stock: 3, resettable: true} + 8: {id: 2, hub_id: 1, variant_id: 8, price: "9.0", count_on_hand: 10, default_stock: '', resettable: false} diff --git a/spec/javascripts/unit/bulk_order_management_spec.js.coffee b/spec/javascripts/unit/bulk_order_management_spec.js.coffee index 122d17f539..3e7b27ae73 100644 --- a/spec/javascripts/unit/bulk_order_management_spec.js.coffee +++ b/spec/javascripts/unit/bulk_order_management_spec.js.coffee @@ -261,59 +261,56 @@ describe "AdminOrderMgmtCtrl", -> scope.selectedUnitsProduct = { variant_unit: "weight", group_buy_unit_size: 1000 } expect(scope.fulfilled(1500)).toEqual 1.5 - describe "allUnitValuesPresent()", -> + describe "allFinalWeightVolumesPresent()", -> it "returns false if the unit_value of any item in filteredLineItems does not exist", -> scope.filteredLineItems = [ - { units_variant: { unit_value: 1000 } } - { units_variant: { unit_value: 2000 } } - { units_variant: { unit_yayay: 1000 } } + { final_weight_volume: 1000 } + { final_weight_volume: 3000 } + { final_weight_yayaya: 2000 } ] - expect(scope.allUnitValuesPresent()).toEqual false + expect(scope.allFinalWeightVolumesPresent()).toEqual false it "returns false if the unit_value of any item in filteredLineItems is not a number greater than 0", -> scope.filteredLineItems = [ - { units_variant: { unit_value: 0 } } - { units_variant: { unit_value: 2000 } } - { units_variant: { unit_value: 1000 } } + { final_weight_volume: 0 } + { final_weight_volume: 3000 } + { final_weight_volume: 2000 } ] - expect(scope.allUnitValuesPresent()).toEqual false + expect(scope.allFinalWeightVolumesPresent()).toEqual false scope.filteredLineItems = [ - { units_variant: { unit_value: 'lala' } } - { units_variant: { unit_value: 2000 } } - { units_variant: { unit_value: 1000 } } + { final_weight_volume: 'lalala' } + { final_weight_volume: 3000 } + { final_weight_volume: 2000 } ] - expect(scope.allUnitValuesPresent()).toEqual false + expect(scope.allFinalWeightVolumesPresent()).toEqual false it "returns true if the unit_value of all items in filteredLineItems are numbers greater than 0", -> scope.filteredLineItems = [ - { units_variant: { unit_value: 100 } } - { units_variant: { unit_value: 2000 } } - { units_variant: { unit_value: 1000 } } + { final_weight_volume: 1000 } + { final_weight_volume: 3000 } + { final_weight_volume: 2000 } ] - expect(scope.allUnitValuesPresent()).toEqual true + expect(scope.allFinalWeightVolumesPresent()).toEqual true describe "sumUnitValues()", -> - it "returns the sum of the product of unit_value and quantity for specified line_items", -> + it "returns the sum of the final_weight_volumes line_items", -> scope.filteredLineItems = [ - { units_variant: { unit_value: 1 }, quantity: 2 } - { units_variant: { unit_value: 2 }, quantity: 3 } - { units_variant: { unit_value: 3 }, quantity: 7 } + { final_weight_volume: 2 } + { final_weight_volume: 7 } + { final_weight_volume: 21 } ] - sp0 = scope.filteredLineItems[0].units_variant.unit_value * scope.filteredLineItems[0].quantity - sp1 = scope.filteredLineItems[1].units_variant.unit_value * scope.filteredLineItems[1].quantity - sp2 = scope.filteredLineItems[2].units_variant.unit_value * scope.filteredLineItems[2].quantity - expect(scope.sumUnitValues()).toEqual (sp0 + sp1 + sp2) + expect(scope.sumUnitValues()).toEqual 30 describe "sumMaxUnitValues()", -> it "returns the sum of the product of unit_value and maxOf(max_quantity,quantity) for specified line_items", -> scope.filteredLineItems = [ - { units_variant: { unit_value: 1 }, quantity: 2, max_quantity: 5 } - { units_variant: { unit_value: 2 }, quantity: 3, max_quantity: 1 } - { units_variant: { unit_value: 3 }, quantity: 7, max_quantity: 10 } + { units_variant: { unit_value: 1 }, original_quantity: 2, max_quantity: 5 } + { units_variant: { unit_value: 2 }, original_quantity: 3, max_quantity: 1 } + { units_variant: { unit_value: 3 }, original_quantity: 7, max_quantity: 10 } ] - sp0 = scope.filteredLineItems[0].units_variant.unit_value * Math.max(scope.filteredLineItems[0].quantity, scope.filteredLineItems[0].max_quantity) - sp1 = scope.filteredLineItems[1].units_variant.unit_value * Math.max(scope.filteredLineItems[1].quantity, scope.filteredLineItems[1].max_quantity) - sp2 = scope.filteredLineItems[2].units_variant.unit_value * Math.max(scope.filteredLineItems[2].quantity, scope.filteredLineItems[2].max_quantity) + sp0 = scope.filteredLineItems[0].units_variant.unit_value * Math.max(scope.filteredLineItems[0].original_quantity, scope.filteredLineItems[0].max_quantity) + sp1 = scope.filteredLineItems[1].units_variant.unit_value * Math.max(scope.filteredLineItems[1].original_quantity, scope.filteredLineItems[1].max_quantity) + sp2 = scope.filteredLineItems[2].units_variant.unit_value * Math.max(scope.filteredLineItems[2].original_quantity, scope.filteredLineItems[2].max_quantity) expect(scope.sumMaxUnitValues()).toEqual (sp0 + sp1 + sp2) describe "formatting a value based upon the properties of a specified Units Variant", -> @@ -351,260 +348,53 @@ describe "AdminOrderMgmtCtrl", -> expect(scope.formattedValueWithUnitName(2000,unitsVariant)).toEqual "2 kg" describe "updating the price upon updating the weight of a line item", -> - - it "resets the weight if the weight is set to zero", -> - scope.filteredLineItems = [ - { units_variant: { unit_value: 100 }, price: 2, unit_value: 0 } - ] - expect(scope.weightAdjustedPrice(scope.filteredLineItems[0], 100)).toEqual scope.filteredLineItems[0].price - it "updates the price if the weight is changed", -> scope.filteredLineItems = [ - { units_variant: { unit_value: 100 }, price: 2, unit_value: 200 } + { original_price: 2.00, price: 2.00, original_quantity: 1, quantity: 1, original_final_weight_volume: 2000, final_weight_volume: 4000 } ] - old_value = scope.filteredLineItems[0].units_variant.unit_value - new_value = scope.filteredLineItems[0].unit_value - sp = scope.filteredLineItems[0].price * new_value / old_value - expect(scope.weightAdjustedPrice(scope.filteredLineItems[0], old_value)).toEqual sp + scope.weightAdjustedPrice(scope.filteredLineItems[0]) + expect(scope.filteredLineItems[0].price).toEqual 4.00 - it "doesn't update the price if the weight is not changed", -> + it "doesn't update the price if the weight <= 0", -> scope.filteredLineItems = [ - { units_variant: { unit_value: 100 }, price: 2, unit_value: 100 } + { original_price: 2.00, price: 2.00, original_quantity: 1, quantity: 1, original_final_weight_volume: 2000, final_weight_volume: 0 } ] - old_value = scope.filteredLineItems[0].unit_value - new_value = scope.filteredLineItems[0].unit_value - sp = scope.filteredLineItems[0].price - expect(scope.weightAdjustedPrice(scope.filteredLineItems[0], old_value)).toEqual sp + scope.weightAdjustedPrice(scope.filteredLineItems[0]) + expect(scope.filteredLineItems[0].price).toEqual 2.00 + it "doesn't update the price if the weight is an empty string", -> + scope.filteredLineItems = [ + { original_price: 2.00, price: 2.00, original_quantity: 1, quantity: 1, original_final_weight_volume: 2000, final_weight_volume: "" } + ] + scope.weightAdjustedPrice(scope.filteredLineItems[0]) + expect(scope.filteredLineItems[0].price).toEqual 2.00 -describe "managing pending changes", -> - dataSubmitter = pendingChangesService = null + describe "updating final_weight_volume upon updating the quantity for a line_item", -> + beforeEach -> + spyOn(scope, "weightAdjustedPrice") - beforeEach -> - dataSubmitter = jasmine.createSpy('dataSubmitter').andReturn { - then: (thenFn) -> - thenFn({propertyName: "new_value"}) - } + it "updates the weight if the quantity is changed, then calls weightAdjustedPrice()", -> + scope.filteredLineItems = [ + { original_price: 2.00, price: 2.00, original_quantity: 1, quantity: 2, original_final_weight_volume: 2000, final_weight_volume: 0 } + ] + scope.updateOnQuantity(scope.filteredLineItems[0]) + expect(scope.filteredLineItems[0].final_weight_volume).toEqual 4000 + expect(scope.weightAdjustedPrice).toHaveBeenCalled() - beforeEach -> - module "ofn.admin", ($provide) -> - $provide.value 'dataSubmitter', dataSubmitter - return + it "doesn't update the weight if the quantity <= 0", -> + scope.filteredLineItems = [ + { original_price: 2.00, price: 2.00, original_quantity: 1, quantity: 0, original_final_weight_volume: 2000, final_weight_volume: 1000 } + ] + scope.updateOnQuantity(scope.filteredLineItems[0]) + expect(scope.filteredLineItems[0].final_weight_volume).toEqual 1000 - beforeEach inject (pendingChanges) -> - pendingChangesService = pendingChanges + it "doesn't update the weight if the quantity is an empty string", -> + scope.filteredLineItems = [ + { original_price: 2.00, price: 2.00, original_quantity: 1, quantity: "", original_final_weight_volume: 2000, final_weight_volume: 1000 } + ] + scope.updateOnQuantity(scope.filteredLineItems[0]) + expect(scope.filteredLineItems[0].final_weight_volume).toEqual 1000 - describe "adding a new change", -> - it "adds a new object with key of id if it does not already exist", -> - expect(pendingChangesService.pendingChanges).toEqual {} - expect(pendingChangesService.pendingChanges["1"]).not.toBeDefined() - pendingChangesService.add 1, "propertyName", { a: 1 } - expect(pendingChangesService.pendingChanges["1"]).toBeDefined() - - it "adds a new object with key of the altered attribute name if it does not already exist", -> - pendingChangesService.add 1, "propertyName", { a: 1 } - expect(pendingChangesService.pendingChanges["1"]).toBeDefined() - expect(pendingChangesService.pendingChanges["1"]["propertyName"]).toEqual { a: 1 } - - it "replaces the existing object when adding a change to an attribute which already exists", -> - pendingChangesService.add 1, "propertyName", { a: 1 } - expect(pendingChangesService.pendingChanges["1"]).toBeDefined() - expect(pendingChangesService.pendingChanges["1"]["propertyName"]).toEqual { a: 1 } - pendingChangesService.add 1, "propertyName", { b: 2 } - expect(pendingChangesService.pendingChanges["1"]["propertyName"]).toEqual { b: 2 } - - it "adds an attribute to key to a line item object when one already exists", -> - pendingChangesService.add 1, "propertyName1", { a: 1 } - pendingChangesService.add 1, "propertyName2", { b: 2 } - expect(pendingChangesService.pendingChanges["1"]).toEqual { propertyName1: { a: 1}, propertyName2: { b: 2 } } - - describe "removing all existing changes", -> - it "resets pendingChanges object", -> - pendingChangesService.pendingChanges = { 1: { "propertyName1": { a: 1 }, "propertyName2": { b: 2 } } } - expect(pendingChangesService.pendingChanges["1"]["propertyName1"]).toBeDefined() - expect(pendingChangesService.pendingChanges["1"]["propertyName2"]).toBeDefined() - pendingChangesService.removeAll() - expect(pendingChangesService.pendingChanges["1"]).not.toBeDefined() - expect(pendingChangesService.pendingChanges).toEqual {} - - describe "removing an existing change", -> - it "deletes a change if it exists", -> - pendingChangesService.pendingChanges = { 1: { "propertyName1": { a: 1 }, "propertyName2": { b: 2 } } } - expect(pendingChangesService.pendingChanges["1"]["propertyName1"]).toBeDefined() - pendingChangesService.remove 1, "propertyName1" - expect(pendingChangesService.pendingChanges["1"]).toBeDefined() - expect(pendingChangesService.pendingChanges["1"]["propertyName1"]).not.toBeDefined() - - it "deletes a line item object if it is empty", -> - pendingChangesService.pendingChanges = { 1: { "propertyName1": { a: 1 } } } - expect(pendingChangesService.pendingChanges["1"]["propertyName1"]).toBeDefined() - pendingChangesService.remove 1, "propertyName1" - expect(pendingChangesService.pendingChanges["1"]).not.toBeDefined() - - it "does nothing if key with specified attribute does not exist", -> - pendingChangesService.pendingChanges = { 1: { "propertyName1": { a: 1 } } } - expect(pendingChangesService.pendingChanges["1"]["propertyName1"]).toBeDefined() - pendingChangesService.remove 1, "propertyName2" - expect(pendingChangesService.pendingChanges["1"]["propertyName1"]).toEqual { a: 1 } - - it "does nothing if key with specified id does not exist", -> - pendingChangesService.pendingChanges = { 1: { "propertyName1": { a: 1 } } } - expect(pendingChangesService.pendingChanges["1"]["propertyName1"]).toBeDefined() - pendingChangesService.remove 2, "propertyName1" - expect(pendingChangesService.pendingChanges["1"]).toEqual { "propertyName1": { a: 1 } } - - describe "submitting an individual change to the server", -> - it "sends the correct object to dataSubmitter", -> - changeObj = { element: {} } - pendingChangesService.submit 1, "propertyName", changeObj - expect(dataSubmitter.calls.length).toEqual 1 - expect(dataSubmitter).toHaveBeenCalledWith changeObj - - it "calls remove with id and attribute name", -> - changeObj = { element: {} } - spyOn(pendingChangesService, "remove").andCallFake(->) - pendingChangesService.submit 1, "propertyName", changeObj - expect(pendingChangesService.remove.calls.length).toEqual 1 - expect(pendingChangesService.remove).toHaveBeenCalledWith 1, "propertyName" - - it "resets the dbValue attribute of the element in question", -> - element = { dbValue: 2 } - changeObj = { element: element } - pendingChangesService.submit 1, "propertyName", changeObj - expect(element.dbValue).toEqual "new_value" - - describe "cycling through all changes to submit to server", -> - it "sends the correct object to dataSubmitter", -> - spyOn(pendingChangesService, "submit").andCallFake(->) - pendingChangesService.pendingChanges = - 1: { "prop1": 1, "prop2": 2 } - 2: { "prop1": 2, "prop2": 4 } - 7: { "prop2": 5 } - pendingChangesService.submitAll() - expect(pendingChangesService.submit.calls.length).toEqual 5 - expect(pendingChangesService.submit).toHaveBeenCalledWith '1', "prop1", 1 - expect(pendingChangesService.submit).toHaveBeenCalledWith '1', "prop2", 2 - expect(pendingChangesService.submit).toHaveBeenCalledWith '2', "prop1", 2 - expect(pendingChangesService.submit).toHaveBeenCalledWith '2', "prop2", 4 - expect(pendingChangesService.submit).toHaveBeenCalledWith '7', "prop2", 5 - - it "returns an array of promises representing all sumbit requests", -> - spyOn(pendingChangesService, "submit").andCallFake (id,attrName,changeObj) -> - id - pendingChangesService.pendingChanges = - 1: { "prop1": 1 } - 2: { "prop1": 2, "prop2": 4 } - expect(pendingChangesService.submitAll()).toEqual [ '1','2','2' ] - -describe "dataSubmitter service", -> - qMock = httpMock = {} - switchClassSpy = resolveSpy = rejectSpy = dataSubmitterService = null - - beforeEach -> - resolveSpy = jasmine.createSpy('resolve') - rejectSpy = jasmine.createSpy('reject') - qMock.defer = -> - resolve: resolveSpy - reject: rejectSpy - promise: "promise1" - - # Can't use httpBackend because the qMock interferes with it - httpMock.put = (url) -> - success: (successFn) -> - successFn("somedata") if url == "successURL" - error: (errorFn) -> - errorFn() if url == "errorURL" - - spyOn(httpMock, "put").andCallThrough() - spyOn(qMock, "defer").andCallThrough() - - switchClassSpy = jasmine.createSpy('switchClass') - - beforeEach -> - module "ofn.admin" , ($provide) -> - $provide.value '$q', qMock - $provide.value '$http', httpMock - $provide.value 'switchClass', switchClassSpy - return - - beforeEach inject (dataSubmitter) -> - dataSubmitterService = dataSubmitter - - it "returns a promise", -> - expect(dataSubmitterService( { url: "successURL" } )).toEqual "promise1" - expect(qMock.defer).toHaveBeenCalled() - - it "sends a PUT request with the url property of changeObj", -> - dataSubmitterService { url: "successURL" } - expect(httpMock.put).toHaveBeenCalledWith "successURL" - - it "calls resolve on deferred object when request is successful", -> - element = { a: 1 } - dataSubmitterService { url: "successURL", element: element } - expect(resolveSpy.calls.length).toEqual 1 - expect(rejectSpy.calls.length).toEqual 0 - expect(resolveSpy).toHaveBeenCalledWith "somedata" - expect(switchClassSpy).toHaveBeenCalledWith element, "update-success", ["update-pending", "update-error"], 3000 - - it "calls reject on deferred object when request is erroneous", -> - element = { b: 2 } - dataSubmitterService { url: "errorURL", element: element } - expect(resolveSpy.calls.length).toEqual 0 - expect(rejectSpy.calls.length).toEqual 1 - expect(switchClassSpy).toHaveBeenCalledWith element, "update-error", ["update-pending", "update-success"], false - -describe "switchClass service", -> - elementMock = timeoutMock = {} - removeClass = addClass = switchClassService = null - - beforeEach -> - addClass = jasmine.createSpy('addClass') - removeClass = jasmine.createSpy('removeClass') - elementMock = - addClass: addClass - removeClass: removeClass - timeoutMock = jasmine.createSpy('timeout').andReturn "new timeout" - timeoutMock.cancel = jasmine.createSpy('timeout.cancel') - - beforeEach -> - module "ofn.admin" , ($provide) -> - $provide.value '$timeout', timeoutMock - return - - beforeEach inject (switchClass) -> - switchClassService = switchClass - - it "calls addClass on the element once", -> - switchClassService elementMock, "addClass", [], false - expect(addClass).toHaveBeenCalledWith "addClass" - expect(addClass.calls.length).toEqual 1 - - it "calls removeClass on the element for ", -> - switchClassService elementMock, "", ["remClass1", "remClass2", "remClass3"], false - expect(removeClass).toHaveBeenCalledWith "remClass1" - expect(removeClass).toHaveBeenCalledWith "remClass2" - expect(removeClass).toHaveBeenCalledWith "remClass3" - expect(removeClass.calls.length).toEqual 3 - - it "call cancel on element.timout only if it exists", -> - switchClassService elementMock, "", [], false - expect(timeoutMock.cancel).not.toHaveBeenCalled() - elementMock.timeout = true - switchClassService elementMock, "", [], false - expect(timeoutMock.cancel).toHaveBeenCalled() - - it "doesn't set up a new timeout if 'timeout' is false", -> - switchClassService elementMock, "class1", ["class2"], false - expect(timeoutMock).not.toHaveBeenCalled() - - it "doesn't set up a new timeout if 'timeout' is a string", -> - switchClassService elementMock, "class1", ["class2"], "string" - expect(timeoutMock).not.toHaveBeenCalled() - - it "sets up a new timeout if 'timeout' parameter is an integer", -> - switchClassService elementMock, "class1", ["class2"], 1000 - expect(timeoutMock).toHaveBeenCalled() - expect(elementMock.timeout).toEqual "new timeout" describe "Auxiliary functions", -> describe "getting a zero filled two digit number", -> @@ -624,14 +414,14 @@ describe "Auxiliary functions", -> beforeEach -> date = new Date date.setYear(2010) - date.setMonth(5) # Zero indexed, so 5 is June + date.setMonth(4) # Zero indexed, so 4 is May date.setDate(15) date.setHours(5) date.setMinutes(10) date.setSeconds(30) it "returns a date formatted as yyyy-mm-dd", -> - expect(formatDate(date)).toEqual "2010-06-15" + expect(formatDate(date)).toEqual "2010-05-15" it "returns a time formatted as hh-MM:ss", -> expect(formatTime(date)).toEqual "05:10:30" diff --git a/spec/javascripts/unit/darkswarm/controllers/home_controller_spec.js.coffee b/spec/javascripts/unit/darkswarm/controllers/home_controller_spec.js.coffee new file mode 100644 index 0000000000..8ba07e2f77 --- /dev/null +++ b/spec/javascripts/unit/darkswarm/controllers/home_controller_spec.js.coffee @@ -0,0 +1,19 @@ +describe "HomeCtrl", -> + ctrl = null + scope = null + + beforeEach -> + module 'Darkswarm' + scope = {} + + inject ($controller) -> + ctrl = $controller 'HomeCtrl', {$scope: scope} + + it "starts with the brand story contracted", -> + expect(scope.brandStoryExpanded).toBe false + + it "toggles the brand story", -> + scope.toggleBrandStory() + expect(scope.brandStoryExpanded).toBe true + scope.toggleBrandStory() + expect(scope.brandStoryExpanded).toBe false diff --git a/spec/javascripts/unit/darkswarm/controllers/order_cycle_controller_spec.js.coffee b/spec/javascripts/unit/darkswarm/controllers/order_cycle_controller_spec.js.coffee index a9746f2d39..deb7aea8b1 100644 --- a/spec/javascripts/unit/darkswarm/controllers/order_cycle_controller_spec.js.coffee +++ b/spec/javascripts/unit/darkswarm/controllers/order_cycle_controller_spec.js.coffee @@ -1,19 +1,17 @@ describe 'OrderCycleCtrl', -> ctrl = null scope = null - event = null - product_ctrl = null OrderCycle = null beforeEach -> module 'Darkswarm' scope = {} - OrderCycle = - order_cycle: "test" + OrderCycle = + order_cycle: + id: 123 inject ($controller) -> scope = {} ctrl = $controller 'OrderCycleCtrl', {$scope: scope, OrderCycle: OrderCycle} it "puts the order cycle in scope", -> - expect(scope.order_cycle).toEqual "test" - + expect(scope.order_cycle).toEqual {id: 123} diff --git a/spec/javascripts/unit/darkswarm/filters/active_spec.js.coffee b/spec/javascripts/unit/darkswarm/filters/active_spec.js.coffee index 337121b1ae..25c6c1d003 100644 --- a/spec/javascripts/unit/darkswarm/filters/active_spec.js.coffee +++ b/spec/javascripts/unit/darkswarm/filters/active_spec.js.coffee @@ -1,12 +1,8 @@ describe 'filtering by active', -> filterByActive = null objects = [ - { - active: true - } - { - active: false - } + {active: true} + {active: false} ] diff --git a/spec/javascripts/unit/darkswarm/filters/distance_within_km_spec.js.coffee b/spec/javascripts/unit/darkswarm/filters/distance_within_km_spec.js.coffee new file mode 100644 index 0000000000..afbc31ee84 --- /dev/null +++ b/spec/javascripts/unit/darkswarm/filters/distance_within_km_spec.js.coffee @@ -0,0 +1,17 @@ +describe "filtering enterprises to those within a certain radius", -> + filter = null + enterprises = [ + {distance: 25000} + {distance: 75000} + ] + + beforeEach -> + module 'Darkswarm' + inject ($filter) -> + filter = $filter('distanceWithinKm') + + it "filters to those enterprises within a distance", -> + expect(filter(enterprises, 50)).toEqual [enterprises[0]] + + it "returns empty array when enterprises array is null", -> + expect(filter(null, 50)).toEqual [] diff --git a/spec/javascripts/unit/darkswarm/services/cart_spec.js.coffee b/spec/javascripts/unit/darkswarm/services/cart_spec.js.coffee index 4be1a13dd8..518a524010 100644 --- a/spec/javascripts/unit/darkswarm/services/cart_spec.js.coffee +++ b/spec/javascripts/unit/darkswarm/services/cart_spec.js.coffee @@ -3,6 +3,8 @@ describe 'Cart service', -> Variants = null variant = null order = null + $httpBackend = null + $timeout = null beforeEach -> module 'Darkswarm' @@ -16,9 +18,11 @@ describe 'Cart service', -> ] } angular.module('Darkswarm').value('currentOrder', order) - inject ($injector)-> + inject ($injector, _$httpBackend_, _$timeout_)-> Variants = $injector.get("Variants") Cart = $injector.get("Cart") + $httpBackend = _$httpBackend_ + $timeout = _$timeout_ it "backreferences line items", -> expect(Cart.line_items[0].variant.line_item).toBe Cart.line_items[0] @@ -44,6 +48,102 @@ describe 'Cart service', -> order.line_items[0].quantity = 2 expect(Cart.total_item_count()).toEqual 2 + describe "triggering cart updates", -> + it "schedules an update when there's no update running", -> + Cart.update_running = false + Cart.update_enqueued = false + spyOn(Cart, 'scheduleUpdate') + spyOn(Cart, 'unsaved') + Cart.orderChanged() + expect(Cart.scheduleUpdate).toHaveBeenCalled() + + it "enqueues an update when there's already an update running", -> + Cart.update_running = true + Cart.update_enqueued = false + spyOn(Cart, 'scheduleUpdate') + spyOn(Cart, 'unsaved') + Cart.orderChanged() + expect(Cart.scheduleUpdate).not.toHaveBeenCalled() + expect(Cart.update_enqueued).toBe(true) + + it "does nothing when there's already an update enqueued", -> + Cart.update_running = true + Cart.update_enqueued = true + spyOn(Cart, 'scheduleUpdate') + spyOn(Cart, 'unsaved') + Cart.orderChanged() + expect(Cart.scheduleUpdate).not.toHaveBeenCalled() + expect(Cart.update_enqueued).toBe(true) + + describe "updating the cart", -> + data = {variants: {}} + + it "sets update_running during the update, and clears it on success", -> + $httpBackend.expectPOST("/orders/populate", data).respond 200, {} + expect(Cart.update_running).toBe(false) + Cart.update() + expect(Cart.update_running).toBe(true) + $httpBackend.flush() + expect(Cart.update_running).toBe(false) + + it "sets update_running during the update, and clears it on failure", -> + $httpBackend.expectPOST("/orders/populate", data).respond 404, {} + expect(Cart.update_running).toBe(false) + Cart.update() + expect(Cart.update_running).toBe(true) + $httpBackend.flush() + expect(Cart.update_running).toBe(false) + + it "marks the form as saved on success", -> + spyOn(Cart, 'saved') + $httpBackend.expectPOST("/orders/populate", data).respond 200, {} + Cart.update() + $httpBackend.flush() + expect(Cart.saved).toHaveBeenCalled() + + it "runs enqueued updates after success", -> + Cart.update_enqueued = true + spyOn(Cart, 'saved') + spyOn(Cart, 'popQueue') + $httpBackend.expectPOST("/orders/populate", data).respond 200, {} + Cart.update() + $httpBackend.flush() + expect(Cart.popQueue).toHaveBeenCalled() + + it "doesn't run an update if it's not enqueued", -> + Cart.update_enqueued = false + spyOn(Cart, 'saved') + spyOn(Cart, 'popQueue') + $httpBackend.expectPOST("/orders/populate", data).respond 200, {} + Cart.update() + $httpBackend.flush() + expect(Cart.popQueue).not.toHaveBeenCalled() + + it "retries the update on failure", -> + spyOn(Cart, 'scheduleRetry') + $httpBackend.expectPOST("/orders/populate", data).respond 404, {} + Cart.update() + $httpBackend.flush() + expect(Cart.scheduleRetry).toHaveBeenCalled() + + it "pops the queue", -> + Cart.update_enqueued = true + spyOn(Cart, 'scheduleUpdate') + Cart.popQueue() + expect(Cart.update_enqueued).toBe(false) + expect(Cart.scheduleUpdate).toHaveBeenCalled() + + it "schedules retries of updates", -> + spyOn(Cart, 'orderChanged') + Cart.scheduleRetry() + $timeout.flush() + expect(Cart.orderChanged).toHaveBeenCalled() + + it "clears the cart", -> + expect(Cart.line_items).not.toEqual [] + Cart.clear() + expect(Cart.line_items).toEqual [] + describe "generating an extended variant name", -> it "returns the product name when it is the same as the variant name", -> variant = {product_name: 'product_name', name_to_display: 'product_name'} diff --git a/spec/javascripts/unit/darkswarm/services/enterprise_registration_spec.js.coffee b/spec/javascripts/unit/darkswarm/services/enterprise_registration_spec.js.coffee index 1852dbd16c..4952c3c4fc 100644 --- a/spec/javascripts/unit/darkswarm/services/enterprise_registration_spec.js.coffee +++ b/spec/javascripts/unit/darkswarm/services/enterprise_registration_spec.js.coffee @@ -56,6 +56,20 @@ describe "EnterpriseRegistrationService", -> it "does not move the user to the about page", -> expect(RegistrationServiceMock.select).not.toHaveBeenCalled + describe "failure due to duplicate name", -> + beforeEach -> + spyOn(RegistrationServiceMock, "select") + spyOn(window, "alert") + $httpBackend.expectPOST("/api/enterprises?token=keykeykeykey").respond 400, {"error": "Invalid resource. Please fix errors and try again.", "errors": {"name": ["has already been taken. If this is your enterprise and you would like to claim ownership, please contact the current manager of this profile at owner@example.com."], "permalink": [] }} + EnterpriseRegistrationService.create() + $httpBackend.flush() + + it "alerts the user to failure", -> + expect(window.alert).toHaveBeenCalledWith 'Failed to create your enterprise.\nName has already been taken. If this is your enterprise and you would like to claim ownership, please contact the current manager of this profile at owner@example.com.' + + it "does not move the user to the about page", -> + expect(RegistrationServiceMock.select).not.toHaveBeenCalled + describe "updating an enterprise", -> beforeEach -> diff --git a/spec/javascripts/unit/darkswarm/services/enterprise_spec.js.coffee b/spec/javascripts/unit/darkswarm/services/enterprise_spec.js.coffee index 94dd7d39d2..67774aecbd 100644 --- a/spec/javascripts/unit/darkswarm/services/enterprise_spec.js.coffee +++ b/spec/javascripts/unit/darkswarm/services/enterprise_spec.js.coffee @@ -1,24 +1,37 @@ describe "Enterprises service", -> Enterprises = null CurrentHubMock = {} + Geo = + OK: 'ok' + succeed: true + geocode: (query, callback) -> + if @succeed + results = [{geometry: {location: "location"}}] + callback(results, @OK) + else + callback(results, 'Oops') + distanceBetween: (locatable, location) -> + 123 + taxons = [ {id: 1, name: "test"} ] enterprises = [ - {id: 1, visible: true, category: "hub", producers: [{id: 5}], taxons: [{id: 1}]}, - {id: 2, visible: true, category: "hub", producers: [{id: 6}]} - {id: 3, visible: true, category: "hub_profile"} - {id: 4, visible: false, category: "hub", producers: [{id: 7}]} - {id: 5, visible: true, category: "producer_hub", hubs: [{id: 1}]}, - {id: 6, visible: true, category: "producer_shop", hubs: [{id: 2}]}, - {id: 7, visible: true, category: "producer", hubs: [{id: 2}]} - {id: 8, visible: false, category: "producer", hubs: [{id: 2}]} + {id: 1, visible: true, name: 'a', category: "hub", producers: [{id: 5}], taxons: [{id: 1}]}, + {id: 2, visible: true, name: 'b', category: "hub", producers: [{id: 6}]} + {id: 3, visible: true, name: 'c', category: "hub_profile"} + {id: 4, visible: false,name: 'd', category: "hub", producers: [{id: 7}]} + {id: 5, visible: true, name: 'e', category: "producer_hub", hubs: [{id: 1}]}, + {id: 6, visible: true, name: 'f', category: "producer_shop", hubs: [{id: 2}]}, + {id: 7, visible: true, name: 'g', category: "producer", hubs: [{id: 2}]} + {id: 8, visible: false,name: 'h', category: "producer", hubs: [{id: 2}]} ] H1: 0 beforeEach -> module 'Darkswarm' module ($provide)-> $provide.value "CurrentHub", CurrentHubMock + $provide.value "Geo", Geo null angular.module('Darkswarm').value('enterprises', enterprises) angular.module('Darkswarm').value('taxons', taxons) @@ -73,3 +86,70 @@ describe "Enterprises service", -> expect(Enterprises.producers).toContain Enterprises.enterprises[4] expect(Enterprises.producers).toContain Enterprises.enterprises[5] expect(Enterprises.producers).toContain Enterprises.enterprises[6] + + describe "flagging enterprises with names matching a query", -> + it "flags enterprises when a query is provided", -> + Enterprises.flagMatching 'c' + expect(e.matches_name_query).toBe true for e in enterprises when e.name == 'c' + expect(e.matches_name_query).toBe false for e in enterprises when e.name != 'c' + + it "clears flags when query is null", -> + Enterprises.flagMatching null + expect(e.matches_name_query).toBe false for e in enterprises + + it "clears flags when query is blank", -> + Enterprises.flagMatching '' + expect(e.matches_name_query).toBe false for e in enterprises + + describe "calculating the distance of enterprises from a location", -> + describe "when a query is provided", -> + it "sets the distance from the enterprise when a name match is available", -> + spyOn(Enterprises, "setDistanceFrom") + Enterprises.calculateDistance "asdf", 'match' + expect(Enterprises.setDistanceFrom).toHaveBeenCalledWith('match') + + it "calculates the distance from the geocoded query otherwise", -> + spyOn(Enterprises, "calculateDistanceGeo") + Enterprises.calculateDistance "asdf", undefined + expect(Enterprises.calculateDistanceGeo).toHaveBeenCalledWith("asdf") + + it "resets the distance when query is null", -> + spyOn(Enterprises, "resetDistance") + Enterprises.calculateDistance null + expect(Enterprises.resetDistance).toHaveBeenCalled() + + it "resets the distance when query is blank", -> + spyOn(Enterprises, "resetDistance") + Enterprises.calculateDistance "" + expect(Enterprises.resetDistance).toHaveBeenCalled() + + describe "calculating the distance of enterprises from a location by geocoding", -> + beforeEach -> + spyOn(Enterprises, "setDistanceFrom") + + it "calculates distance for all enterprises when geocoding succeeds", -> + Geo.succeed = true + Enterprises.calculateDistanceGeo('query') + expect(Enterprises.setDistanceFrom).toHaveBeenCalledWith("location") + + it "resets distance when geocoding fails", -> + Geo.succeed = false + spyOn(Enterprises, "resetDistance") + Enterprises.calculateDistanceGeo('query') + expect(Enterprises.setDistanceFrom).not.toHaveBeenCalled() + expect(Enterprises.resetDistance).toHaveBeenCalled() + + describe "setting the distance of each enterprise from a central location", -> + it "sets the distances", -> + Enterprises.setDistanceFrom 'location' + for e in Enterprises.enterprises + expect(e.distance).toEqual 123 + + describe "resetting the distance measurement of all enterprises", -> + beforeEach -> + e.distance = 123 for e in Enterprises.enterprises + + it "resets the distance", -> + Enterprises.resetDistance() + for e in Enterprises.enterprises + expect(e.distance).toBeNull() \ No newline at end of file diff --git a/spec/javascripts/unit/darkswarm/services/groups_spec.js.coffee b/spec/javascripts/unit/darkswarm/services/groups_spec.js.coffee index abd9c5c617..50e2159338 100644 --- a/spec/javascripts/unit/darkswarm/services/groups_spec.js.coffee +++ b/spec/javascripts/unit/darkswarm/services/groups_spec.js.coffee @@ -1,7 +1,8 @@ describe "Groups service", -> Groups = null Enterprises = null - CurrentHubMock = {} + CurrentHubMock = {} + Geo = {} groups = [{ id: 1 name: "Test Group" @@ -17,17 +18,18 @@ describe "Groups service", -> beforeEach -> module 'Darkswarm' - angular.module('Darkswarm').value('groups', groups) - angular.module('Darkswarm').value('enterprises', enterprises) + angular.module('Darkswarm').value('groups', groups) + angular.module('Darkswarm').value('enterprises', enterprises) module ($provide)-> - $provide.value "CurrentHub", CurrentHubMock + $provide.value "CurrentHub", CurrentHubMock + $provide.value "Geo", Geo null inject (_Groups_, _Enterprises_)-> - Groups = _Groups_ - Enterprises = _Enterprises_ + Groups = _Groups_ + Enterprises = _Enterprises_ it "dereferences group enterprises", -> expect(Groups.groups[0].enterprises[0]).toBe enterprises[0] - + it "dereferences enterprise groups", -> expect(Enterprises.enterprises[0].groups[0]).toBe groups[0] diff --git a/spec/javascripts/unit/darkswarm/services/map_spec.js.coffee b/spec/javascripts/unit/darkswarm/services/map_spec.js.coffee index 3ef21705d3..4252000460 100644 --- a/spec/javascripts/unit/darkswarm/services/map_spec.js.coffee +++ b/spec/javascripts/unit/darkswarm/services/map_spec.js.coffee @@ -1,6 +1,7 @@ describe "Hubs service", -> OfnMap = null - CurrentHubMock = {} + CurrentHubMock = {} + Geo = {} enterprises = [ { id: 2 @@ -13,12 +14,13 @@ describe "Hubs service", -> beforeEach -> module 'Darkswarm' - angular.module('Darkswarm').value('enterprises', enterprises) + angular.module('Darkswarm').value('enterprises', enterprises) module ($provide)-> - $provide.value "CurrentHub", CurrentHubMock + $provide.value "CurrentHub", CurrentHubMock + $provide.value "Geo", Geo null inject ($injector)-> - OfnMap = $injector.get("OfnMap") + OfnMap = $injector.get("OfnMap") it "builds MapMarkers from enterprises", -> expect(OfnMap.enterprises[0].id).toBe enterprises[0].id diff --git a/spec/javascripts/unit/darkswarm/services/products_spec.js.coffee b/spec/javascripts/unit/darkswarm/services/products_spec.js.coffee index 590156260d..778e701c7d 100644 --- a/spec/javascripts/unit/darkswarm/services/products_spec.js.coffee +++ b/spec/javascripts/unit/darkswarm/services/products_spec.js.coffee @@ -10,6 +10,7 @@ describe 'Products service', -> productWithImage = null properties = null taxons = null + Geo = {} beforeEach -> product = @@ -40,6 +41,7 @@ describe 'Products service', -> $provide.value "currentOrder", currentOrder $provide.value "taxons", taxons $provide.value "properties", properties + $provide.value "Geo", Geo null inject ($injector, _$httpBackend_)-> diff --git a/spec/javascripts/unit/darkswarm/services/variants_spec.js.coffee b/spec/javascripts/unit/darkswarm/services/variants_spec.js.coffee index 5c6e138e0d..a235bafaee 100644 --- a/spec/javascripts/unit/darkswarm/services/variants_spec.js.coffee +++ b/spec/javascripts/unit/darkswarm/services/variants_spec.js.coffee @@ -23,3 +23,9 @@ describe 'Variants service', -> it "initialises base price percentage", -> expect(Variants.register(variant).basePricePercentage).toEqual 81 + + it "clears registered variants", -> + Variants.register(variant) + expect(Variants.variants[variant.id]).toBe variant + Variants.clear() + expect(Variants.variants[variant.id]).toBeUndefined() \ No newline at end of file diff --git a/spec/javascripts/unit/order_cycle_spec.js.coffee b/spec/javascripts/unit/order_cycle_spec.js.coffee index c14de6742d..973b348383 100644 --- a/spec/javascripts/unit/order_cycle_spec.js.coffee +++ b/spec/javascripts/unit/order_cycle_spec.js.coffee @@ -39,7 +39,7 @@ describe 'OrderCycle controllers', -> forEnterprise: jasmine.createSpy('forEnterprise').andReturn('enterprise fees for enterprise') ocInstance = {} - module('admin.order_cycles') + module('admin.orderCycles') inject ($controller) -> ctrl = $controller 'AdminCreateOrderCycleCtrl', {$scope: scope, OrderCycle: OrderCycle, Enterprise: Enterprise, EnterpriseFee: EnterpriseFee, ocInstance: ocInstance} @@ -156,9 +156,8 @@ describe 'OrderCycle controllers', -> expect(OrderCycle.removeDistributionOfVariant).toHaveBeenCalledWith('variant') it 'Submits the order cycle via OrderCycle create', -> - eventMock = { preventDefault: -> } - scope.submit(eventMock) - expect(OrderCycle.create).toHaveBeenCalled() + scope.submit('/admin/order_cycles') + expect(OrderCycle.create).toHaveBeenCalledWith('/admin/order_cycles') describe 'AdminEditOrderCycleCtrl', -> ctrl = null @@ -202,7 +201,7 @@ describe 'OrderCycle controllers', -> index: jasmine.createSpy('index').andReturn('enterprise fees list') forEnterprise: jasmine.createSpy('forEnterprise').andReturn('enterprise fees for enterprise') - module('admin.order_cycles') + module('admin.orderCycles') inject ($controller) -> ctrl = $controller 'AdminEditOrderCycleCtrl', {$scope: scope, $location: location, OrderCycle: OrderCycle, Enterprise: Enterprise, EnterpriseFee: EnterpriseFee} @@ -319,9 +318,8 @@ describe 'OrderCycle controllers', -> expect(OrderCycle.removeDistributionOfVariant).toHaveBeenCalledWith('variant') it 'Submits the order cycle via OrderCycle update', -> - eventMock = { preventDefault: -> } - scope.submit(eventMock) - expect(OrderCycle.update).toHaveBeenCalled() + scope.submit('/admin/order_cycles') + expect(OrderCycle.update).toHaveBeenCalledWith('/admin/order_cycles') describe 'OrderCycle services', -> @@ -330,23 +328,23 @@ describe 'OrderCycle services', -> Enterprise = null beforeEach -> - module 'admin.order_cycles' + module 'admin.orderCycles' inject ($injector, _$httpBackend_)-> Enterprise = $injector.get('Enterprise') $httpBackend = _$httpBackend_ $httpBackend.whenGET('/admin/enterprises/for_order_cycle.json?').respond [ - {id: 1, name: 'One', supplied_products: [1, 2]} + {id: 1, name: 'One', supplied_products: [1, 2], is_primary_producer: true} {id: 2, name: 'Two', supplied_products: [3, 4]} - {id: 3, name: 'Three', supplied_products: [5, 6]} + {id: 3, name: 'Three', supplied_products: [5, 6], sells: 'any'} ] it 'loads enterprises as a hash', -> enterprises = Enterprise.index() $httpBackend.flush() expect(enterprises).toEqual - 1: new Enterprise.Enterprise({id: 1, name: 'One', supplied_products: [1, 2]}) + 1: new Enterprise.Enterprise({id: 1, name: 'One', supplied_products: [1, 2], is_primary_producer: true}) 2: new Enterprise.Enterprise({id: 2, name: 'Two', supplied_products: [3, 4]}) - 3: new Enterprise.Enterprise({id: 3, name: 'Three', supplied_products: [5, 6]}) + 3: new Enterprise.Enterprise({id: 3, name: 'Three', supplied_products: [5, 6], sells: 'any'}) it 'reports its loadedness', -> expect(Enterprise.loaded).toBe(false) @@ -354,6 +352,16 @@ describe 'OrderCycle services', -> $httpBackend.flush() expect(Enterprise.loaded).toBe(true) + it 'loads producers as an array', -> + Enterprise.index() + $httpBackend.flush() + expect(Enterprise.producer_enterprises).toEqual [new Enterprise.Enterprise({id: 1, name: 'One', supplied_products: [1, 2], is_primary_producer: true})] + + it 'loads hubs as an array', -> + Enterprise.index() + $httpBackend.flush() + expect(Enterprise.hub_enterprises).toEqual [new Enterprise.Enterprise({id: 3, name: 'Three', supplied_products: [5, 6], sells: 'any'})] + it 'collates all supplied products', -> enterprises = Enterprise.index() $httpBackend.flush() @@ -396,7 +404,7 @@ describe 'OrderCycle services', -> EnterpriseFee = null beforeEach -> - module 'admin.order_cycles' + module 'admin.orderCycles' inject ($injector, _$httpBackend_)-> EnterpriseFee = $injector.get('EnterpriseFee') $httpBackend = _$httpBackend_ @@ -438,7 +446,7 @@ describe 'OrderCycle services', -> beforeEach -> $window = {navigator: {userAgent: 'foo'}} - module 'admin.order_cycles', ($provide)-> + module 'admin.orderCycles', ($provide)-> $provide.value('$window', $window) null @@ -462,12 +470,49 @@ describe 'OrderCycle services', -> exchanges: [] it 'initialises order cycle', -> - expect(OrderCycle.order_cycle).toEqual {} + expect(OrderCycle.order_cycle).toEqual {incoming_exchanges: [], outgoing_exchanges: []} it 'counts selected variants in an exchange', -> result = OrderCycle.exchangeSelectedVariants({variants: {1: true, 2: false, 3: true}}) expect(result).toEqual(2) + describe "fetching exchange ids", -> + it "gets enterprise ids as ints", -> + OrderCycle.order_cycle.incoming_exchanges = [ + {enterprise_id: 1} + {enterprise_id: '2'} + ] + OrderCycle.order_cycle.outgoing_exchanges = [ + {enterprise_id: 3} + {enterprise_id: '4'} + ] + expect(OrderCycle.exchangeIds('incoming')).toEqual [1, 2] + + describe "checking for novel enterprises", -> + e1 = {id: 1} + e2 = {id: 2} + + beforeEach -> + OrderCycle.order_cycle.incoming_exchanges = [{enterprise_id: 1}] + OrderCycle.order_cycle.outgoing_exchanges = [{enterprise_id: 1}] + + it "detects novel suppliers", -> + expect(OrderCycle.novelSupplier(e1)).toBe false + expect(OrderCycle.novelSupplier(e2)).toBe true + + it "detects novel suppliers with enterprise as string id", -> + expect(OrderCycle.novelSupplier('1')).toBe false + expect(OrderCycle.novelSupplier('2')).toBe true + + it "detects novel distributors", -> + expect(OrderCycle.novelDistributor(e1)).toBe false + expect(OrderCycle.novelDistributor(e2)).toBe true + + it "detects novel distributors with enterprise as string id", -> + expect(OrderCycle.novelDistributor('1')).toBe false + expect(OrderCycle.novelDistributor('2')).toBe true + + describe 'fetching the direction for an exchange', -> it 'returns "incoming" for incoming exchanges', -> exchange = {id: 1} @@ -760,16 +805,19 @@ describe 'OrderCycle services', -> expect(OrderCycle.order_cycle.exchanges).toBeUndefined() describe 'creating an order cycle', -> - it 'redirects to the order cycles page on success', -> + beforeEach -> + spyOn(OrderCycle, 'confirmNoDistributors').andReturn true + + it 'redirects to the destination page on success', -> OrderCycle.order_cycle = 'this is the order cycle' spyOn(OrderCycle, 'dataForSubmit').andReturn('this is the submit data') $httpBackend.expectPOST('/admin/order_cycles.json', { order_cycle: 'this is the submit data' }).respond {success: true} - OrderCycle.create() + OrderCycle.create('/destination/page') $httpBackend.flush() - expect($window.location).toEqual('/admin/order_cycles') + expect($window.location).toEqual('/destination/page') it 'does not redirect on error', -> OrderCycle.order_cycle = 'this is the order cycle' @@ -778,30 +826,33 @@ describe 'OrderCycle services', -> order_cycle: 'this is the submit data' }).respond {success: false} - OrderCycle.create() + OrderCycle.create('/destination/page') $httpBackend.flush() expect($window.location).toEqual(undefined) describe 'updating an order cycle', -> - it 'redirects to the order cycles page on success', -> + beforeEach -> + spyOn(OrderCycle, 'confirmNoDistributors').andReturn true + + it 'redirects to the destination page on success', -> OrderCycle.order_cycle = 'this is the order cycle' spyOn(OrderCycle, 'dataForSubmit').andReturn('this is the submit data') - $httpBackend.expectPUT('/admin/order_cycles.json', { + $httpBackend.expectPUT('/admin/order_cycles.json?reloading=1', { order_cycle: 'this is the submit data' }).respond {success: true} - OrderCycle.update() + OrderCycle.update('/destination/page') $httpBackend.flush() - expect($window.location).toEqual('/admin/order_cycles') + expect($window.location).toEqual('/destination/page') it 'does not redirect on error', -> OrderCycle.order_cycle = 'this is the order cycle' spyOn(OrderCycle, 'dataForSubmit').andReturn('this is the submit data') - $httpBackend.expectPUT('/admin/order_cycles.json', { + $httpBackend.expectPUT('/admin/order_cycles.json?reloading=1', { order_cycle: 'this is the submit data' }).respond {success: false} - OrderCycle.update() + OrderCycle.update('/destination/page') $httpBackend.flush() expect($window.location).toEqual(undefined) @@ -898,3 +949,30 @@ describe 'OrderCycle services', -> expect(order_cycle.outgoing_exchanges[0].enterprise_fees).toEqual [{id: 3}, {id: 4}] expect(order_cycle.incoming_exchanges[0].enterprise_fee_ids).toBeUndefined() expect(order_cycle.outgoing_exchanges[0].enterprise_fee_ids).toBeUndefined() + + describe "confirming when there are no distributors", -> + order_cycle_with_exchanges = order_cycle_without_exchanges = null + + beforeEach -> + order_cycle_with_exchanges = + outgoing_exchanges: [{}] + order_cycle_without_exchanges = + outgoing_exchanges: [] + + it "returns true when there are distributors", -> + spyOn window, 'confirm' + OrderCycle.order_cycle = order_cycle_with_exchanges + expect(OrderCycle.confirmNoDistributors()).toBe true + expect(window.confirm).not.toHaveBeenCalled() + + it "returns true when there are no distributors but the user confirms", -> + spyOn(window, 'confirm').andReturn true + OrderCycle.order_cycle = order_cycle_without_exchanges + expect(OrderCycle.confirmNoDistributors()).toBe true + expect(window.confirm).toHaveBeenCalled() + + it "returns false when there are no distributors and the user does not confirm", -> + spyOn(window, 'confirm').andReturn false + OrderCycle.order_cycle = order_cycle_without_exchanges + expect(OrderCycle.confirmNoDistributors()).toBe false + expect(window.confirm).toHaveBeenCalled() diff --git a/spec/jobs/finalize_account_invoices_spec.rb b/spec/jobs/finalize_account_invoices_spec.rb new file mode 100644 index 0000000000..d4b250d42f --- /dev/null +++ b/spec/jobs/finalize_account_invoices_spec.rb @@ -0,0 +1,215 @@ +require 'spec_helper' + +def travel_to(time) + around { |example| Timecop.travel(start_of_july + time) { example.run } } +end + + +describe FinalizeAccountInvoices do + let!(:year) { Time.zone.now.year } + + describe "unit specs" do + let!(:finalizer) { FinalizeAccountInvoices.new } + let!(:start_of_july) { Time.zone.local(year, 7) } + + describe "perform" do + let!(:accounts_distributor) { create(:distributor_enterprise) } + + #Invoice from June + let!(:account_invoice1) { create(:account_invoice, year: year, month: 6, order: create(:order, completed_at: nil))} + + # We don't care when it was completed, in the future or past + let!(:account_invoice2) { create(:account_invoice, year: year, month: 6, order: create(:order, completed_at: start_of_july - 10.days))} + let!(:account_invoice3) { create(:account_invoice, year: year, month: 6, order: create(:order, completed_at: start_of_july + 10.days))} + + # Invoices from July + let!(:account_invoice4) { create(:account_invoice, year: year, month: 7, order: create(:order, completed_at: nil))} + let!(:account_invoice5) { create(:account_invoice, year: year, month: 7, order: create(:order, completed_at: start_of_july + 10.days))} + + before do + allow(Enterprise).to receive(:find_by_id) { accounts_distributor } + allow(accounts_distributor).to receive(:payment_methods) { double(:payment_methods, find_by_id: true) } + allow(accounts_distributor).to receive(:shipping_methods) { double(:shipping_methods, find_by_id: true) } + allow(finalizer).to receive(:finalize) + allow(Bugsnag).to receive(:notify) + end + + context "when necessary global config setting have not been set" do + travel_to(20.days) + + context "when accounts_distributor has been set" do + before do + allow(Enterprise).to receive(:find_by_id) { false } + finalizer.perform + end + + it "snags errors and doesn't run" do + expect(Bugsnag).to have_received(:notify).with(RuntimeError.new("InvalidJobSettings"), anything) + expect(finalizer).to_not have_received(:finalize) + end + end + + context "when default payment method has been set" do + before do + allow(accounts_distributor).to receive(:payment_methods) { double(:payment_methods, find_by_id: false) } + finalizer.perform + end + + it "snags errors and doesn't run" do + expect(Bugsnag).to have_received(:notify).with(RuntimeError.new("InvalidJobSettings"), anything) + expect(finalizer).to_not have_received(:finalize) + end + end + + context "when default shipping method has been set" do + before do + allow(accounts_distributor).to receive(:shipping_methods) { double(:shipping_methods, find_by_id: false) } + finalizer.perform + end + + it "snags errors and doesn't run" do + expect(Bugsnag).to have_received(:notify).with(RuntimeError.new("InvalidJobSettings"), anything) + expect(finalizer).to_not have_received(:finalize) + end + end + end + + context "when necessary global config setting have been set" do + context "and no date arguments are passed to the job" do + travel_to(3.days) + + it "finalizes the uncompleted orders from account_invoices for the previous calendar month" do + finalizer.perform + expect(finalizer).to have_received(:finalize).with(account_invoice1.order) + expect(finalizer).to_not have_received(:finalize).with(account_invoice2.order) + expect(finalizer).to_not have_received(:finalize).with(account_invoice3.order) + expect(finalizer).to_not have_received(:finalize).with(account_invoice4.order) + expect(finalizer).to_not have_received(:finalize).with(account_invoice5.order) + end + end + + context "and a specific year and month are passed as arguments" do + let!(:finalizer) { FinalizeAccountInvoices.new(year, 7) } + + before do + allow(finalizer).to receive(:finalizer) + end + + context "that ends in the past" do + travel_to(1.month + 3.hours) + + it "finalizes the uncompleted orders from account_invoices for the specified calendar month" do + finalizer.perform + expect(finalizer).to_not have_received(:finalize).with(account_invoice1.order) + expect(finalizer).to_not have_received(:finalize).with(account_invoice2.order) + expect(finalizer).to_not have_received(:finalize).with(account_invoice3.order) + expect(finalizer).to have_received(:finalize).with(account_invoice4.order) + expect(finalizer).to_not have_received(:finalize).with(account_invoice5.order) + end + end + + context "that ends in the future" do + travel_to 3.days + + it "does not finalize any orders" do + finalizer.perform + expect(Bugsnag).to have_received(:notify).with(RuntimeError.new("InvalidJobSettings"), anything) + expect(finalizer).to_not have_received(:finalize) + end + end + end + end + end + + describe "finalize" do + let!(:pm) { create(:payment_method, name: "PM1") } + let!(:sm) { create(:shipping_method, name: "ship1") } + let!(:accounts_distributor) { create(:distributor_enterprise, payment_methods: [pm], shipping_methods: [sm]) } + let!(:invoice_order) { create(:order, distributor: accounts_distributor) } + + before do + Spree::Config.set({ accounts_distributor_id: accounts_distributor.id }) + Spree::Config.set({ default_accounts_payment_method_id: pm.id }) + Spree::Config.set({ default_accounts_shipping_method_id: sm.id }) + invoice_order.line_items.clear + end + + it "creates payment, assigns shipping method and finalizes the order" do + expect(invoice_order.completed_at).to be nil + finalizer.finalize(invoice_order) + expect(invoice_order.completed_at).to_not be nil + expect(invoice_order.payments.count).to eq 1 + expect(invoice_order.payments.first.payment_method).to eq pm + expect(invoice_order.shipping_method).to eq sm + end + + it "does not send a confirmation email" do + expect(invoice_order).to receive(:deliver_order_confirmation_email).and_call_original + expect{finalizer.finalize(invoice_order)}.to_not enqueue_job ConfirmOrderJob + end + + context "when errors exist on the order" do + before do + allow(invoice_order).to receive(:errors) { double(:errors, any?: true, full_messages: ["Error message 1", "Error message 2"]) } + allow(Bugsnag).to receive(:notify) + end + + it "Snags a bug and does not finalize the order" do + finalizer.finalize(invoice_order) + expect(Bugsnag).to have_received(:notify).with(RuntimeError.new("FinalizeInvoiceError"), anything) + expect(invoice_order).to_not be_completed + end + end + end + end + + describe "validation spec" do + let!(:start_of_july) { Time.zone.local(year, 7) } + + let!(:updater) { UpdateAccountInvoices.new } + let!(:finalizer) { FinalizeAccountInvoices.new } + + let!(:pm) { create(:payment_method, name: "Default Payment Method") } + let!(:sm) { create(:shipping_method, name: "Default Shipping Method") } + let!(:accounts_distributor) { create(:distributor_enterprise, payment_methods: [pm], shipping_methods: [sm]) } + + let!(:user) { create(:user) } + let!(:billable_period1) { create(:billable_period, sells: 'any', owner: user, begins_at: start_of_july - 1.month, ends_at: start_of_july) } + let!(:billable_period2) { create(:billable_period, owner: user, begins_at: start_of_july, ends_at: start_of_july + 10.days) } + let!(:billable_period3) { create(:billable_period, owner: user, begins_at: start_of_july + 12.days, ends_at: start_of_july + 20.days) } + + before do + sm.calculator.set_preference(:amount, 0); sm.calculator.save! + + Spree::Config.set({ accounts_distributor_id: accounts_distributor.id }) + Spree::Config.set({ default_accounts_payment_method_id: pm.id }) + Spree::Config.set({ default_accounts_shipping_method_id: sm.id }) + + # Make sure that bills are > 0 + Spree::Config.set(:account_invoices_monthly_fixed, 5) + Spree::Config.set(:account_invoices_monthly_rate, 0.02) + Spree::Config.set(:account_invoices_monthly_cap, 50) + end + + context "finalizing an invoice" do + travel_to(3.hours) + + it "finalizes it" do + # Create an invoice using the updater, to make sure we are using + # an order as it would be when generated this way + expect{updater.perform}.to change{Spree::Order.count}.from(0).to(1) + invoice = user.orders.first + + # Finalize invoices + finalizer.perform + invoice.reload + + expect(invoice.completed_at).to_not be_nil + expect(invoice.total).to eq billable_period1.bill.round(2) + expect(invoice.payments.count).to eq 1 + expect(invoice.payments.first.amount).to eq billable_period1.bill.round(2) + expect(invoice.state).to eq 'complete' + end + end + end +end diff --git a/spec/jobs/order_cycle_notification_job_spec.rb b/spec/jobs/order_cycle_notification_job_spec.rb new file mode 100644 index 0000000000..674bdbdd24 --- /dev/null +++ b/spec/jobs/order_cycle_notification_job_spec.rb @@ -0,0 +1,12 @@ +require 'spec_helper' + +describe OrderCycleNotificationJob do + let(:order_cycle) { create(:order_cycle) } + + it "sends a mail to each supplier" do + mail = double(:mail) + allow(mail).to receive(:deliver) + expect(ProducerMailer).to receive(:order_cycle_report).twice.and_return(mail) + run_job OrderCycleNotificationJob.new(order_cycle.id) + end +end diff --git a/spec/jobs/update_account_invoices_spec.rb b/spec/jobs/update_account_invoices_spec.rb new file mode 100644 index 0000000000..87d601b60a --- /dev/null +++ b/spec/jobs/update_account_invoices_spec.rb @@ -0,0 +1,447 @@ +require 'spec_helper' + +def travel_to(time) + around { |example| Timecop.travel(start_of_july + time) { example.run } } +end + +describe UpdateAccountInvoices do + let(:year) { Time.zone.now.year } + + before do + # Make sure that bills are > 0 + Spree::Config.set(:account_invoices_monthly_fixed, 5) + Spree::Config.set(:account_invoices_monthly_rate, 0.02) + Spree::Config.set(:account_invoices_monthly_cap, 50) + end + + describe "units specs" do + let!(:start_of_july) { Time.zone.local(year, 7) } + + let!(:updater) { UpdateAccountInvoices.new } + + let!(:user) { create(:user) } + let!(:june_billable_period1) { create(:billable_period, owner: user, begins_at: start_of_july - 1.month, ends_at: start_of_july - 20.days) } + let!(:june_billable_period2) { create(:billable_period, owner: user, begins_at: start_of_july - 20.days, ends_at: start_of_july - 10.days, turnover: 45, sells: "none" ) } + let!(:june_billable_period3) { create(:billable_period, owner: user, begins_at: start_of_july - 10.days, ends_at: start_of_july - 1.days, turnover: 0, sells: "any" ) } + let!(:july_billable_period1) { create(:billable_period, owner: user, begins_at: start_of_july, ends_at: start_of_july + 12.days) } + let!(:july_billable_period2) { create(:billable_period, owner: user, begins_at: start_of_july + 12.days, ends_at: start_of_july + 20.days) } + let!(:july_billable_period3) { create(:billable_period, owner: user, begins_at: start_of_july + 20.days, ends_at: start_of_july + 25.days, turnover: 45, sells: 'none') } + let!(:july_billable_period4) { create(:billable_period, owner: user, begins_at: start_of_july + 25.days, ends_at: start_of_july + 28.days, turnover: 0, sells: 'any') } + let(:june_account_invoice) { june_billable_period1.account_invoice } + let(:july_account_invoice) { july_billable_period1.account_invoice } + + describe "perform" do + let(:accounts_distributor) { double(:accounts_distributor) } + before do + allow(Enterprise).to receive(:find_by_id) { accounts_distributor } + allow(updater).to receive(:update) + allow(Bugsnag).to receive(:notify) + end + + context "when necessary global config setting have not been set" do + travel_to(20.days) + + context "when accounts_distributor has been set" do + before do + allow(Enterprise).to receive(:find_by_id) { false } + updater.perform + end + + it "snags errors and doesn't run" do + expect(Bugsnag).to have_received(:notify).with(RuntimeError.new("InvalidJobSettings"), anything) + expect(updater).to_not have_received(:update) + end + end + end + + context "when necessary global config setting have been set" do + context "on the first of the month" do + travel_to(3.hours) + + it "updates invoices from the previous month" do + updater.perform + expect(updater).to have_received(:update).once + .with(june_account_invoice) + expect(updater).to_not have_received(:update) + .with(july_account_invoice) + end + end + + context "on other days" do + travel_to(20.days) + + it "updates invoices from the current month" do + updater.perform + expect(updater).to have_received(:update).once + .with(july_account_invoice) + end + end + + context "when specfic a specific month (and year) are passed as arguments" do + let!(:updater) { UpdateAccountInvoices.new(year, 7) } + + before do + allow(updater).to receive(:update) + end + + context "that just ended (in the past)" do + travel_to(1.month) + + it "updates invoices from the previous month" do + updater.perform + expect(updater).to have_received(:update).once + .with(july_account_invoice) + end + end + + context "that starts in the past and ends in the future (ie. current_month)" do + travel_to 30.days + + it "updates invoices from that current month" do + updater.perform + expect(updater).to have_received(:update).once + .with(july_account_invoice) + end + end + + context "that starts in the future" do + travel_to -1.days + + it "snags an error and does not update invoices" do + updater.perform + expect(Bugsnag).to have_received(:notify).with(RuntimeError.new("InvalidJobSettings"), anything) + expect(updater).to_not have_received(:update) + end + end + end + end + end + + describe "update" do + before do + allow(june_account_invoice).to receive(:save).and_call_original + allow(july_account_invoice).to receive(:save).and_call_original + allow(updater).to receive(:clean_up) + allow(updater).to receive(:finalize) + allow(Bugsnag).to receive(:notify) + end + + context "where an order for the invoice already exists" do + let!(:invoice_order) { create(:order, user: user) } + + before do + expect(Spree::Order).to_not receive(:new) + allow(june_account_invoice).to receive(:order) { invoice_order } + end + + context "where the order is already complete" do + before do + allow(invoice_order).to receive(:complete?) { true } + updater.update(june_account_invoice) + end + + it "snags a bug" do + expect(Bugsnag).to have_received(:notify) + end + + it "does not save the order" do + expect(june_account_invoice).to_not have_received(:save) + end + + it "does not clean up the order" do + expect(updater).to_not have_received(:clean_up).with(invoice_order, anything) + end + end + + context "where the order is not complete" do + before do + allow(invoice_order).to receive(:complete?) { false } + june_billable_period1.enterprise.update_attributes(contact: "Firstname Lastname Something Else", phone: '12345') + updater.update(june_account_invoice) + end + + it "creates adjustments for each billing item where bill is not 0" do + adjustments = invoice_order.adjustments + expect(adjustments.map(&:source_id)).to eq [june_billable_period1.id, june_billable_period3.id] + expect(adjustments.map(&:amount)).to eq [june_billable_period1.bill.round(2), june_billable_period3.bill.round(2)] + expect(adjustments.map(&:label)).to eq [june_billable_period1.adjustment_label, june_billable_period3.adjustment_label] + end + + it "assigns a addresses to the order" do + expect(invoice_order.billing_address).to be_a Spree::Address + expect(invoice_order.shipping_address).to be_a Spree::Address + expect(invoice_order.shipping_address).to eq invoice_order.billing_address + [:address1, :address2, :city, :zipcode, :state_id, :country_id].each do |attr| + expect(invoice_order.billing_address[attr]).to eq june_billable_period1.enterprise.address[attr] + end + expect(invoice_order.billing_address.firstname).to eq "Firstname" + expect(invoice_order.billing_address.lastname).to eq "Lastname Something Else" + expect(invoice_order.billing_address.phone).to eq "12345" + end + + it "saves the order" do + expect(june_account_invoice).to have_received(:save) + expect(june_account_invoice.order).to be_persisted + end + + it "cleans up the order" do + expect(updater).to have_received(:clean_up).with(invoice_order, anything).once + end + end + end + + context "where an order for the invoice does not already exist" do + let!(:accounts_distributor) { create(:distributor_enterprise) } + before do + Spree::Config.set({ accounts_distributor_id: accounts_distributor.id }) + updater.update(july_account_invoice) + end + + it "creates adjustments for each billing item where bill is not 0" do + adjustments = july_account_invoice.order.adjustments + expect(adjustments.map(&:source_id)).to eq [july_billable_period1.id, july_billable_period2.id,july_billable_period4.id] + expect(adjustments.map(&:amount)).to eq [july_billable_period1.bill.round(2), july_billable_period2.bill.round(2), july_billable_period4.bill.round(2)] + expect(adjustments.map(&:label)).to eq [july_billable_period1.adjustment_label, july_billable_period2.adjustment_label, july_billable_period4.adjustment_label] + end + + it "saves the order" do + expect(july_account_invoice).to have_received(:save) + expect(july_account_invoice.order).to be_persisted + end + + it "cleans up order" do + expect(updater).to have_received(:clean_up).with(july_account_invoice.order, anything).once + end + end + end + + describe "clean_up" do + let!(:invoice_order) { create(:order) } + let!(:obsolete1) { create(:adjustment, adjustable: invoice_order) } + let!(:obsolete2) { create(:adjustment, adjustable: invoice_order) } + let!(:current1) { create(:adjustment, adjustable: invoice_order) } + let!(:current2) { create(:adjustment, adjustable: invoice_order) } + + before do + allow(invoice_order).to receive(:save) + allow(invoice_order).to receive(:destroy) + allow(Bugsnag).to receive(:notify) + end + + context "when current adjustments are present" do + let!(:current_adjustments) { [current1, current2] } + + context "and obsolete adjustments are present" do + let!(:obsolete_adjustments) { [obsolete1, obsolete2] } + + before do + allow(obsolete_adjustments).to receive(:destroy_all) + allow(invoice_order).to receive(:adjustments) { double(:adjustments, where: obsolete_adjustments) } + updater.clean_up(invoice_order, current_adjustments) + end + + it "destroys obsolete adjustments and snags a bug" do + expect(obsolete_adjustments).to have_received(:destroy_all) + expect(Bugsnag).to have_received(:notify).with(RuntimeError.new("Obsolete Adjustments"), anything) + end + end + + context "and obsolete adjustments are not present" do + let!(:obsolete_adjustments) { [] } + + before do + allow(invoice_order).to receive(:adjustments) { double(:adjustments, where: obsolete_adjustments) } + updater.clean_up(invoice_order, current_adjustments) + end + + it "has no bugs to snag" do + expect(Bugsnag).to_not have_received(:notify) + end + end + end + + context "when current adjustments are not present" do + let!(:current_adjustments) { [] } + + context "and obsolete adjustments are present" do + let!(:obsolete_adjustments) { [obsolete1, obsolete2] } + + before do + allow(obsolete_adjustments).to receive(:destroy_all) + allow(invoice_order).to receive(:adjustments) { double(:adjustments, where: obsolete_adjustments) } + end + + it "destroys obsolete adjustments and snags a bug" do + updater.clean_up(invoice_order, current_adjustments) + expect(obsolete_adjustments).to have_received(:destroy_all) + expect(Bugsnag).to have_received(:notify).with(RuntimeError.new("Obsolete Adjustments"), anything) + end + + context "when the order is not persisted" do + before do + allow(invoice_order).to receive(:persisted?) { false } + end + + it "destroys the order" do + updater.clean_up(invoice_order, current_adjustments) + expect(invoice_order).to have_received(:destroy) + end + end + + context "when the order is persisted" do + before do + allow(invoice_order).to receive(:persisted?) { true } + end + + it "snags a bug" do + updater.clean_up(invoice_order, current_adjustments) + expect(Bugsnag).to have_received(:notify).with(RuntimeError.new("Empty Persisted Invoice"), anything) + end + end + end + + context "and obsolete adjustments are not present" do + let!(:obsolete_adjustments) { [] } + + before do + allow(invoice_order).to receive(:adjustments) { double(:adjustments, where: obsolete_adjustments) } + end + + it "has no bugs to snag" do + expect(Bugsnag).to_not have_received(:notify).with(RuntimeError.new("Obsolete Adjustments"), anything) + end + + context "when the order is not persisted" do + before do + allow(invoice_order).to receive(:persisted?) { false } + end + + it "destroys the order" do + updater.clean_up(invoice_order, current_adjustments) + expect(invoice_order).to have_received(:destroy) + end + end + + context "when the order is persisted" do + before do + allow(invoice_order).to receive(:persisted?) { true } + end + + it "snags a bug" do + updater.clean_up(invoice_order, current_adjustments) + expect(Bugsnag).to have_received(:notify).with(RuntimeError.new("Empty Persisted Invoice"), anything) + end + end + end + end + end + end + + describe "validation spec" do + let!(:start_of_july) { Time.zone.local(year, 7) } + + let!(:updater) { UpdateAccountInvoices.new } + + let!(:accounts_distributor) { create(:distributor_enterprise) } + + let!(:user) { create(:user) } + let!(:july_billable_period1) { create(:billable_period, sells: 'any', owner: user, begins_at: start_of_july - 1.month, ends_at: start_of_july) } + let!(:july_billable_period2) { create(:billable_period, owner: user, begins_at: start_of_july, ends_at: start_of_july + 10.days) } + let!(:july_billable_period3) { create(:billable_period, owner: user, begins_at: start_of_july + 12.days, ends_at: start_of_july + 20.days) } + let!(:july_account_invoice) { july_billable_period2.account_invoice } + let!(:august_account_invoice) { create(:account_invoice, user: user, year: july_account_invoice.year, month: 8)} + + before do + Spree::Config.set({ accounts_distributor_id: accounts_distributor.id }) + july_billable_period2.enterprise.update_attributes(contact: 'Anna Karenina', phone: '3433523') + end + + context "when no invoice_order currently exists" do + context "when relevant billable periods exist" do + travel_to(20.days) + + it "creates an invoice_order" do + expect{updater.perform}.to change{Spree::Order.count}.from(0).to(1) + invoice_order = july_account_invoice.reload.order + expect(user.orders.first).to eq invoice_order + expect(invoice_order.completed_at).to be_nil + billable_adjustments = invoice_order.adjustments.where('source_type = (?)', 'BillablePeriod') + expect(billable_adjustments.map(&:amount)).to eq [july_billable_period2.bill.round(2), july_billable_period3.bill.round(2)] + expect(invoice_order.total).to eq july_billable_period2.bill.round(2) + july_billable_period3.bill.round(2) + expect(invoice_order.payments.count).to eq 0 + expect(invoice_order.state).to eq 'cart' + expect(invoice_order.bill_address).to be_a Spree::Address + expect(invoice_order.ship_address).to be_a Spree::Address + expect(invoice_order.shipping_address).to eq invoice_order.billing_address + [:address1, :address2, :city, :zipcode, :state_id, :country_id].each do |attr| + expect(invoice_order.billing_address[attr]).to eq july_billable_period2.enterprise.address[attr] + end + expect(invoice_order.billing_address.firstname).to eq "Anna" + expect(invoice_order.billing_address.lastname).to eq "Karenina" + expect(invoice_order.billing_address.phone).to eq "3433523" + end + end + + context "when no relevant billable periods exist" do + travel_to(1.month + 5.days) + + it "does not create an order" do + expect(updater).to receive(:update).with(august_account_invoice).and_call_original + expect{updater.perform}.to_not change{Spree::Order.count}.from(0) + end + end + end + + context "when an order already exists" do + context "when relevant billable periods exist" do + let!(:invoice_order) { create(:order, user: user, distributor: accounts_distributor, created_at: start_of_july) } + let!(:billable_adjustment) { create(:adjustment, adjustable: invoice_order, source_type: 'BillablePeriod') } + + before do + invoice_order.line_items.clear + july_account_invoice.update_attribute(:order, invoice_order) + end + + travel_to(20.days) + + it "updates the order, and clears any obsolete invoices" do + expect{updater.perform}.to_not change{Spree::Order.count} + invoice_order = user.orders.first + expect(invoice_order.completed_at).to be_nil + billable_adjustments = invoice_order.adjustments.where('source_type = (?)', 'BillablePeriod') + expect(billable_adjustments).to_not include billable_adjustment + expect(billable_adjustments.map(&:amount)).to eq [july_billable_period2.bill.round(2), july_billable_period3.bill.round(2)] + expect(invoice_order.total).to eq july_billable_period2.bill.round(2) + july_billable_period3.bill.round(2) + expect(invoice_order.payments.count).to eq 0 + expect(invoice_order.state).to eq 'cart' + expect(invoice_order.bill_address).to be_a Spree::Address + expect(invoice_order.ship_address).to be_a Spree::Address + expect(invoice_order.shipping_address).to eq invoice_order.billing_address + [:address1, :address2, :city, :zipcode, :state_id, :country_id].each do |attr| + expect(invoice_order.billing_address[attr]).to eq july_billable_period2.enterprise.address[attr] + end + expect(invoice_order.billing_address.firstname).to eq "Anna" + expect(invoice_order.billing_address.lastname).to eq "Karenina" + expect(invoice_order.billing_address.phone).to eq "3433523" + end + end + + context "when no relevant billable periods exist" do + let!(:invoice_order) { create(:order, user: user, distributor: accounts_distributor) } + + before do + invoice_order.line_items.clear + august_account_invoice.update_attribute(:order, invoice_order) + end + + travel_to(1.month + 5.days) + + it "snags a bug" do + expect(updater).to receive(:update).with(august_account_invoice).and_call_original + expect(Bugsnag).to receive(:notify).with(RuntimeError.new("Empty Persisted Invoice"), anything) + expect{updater.perform}.to_not change{Spree::Order.count} + end + end + end + end +end diff --git a/spec/jobs/update_billable_periods_spec.rb b/spec/jobs/update_billable_periods_spec.rb new file mode 100644 index 0000000000..d868550e7e --- /dev/null +++ b/spec/jobs/update_billable_periods_spec.rb @@ -0,0 +1,724 @@ +require 'spec_helper' + +def travel_to(time) + around { |example| Timecop.travel(start_of_july + time) { example.run } } +end + +describe UpdateBillablePeriods do + let!(:year) { Time.zone.now.year } + + describe "unit specs" do + let!(:start_of_july) { Time.zone.local(year, 7) } + + let!(:updater) { UpdateBillablePeriods.new } + + describe "perform", versioning: true do + let!(:enterprise) { create(:supplier_enterprise, created_at: start_of_july - 1.month, sells: 'any') } + + context "when no date arguments are passed to the job" do + before do + expect(updater).to receive(:clean_up_untouched_billable_periods_for).once + end + + context "on the first of the month" do + travel_to(3.hours) + + it "processes the previous month" do + expect(updater).to receive(:split_for_trial) + .with(enterprise, start_of_july - 1.month, start_of_july, nil, nil) + updater.perform + end + end + + context "on all other days" do + travel_to(1.day + 3.hours) + + it "processes the current month up until previous midnight" do + expect(updater).to receive(:split_for_trial) + .with(enterprise, start_of_july, start_of_july + 1.day, nil, nil) + updater.perform + end + end + end + + context "when a specfic year and month are passed as arguments" do + let!(:updater) { UpdateBillablePeriods.new(year, 6) } + + before do + allow(updater).to receive(:split_for_trial) + end + + context "that ends in the past" do + travel_to(3.hours) + + it "processes the month" do + expect(updater).to receive(:split_for_trial) + .with(enterprise, start_of_july - 1.month, start_of_july, nil, nil) + updater.perform + end + end + + context "that starts in the past and ends in the future (ie. current month)" do + travel_to(-3.days) + + it "processes the current month up to the previous midnight" do + expect(updater).to receive(:split_for_trial) + .with(enterprise, start_of_july - 1.month, start_of_july-3.days, nil, nil) + updater.perform + end + end + + context "that starts in the future" do + travel_to(-31.days) + + it "does not run" do + expect(updater).to_not receive(:split_for_trial) + updater.perform + end + end + end + + context "when an enterprise is created before the beginning of the current month" do + before do + expect(updater).to receive(:clean_up_untouched_billable_periods_for).once + end + + travel_to(28.days) + + context "when no alterations to sells or owner have been made during the current month" do + + it "begins at the start of the month" do + expect(updater).to receive(:split_for_trial) + .with(enterprise, start_of_july, start_of_july + 28.days, nil, nil) + updater.perform + end + end + + context "when sells has been changed within the current month" do + before do + Timecop.freeze(start_of_july + 10.days) do + # NOTE: Sells is changed between when order1 and order2 are placed + enterprise.update_attribute(:sells, 'own') + end + end + + travel_to(28.days) + + it "processes each sells period separately" do + allow(updater).to receive(:split_for_trial).twice + updater.perform + + expect(updater).to have_received(:split_for_trial) + .with(enterprise.versions.first.reify, start_of_july, start_of_july + 10.days, nil, nil) + + expect(updater).to have_received(:split_for_trial) + .with(enterprise, start_of_july + 10.days, start_of_july + 28.days, nil, nil) + end + end + + context "when owner has been changed within the current month" do + let!(:new_owner) { create(:user) } + + before do + Timecop.freeze(start_of_july + 10.days) do + # NOTE: Sells is changed between when order1 and order2 are placed + enterprise.update_attribute(:owner, new_owner) + end + end + + travel_to(28.days) + + it "processes each ownership period separately" do + allow(updater).to receive(:split_for_trial).twice + updater.perform + + expect(updater).to have_received(:split_for_trial) + .with(enterprise.versions.first.reify, start_of_july, start_of_july + 10.days, nil, nil) + + expect(updater).to have_received(:split_for_trial) + .with(enterprise, start_of_july + 10.days, start_of_july + 28.days, nil, nil) + end + end + + context "when some other attribute has been changed within the current month" do + before do + Timecop.freeze(start_of_july + 10.days) do + # NOTE: Sells is changed between when order1 and order2 are placed + enterprise.update_attribute(:name, 'Some New Name') + end + end + + travel_to(28.days) + + it "does not create a version, and so does not split the period" do + expect(enterprise.versions).to eq [] + allow(updater).to receive(:split_for_trial).once + updater.perform + expect(updater).to have_received(:split_for_trial) + .with(enterprise, start_of_july, start_of_july + 28.days, nil, nil) + end + end + + context "where sells or owner_id were altered during the previous month (ie. June)" do + let!(:new_owner) { create(:user) } + + before do + Timecop.freeze(start_of_july - 20.days) do + # NOTE: Sells is changed between when order1 and order2 are placed + enterprise.update_attribute(:sells, 'own') + end + Timecop.freeze(start_of_july - 10.days) do + # NOTE: Sells is changed between when order1 and order2 are placed + enterprise.update_attribute(:owner, new_owner) + end + end + + travel_to(28.days) + + it "ignores those verions" do + allow(updater).to receive(:split_for_trial).once + updater.perform + expect(updater).to have_received(:split_for_trial) + .with(enterprise, start_of_july, start_of_july + 28.days, nil, nil) + end + end + + context "where sells or owner_id were altered in the future" do + let!(:new_owner) { create(:user) } + + before do + Timecop.freeze(start_of_july + 17.days) do + enterprise.update_attribute(:sells, 'own') + end + Timecop.freeze(start_of_july + 35.days) do + enterprise.update_attribute(:owner, new_owner) + end + end + + travel_to(15.days) + + it "ignores those verions" do + allow(updater).to receive(:split_for_trial).once + updater.perform + expect(updater).to have_received(:split_for_trial) + .with(enterprise, start_of_july, start_of_july + 15.days, nil, nil) + end + end + end + + context "when an enterprise is created during the current month" do + before do + expect(updater).to receive(:clean_up_untouched_billable_periods_for).once + enterprise.update_attribute(:created_at, start_of_july + 10.days) + end + + travel_to(28.days) + + it "begins at the date the enterprise was created" do + allow(updater).to receive(:split_for_trial).once + updater.perform + expect(updater).to have_received(:split_for_trial) + .with(enterprise, start_of_july + 10.days, start_of_july + 28.days, nil, nil) + end + end + + context "when an enterprise is created after the previous midnight" do + before do + expect(updater).to_not receive(:clean_up_untouched_billable_periods_for) + enterprise.update_attribute(:created_at, start_of_july + 29.days) + end + + travel_to(28.days) + + it "ignores the enterprise" do + allow(updater).to receive(:split_for_trial) + updater.perform + expect(updater).to_not have_received(:split_for_trial) + end + end + + pending "when an enterprise is deleted during the current month" do + before do + expect(updater).to receive(:clean_up_untouched_billable_periods_for).once + enterprise.update_attribute(:deleted_at, start_of_july + 20.days) + end + + travel_to(28.days) + + it "ends at the date the enterprise was deleted" do + allow(updater).to receive(:split_for_trial) + updater.perform + expect(updater).to have_received(:split_for_trial) + .with(enterprise, start_of_july, start_of_july + 20.days, nil, nil) + end + end + end + + describe "split_for_trial" do + let!(:enterprise) { double(:enterprise) } + let(:begins_at) { start_of_july } + let(:ends_at) { begins_at + 30.days } + + context "when trial_start is nil" do + let(:trial_start) { nil } + let(:trial_expiry) { begins_at + 3.days } + + before do + allow(updater).to receive(:update_billable_period).once + updater.split_for_trial(enterprise, begins_at, ends_at, trial_start, trial_expiry) + end + + it "calls update_billable_period once for the entire period" do + expect(updater).to have_received(:update_billable_period) + .with(enterprise, begins_at, ends_at, false) + end + end + + context "when trial_expiry is nil" do + let(:trial_start) { begins_at + 3.days } + let(:trial_expiry) { nil } + + before do + allow(updater).to receive(:update_billable_period).once + updater.split_for_trial(enterprise, begins_at, ends_at, trial_start, trial_expiry) + end + + it "calls update_billable_period once for the entire period" do + expect(updater).to have_received(:update_billable_period) + .with(enterprise, begins_at, ends_at, false) + end + end + + context "when the trial begins before begins_at" do + let(:trial_start) { begins_at - 10.days } + + context "and the trial ends before begins_at" do + let(:trial_expiry) { begins_at - 5.days } + + before do + allow(updater).to receive(:update_billable_period).once + updater.split_for_trial(enterprise, begins_at, ends_at, trial_start, trial_expiry) + end + + it "calls update_billable_period once for the entire period" do + expect(updater).to have_received(:update_billable_period) + .with(enterprise, begins_at, ends_at, false) + end + end + + context "and the trial ends after begins_at" do + let(:trial_expiry) { begins_at + 5.days } + + before do + allow(updater).to receive(:update_billable_period).twice + updater.split_for_trial(enterprise, begins_at, ends_at, trial_start, trial_expiry) + end + + it "calls update_billable_period once for the trial period" do + expect(updater).to have_received(:update_billable_period) + .with(enterprise, begins_at, trial_expiry, true) + end + + it "calls update_billable_period once for the non-trial period" do + expect(updater).to have_received(:update_billable_period) + .with(enterprise, trial_expiry, ends_at, false) + end + end + + context "and the trial ends after ends_at" do + let(:trial_expiry) { ends_at + 5.days } + + before do + allow(updater).to receive(:update_billable_period).once + updater.split_for_trial(enterprise, begins_at, ends_at, trial_start, trial_expiry) + end + + it "calls update_billable_period once for the entire (trial) period" do + expect(updater).to have_received(:update_billable_period) + .with(enterprise, begins_at, ends_at, true) + end + end + end + + context "when the trial begins after begins_at" do + let(:trial_start) { begins_at + 5.days } + + context "and the trial begins after ends_at" do + let(:trial_start) { ends_at + 5.days } + let(:trial_expiry) { ends_at + 10.days } + + before do + allow(updater).to receive(:update_billable_period).once + updater.split_for_trial(enterprise, begins_at, ends_at, trial_start, trial_expiry) + end + + it "calls update_billable_period once for the entire period" do + expect(updater).to have_received(:update_billable_period) + .with(enterprise, begins_at, ends_at, false) + end + end + + context "and the trial ends before ends_at" do + let(:trial_expiry) { ends_at - 2.days } + + before do + allow(updater).to receive(:update_billable_period).exactly(3).times + updater.split_for_trial(enterprise, begins_at, ends_at, trial_start, trial_expiry) + end + + it "calls update_billable_period once for the non-trial period before the trial" do + expect(updater).to have_received(:update_billable_period) + .with(enterprise, begins_at, trial_start, false) + end + + it "calls update_billable_period once for the trial period" do + expect(updater).to have_received(:update_billable_period) + .with(enterprise, trial_start, trial_expiry, true) + end + + it "calls update_billable_period once for the non-trial period after the trial" do + expect(updater).to have_received(:update_billable_period) + .with(enterprise, trial_expiry, ends_at, false) + end + end + + context "and the trial ends after ends_at" do + let(:trial_expiry) { ends_at + 5.days } + + before do + allow(updater).to receive(:update_billable_period).twice + updater.split_for_trial(enterprise, begins_at, ends_at, trial_start, trial_expiry) + end + + it "calls update_billable_period once for the non-trial period" do + expect(updater).to have_received(:update_billable_period) + .with(enterprise, begins_at, trial_start, false) + end + + it "calls update_billable_period once for the trial period" do + expect(updater).to have_received(:update_billable_period) + .with(enterprise, trial_start, ends_at, true) + end + end + end + end + + describe "update_billable_period" do + let!(:enterprise) { create(:enterprise, sells: 'any') } + + let!(:existing) { create(:billable_period, enterprise: enterprise, begins_at: start_of_july) } + + before do + allow(Spree::Order).to receive(:where) { [ + double(:order, total: 10), + double(:order, total: 20), + double(:order, total: 30) + ]} + end + + context "when the account invoice is already_complete" do + before do + allow(BillablePeriod).to receive(:where) { [existing] } + allow(existing.account_invoice).to receive(:order) { double(:order, complete?: true ) } + allow(AccountInvoice).to receive(:find_or_create_by_user_id_and_year_and_month) { existing.account_invoice } + end + + it "does not update the billing period, but changes updated_at by touching the billable period " do + expect(existing).to_not receive(:update_attributes) + expect(existing).to receive(:touch) + expect(Bugsnag).to_not receive(:notify) + expect{ + updater.update_billable_period(enterprise, start_of_july, start_of_july + 20.days, false) + }.to_not change{ BillablePeriod.count } + end + end + + context "when arguments match both 'begins_at' and 'enterprise_id' of an existing billable period" do + it "updates the existing billable period" do + expect{ + updater.update_billable_period(enterprise, start_of_july, start_of_july + 20.days, false) + }.to_not change{ BillablePeriod.count } + existing.reload + expect(existing.owner_id).to eq enterprise.owner_id + expect(existing.ends_at).to eq start_of_july + 20.days + expect(existing.sells).to eq enterprise.sells + expect(existing.trial).to eq false + expect(existing.turnover).to eq 60 + end + + context "when there is nothing to update" do + before do + Timecop.freeze(start_of_july + 3.days) { + existing.update_attributes( + begins_at: start_of_july, + ends_at: start_of_july + 20.days, + trial: false, + sells: enterprise.sells, + turnover: 60 + ) + } + end + + it "changes updated_at anyway by touching the billable period" do + Timecop.freeze(start_of_july + 10.days) { + expect{ + updater.update_billable_period(enterprise, start_of_july, start_of_july + 20.days, false) + }.to change{ existing.reload.updated_at } + .from(start_of_july + 3.days) + .to(start_of_july + 10.days) + } + end + end + end + + context "when 'begins_at' does not match an existing billable period" do + before do + expect{ + updater.update_billable_period(enterprise, start_of_july + 20.days, start_of_july + 30.days, false) + }.to change{ BillablePeriod.count }.from(1).to(2) + end + + it "creates a new existing billable period" do + billable_period = BillablePeriod.last + expect(billable_period.owner_id).to eq enterprise.owner_id + expect(billable_period.ends_at).to eq start_of_july + 30.days + expect(billable_period.sells).to eq enterprise.sells + expect(billable_period.trial).to eq false + expect(billable_period.turnover).to eq 60 + end + end + + context "when 'enterprise_id' does not match an existing billable period" do + let!(:new_enterprise) { create(:enterprise, sells: 'own') } + + before do + expect{ + updater.update_billable_period(new_enterprise, start_of_july, start_of_july + 20.days, false) + }.to change{ BillablePeriod.count }.from(1).to(2) + end + + it "creates a new existing billable period" do + billable_period = BillablePeriod.last + expect(billable_period.owner_id).to eq new_enterprise.owner_id + expect(billable_period.ends_at).to eq start_of_july + 20.days + expect(billable_period.sells).to eq new_enterprise.sells + expect(billable_period.trial).to eq false + expect(billable_period.turnover).to eq 60 + end + end + end + + context "cleaning up untouched billable periods" do + let(:job_start_time) { Time.zone.now } + let(:enterprise) { create(:enterprise) } + # Updated after start + let!(:bp1) { create(:billable_period, enterprise: enterprise, updated_at: job_start_time + 2.seconds, begins_at: start_of_july, ends_at: start_of_july + 5.days ) } + let!(:bp2) { create(:billable_period, enterprise: enterprise, updated_at: job_start_time + 2.seconds, begins_at: start_of_july + 5.days, ends_at: start_of_july + 10.days ) } + # Updated before start + let!(:bp3) { create(:billable_period, enterprise: enterprise, updated_at: job_start_time - 5.seconds, begins_at: start_of_july, ends_at: start_of_july + 10.days ) } + # Updated before start but begins after end_date + let!(:bp4) { create(:billable_period, enterprise: enterprise, updated_at: job_start_time - 5.seconds, begins_at: start_of_july + 10.days, ends_at: start_of_july + 15.days ) } + # Updated before start but begins at end_date (ie. not before end_date, so should be ignored) EDGE CASE + let!(:bp5) { create(:billable_period, enterprise: enterprise, updated_at: job_start_time - 5.seconds, begins_at: start_of_july + 8.days, ends_at: start_of_july + 10.days ) } + # Updated before start but ends before start_date + let!(:bp6) { create(:billable_period, enterprise: enterprise, updated_at: job_start_time - 5.seconds, begins_at: start_of_july - 10.days, ends_at: start_of_july - 5.days ) } + # Updated before start but ends at start_date (ie. not after start_date, so should be ignored) EDGE CASE + let!(:bp7) { create(:billable_period, enterprise: enterprise, updated_at: job_start_time - 5.seconds, begins_at: start_of_july - 5.days, ends_at: start_of_july ) } + # Updated before start, but order is already complete, so should not be deleted + let!(:bp8) { create(:billable_period, enterprise: enterprise, updated_at: job_start_time - 5.seconds, begins_at: start_of_july, ends_at: start_of_july + 10.days, account_invoice: create(:account_invoice, order: create(:order, state: 'complete', completed_at: 5.minutes.ago))) } + + before do + allow(Bugsnag).to receive(:notify) + allow(updater).to receive(:start_date) { start_of_july } + allow(updater).to receive(:end_date) { start_of_july + 8.days } + updater.clean_up_untouched_billable_periods_for(enterprise, job_start_time) + end + + it "soft deletes untouched billable_periods" do + expect(bp1.reload.deleted_at).to be_nil + expect(bp2.reload.deleted_at).to be_nil + expect(bp3.reload.deleted_at).to_not be_nil + expect(bp4.reload.deleted_at).to be_nil + expect(bp5.reload.deleted_at).to be_nil + expect(bp6.reload.deleted_at).to be_nil + expect(bp7.reload.deleted_at).to be_nil + expect(bp8.reload.deleted_at).to be_nil + end + + it "notifies bugsnag" do + expect(Bugsnag).to have_received(:notify).once + end + end + end + + describe "validation spec" do + # Chose july to test with because June has 30 days and so is easy to calculate end date for shop trial + let!(:year) { Time.zone.now.year } + let!(:start_of_july) { Time.zone.local(year, 7) } + + let!(:enterprise) { create(:supplier_enterprise, sells: 'any') } + + let!(:original_owner) { enterprise.owner } + + let!(:new_owner) { create(:user) } + + let!(:account_invoice1) { create(:account_invoice, user: original_owner, year: year, month: 7)} + let!(:account_invoice2) { create(:account_invoice, user: new_owner, year: year, month: 7)} + + # This BP was updated before the current run and so should be marked for deletion at the end of the run + let!(:obsolete_bp) { create(:billable_period, enterprise: enterprise, updated_at: start_of_july + 10.days, begins_at: start_of_july + 6.5.days, ends_at: start_of_july + 10.days ) } + + # This one has an updated_at in the future (so that it doesn't get deleted) + # It also has a begins_at date which matches a period that would otherwise be created, + # and so it should be picked up and overwritten + let!(:bp_to_overwrite) { create(:billable_period, enterprise: enterprise, updated_at: start_of_july + 21.days, begins_at: start_of_july + 10.days, ends_at: start_of_july + 15.days ) } + + let!(:order1) { create(:order, completed_at: start_of_july + 1.days, distributor: enterprise) } + let!(:order2) { create(:order, completed_at: start_of_july + 3.days, distributor: enterprise) } + let!(:order3) { create(:order, completed_at: start_of_july + 5.days, distributor: enterprise) } + let!(:order4) { create(:order, completed_at: start_of_july + 7.days, distributor: enterprise) } + let!(:order5) { create(:order, completed_at: start_of_july + 9.days, distributor: enterprise) } + let!(:order6) { create(:order, completed_at: start_of_july + 11.days, distributor: enterprise) } + let!(:order7) { create(:order, completed_at: start_of_july + 13.days, distributor: enterprise) } + let!(:order8) { create(:order, completed_at: start_of_july + 15.days, distributor: enterprise) } + let!(:order9) { create(:order, completed_at: start_of_july + 17.days, distributor: enterprise) } + let!(:order10) { create(:order, completed_at: start_of_july + 19.days, distributor: enterprise) } + + before do + order1.line_items = [ create(:line_item, price: 12.56, order: order1) ] + order2.line_items = [ create(:line_item, price: 87.44, order: order2) ] + order3.line_items = [ create(:line_item, price: 50.00, order: order3) ] + order4.line_items = [ create(:line_item, price: 73.37, order: order4) ] + order5.line_items = [ create(:line_item, price: 22.46, order: order5) ] + order6.line_items = [ create(:line_item, price: 44.85, order: order6) ] + order7.line_items = [ create(:line_item, price: 93.45, order: order7) ] + order8.line_items = [ create(:line_item, price: 59.38, order: order8) ] + order9.line_items = [ create(:line_item, price: 47.23, order: order9) ] + order10.line_items = [ create(:line_item, price: 2.35, order: order10) ] + [order1, order2, order3, order4, order5, order6, order7, order8, order9, order10].each(&:update!) + + allow(Enterprise).to receive(:where) { double(:enterprises, select: [enterprise]) } + end + + context "super complex example", versioning: true do + before do + enterprise.update_attribute(:created_at, start_of_july + 2.days) + + Timecop.freeze(start_of_july + 4.days) { enterprise.update_attribute(:sells, 'own') } + + Timecop.freeze(start_of_july + 6.days) { enterprise.update_attribute(:owner, new_owner) } + + enterprise.update_attribute(:shop_trial_start_date, start_of_july + 8.days) + + Timecop.freeze(start_of_july + 10.days) { enterprise.update_attribute(:owner, original_owner) } + + Timecop.freeze(start_of_july + 12.days) { enterprise.update_attribute(:sells, 'any') } + + allow(enterprise).to receive(:shop_trial_expiry) { start_of_july + 14.days } + + Timecop.freeze(start_of_july + 16.days) { enterprise.update_attribute(:sells, 'own') } + + Timecop.freeze(start_of_july + 18.days) { enterprise.update_attribute(:owner, new_owner) } + end + + travel_to(20.days) + + before do + UpdateBillablePeriods.new.perform + end + + let(:billable_periods) { BillablePeriod.order(:updated_at) } + + it "creates the correct billable periods and deleted obsolete ones" do + expect(obsolete_bp.reload.deleted_at).to_not be_nil + + bp_to_overwrite.reload + + expect(bp_to_overwrite.sells).to eq 'own' + expect(bp_to_overwrite.trial).to be true + expect(bp_to_overwrite.owner).to eq original_owner + expect(bp_to_overwrite.begins_at).to eq start_of_july + 10.days + expect(bp_to_overwrite.ends_at).to eq start_of_july + 12.days + expect(bp_to_overwrite.turnover).to eq order6.total + expect(bp_to_overwrite.account_invoice).to eq account_invoice1 + + expect(billable_periods.count).to eq 9 + + expect(account_invoice1.billable_periods.sort).to eq billable_periods.sort.select{ |bp| bp.owner == original_owner } + expect(account_invoice2.billable_periods.sort).to eq billable_periods.sort.select{ |bp| bp.owner == new_owner } + + expect(billable_periods.map(&:begins_at)).to eq [ + start_of_july + 2.days, + start_of_july + 4.days, + start_of_july + 6.days, + start_of_july + 8.days, + start_of_july + 10.days, + start_of_july + 12.days, + start_of_july + 14.days, + start_of_july + 16.days, + start_of_july + 18.days + ] + + expect(billable_periods.map(&:ends_at)).to eq [ + start_of_july + 4.days, + start_of_july + 6.days, + start_of_july + 8.days, + start_of_july + 10.days, + start_of_july + 12.days, + start_of_july + 14.days, + start_of_july + 16.days, + start_of_july + 18.days, + start_of_july + 20.days + ] + + expect(billable_periods.map(&:owner)).to eq [ + original_owner, + original_owner, + new_owner, + new_owner, + original_owner, + original_owner, + original_owner, + original_owner, + new_owner + ] + + expect(billable_periods.map(&:sells)).to eq [ + 'any', + 'own', + 'own', + 'own', + 'own', + 'any', + 'any', + 'own', + 'own' + ] + + expect(billable_periods.map(&:trial)).to eq [ + false, + false, + false, + true, + true, + true, + false, + false, + false + ] + + expect(billable_periods.map(&:turnover)).to eq [ + order2.total, + order3.total, + order4.total, + order5.total, + order6.total, + order7.total, + order8.total, + order9.total, + order10.total + ] + end + end + end +end diff --git a/spec/lib/open_food_network/bulk_coop_report_spec.rb b/spec/lib/open_food_network/bulk_coop_report_spec.rb new file mode 100644 index 0000000000..53c2dd2ffe --- /dev/null +++ b/spec/lib/open_food_network/bulk_coop_report_spec.rb @@ -0,0 +1,97 @@ +require 'spec_helper' + +include AuthenticationWorkflow + +module OpenFoodNetwork + describe BulkCoopReport do + describe "fetching orders" do + let(:d1) { create(:distributor_enterprise) } + let(:oc1) { create(:simple_order_cycle) } + let(:o1) { create(:order, completed_at: 1.day.ago, order_cycle: oc1, distributor: d1) } + let(:li1) { build(:line_item) } + + before { o1.line_items << li1 } + + context "as a site admin" do + let(:user) { create(:admin_user) } + subject { BulkCoopReport.new user } + + it "fetches completed orders" do + o2 = create(:order) + o2.line_items << build(:line_item) + subject.table_items.should == [li1] + end + + it "does not show cancelled orders" do + o2 = create(:order, state: "canceled", completed_at: 1.day.ago) + o2.line_items << build(:line_item) + subject.table_items.should == [li1] + end + end + + context "as a manager of a supplier" do + let!(:user) { create(:user) } + subject { BulkCoopReport.new user } + + let(:s1) { create(:supplier_enterprise) } + + before do + s1.enterprise_roles.create!(user: user) + end + + context "that has granted P-OC to the distributor" do + let(:o2) { create(:order, distributor: d1, completed_at: 1.day.ago, bill_address: create(:address), ship_address: create(:address)) } + let(:li2) { build(:line_item, product: create(:simple_product, supplier: s1)) } + + before do + o2.line_items << li2 + create(:enterprise_relationship, parent: s1, child: d1, permissions_list: [:add_to_order_cycle]) + end + + it "shows line items supplied by my producers, with names hidden" do + subject.table_items.should == [li2] + subject.table_items.first.order.bill_address.firstname.should == "HIDDEN" + end + end + + context "that has not granted P-OC to the distributor" do + let(:o2) { create(:order, distributor: d1, completed_at: 1.day.ago, bill_address: create(:address), ship_address: create(:address)) } + let(:li2) { build(:line_item, product: create(:simple_product, supplier: s1)) } + + before do + o2.line_items << li2 + end + + it "shows line items supplied by my producers, with names hidden" do + subject.table_items.should == [] + end + end + end + + context "as a manager of a distributor" do + let!(:user) { create(:user) } + subject { PackingReport.new user } + + before do + d1.enterprise_roles.create!(user: user) + end + + it "only shows line items distributed by enterprises managed by the current user" do + d2 = create(:distributor_enterprise) + d2.enterprise_roles.create!(user: create(:user)) + o2 = create(:order, distributor: d2, completed_at: 1.day.ago) + o2.line_items << build(:line_item) + subject.table_items.should == [li1] + end + + it "only shows the selected order cycle" do + oc2 = create(:simple_order_cycle) + o2 = create(:order, distributor: d1, order_cycle: oc2) + o2.line_items << build(:line_item) + subject.stub(:params).and_return(order_cycle_id_in: oc1.id) + subject.table_items.should == [li1] + end + end + end + end +end diff --git a/spec/lib/open_food_network/enterprise_fee_applicator_spec.rb b/spec/lib/open_food_network/enterprise_fee_applicator_spec.rb index 6703f844ab..9ad1d222b3 100644 --- a/spec/lib/open_food_network/enterprise_fee_applicator_spec.rb +++ b/spec/lib/open_food_network/enterprise_fee_applicator_spec.rb @@ -65,33 +65,4 @@ module OpenFoodNetwork efa.send(:order_adjustment_label).should == "Whole order - packing fee by distributor Ballantyne" end end - - describe "ensuring that tax rate is marked as tax included_in_price" do - let(:efa) { EnterpriseFeeApplicator.new nil, nil, nil } - let(:tax_rate) { create(:tax_rate, included_in_price: false, calculator: Spree::Calculator::DefaultTax.new) } - - it "sets included_in_price to true" do - efa.send(:with_tax_included_in_price, tax_rate) do - tax_rate.included_in_price.should be_true - end - end - - it "sets the included_in_price value accessible to the calculator to true" do - efa.send(:with_tax_included_in_price, tax_rate) do - tax_rate.calculator.calculable.included_in_price.should be_true - end - end - - it "passes through the return value of the block" do - efa.send(:with_tax_included_in_price, tax_rate) do - 'asdf' - end.should == 'asdf' - end - - it "restores both values to their original afterwards" do - efa.send(:with_tax_included_in_price, tax_rate) {} - tax_rate.included_in_price.should be_false - tax_rate.calculator.calculable.included_in_price.should be_false - end - end end diff --git a/spec/lib/open_food_network/enterprise_fee_calculator_spec.rb b/spec/lib/open_food_network/enterprise_fee_calculator_spec.rb index 9c950234cd..d2d673e5ab 100644 --- a/spec/lib/open_food_network/enterprise_fee_calculator_spec.rb +++ b/spec/lib/open_food_network/enterprise_fee_calculator_spec.rb @@ -3,32 +3,80 @@ require 'open_food_network/enterprise_fee_calculator' module OpenFoodNetwork describe EnterpriseFeeCalculator do describe "integration" do + let(:supplier1) { create(:supplier_enterprise) } + let(:supplier2) { create(:supplier_enterprise) } let(:coordinator) { create(:distributor_enterprise) } let(:distributor) { create(:distributor_enterprise) } let(:order_cycle) { create(:simple_order_cycle) } - let(:product) { create(:simple_product, price: 10.00) } + let(:product1) { create(:simple_product, supplier: supplier1, price: 10.00) } + let(:product2) { create(:simple_product, supplier: supplier2, price: 20.00) } describe "calculating fees for a variant" do - it "sums all the per-item fees for the variant in the specified hub + order cycle" do - enterprise_fee1 = create(:enterprise_fee, amount: 20) - enterprise_fee2 = create(:enterprise_fee, amount: 3) - enterprise_fee3 = create(:enterprise_fee, - calculator: Spree::Calculator::FlatRate.new(preferred_amount: 2)) + describe "summing all the per-item fees for the variant in the specified hub + order cycle" do + let(:enterprise_fee1) { create(:enterprise_fee, amount: 20) } + let(:enterprise_fee2) { create(:enterprise_fee, amount: 3) } + let(:enterprise_fee3) { create(:enterprise_fee, calculator: Spree::Calculator::FlatRate.new(preferred_amount: 2)) } - create(:exchange, order_cycle: order_cycle, sender: coordinator, receiver: distributor, incoming: false, - enterprise_fees: [enterprise_fee1, enterprise_fee2, enterprise_fee3], variants: [product.master]) + describe "supplier fees" do + let!(:exchange1) { create(:exchange, order_cycle: order_cycle, sender: supplier1, receiver: coordinator, incoming: true, + enterprise_fees: [enterprise_fee1], variants: [product1.master]) } + let!(:exchange2) { create(:exchange, order_cycle: order_cycle, sender: supplier2, receiver: coordinator, incoming: true, + enterprise_fees: [enterprise_fee2], variants: [product2.master]) } - EnterpriseFeeCalculator.new(distributor, order_cycle).fees_for(product.master).should == 23 + it "calculates via regular computation" do + EnterpriseFeeCalculator.new(distributor, order_cycle).fees_for(product1.master).should == 20 + EnterpriseFeeCalculator.new(distributor, order_cycle).fees_for(product2.master).should == 3 + end + + it "calculates via indexed computation" do + EnterpriseFeeCalculator.new(distributor, order_cycle).indexed_fees_for(product1.master).should == 20 + EnterpriseFeeCalculator.new(distributor, order_cycle).indexed_fees_for(product2.master).should == 3 + end + end + + describe "coordinator fees" do + let!(:exchange) { create(:exchange, order_cycle: order_cycle, sender: coordinator, receiver: distributor, incoming: false, + enterprise_fees: [], variants: [product1.master]) } + + before do + order_cycle.coordinator_fees = [enterprise_fee1, enterprise_fee2, enterprise_fee3] + end + + it "sums via regular computation" do + EnterpriseFeeCalculator.new(distributor, order_cycle).fees_for(product1.master).should == 23 + end + + it "sums via indexed computation" do + EnterpriseFeeCalculator.new(distributor, order_cycle).indexed_fees_for(product1.master).should == 23 + end + end + + describe "distributor fees" do + let!(:exchange) { create(:exchange, order_cycle: order_cycle, sender: coordinator, receiver: distributor, incoming: false, + enterprise_fees: [enterprise_fee1, enterprise_fee2, enterprise_fee3], variants: [product1.master]) } + + it "sums via regular computation" do + EnterpriseFeeCalculator.new(distributor, order_cycle).fees_for(product1.master).should == 23 + end + + it "sums via indexed computation" do + EnterpriseFeeCalculator.new(distributor, order_cycle).indexed_fees_for(product1.master).should == 23 + end + end end - it "sums percentage fees for the variant" do - enterprise_fee1 = create(:enterprise_fee, amount: 20, fee_type: "admin", calculator: Spree::Calculator::FlatPercentItemTotal.new(preferred_flat_percent: 20)) + describe "summing percentage fees for the variant" do + let!(:enterprise_fee1) { create(:enterprise_fee, amount: 20, fee_type: "admin", calculator: Spree::Calculator::FlatPercentItemTotal.new(preferred_flat_percent: 20)) } + let!(:exchange) { create(:exchange, order_cycle: order_cycle, sender: coordinator, receiver: distributor, incoming: false, + enterprise_fees: [enterprise_fee1], variants: [product1.master]) } - create(:exchange, order_cycle: order_cycle, sender: coordinator, receiver: distributor, incoming: false, - enterprise_fees: [enterprise_fee1], variants: [product.master]) + it "sums via regular computation" do + EnterpriseFeeCalculator.new(distributor, order_cycle).fees_for(product1.master).should == 2.00 + end - product.master.price.should == 10.00 - EnterpriseFeeCalculator.new(distributor, order_cycle).fees_for(product.master).should == 2.00 + it "sums via indexed computation" do + EnterpriseFeeCalculator.new(distributor, order_cycle).indexed_fees_for(product1.master).should == 2.00 + end end end @@ -41,25 +89,37 @@ module OpenFoodNetwork let!(:exchange) { create(:exchange, order_cycle: order_cycle, sender: coordinator, receiver: distributor, incoming: false, enterprise_fees: [ef_admin, ef_sales, ef_packing, ef_transport, ef_fundraising], - variants: [product.master]) } + variants: [product1.master]) } - it "returns a breakdown of fees" do - EnterpriseFeeCalculator.new(distributor, order_cycle).fees_by_type_for(product.master).should == {admin: 1.23, sales: 4.56, packing: 7.89, transport: 0.12, fundraising: 3.45} + describe "regular computation" do + it "returns a breakdown of fees" do + EnterpriseFeeCalculator.new(distributor, order_cycle).fees_by_type_for(product1.master).should == {admin: 1.23, sales: 4.56, packing: 7.89, transport: 0.12, fundraising: 3.45} + end + + it "filters out zero fees" do + ef_admin.calculator.update_attribute :preferred_amount, 0 + EnterpriseFeeCalculator.new(distributor, order_cycle).fees_by_type_for(product1.master).should == {sales: 4.56, packing: 7.89, transport: 0.12, fundraising: 3.45} + end end - it "filters out zero fees" do - ef_admin.calculator.update_attribute :preferred_amount, 0 + describe "indexed computation" do + it "returns a breakdown of fees" do + EnterpriseFeeCalculator.new(distributor, order_cycle).indexed_fees_by_type_for(product1.master).should == {admin: 1.23, sales: 4.56, packing: 7.89, transport: 0.12, fundraising: 3.45} + end - EnterpriseFeeCalculator.new(distributor, order_cycle).fees_by_type_for(product.master).should == {sales: 4.56, packing: 7.89, transport: 0.12, fundraising: 3.45} + it "filters out zero fees" do + ef_admin.calculator.update_attribute :preferred_amount, 0 + EnterpriseFeeCalculator.new(distributor, order_cycle).indexed_fees_by_type_for(product1.master).should == {sales: 4.56, packing: 7.89, transport: 0.12, fundraising: 3.45} + end end end describe "creating adjustments" do let(:order) { create(:order, distributor: distributor, order_cycle: order_cycle) } - let!(:line_item) { create(:line_item, order: order, variant: product.master) } + let!(:line_item) { create(:line_item, order: order, variant: product1.master) } let(:enterprise_fee_line_item) { create(:enterprise_fee) } let(:enterprise_fee_order) { create(:enterprise_fee, calculator: Spree::Calculator::FlatRate.new(preferred_amount: 2)) } - let!(:exchange) { create(:exchange, order_cycle: order_cycle, sender: coordinator, receiver: distributor, incoming: false, variants: [product.master]) } + let!(:exchange) { create(:exchange, order_cycle: order_cycle, sender: coordinator, receiver: distributor, incoming: false, variants: [product1.master]) } before { order.reload } @@ -83,6 +143,57 @@ module OpenFoodNetwork end end + + describe "indexed fee retrieval" do + subject { EnterpriseFeeCalculator.new distributor, order_cycle } + let(:order_cycle) { create(:simple_order_cycle, coordinator_fees: [ef_coordinator]) } + let(:distributor) { create(:distributor_enterprise) } + let(:distributor_other) { create(:distributor_enterprise) } + let!(:ef_absent) { create(:enterprise_fee) } + let!(:ef_exchange) { create(:enterprise_fee) } + let!(:ef_coordinator) { create(:enterprise_fee) } + let!(:ef_other_distributor) { create(:enterprise_fee) } + let!(:exchange) { create(:exchange, sender: order_cycle.coordinator, receiver: distributor, order_cycle: order_cycle, enterprise_fees: [ef_exchange], variants: [v]) } + let(:v) { create(:variant) } + let(:indexed_variants) { {v.id => v} } + let(:indexed_enterprise_fees) { subject.instance_variable_get(:@indexed_enterprise_fees) } + + before { subject.instance_variable_set(:@indexed_enterprise_fees, {}) } + + describe "fetching enterprise fees with pre-loaded exchange details" do + it "scopes enterprise fees to those on exchanges for the current order cycle" do + subject.send(:per_item_enterprise_fees_with_exchange_details).should == [ef_exchange] + end + + it "includes the exchange variant id" do + subject.send(:per_item_enterprise_fees_with_exchange_details).first.variant_id.to_i.should == + v.id + end + + it "does not include outgoing exchanges to other distributors" do + create(:exchange, order_cycle: order_cycle, sender: order_cycle.coordinator, receiver: distributor_other, enterprise_fees: [ef_other_distributor], variants: [v]) + + subject.send(:per_item_enterprise_fees_with_exchange_details).should == [ef_exchange] + end + end + + describe "loading exchange fees" do + let(:exchange_fees) { subject.send(:per_item_enterprise_fees_with_exchange_details) } + + it "loads exchange fees" do + subject.send(:load_exchange_fees, exchange_fees) + indexed_enterprise_fees.should == {v.id => [ef_exchange]} + end + end + + describe "loading coordinator fees" do + it "loads coordinator fees" do + subject.send(:load_coordinator_fees) + indexed_enterprise_fees.should == {v.id => [ef_coordinator]} + end + end + end + describe "creating adjustments for a line item" do let(:oc) { OrderCycle.new } let(:variant) { double(:variant) } diff --git a/spec/lib/open_food_network/enterprise_injection_data_spec.rb b/spec/lib/open_food_network/enterprise_injection_data_spec.rb new file mode 100644 index 0000000000..ded13c9a6b --- /dev/null +++ b/spec/lib/open_food_network/enterprise_injection_data_spec.rb @@ -0,0 +1,22 @@ +require 'spec_helper' + +module OpenFoodNetwork + describe EnterpriseInjectionData do + describe "relatives" do + let!(:enterprise) { create(:distributor_enterprise) } + let!(:producer) { create(:supplier_enterprise) } + let!(:producer_inactive) { create(:supplier_enterprise, confirmed_at: nil) } + let!(:er_p) { create(:enterprise_relationship, parent: producer, child: enterprise) } + let!(:er_pi) { create(:enterprise_relationship, parent: producer_inactive, child: enterprise) } + + it "only loads activated relatives" do + subject.relatives[enterprise.id][:producers].should_not include producer_inactive.id + end + + it "loads self where appropiate" do + subject.relatives[producer.id][:producers].should include producer.id + subject.relatives[enterprise.id][:distributors].should include enterprise.id + end + end + end +end diff --git a/spec/lib/open_food_network/enterprise_issue_validator_spec.rb b/spec/lib/open_food_network/enterprise_issue_validator_spec.rb new file mode 100644 index 0000000000..1d0b065ee4 --- /dev/null +++ b/spec/lib/open_food_network/enterprise_issue_validator_spec.rb @@ -0,0 +1,30 @@ +require 'open_food_network/enterprise_issue_validator' + +module OpenFoodNetwork + describe EnterpriseIssueValidator do + describe "issues" do + let(:enterprise) { create(:enterprise) } + let(:eiv) { EnterpriseIssueValidator.new(enterprise) } + let(:issues) { eiv.issues } + + it "reports enterprises requiring email confirmation" do + eiv.stub(:shipping_methods_ok?) { true } + eiv.stub(:payment_methods_ok?) { true } + eiv.stub(:confirmed?) { false } + + issues.count.should == 1 + issues.first[:description].should include "Email confirmation is pending" + end + end + + describe "warnings" do + let(:enterprise_invisible) { create(:enterprise, visible: false) } + let(:warnings) { EnterpriseIssueValidator.new(enterprise_invisible).warnings } + + it "reports invisible enterprises" do + warnings.count.should == 1 + warnings.first[:description].should include "is not visible" + end + end + end +end diff --git a/spec/lib/open_food_network/last_used_address_spec.rb b/spec/lib/open_food_network/last_used_address_spec.rb new file mode 100644 index 0000000000..edb3232e8a --- /dev/null +++ b/spec/lib/open_food_network/last_used_address_spec.rb @@ -0,0 +1,58 @@ +require 'open_food_network/last_used_address' + +module OpenFoodNetwork + describe LastUsedAddress do + let(:email) { 'test@example.com' } + let(:address) { 'address' } + + describe "last used bill address" do + let(:lua) { LastUsedAddress.new(email) } + let(:order_with_bill_address) { double(:order, bill_address: address) } + let(:order_without_bill_address) { double(:order, bill_address: nil) } + + it "returns the bill address when present" do + lua.stub(:recent_orders) { [order_with_bill_address] } + lua.last_used_bill_address.should == address + end + + it "returns nil when there's no order with a bill address" do + lua.stub(:recent_orders) { [order_without_bill_address] } + lua.last_used_bill_address.should be_nil + end + + it "returns nil when there are no recent orders" do + lua.stub(:recent_orders) { [] } + lua.last_used_bill_address.should be_nil + end + end + + describe "last used ship address" do + let(:lua) { LastUsedAddress.new(email) } + let(:pickup) { double(:shipping_method, require_ship_address: false) } + let(:delivery) { double(:shipping_method, require_ship_address: true) } + let(:order_with_ship_address) { double(:order, ship_address: address, shipping_method: delivery) } + let(:order_with_unrequired_ship_address) { double(:order, ship_address: address, shipping_method: pickup) } + let(:order_without_ship_address) { double(:order, ship_address: nil) } + + it "returns the ship address when present" do + lua.stub(:recent_orders) { [order_with_ship_address] } + lua.last_used_ship_address.should == address + end + + it "returns nil when the order doesn't require a ship address" do + lua.stub(:recent_orders) { [order_with_unrequired_ship_address] } + lua.last_used_ship_address.should be_nil + end + + it "returns nil when there's no order with a ship address" do + lua.stub(:recent_orders) { [order_without_ship_address] } + lua.last_used_ship_address.should be_nil + end + + it "returns nil when there are no recent orders" do + lua.stub(:recent_orders) { [] } + lua.last_used_ship_address.should be_nil + end + end + end +end diff --git a/spec/lib/open_food_network/lettuce_share_report_spec.rb b/spec/lib/open_food_network/lettuce_share_report_spec.rb new file mode 100644 index 0000000000..f37e15b69e --- /dev/null +++ b/spec/lib/open_food_network/lettuce_share_report_spec.rb @@ -0,0 +1,71 @@ +require 'open_food_network/lettuce_share_report' + +module OpenFoodNetwork + describe LettuceShareReport do + let(:user) { create(:user) } + let(:report) { LettuceShareReport.new user } + let(:v) { create(:variant) } + + describe "grower and method" do + it "shows just the producer when there is no certification" do + report.stub(:producer_name) { "Producer" } + report.stub(:certification) { "" } + + report.send(:grower_and_method, v).should == "Producer" + end + + it "shows producer and certification when a certification is present" do + report.stub(:producer_name) { "Producer" } + report.stub(:certification) { "Method" } + + report.send(:grower_and_method, v).should == "Producer (Method)" + end + end + + describe "gst" do + it "handles tax category without rates" do + report.send(:gst, v).should == 0 + end + end + + describe "table" do + it "handles no items" do + report.send(:table).should eq [] + end + + describe "lists" do + let(:v2) { create(:variant) } + let(:v3) { create(:variant) } + let(:v4) { create(:variant, count_on_hand: 0, on_demand: true) } + let(:hub_address) { create(:address, :address1 => "distributor address", :city => 'The Shire', :zipcode => "1234") } + let(:hub) { create(:distributor_enterprise, :address => hub_address) } + let(:v2o) { create(:variant_override, hub: hub, variant: v2) } + let(:v3o) { create(:variant_override, hub: hub, variant: v3, count_on_hand: 0) } + + it "all items" do + report.stub(:child_variants) { Spree::Variant.where(id: [v, v2, v3]) } + report.send(:table).count.should eq 3 + end + + it "only available items" do + v.update_column(:count_on_hand, 0) + report.stub(:child_variants) { Spree::Variant.where(id: [v, v2, v3, v4]) } + report.send(:table).count.should eq 3 + end + + it "only available items considering overrides" do + create(:exchange, incoming: false, receiver_id: hub.id, variants: [v, v2, v3]) + # create the overrides + v2o + v3o + report.stub(:child_variants) { Spree::Variant.where(id: [v, v2, v3]) } + report.stub(:params) { {distributor_id: hub.id} } + rows = report.send(:table) + rows.count.should eq 2 + rows.map{ |row| row[0] }.should include v.product.name, v2.product.name + end + + end + end + end +end diff --git a/spec/lib/open_food_network/order_and_distributor_report_spec.rb b/spec/lib/open_food_network/order_and_distributor_report_spec.rb index e2f693d03c..e8759823db 100644 --- a/spec/lib/open_food_network/order_and_distributor_report_spec.rb +++ b/spec/lib/open_food_network/order_and_distributor_report_spec.rb @@ -40,7 +40,7 @@ module OpenFoodNetwork table[0].should == [@order.created_at, @order.id, @bill_address.full_name, @order.email, @bill_address.phone, @bill_address.city, - @line_item.product.sku, @line_item.product.name, @line_item.variant.options_text, @line_item.quantity, @line_item.max_quantity, @line_item.price * @line_item.quantity, @line_item.distribution_fee, + @line_item.product.sku, @line_item.product.name, @line_item.options_text, @line_item.quantity, @line_item.max_quantity, @line_item.price * @line_item.quantity, @line_item.distribution_fee, @payment_method.name, @distributor.name, @distributor.address.address1, @distributor.address.city, @distributor.address.zipcode, @shipping_instructions ] end diff --git a/spec/lib/open_food_network/order_cycle_form_applicator_spec.rb b/spec/lib/open_food_network/order_cycle_form_applicator_spec.rb index 5314c4376c..6cbe6693aa 100644 --- a/spec/lib/open_food_network/order_cycle_form_applicator_spec.rb +++ b/spec/lib/open_food_network/order_cycle_form_applicator_spec.rb @@ -11,7 +11,7 @@ module OpenFoodNetwork coordinator_id = 123 supplier_id = 456 - incoming_exchange = {:enterprise_id => supplier_id, :incoming => true, :variants => {'1' => true, '2' => false, '3' => true}, :enterprise_fee_ids => [1, 2]} + incoming_exchange = {:enterprise_id => supplier_id, :incoming => true, :variants => {'1' => true, '2' => false, '3' => true}, :enterprise_fee_ids => [1, 2], :receival_instructions => 'receival instructions'} oc = double(:order_cycle, :coordinator_id => coordinator_id, :exchanges => [], :incoming_exchanges => [incoming_exchange], :outgoing_exchanges => []) @@ -19,7 +19,7 @@ module OpenFoodNetwork applicator.should_receive(:incoming_exchange_variant_ids).with(incoming_exchange).and_return([1, 3]) applicator.should_receive(:exchange_exists?).with(supplier_id, coordinator_id, true).and_return(false) - applicator.should_receive(:add_exchange).with(supplier_id, coordinator_id, true, {:variant_ids => [1, 3], :enterprise_fee_ids => [1, 2]}) + applicator.should_receive(:add_exchange).with(supplier_id, coordinator_id, true, {:variant_ids => [1, 3], :enterprise_fee_ids => [1, 2], :receival_instructions => 'receival instructions'}) applicator.should_receive(:destroy_untouched_exchanges) applicator.go! @@ -47,7 +47,7 @@ module OpenFoodNetwork coordinator_id = 123 supplier_id = 456 - incoming_exchange = {:enterprise_id => supplier_id, :incoming => true, :variants => {'1' => true, '2' => false, '3' => true}, :enterprise_fee_ids => [1, 2]} + incoming_exchange = {:enterprise_id => supplier_id, :incoming => true, :variants => {'1' => true, '2' => false, '3' => true}, :enterprise_fee_ids => [1, 2], :receival_instructions => 'receival instructions'} oc = double(:order_cycle, :coordinator_id => coordinator_id, @@ -59,7 +59,7 @@ module OpenFoodNetwork applicator.should_receive(:incoming_exchange_variant_ids).with(incoming_exchange).and_return([1, 3]) applicator.should_receive(:exchange_exists?).with(supplier_id, coordinator_id, true).and_return(true) - applicator.should_receive(:update_exchange).with(supplier_id, coordinator_id, true, {:variant_ids => [1, 3], :enterprise_fee_ids => [1, 2]}) + applicator.should_receive(:update_exchange).with(supplier_id, coordinator_id, true, {:variant_ids => [1, 3], :enterprise_fee_ids => [1, 2], :receival_instructions => 'receival instructions'}) applicator.should_receive(:destroy_untouched_exchanges) applicator.go! @@ -175,6 +175,7 @@ module OpenFoodNetwork before do applicator.stub(:find_outgoing_exchange) { nil } + applicator.stub(:incoming_variant_ids) { [1, 2, 3, 4] } expect(applicator).to receive(:editable_variant_ids_for_outgoing_exchange_between). with(coordinator_mock, enterprise_mock) { [1, 2, 3] } end @@ -207,6 +208,7 @@ module OpenFoodNetwork before do applicator.stub(:find_outgoing_exchange) { exchange_mock } + applicator.stub(:incoming_variant_ids) { [1, 2, 3, 4] } expect(applicator).to receive(:editable_variant_ids_for_outgoing_exchange_between). with(coordinator_mock, enterprise_mock) { [1, 2, 3] } end @@ -231,6 +233,14 @@ module OpenFoodNetwork ids = applicator.send(:outgoing_exchange_variant_ids, {:enterprise_id => 123, :variants => {'1' => true, '2' => false, '3' => true}}) expect(ids).to eq [1, 3] end + + it "removes variants which are not included in incoming exchanges" do + applicator.stub(:incoming_variant_ids) { [1, 2] } + applicator.stub(:persisted_variants_hash) { {3 => true} } + expect(exchange_mock).to receive(:receiver) { enterprise_mock } + ids = applicator.send(:outgoing_exchange_variant_ids, {:enterprise_id => 123, :variants => {'1' => true, '2' => false, '3' => true}}) + expect(ids).to eq [1] + end end end diff --git a/spec/lib/open_food_network/order_cycle_permissions_spec.rb b/spec/lib/open_food_network/order_cycle_permissions_spec.rb index d135a3500a..275d91d13d 100644 --- a/spec/lib/open_food_network/order_cycle_permissions_spec.rb +++ b/spec/lib/open_food_network/order_cycle_permissions_spec.rb @@ -545,6 +545,16 @@ module OpenFoodNetwork expect(visible).to_not include v2 end + context "where the hub produces products" do + # NOTE: No relationship to self required + let!(:v3) { create(:variant, product: create(:simple_product, supplier: hub)) } + + it "returns any variants produced by the hub" do + visible = permissions.visible_variants_for_outgoing_exchanges_to(hub) + expect(visible).to include v3 + end + end + # TODO: for backwards compatability, remove later context "when an exchange exists between the coordinator and the hub within this order cycle" do let!(:ex) { create(:exchange, order_cycle: oc, sender: coordinator, receiver: hub, incoming: false) } @@ -712,6 +722,16 @@ module OpenFoodNetwork expect(visible).to_not include v2 end + context "where the hub produces products" do + # NOTE: No relationship to self required + let!(:v3) { create(:variant, product: create(:simple_product, supplier: hub)) } + + it "returns any variants produced by the hub" do + visible = permissions.visible_variants_for_outgoing_exchanges_to(hub) + expect(visible).to include v3 + end + end + # TODO: for backwards compatability, remove later context "when an exchange exists between the coordinator and the hub within this order cycle" do let!(:ex) { create(:exchange, order_cycle: oc, sender: coordinator, receiver: hub, incoming: false) } diff --git a/spec/lib/open_food_network/orders_and_fulfillments_report_spec.rb b/spec/lib/open_food_network/orders_and_fulfillments_report_spec.rb new file mode 100644 index 0000000000..55c8bd1197 --- /dev/null +++ b/spec/lib/open_food_network/orders_and_fulfillments_report_spec.rb @@ -0,0 +1,97 @@ +require 'spec_helper' + +include AuthenticationWorkflow + +module OpenFoodNetwork + describe OrdersAndFulfillmentsReport do + describe "fetching orders" do + let(:d1) { create(:distributor_enterprise) } + let(:oc1) { create(:simple_order_cycle) } + let(:o1) { create(:order, completed_at: 1.day.ago, order_cycle: oc1, distributor: d1) } + let(:li1) { build(:line_item) } + + before { o1.line_items << li1 } + + context "as a site admin" do + let(:user) { create(:admin_user) } + subject { PackingReport.new user } + + it "fetches completed orders" do + o2 = create(:order) + o2.line_items << build(:line_item) + subject.table_items.should == [li1] + end + + it "does not show cancelled orders" do + o2 = create(:order, state: "canceled", completed_at: 1.day.ago) + o2.line_items << build(:line_item) + subject.table_items.should == [li1] + end + end + + context "as a manager of a supplier" do + let!(:user) { create(:user) } + subject { OrdersAndFulfillmentsReport.new user } + + let(:s1) { create(:supplier_enterprise) } + + before do + s1.enterprise_roles.create!(user: user) + end + + context "that has granted P-OC to the distributor" do + let(:o2) { create(:order, distributor: d1, completed_at: 1.day.ago, bill_address: create(:address), ship_address: create(:address)) } + let(:li2) { build(:line_item, product: create(:simple_product, supplier: s1)) } + + before do + o2.line_items << li2 + create(:enterprise_relationship, parent: s1, child: d1, permissions_list: [:add_to_order_cycle]) + end + + it "shows line items supplied by my producers, with names hidden" do + subject.table_items.should == [li2] + subject.table_items.first.order.bill_address.firstname.should == "HIDDEN" + end + end + + context "that has not granted P-OC to the distributor" do + let(:o2) { create(:order, distributor: d1, completed_at: 1.day.ago, bill_address: create(:address), ship_address: create(:address)) } + let(:li2) { build(:line_item, product: create(:simple_product, supplier: s1)) } + + before do + o2.line_items << li2 + end + + it "shows line items supplied by my producers, with names hidden" do + subject.table_items.should == [] + end + end + end + + context "as a manager of a distributor" do + let!(:user) { create(:user) } + subject { OrdersAndFulfillmentsReport.new user } + + before do + d1.enterprise_roles.create!(user: user) + end + + it "only shows line items distributed by enterprises managed by the current user" do + d2 = create(:distributor_enterprise) + d2.enterprise_roles.create!(user: create(:user)) + o2 = create(:order, distributor: d2, completed_at: 1.day.ago) + o2.line_items << build(:line_item) + subject.table_items.should == [li1] + end + + it "only shows the selected order cycle" do + oc2 = create(:simple_order_cycle) + o2 = create(:order, distributor: d1, order_cycle: oc2) + o2.line_items << build(:line_item) + subject.stub(:params).and_return(order_cycle_id_in: oc1.id) + subject.table_items.should == [li1] + end + end + end + end +end diff --git a/spec/lib/open_food_network/packing_report_spec.rb b/spec/lib/open_food_network/packing_report_spec.rb new file mode 100644 index 0000000000..6b02a26fed --- /dev/null +++ b/spec/lib/open_food_network/packing_report_spec.rb @@ -0,0 +1,97 @@ +require 'spec_helper' + +include AuthenticationWorkflow + +module OpenFoodNetwork + describe PackingReport do + describe "fetching orders" do + let(:d1) { create(:distributor_enterprise) } + let(:oc1) { create(:simple_order_cycle) } + let(:o1) { create(:order, completed_at: 1.day.ago, order_cycle: oc1, distributor: d1) } + let(:li1) { build(:line_item) } + + before { o1.line_items << li1 } + + context "as a site admin" do + let(:user) { create(:admin_user) } + subject { PackingReport.new user } + + it "fetches completed orders" do + o2 = create(:order) + o2.line_items << build(:line_item) + subject.table_items.should == [li1] + end + + it "does not show cancelled orders" do + o2 = create(:order, state: "canceled", completed_at: 1.day.ago) + o2.line_items << build(:line_item) + subject.table_items.should == [li1] + end + end + + context "as a manager of a supplier" do + let!(:user) { create(:user) } + subject { PackingReport.new user } + + let(:s1) { create(:supplier_enterprise) } + + before do + s1.enterprise_roles.create!(user: user) + end + + context "that has granted P-OC to the distributor" do + let(:o2) { create(:order, distributor: d1, completed_at: 1.day.ago, bill_address: create(:address), ship_address: create(:address)) } + let(:li2) { build(:line_item, product: create(:simple_product, supplier: s1)) } + + before do + o2.line_items << li2 + create(:enterprise_relationship, parent: s1, child: d1, permissions_list: [:add_to_order_cycle]) + end + + it "shows line items supplied by my producers, with names hidden" do + subject.table_items.should == [li2] + subject.table_items.first.order.bill_address.firstname.should == "HIDDEN" + end + end + + context "that has not granted P-OC to the distributor" do + let(:o2) { create(:order, distributor: d1, completed_at: 1.day.ago, bill_address: create(:address), ship_address: create(:address)) } + let(:li2) { build(:line_item, product: create(:simple_product, supplier: s1)) } + + before do + o2.line_items << li2 + end + + it "shows line items supplied by my producers, with names hidden" do + subject.table_items.should == [] + end + end + end + + context "as a manager of a distributor" do + let!(:user) { create(:user) } + subject { PackingReport.new user } + + before do + d1.enterprise_roles.create!(user: user) + end + + it "only shows line items distributed by enterprises managed by the current user" do + d2 = create(:distributor_enterprise) + d2.enterprise_roles.create!(user: create(:user)) + o2 = create(:order, distributor: d2, completed_at: 1.day.ago) + o2.line_items << build(:line_item) + subject.table_items.should == [li1] + end + + it "only shows the selected order cycle" do + oc2 = create(:simple_order_cycle) + o2 = create(:order, distributor: d1, order_cycle: oc2) + o2.line_items << build(:line_item) + subject.stub(:params).and_return(order_cycle_id_in: oc1.id) + subject.table_items.should == [li1] + end + end + end + end +end diff --git a/spec/lib/open_food_network/permissions_spec.rb b/spec/lib/open_food_network/permissions_spec.rb index 1b6e23eeb0..a91db5f579 100644 --- a/spec/lib/open_food_network/permissions_spec.rb +++ b/spec/lib/open_food_network/permissions_spec.rb @@ -1,3 +1,4 @@ +require 'spec_helper' require 'open_food_network/permissions' module OpenFoodNetwork @@ -64,7 +65,7 @@ module OpenFoodNetwork end end - describe "finding enterprises that can be added to an order cycle" do + describe "finding visible enterprises" do let(:e) { double(:enterprise) } it "returns managed and related enterprises with add_to_order_cycle permission" do @@ -72,7 +73,7 @@ module OpenFoodNetwork with(:add_to_order_cycle). and_return([e]) - expect(permissions.order_cycle_enterprises).to eq [e] + expect(permissions.visible_enterprises).to eq [e] end end diff --git a/spec/lib/open_food_network/products_and_inventory_report_spec.rb b/spec/lib/open_food_network/products_and_inventory_report_spec.rb index 368eb4d4d7..fba19a2c7a 100644 --- a/spec/lib/open_food_network/products_and_inventory_report_spec.rb +++ b/spec/lib/open_food_network/products_and_inventory_report_spec.rb @@ -22,7 +22,8 @@ module OpenFoodNetwork "Variant Value", "Price", "Group Buy Unit Quantity", - "Amount" + "Amount", + "SKU" ] end @@ -48,16 +49,15 @@ module OpenFoodNetwork "Variant Name", 100, 21, - "" + "", + "sku" ]] end it "fetches variants for some params" do subject.should_receive(:child_variants).and_return ["children"] - subject.should_receive(:master_variants).and_return ["masters"] subject.should_receive(:filter).with(['children']).and_return ["filter_children"] - subject.should_receive(:filter).with(['masters']).and_return ["filter_masters"] - subject.variants.should == ["filter_children", "filter_masters"] + subject.variants.should == ["filter_children"] end end @@ -70,9 +70,11 @@ module OpenFoodNetwork user.save! user end + subject do ProductsAndInventoryReport.new enterprise_user end + describe "fetching child variants" do it "returns some variants" do product1 = create(:simple_product, supplier: supplier) @@ -92,14 +94,6 @@ module OpenFoodNetwork end end - describe "fetching master variants" do - it "doesn't return master variants with siblings" do - product = create(:simple_product, supplier: supplier) - - subject.master_variants.should be_empty - end - end - describe "Filtering variants" do let(:variants) { Spree::Variant.scoped.joins(:product).where(is_master: false) } it "should return unfiltered variants sans-params" do @@ -134,15 +128,16 @@ module OpenFoodNetwork it "filters to a specific distributor" do distributor = create(:distributor_enterprise) product1 = create(:simple_product, supplier: supplier) - product2 = create(:simple_product, supplier: supplier, distributors: [distributor]) + product2 = create(:simple_product, supplier: supplier) + order_cycle = create(:simple_order_cycle, suppliers: [supplier], distributors: [distributor], variants: [product2.variants.first]) subject.stub(:params).and_return(distributor_id: distributor.id) subject.filter(variants).should == [product2.variants.first] end it "filters to a specific order cycle" do distributor = create(:distributor_enterprise) - product1 = create(:simple_product, supplier: supplier, distributors: [distributor]) - product2 = create(:simple_product, supplier: supplier, distributors: [distributor]) + product1 = create(:simple_product, supplier: supplier) + product2 = create(:simple_product, supplier: supplier) order_cycle = create(:simple_order_cycle, suppliers: [supplier], distributors: [distributor], variants: [product1.variants.first]) subject.stub(:params).and_return(order_cycle_id: order_cycle.id) @@ -151,8 +146,8 @@ module OpenFoodNetwork it "should do all the filters at once" do distributor = create(:distributor_enterprise) - product1 = create(:simple_product, supplier: supplier, distributors: [distributor]) - product2 = create(:simple_product, supplier: supplier, distributors: [distributor]) + product1 = create(:simple_product, supplier: supplier) + product2 = create(:simple_product, supplier: supplier) order_cycle = create(:simple_order_cycle, suppliers: [supplier], distributors: [distributor], variants: [product1.variants.first]) subject.stub(:params).and_return( @@ -163,6 +158,28 @@ module OpenFoodNetwork subject.filter(variants) end end + + describe "fetching SKU for a variant" do + let(:variant) { create(:variant) } + let(:product) { variant.product } + + before { product.update_attribute(:sku, "Product SKU") } + + context "when the variant has an SKU set" do + before { variant.update_attribute(:sku, "Variant SKU") } + it "returns it" do + expect(subject.send(:sku_for, variant)).to eq "Variant SKU" + end + end + + context "when the variant has bo SKU set" do + before { variant.update_attribute(:sku, "") } + + it "returns the product's SKU" do + expect(subject.send(:sku_for, variant)).to eq "Product SKU" + end + end + end end end end diff --git a/spec/lib/open_food_network/referer_parser_spec.rb b/spec/lib/open_food_network/referer_parser_spec.rb new file mode 100644 index 0000000000..13cde6099e --- /dev/null +++ b/spec/lib/open_food_network/referer_parser_spec.rb @@ -0,0 +1,23 @@ +require 'open_food_network/referer_parser' +require 'spec_helper' + +module OpenFoodNetwork + describe RefererParser do + + it "handles requests without referer" do + RefererParser.path(nil).should be_nil + end + + it "handles requests with referer" do + RefererParser.path('http://example.org/').should eq('/') + end + + it "handles requests with invalid referer" do + RefererParser.path('this is not a URI').should be_nil + end + + it "handles requests with known issue of referer" do + RefererParser.path('http://example.org/##invalid-fragment').should eq('/') + end + end +end diff --git a/spec/lib/open_food_network/reports/report_spec.rb b/spec/lib/open_food_network/reports/report_spec.rb new file mode 100644 index 0000000000..0421320119 --- /dev/null +++ b/spec/lib/open_food_network/reports/report_spec.rb @@ -0,0 +1,100 @@ +require 'open_food_network/reports/report' + +module OpenFoodNetwork::Reports + P1 = Proc.new { |o| o[:one] } + P2 = Proc.new { |o| o[:two] } + P3 = Proc.new { |o| o[:three] } + P4 = Proc.new { |o| o[:four] } + + class TestReport < Report + header 'One', 'Two', 'Three', 'Four' + + columns do + column &P1 + column &P2 + column &P3 + column &P4 + end + + organise do + group &P1 + sort &P2 + + organise do + group &P3 + sort &P4 + + summary_row do + column &P1 + column &P4 + end + end + end + end + + class HelperReport < Report + columns do + column { |o| my_helper(o) } + end + + + private + + def self.my_helper(o) + o[:one] + end + end + + + describe Report do + let(:report) { TestReport.new } + let(:helper_report) { HelperReport.new } + let(:rules_head) { TestReport._rules_head } + let(:data) { {one: 1, two: 2, three: 3, four: 4} } + + it "returns the header" do + report.header.should == %w(One Two Three Four) + end + + it "returns columns as an array of procs" do + report.columns[0].call(data).should == 1 + report.columns[1].call(data).should == 2 + report.columns[2].call(data).should == 3 + report.columns[3].call(data).should == 4 + end + + it "supports helpers when outputting columns" do + helper_report.columns[0].call(data).should == 1 + end + + describe "rules" do + let(:group_by) { rules_head.to_h[:group_by] } + let(:sort_by) { rules_head.to_h[:sort_by] } + let(:next_group_by) { rules_head.next.to_h[:group_by] } + let(:next_sort_by) { rules_head.next.to_h[:sort_by] } + let(:next_summary_columns) { rules_head.next.to_h[:summary_columns] } + + it "constructs the head of the rules list" do + group_by.call(data).should == 1 + sort_by.call(data).should == 2 + end + + it "constructs nested rules" do + next_group_by.call(data).should == 3 + next_sort_by.call(data).should == 4 + end + + it "constructs summary columns for rules" do + next_summary_columns[0].call(data).should == 1 + next_summary_columns[1].call(data).should == 4 + end + end + + describe "outputting rules" do + it "outputs the rules" do + report.rules.should == [{group_by: P1, sort_by: P2}, + {group_by: P3, sort_by: P4, summary_columns: [P1, P4]}] + end + end + end +end diff --git a/spec/lib/open_food_network/reports/row_spec.rb b/spec/lib/open_food_network/reports/row_spec.rb new file mode 100644 index 0000000000..deaa84e491 --- /dev/null +++ b/spec/lib/open_food_network/reports/row_spec.rb @@ -0,0 +1,16 @@ +require 'open_food_network/reports/row' + +module OpenFoodNetwork::Reports + describe Row do + let(:row) { Row.new } + let(:proc) { Proc.new {} } + + it "can define a number of columns and return them as an array" do + row.column &proc + row.column &proc + row.column &proc + + row.to_a.should == [proc, proc, proc] + end + end +end diff --git a/spec/lib/open_food_network/reports/rule_spec.rb b/spec/lib/open_food_network/reports/rule_spec.rb new file mode 100644 index 0000000000..0e7a1d979b --- /dev/null +++ b/spec/lib/open_food_network/reports/rule_spec.rb @@ -0,0 +1,36 @@ +require 'open_food_network/reports/rule' + +module OpenFoodNetwork::Reports + describe Rule do + let(:rule) { Rule.new } + let(:proc) { Proc.new {} } + + it "can define a group proc and return it in a hash" do + rule.group &proc + rule.to_h.should == {group_by: proc, sort_by: nil} + end + + it "can define a sort proc and return it in a hash" do + rule.sort &proc + rule.to_h.should == {group_by: nil, sort_by: proc} + end + + it "can define a nested rule" do + rule.organise &proc + rule.next.should be_a Rule + end + + it "can define a summary row and return it in a hash" do + rule.summary_row do + column {} + column {} + column {} + end + + rule.to_h[:summary_columns].count.should == 3 + rule.to_h[:summary_columns][0].should be_a Proc + rule.to_h[:summary_columns][1].should be_a Proc + rule.to_h[:summary_columns][2].should be_a Proc + end + end +end diff --git a/spec/lib/open_food_network/sales_tax_report_spec.rb b/spec/lib/open_food_network/sales_tax_report_spec.rb index 00640d8f08..a6445fa1f9 100644 --- a/spec/lib/open_food_network/sales_tax_report_spec.rb +++ b/spec/lib/open_food_network/sales_tax_report_spec.rb @@ -2,7 +2,8 @@ require 'open_food_network/sales_tax_report' module OpenFoodNetwork describe SalesTaxReport do - let(:report) { SalesTaxReport.new(nil) } + let(:user) { create(:user) } + let(:report) { SalesTaxReport.new(user, {}) } describe "calculating totals for line items" do let(:li1) { double(:line_item, quantity: 1, amount: 12) } diff --git a/spec/lib/open_food_network/scope_variant_to_hub_spec.rb b/spec/lib/open_food_network/scope_variant_to_hub_spec.rb index 429bfbe082..82b24c7e02 100644 --- a/spec/lib/open_food_network/scope_variant_to_hub_spec.rb +++ b/spec/lib/open_food_network/scope_variant_to_hub_spec.rb @@ -3,18 +3,20 @@ require 'open_food_network/scope_variant_to_hub' module OpenFoodNetwork describe ScopeVariantToHub do let(:hub) { create(:distributor_enterprise) } - let(:v) { create(:variant, price: 11.11, count_on_hand: 1) } - let(:vo) { create(:variant_override, hub: hub, variant: v, price: 22.22, count_on_hand: 2) } + let(:v) { create(:variant, price: 11.11, count_on_hand: 1, on_demand: true, sku: "VARIANTSKU") } + let(:vo) { create(:variant_override, hub: hub, variant: v, price: 22.22, count_on_hand: 2, on_demand: false, sku: "VOSKU") } + let(:vo_price_only) { create(:variant_override, hub: hub, variant: v, price: 22.22, count_on_hand: nil) } + let(:scoper) { ScopeVariantToHub.new(hub) } describe "overriding price" do it "returns the overridden price when one is present" do vo - v.scope_to_hub hub + scoper.scope v v.price.should == 22.22 end it "returns the variant's price otherwise" do - v.scope_to_hub hub + scoper.scope v v.price.should == 11.11 end end @@ -22,12 +24,12 @@ module OpenFoodNetwork describe "overriding price_in" do it "returns the overridden price when one is present" do vo - v.scope_to_hub hub + scoper.scope v v.price_in('AUD').amount.should == 22.22 end it "returns the variant's price otherwise" do - v.scope_to_hub hub + scoper.scope v v.price_in('AUD').amount.should == 11.11 end end @@ -35,14 +37,104 @@ module OpenFoodNetwork describe "overriding stock levels" do it "returns the overridden stock level when one is present" do vo - v.scope_to_hub hub + scoper.scope v v.count_on_hand.should == 2 end it "returns the variant's stock level otherwise" do - v.scope_to_hub hub + scoper.scope v v.count_on_hand.should == 1 end + + describe "overriding stock on an on_demand variant" do + let(:v) { create(:variant, price: 11.11, on_demand: true) } + + it "clears on_demand when the stock is overridden" do + vo + scoper.scope v + v.on_demand.should be_false + end + + it "does not clear on_demand when only the price is overridden" do + vo_price_only + scoper.scope v + v.on_demand.should be_true + end + + it "does not clear on_demand when there is no override" do + scoper.scope v + v.on_demand.should be_true + end + end + + describe "overriding on_demand" do + context "when an override exists" do + before { vo } + + context "with an on_demand set" do + it "returns the overridden on_demand" do + scoper.scope v + expect(v.on_demand).to be_false + end + end + + context "without an on_demand set" do + before { vo.update_column(:on_demand, nil) } + + context "when count_on_hand is set" do + it "returns false" do + scoper.scope v + expect(v.on_demand).to be_false + end + end + + context "when count_on_hand is not set" do + before { vo.update_column(:count_on_hand, nil) } + + it "returns the variant's on_demand" do + scoper.scope v + expect(v.on_demand).to be_true + end + end + end + end + + context "when no override exists" do + it "returns the variant's on_demand" do + scoper.scope v + expect(v.on_demand).to be_true + end + end + end + + describe "overriding sku" do + context "when an override exists" do + before { vo } + + context "with an sku set" do + it "returns the overridden sku" do + scoper.scope v + expect(v.sku).to eq "VOSKU" + end + end + + context "without an sku set" do + before { vo.update_column(:sku, nil) } + + it "returns the variant's sku" do + scoper.scope v + expect(v.sku).to eq "VARIANTSKU" + end + end + end + + context "when no override exists" do + it "returns the variant's sku" do + scoper.scope v + expect(v.sku).to eq "VARIANTSKU" + end + end + end end end end diff --git a/spec/lib/open_food_network/xero_invoices_report_spec.rb b/spec/lib/open_food_network/xero_invoices_report_spec.rb new file mode 100644 index 0000000000..45ab77be6d --- /dev/null +++ b/spec/lib/open_food_network/xero_invoices_report_spec.rb @@ -0,0 +1,113 @@ +require 'open_food_network/xero_invoices_report' + +module OpenFoodNetwork + describe XeroInvoicesReport do + subject { XeroInvoicesReport.new user } + + let(:user) { create(:user) } + + describe "option defaults" do + let(:report) { XeroInvoicesReport.new user, {initial_invoice_number: '', invoice_date: '', due_date: '', account_code: ''} } + + around { |example| Timecop.travel(Time.zone.local(2015, 5, 5, 14, 0, 0)) { example.run } } + + it "uses defaults when blank params are passed" do + report.instance_variable_get(:@opts).should == {invoice_date: Date.civil(2015, 5, 5), + due_date: Date.civil(2015, 6, 5), + account_code: 'food sales', + report_type: 'summary'} + end + end + + describe "summary rows" do + let(:report) { XeroInvoicesReport.new user, {initial_invoice_number: '', invoice_date: '', due_date: '', account_code: ''} } + let(:order) { double(:order) } + let(:summary_rows) { report.send(:summary_rows_for_order, order, 1, {}) } + + before do + report.stub(:produce_summary_rows) { ['produce'] } + report.stub(:fee_summary_rows) { ['fee'] } + report.stub(:shipping_summary_rows) { ['shipping'] } + report.stub(:admin_adjustment_summary_rows) { ['admin'] } + order.stub(:account_invoice?) { false } + end + + it "displays produce summary rows when summary report" do + report.stub(:detail?) { false } + summary_rows.should include 'produce' + end + + it "does not display produce summary rows when detail report" do + report.stub(:detail?) { true } + summary_rows.should_not include 'produce' + end + + it "displays fee summary rows when summary report" do + report.stub(:detail?) { false } + order.stub(:account_invoice?) { true } + summary_rows.should include 'fee' + end + + it "displays fee summary rows when this is not an account invoice" do + report.stub(:detail?) { true } + order.stub(:account_invoice?) { false } + summary_rows.should include 'fee' + end + + it "does not display fee summary rows when this is a detail report for an account invoice" do + report.stub(:detail?) { true } + order.stub(:account_invoice?) { true } + summary_rows.should_not include 'fee' + end + + it "always displays shipping summary rows" do + summary_rows.should include 'shipping' + end + + it "displays admin adjustment summary rows when summary report" do + summary_rows.should include 'admin' + end + + it "does not display admin adjustment summary rows when detail report" do + report.stub(:detail?) { true } + summary_rows.should_not include 'admin' + end + end + + describe "finding account invoice adjustments" do + let(:report) { XeroInvoicesReport.new user, {initial_invoice_number: '', invoice_date: '', due_date: '', account_code: ''} } + let!(:order) { create(:order) } + let(:billable_period) { create(:billable_period) } + let(:shipping_method) { create(:shipping_method) } + let!(:adj_invoice) { create(:adjustment, adjustable: order, label: 'Account invoice item', source: billable_period) } + let!(:adj_shipping) { create(:adjustment, adjustable: order, label: "Shipping", originator: shipping_method) } + + it "returns BillablePeriod adjustments only" do + report.send(:account_invoice_adjustments, order).should == [adj_invoice] + end + + it "excludes adjustments where the source is missing" do + billable_period.destroy + report.send(:account_invoice_adjustments, order).should be_empty + end + end + + describe "generating invoice numbers" do + let(:order) { double(:order, number: 'R731032860') } + + describe "when no initial invoice number is given" do + it "returns the order number" do + subject.send(:invoice_number_for, order, 123).should == 'R731032860' + end + end + + describe "when an initial invoice number is given" do + subject { XeroInvoicesReport.new user, {initial_invoice_number: '123'} } + + it "increments the number by the index" do + subject.send(:invoice_number_for, order, 456).should == 579 + end + end + end + end +end diff --git a/spec/mailers/enterprise_mailer_spec.rb b/spec/mailers/enterprise_mailer_spec.rb index 65480ccdad..eaa21b54ae 100644 --- a/spec/mailers/enterprise_mailer_spec.rb +++ b/spec/mailers/enterprise_mailer_spec.rb @@ -14,6 +14,7 @@ describe EnterpriseMailer do mail = ActionMailer::Base.deliveries.first expect(mail.subject).to eq "Please confirm your email for #{enterprise.name}" expect(mail.to).to include enterprise.email + expect(mail.reply_to).to be_nil end end @@ -38,4 +39,4 @@ describe EnterpriseMailer do mail = ActionMailer::Base.deliveries.first expect(mail.subject).to eq "#{enterprise.name} is now on #{Spree::Config[:site_name]}" end -end \ No newline at end of file +end diff --git a/spec/mailers/producer_mailer_spec.rb b/spec/mailers/producer_mailer_spec.rb new file mode 100644 index 0000000000..0c7e82b89d --- /dev/null +++ b/spec/mailers/producer_mailer_spec.rb @@ -0,0 +1,77 @@ +require 'spec_helper' +require 'yaml' + +describe ProducerMailer do + let(:s1) { create(:supplier_enterprise) } + let(:s2) { create(:supplier_enterprise) } + let(:s3) { create(:supplier_enterprise) } + let(:d1) { create(:distributor_enterprise) } + let(:d2) { create(:distributor_enterprise) } + let(:p1) { create(:product, price: 12.34, supplier: s1) } + let(:p2) { create(:product, price: 23.45, supplier: s2) } + let(:p3) { create(:product, price: 34.56, supplier: s1) } + let(:order_cycle) { create(:simple_order_cycle) } + let!(:incoming_exchange) { order_cycle.exchanges.create! sender: s1, receiver: d1, incoming: true, receival_instructions: 'Outside shed.' } + + let!(:order) do + order = create(:order, distributor: d1, order_cycle: order_cycle, state: 'complete') + order.line_items << create(:line_item, variant: p1.master) + order.line_items << create(:line_item, variant: p1.master) + order.line_items << create(:line_item, variant: p2.master) + order.finalize! + order.save + order + end + let!(:order_incomplete) do + order = create(:order, distributor: d1, order_cycle: order_cycle, state: 'payment') + order.line_items << create(:line_item, variant: p3.master) + order.save + order + end + let(:mail) { ActionMailer::Base.deliveries.last } + + before do + ActionMailer::Base.deliveries.clear + ProducerMailer.order_cycle_report(s1, order_cycle).deliver + end + + it "should send an email when an order cycle is closed" do + ActionMailer::Base.deliveries.count.should == 1 + end + + it "sets a reply-to of the enterprise email" do + mail.reply_to.should == [s1.email] + end + + it "includes receival instructions" do + mail.body.should include 'Outside shed.' + end + + it "cc's the enterprise" do + mail.cc.should == [s1.email] + end + + it "contains an aggregated list of produce" do + body_lines_including(mail, p1.name).each do |line| + line.should include 'QTY: 2' + line.should include '@ $10.00 = $20.00' + end + end + + it "does not include incomplete orders" do + mail.body.should_not include p3.name + end + + it "sends no mail when the producer has no orders" do + expect do + ProducerMailer.order_cycle_report(s3, order_cycle).deliver + end.to change(ActionMailer::Base.deliveries, :count).by(0) + end + + + private + + def body_lines_including(mail, s) + mail.body.to_s.lines.select { |line| line.include? s } + end +end diff --git a/spec/models/account_invoice_spec.rb b/spec/models/account_invoice_spec.rb new file mode 100644 index 0000000000..0473d1d455 --- /dev/null +++ b/spec/models/account_invoice_spec.rb @@ -0,0 +1,5 @@ +require 'spec_helper' + +describe AccountInvoice do + pending "add some examples to (or delete) #{__FILE__}" +end diff --git a/spec/models/billable_period_spec.rb b/spec/models/billable_period_spec.rb new file mode 100644 index 0000000000..3037ebc6bc --- /dev/null +++ b/spec/models/billable_period_spec.rb @@ -0,0 +1,214 @@ +require 'spec_helper' + +describe BillablePeriod, type: :model do + + require 'spec_helper' + + describe 'ensure_correct_adjustment' do + let!(:start_of_july) { Time.zone.now.beginning_of_year + 6.months } + let!(:user) { create(:user) } + let!(:invoice) { create(:order, user: user) } + let!(:subject) { create(:billable_period, owner: user, begins_at: start_of_july, ends_at: start_of_july + 12.days) } + + before do + allow(subject).to receive(:bill) { 99 } + allow(subject).to receive(:adjustment_label) { "Label for adjustment" } + Spree::Config.set({ account_invoices_tax_rate: 0.1 }) + end + + context "when no adjustment currently exists" do + it "creates an adjustment on the given order" do + expect(invoice.total_tax).to eq 0.0 + expect(subject.adjustment).to be nil + subject.ensure_correct_adjustment_for(invoice) + expect(subject.adjustment).to be_a Spree::Adjustment + expect(invoice.total_tax).to eq 9.0 + end + end + end + + + describe "calculating monthly bills for enterprises" do + let!(:subject) { create(:billable_period, turnover: 100) } + + context "when no tax is charged" do + before { Spree::Config.set(:account_invoices_tax_rate, 0) } + + context "when a fixed cost is included" do + before { Spree::Config.set(:account_invoices_monthly_fixed, 10) } + + context "when a percentage of turnover is included" do + before { Spree::Config.set(:account_invoices_monthly_rate, 0.5) } + + context "when the bill is capped" do + context "at a level higher than the fixed charge plus the product of the rate and turnover" do + before { Spree::Config.set(:account_invoices_monthly_cap, 65) } + it { expect(subject.bill).to eq 60 } + end + + context "at a level lower than the fixed charge plus the product of the rate and turnover" do + before { Spree::Config.set(:account_invoices_monthly_cap, 55) } + it { expect(subject.bill).to eq 55 } + end + end + + context "when the bill is not capped" do + before { Spree::Config.set(:account_invoices_monthly_cap, 0) } + it { expect(subject.bill).to eq 60 } + end + end + + context "when a percentage of turnover is not included" do + before { Spree::Config.set(:account_invoices_monthly_rate, 0) } + + context "when the bill is capped" do + context "at a level higher than the fixed charge" do + before { Spree::Config.set(:account_invoices_monthly_cap, 15) } + it { expect(subject.bill).to eq 10 } + end + + context "at a level lower than the fixed charge" do + before { Spree::Config.set(:account_invoices_monthly_cap, 5) } + it { expect(subject.bill).to eq 5 } + end + end + + context "when the bill is not capped" do + before { Spree::Config.set(:account_invoices_monthly_cap, 0) } + it { expect(subject.bill).to eq 10 } + end + end + end + + context "when a fixed cost is not included" do + before { Spree::Config.set(:account_invoices_monthly_fixed, 0) } + + context "when a percentage of turnover is included" do + before { Spree::Config.set(:account_invoices_monthly_rate, 0.5) } + + context "when the bill is capped" do + context "at a level higher than the product of the rate and turnover" do + before { Spree::Config.set(:account_invoices_monthly_cap, 55) } + it { expect(subject.bill).to eq 50 } + end + + context "at a level lower than the product of the rate and turnover" do + before { Spree::Config.set(:account_invoices_monthly_cap, 45) } + it { expect(subject.bill).to eq 45 } + end + end + + context "when the bill is not capped" do + before { Spree::Config.set(:account_invoices_monthly_cap, 0) } + it { expect(subject.bill).to eq 50 } + end + end + + context "when a percentage of turnover is not included" do + before { Spree::Config.set(:account_invoices_monthly_rate, 0) } + + context "when the bill is capped" do + before { Spree::Config.set(:account_invoices_monthly_cap, 20) } + it { expect(subject.bill).to eq 0 } + end + + context "when the bill is not capped" do + before { Spree::Config.set(:account_invoices_monthly_cap, 0) } + it { expect(subject.bill).to eq 0 } + end + end + end + end + + context "when tax is charged" do + before { Spree::Config.set(:account_invoices_tax_rate, 0.1) } + + context "when a fixed cost is included" do + before { Spree::Config.set(:account_invoices_monthly_fixed, 10) } + + context "when a percentage of turnover is included" do + before { Spree::Config.set(:account_invoices_monthly_rate, 0.5) } + + context "when the bill is capped" do + context "at a level higher than the fixed charge plus the product of the rate and turnover" do + before { Spree::Config.set(:account_invoices_monthly_cap, 61) } + it { expect(subject.bill).to eq 66 } + end + + context "at a level lower than the fixed charge plus the product of the rate and turnover" do + before { Spree::Config.set(:account_invoices_monthly_cap, 59) } + it { + expect(subject.bill.to_f).to eq 64.9 + } + end + end + + context "when the bill is not capped" do + before { Spree::Config.set(:account_invoices_monthly_cap, 0) } + it { expect(subject.bill).to eq 66 } + end + end + + context "when a percentage of turnover is not included" do + before { Spree::Config.set(:account_invoices_monthly_rate, 0) } + + context "when the bill is capped" do + context "at a level higher than the fixed charge" do + before { Spree::Config.set(:account_invoices_monthly_cap, 11) } + it { expect(subject.bill).to eq 11 } + end + + context "at a level lower than the fixed charge" do + before { Spree::Config.set(:account_invoices_monthly_cap, 9) } + it { expect(subject.bill.to_f).to eq 9.9 } + end + end + + context "when the bill is not capped" do + before { Spree::Config.set(:account_invoices_monthly_cap, 0) } + it { expect(subject.bill).to eq 11 } + end + end + end + + context "when a fixed cost is not included" do + before { Spree::Config.set(:account_invoices_monthly_fixed, 0) } + + context "when a percentage of turnover is included" do + before { Spree::Config.set(:account_invoices_monthly_rate, 0.5) } + + context "when the bill is capped" do + context "at a level higher than the product of the rate and turnover" do + before { Spree::Config.set(:account_invoices_monthly_cap, 51) } + it { expect(subject.bill).to eq 55 } + end + + context "at a level lower than the product of the rate and turnover" do + before { Spree::Config.set(:account_invoices_monthly_cap, 49) } + it { expect(subject.bill.to_f).to eq 53.9 } + end + end + + context "when the bill is not capped" do + before { Spree::Config.set(:account_invoices_monthly_cap, 0) } + it { expect(subject.bill).to eq 55 } + end + end + + context "when a percentage of turnover is not included" do + before { Spree::Config.set(:account_invoices_monthly_rate, 0) } + + context "when the bill is capped" do + before { Spree::Config.set(:account_invoices_monthly_cap, 20) } + it { expect(subject.bill).to eq 0 } + end + + context "when the bill is not capped" do + before { Spree::Config.set(:account_invoices_monthly_cap, 0) } + it { expect(subject.bill).to eq 0 } + end + end + end + end + end +end diff --git a/spec/models/customer_spec.rb b/spec/models/customer_spec.rb new file mode 100644 index 0000000000..0e43ce9df5 --- /dev/null +++ b/spec/models/customer_spec.rb @@ -0,0 +1,20 @@ +require 'spec_helper' + +describe Customer, type: :model do + describe 'creation callbacks' do + let!(:user1) { create(:user) } + let!(:user2) { create(:user) } + let!(:enterprise) { create(:distributor_enterprise) } + + it "associates an existing user using email" do + c1 = Customer.create(enterprise: enterprise, email: 'some-email-not-associated-with-a-user@email.com') + expect(c1.user).to be_nil + + c2 = Customer.create(enterprise: enterprise, email: 'some-email-not-associated-with-a-user@email.com', user: user1) + expect(c2.user).to eq user1 + + c3 = Customer.create(enterprise: enterprise, email: user2.email) + expect(c3.user).to eq user2 + end + end +end diff --git a/spec/models/enterprise_group_spec.rb b/spec/models/enterprise_group_spec.rb index 45e7b0692c..63b3ef58ae 100644 --- a/spec/models/enterprise_group_spec.rb +++ b/spec/models/enterprise_group_spec.rb @@ -51,7 +51,7 @@ describe EnterpriseGroup do # it "can have an image" do # eg = create(:enterprise_group) - # image_file = File.open(File.expand_path('../../../app/assets/images/logo.jpg', __FILE__)) + # image_file = File.open(File.expand_path('../../../app/assets/images/logo-white.png', __FILE__)) # image = Spree::Image.create(viewable_id: eg.id, viewable_type: 'EnterpriseGroup', attachment: image_file) # eg.reload.image.should == image # end diff --git a/spec/models/enterprise_relationship_spec.rb b/spec/models/enterprise_relationship_spec.rb index cbbdd00504..fcdfa8b250 100644 --- a/spec/models/enterprise_relationship_spec.rb +++ b/spec/models/enterprise_relationship_spec.rb @@ -69,4 +69,38 @@ describe EnterpriseRelationship do EnterpriseRelationship.with_permission('two').should match_array [er1, er2] end end + + describe "finding relatives" do + let(:e1) { create(:supplier_enterprise) } + let(:e2) { create(:distributor_enterprise) } + let!(:er) { create(:enterprise_relationship, parent: e1, child: e2) } + let(:er_reverse) { create(:enterprise_relationship, parent: e2, child: e1) } + + it "includes self where appropriate" do + EnterpriseRelationship.relatives[e2.id][:distributors].should include e2.id + EnterpriseRelationship.relatives[e2.id][:producers].should_not include e2.id + end + + it "categorises enterprises into distributors and producers" do + e2.update_attribute :is_primary_producer, true + EnterpriseRelationship.relatives.should == + {e1.id => {distributors: Set.new([e2.id]), producers: Set.new([e1.id, e2.id])}, + e2.id => {distributors: Set.new([e2.id]), producers: Set.new([e2.id, e1.id])}} + end + + it "finds inactive enterprises by default" do + e1.update_attribute :confirmed_at, nil + EnterpriseRelationship.relatives[e2.id][:producers].should == Set.new([e1.id]) + end + + it "does not find inactive enterprises when requested" do + e1.update_attribute :confirmed_at, nil + EnterpriseRelationship.relatives(true)[e2.id][:producers].should be_empty + end + + it "does not show duplicates" do + er_reverse + EnterpriseRelationship.relatives[e2.id][:producers].should == Set.new([e1.id]) + end + end end diff --git a/spec/models/enterprise_spec.rb b/spec/models/enterprise_spec.rb index df04324af2..08c10dd31e 100644 --- a/spec/models/enterprise_spec.rb +++ b/spec/models/enterprise_spec.rb @@ -109,6 +109,17 @@ describe Enterprise do Spree::Product.where(id: p.id).should be_empty end + it "destroys relationships upon destroy" do + e = create(:enterprise) + e_other = create(:enterprise) + er1 = create(:enterprise_relationship, parent: e, child: e_other) + er2 = create(:enterprise_relationship, child: e, parent: e_other) + + e.destroy + + EnterpriseRelationship.where(id: [er1, er2]).should be_empty + end + describe "relationships to other enterprises" do let(:e) { create(:distributor_enterprise) } let(:p) { create(:supplier_enterprise) } @@ -121,14 +132,18 @@ describe Enterprise do e.relatives.should match_array [p, c] end + it "finds relatives_including_self" do + expect(e.relatives_including_self).to include e + end + it "scopes relatives to visible distributors" do - e.should_receive(:relatives).and_return(relatives = []) + e.should_receive(:relatives_including_self).and_return(relatives = []) relatives.should_receive(:is_distributor).and_return relatives e.distributors end it "scopes relatives to visible producers" do - e.should_receive(:relatives).and_return(relatives = []) + e.should_receive(:relatives_including_self).and_return(relatives = []) relatives.should_receive(:is_primary_producer).and_return relatives e.suppliers end @@ -175,6 +190,31 @@ describe Enterprise do }.to raise_error ActiveRecord::RecordInvalid, "Validation failed: Owner can't be blank" end + describe "name uniqueness" do + let(:owner) { create(:user, email: 'owner@example.com') } + let!(:enterprise) { create(:enterprise, name: 'Enterprise', owner: owner) } + + it "prevents duplicate names for new records" do + e = Enterprise.new name: enterprise.name + e.should_not be_valid + e.errors[:name].first.should == + "has already been taken. If this is your enterprise and you would like to claim ownership, please contact the current manager of this profile at owner@example.com." + end + + it "prevents duplicate names for existing records" do + e = create(:enterprise, name: 'foo') + e.name = enterprise.name + e.should_not be_valid + e.errors[:name].first.should == + "has already been taken. If this is your enterprise and you would like to claim ownership, please contact the current manager of this profile at owner@example.com." + end + + it "does not prohibit the saving of an enterprise with no name clash" do + enterprise.email = 'new@email.com' + enterprise.should be_valid + end + end + describe "preferred_shopfront_taxon_order" do it "empty strings are valid" do enterprise = build(:enterprise, preferred_shopfront_taxon_order: "") @@ -262,9 +302,9 @@ describe Enterprise do end describe "activated" do - let!(:inactive_enterprise1) { create(:enterprise, sells: "unspecified", confirmed_at: Time.now) ;} + let!(:inactive_enterprise1) { create(:enterprise, sells: "unspecified", confirmed_at: Time.zone.now) ;} let!(:inactive_enterprise2) { create(:enterprise, sells: "none", confirmed_at: nil) } - let!(:active_enterprise) { create(:enterprise, sells: "none", confirmed_at: Time.now) } + let!(:active_enterprise) { create(:enterprise, sells: "none", confirmed_at: Time.zone.now) } it "finds enterprises that have a sells property other than 'unspecified' and that are confirmed" do activated_enterprises = Enterprise.activated @@ -379,7 +419,7 @@ describe Enterprise do it "doesn't show distributors of deleted products" do d = create(:distributor_enterprise) - create(:product, :distributors => [d], :deleted_at => Time.now) + create(:product, :distributors => [d], :deleted_at => Time.zone.now) Enterprise.active_distributors.should be_empty end @@ -700,41 +740,6 @@ describe Enterprise do end end - describe "geo search" do - before(:each) do - Enterprise.delete_all - - state_id_vic = Spree::State.where(abbr: "Vic").first.id - state_id_nsw = Spree::State.where(abbr: "NSW").first.id - - @suburb_in_vic = Suburb.create(name: "Camberwell", postcode: 3124, latitude: -37.824818, longitude: 145.057957, state_id: state_id_vic) - @suburb_in_nsw = Suburb.create(name: "Cabramatta", postcode: 2166, latitude: -33.89507, longitude: 150.935889, state_id: state_id_nsw) - - address_vic1 = FactoryGirl.create(:address, state_id: state_id_vic, city: "Hawthorn", zipcode: "3123") - address_vic1.update_column(:latitude, -37.842105) - address_vic1.update_column(:longitude, 145.045951) - - address_vic2 = FactoryGirl.create(:address, state_id: state_id_vic, city: "Richmond", zipcode: "3121") - address_vic2.update_column(:latitude, -37.826869) - address_vic2.update_column(:longitude, 145.007098) - - FactoryGirl.create(:distributor_enterprise, address: address_vic1) - FactoryGirl.create(:distributor_enterprise, address: address_vic2) - end - - it "should find nearby hubs if there are any" do - Enterprise.find_near(@suburb_in_vic).count.should eql(2) - end - - it "should not have nils in the result" do - Enterprise.find_near(@suburb_in_vic).should_not include(nil) - end - - it "should not find hubs if not nearby " do - Enterprise.find_near(@suburb_in_nsw).count.should eql(0) - end - end - describe "taxons" do let(:distributor) { create(:distributor_enterprise) } let(:supplier) { create(:supplier_enterprise) } diff --git a/spec/models/exchange_spec.rb b/spec/models/exchange_spec.rb index 0df7aeafab..ac6b7681a8 100644 --- a/spec/models/exchange_spec.rb +++ b/spec/models/exchange_spec.rb @@ -246,6 +246,20 @@ describe Exchange do Exchange.with_product(p).should == [ex] end + + describe "sorting exchanges by primary enterprise name" do + let(:e1) { create(:supplier_enterprise, name: 'ZZZ') } + let(:e2) { create(:distributor_enterprise, name: 'AAA') } + let(:e3) { create(:supplier_enterprise, name: 'CCC') } + + let!(:ex1) { create(:exchange, sender: e1, incoming: true) } + let!(:ex2) { create(:exchange, receiver: e2, incoming: false) } + let!(:ex3) { create(:exchange, sender: e3, incoming: true) } + + it "sorts" do + Exchange.by_enterprise_name.should == [ex2, ex3, ex1] + end + end end it "clones itself" do @@ -277,6 +291,7 @@ describe Exchange do 'payment_enterprise_id' => exchange.payment_enterprise_id, 'variant_ids' => exchange.variant_ids.sort, 'enterprise_fee_ids' => exchange.enterprise_fee_ids.sort, 'pickup_time' => exchange.pickup_time, 'pickup_instructions' => exchange.pickup_instructions, + 'receival_instructions' => exchange.receival_instructions, 'created_at' => exchange.created_at, 'updated_at' => exchange.updated_at} end @@ -286,7 +301,8 @@ describe Exchange do 'incoming' => exchange.incoming, 'payment_enterprise_id' => exchange.payment_enterprise_id, 'variant_ids' => exchange.variant_ids.sort, 'enterprise_fee_ids' => exchange.enterprise_fee_ids.sort, - 'pickup_time' => exchange.pickup_time, 'pickup_instructions' => exchange.pickup_instructions} + 'pickup_time' => exchange.pickup_time, 'pickup_instructions' => exchange.pickup_instructions, + 'receival_instructions' => exchange.receival_instructions} end end diff --git a/spec/models/model_set_spec.rb b/spec/models/model_set_spec.rb index e45018747c..0533285cec 100644 --- a/spec/models/model_set_spec.rb +++ b/spec/models/model_set_spec.rb @@ -6,11 +6,11 @@ describe ModelSet do attrs = {collection_attributes: {'1' => {name: 's1'}, '2' => {name: 's2'}}} - ms = ModelSet.new(Suburb, Suburb.all, attrs) + ms = ModelSet.new(EnterpriseRelationshipPermission, EnterpriseRelationshipPermission.all, attrs) - expect { ms.save }.to change(Suburb, :count).by(2) + expect { ms.save }.to change(EnterpriseRelationshipPermission, :count).by(2) - Suburb.where(name: ['s1', 's2']).count.should == 2 + EnterpriseRelationshipPermission.where(name: ['s1', 's2']).count.should == 2 end diff --git a/spec/models/order_cycle_spec.rb b/spec/models/order_cycle_spec.rb index 21b3d061e5..90465fb80e 100644 --- a/spec/models/order_cycle_spec.rb +++ b/spec/models/order_cycle_spec.rb @@ -35,12 +35,14 @@ describe OrderCycle do oc_not_yet_open = create(:simple_order_cycle, orders_open_at: 1.week.from_now, orders_close_at: 2.weeks.from_now) oc_already_closed = create(:simple_order_cycle, orders_open_at: 2.weeks.ago, orders_close_at: 1.week.ago) oc_undated = create(:simple_order_cycle, orders_open_at: nil, orders_close_at: nil) + oc_undated_open = create(:simple_order_cycle, orders_open_at: 1.week.ago, orders_close_at: nil) + oc_undated_close = create(:simple_order_cycle, orders_open_at: nil, orders_close_at: 1.week.from_now) OrderCycle.active.should == [oc_active] OrderCycle.inactive.should match_array [oc_not_yet_open, oc_already_closed] OrderCycle.upcoming.should == [oc_not_yet_open] OrderCycle.closed.should == [oc_already_closed] - OrderCycle.undated.should == [oc_undated] + OrderCycle.undated.should == [oc_undated, oc_undated_open, oc_undated_close] end it "finds order cycles accessible by a user" do @@ -90,17 +92,6 @@ describe OrderCycle do end end - describe "#recently_closed" do - it "finds the orders closed in the last 30 days sorted in descending order" do - create(:simple_order_cycle, orders_close_at: 3.days.from_now) - oc1 = create(:simple_order_cycle, orders_close_at: 1.day.ago) - oc2 = create(:simple_order_cycle, orders_close_at: 30.days.ago) - create(:simple_order_cycle, orders_close_at: 31.days.ago) - - OrderCycle.recently_closed.should == [oc1 , oc2] - end - end - it "finds the most recently closed order cycles" do oc1 = create(:simple_order_cycle, orders_close_at: 2.hours.ago) oc2 = create(:simple_order_cycle, orders_close_at: 1.hour.ago) @@ -202,7 +193,7 @@ describe OrderCycle do @p0 = create(:simple_product) @p1 = create(:simple_product) - @p1_v_deleted = create(:variant, product: @p1, deleted_at: Time.now) + @p1_v_deleted = create(:variant, product: @p1, deleted_at: Time.zone.now) @p2 = create(:simple_product) @p2_v = create(:variant, product: @p2) @@ -218,6 +209,14 @@ describe OrderCycle do @oc.variants.should match_array [@p0.master, @p1.master, @p2.master, @p2_v] end + it "returns the correct count of variants" do + @oc.variants.count.should == 4 + end + + it "reports on the variants supplied" do + @oc.supplied_variants.should match_array [@p0.master] + end + it "reports on the variants distributed" do @oc.distributed_variants.should match_array [@p1.master, @p2.master, @p2_v] end @@ -359,6 +358,24 @@ describe OrderCycle do oc.should_not be_open oc.should_not be_closed end + + it "reports status when an order cycle is partially dated - opening time only" do + oc.update_attributes!(orders_close_at: nil) + + oc.should be_undated + oc.should_not be_upcoming + oc.should_not be_open + oc.should_not be_closed + end + + it "reports status when an order cycle is partially dated - closing time only" do + oc.update_attributes!(orders_open_at: nil) + + oc.should be_undated + oc.should_not be_upcoming + oc.should_not be_open + oc.should_not be_closed + end end it "clones itself" do @@ -422,4 +439,23 @@ describe OrderCycle do OrderCycle.first_closing_for(distributor).should == oc end end + + describe "finding the earliest closing times for each distributor" do + let(:time1) { 1.week.from_now } + let(:time2) { 2.weeks.from_now } + let(:time3) { 3.weeks.from_now } + let(:e1) { create(:distributor_enterprise) } + let(:e2) { create(:distributor_enterprise) } + let!(:oc1) { create(:simple_order_cycle, orders_close_at: time1, distributors: [e1]) } + let!(:oc2) { create(:simple_order_cycle, orders_close_at: time2, distributors: [e2]) } + let!(:oc3) { create(:simple_order_cycle, orders_close_at: time3, distributors: [e2]) } + + it "returns the closing time, indexed by enterprise id" do + OrderCycle.earliest_closing_times[e1.id].should == time1 + end + + it "returns the earliest closing time" do + OrderCycle.earliest_closing_times[e2.id].should == time2 + end + end end diff --git a/spec/models/spree/ability_spec.rb b/spec/models/spree/ability_spec.rb index 9ed432e4af..4af3d233b1 100644 --- a/spec/models/spree/ability_spec.rb +++ b/spec/models/spree/ability_spec.rb @@ -217,7 +217,11 @@ module Spree end it "should not be able to read other reports" do - should_not have_ability([:sales_total, :group_buys, :payments, :orders_and_distributors, :users_and_enterprises], for: :report) + should_not have_ability([:sales_total, :group_buys, :payments, :orders_and_distributors, :users_and_enterprises, :xero_invoices], for: :report) + end + + it "should not be able to access customer actions" do + should_not have_ability([:admin, :index, :update], for: Customer) end describe "order_cycles abilities" do @@ -294,11 +298,11 @@ module Spree let!(:er_pd) { create(:enterprise_relationship, parent: d_related, child: d1, permissions_list: [:edit_profile]) } it "should be able to edit enterprises it manages" do - should have_ability([:read, :edit, :update, :bulk_update, :set_sells, :resend_confirmation], for: d1) + should have_ability([:read, :edit, :update, :bulk_update, :resend_confirmation], for: d1) end it "should be able to edit enterprises it has permission to" do - should have_ability([:read, :edit, :update, :bulk_update, :set_sells, :resend_confirmation], for: d_related) + should have_ability([:read, :edit, :update, :bulk_update, :resend_confirmation], for: d_related) end it "should be able to manage shipping methods, payment methods and enterprise fees for enterprises it manages" do @@ -319,7 +323,7 @@ module Spree let!(:er1) { create(:enterprise_relationship, parent: s1, child: d1, permissions_list: [:create_variant_overrides]) } it "should be able to access variant overrides page" do - should have_ability([:admin, :index, :bulk_update], for: VariantOverride) + should have_ability([:admin, :index, :bulk_update, :bulk_reset], for: VariantOverride) end it "should be able to read/write their own variant overrides" do @@ -400,13 +404,17 @@ module Spree end it "should be able to read some reports" do - should have_ability([:admin, :index, :customers, :sales_tax, :group_buys, :bulk_coop, :payments, :orders_and_distributors, :orders_and_fulfillment, :products_and_inventory, :order_cycle_management], for: :report) + should have_ability([:admin, :index, :customers, :sales_tax, :group_buys, :bulk_coop, :payments, :orders_and_distributors, :orders_and_fulfillment, :products_and_inventory, :order_cycle_management, :xero_invoices], for: :report) end it "should not be able to read other reports" do should_not have_ability([:sales_total, :users_and_enterprises], for: :report) end + it "should be able to access customer actions" do + should have_ability([:admin, :index, :update], for: Customer) + end + context "for a given order_cycle" do let!(:order_cycle) { create(:simple_order_cycle) } let!(:exchange){ create(:exchange, incoming: false, order_cycle: order_cycle, receiver: d1, sender: order_cycle.coordinator) } @@ -469,12 +477,20 @@ module Spree user end + it 'should have the ability to view the admin account page' do + should have_ability([:admin, :show], for: :account) + end + it 'should have the ability to read and edit enterprises that I manage' do - should have_ability([:read, :edit, :update, :bulk_update, :set_sells], for: s1) + should have_ability([:read, :edit, :update, :bulk_update], for: s1) end it 'should not have the ability to read and edit enterprises that I do not manage' do - should_not have_ability([:read, :edit, :update, :bulk_update, :set_sells], for: s2) + should_not have_ability([:read, :edit, :update, :bulk_update], for: s2) + end + + it 'should not have the ability to welcome and register enterprises that I do not own' do + should_not have_ability([:welcome, :register], for: s1) end it 'should have the ability administrate and create enterpises' do @@ -486,6 +502,18 @@ module Spree should_not have_ability([:users], for: :search) end end + + context 'enterprise owner' do + let (:user) { s1.owner } + + it 'should have the ability to welcome and register enterprises that I own' do + should have_ability([:welcome, :register], for: s1) + end + + it 'should have the ability to view the admin account page' do + should have_ability([:admin, :show], for: :account) + end + end end end end diff --git a/spec/models/spree/adjustment_spec.rb b/spec/models/spree/adjustment_spec.rb index 579965aa7a..512e720f54 100644 --- a/spec/models/spree/adjustment_spec.rb +++ b/spec/models/spree/adjustment_spec.rb @@ -5,6 +5,33 @@ module Spree adjustment.metadata.should be end + describe "querying included tax" do + let!(:adjustment_with_tax) { create(:adjustment, included_tax: 123) } + let!(:adjustment_without_tax) { create(:adjustment, included_tax: 0) } + + describe "finding adjustments with and without tax included" do + it "finds adjustments with tax" do + Adjustment.with_tax.should include adjustment_with_tax + Adjustment.with_tax.should_not include adjustment_without_tax + end + + it "finds adjustments without tax" do + Adjustment.without_tax.should include adjustment_without_tax + Adjustment.without_tax.should_not include adjustment_with_tax + end + end + + describe "checking if an adjustment includes tax" do + it "returns true when it has > 0 tax" do + adjustment_with_tax.should have_tax + end + + it "returns false when it has 0 tax" do + adjustment_without_tax.should_not have_tax + end + end + end + describe "recording included tax" do describe "TaxRate adjustments" do let!(:zone) { create(:zone_with_member) } diff --git a/spec/models/spree/line_item_spec.rb b/spec/models/spree/line_item_spec.rb index 4058ba30e1..6bb18fafa3 100644 --- a/spec/models/spree/line_item_spec.rb +++ b/spec/models/spree/line_item_spec.rb @@ -24,6 +24,22 @@ module Spree LineItem.supplied_by_any([s2]).should == [li2] LineItem.supplied_by_any([s1, s2]).should match_array [li1, li2] end + + describe "finding line items with and without tax" do + let(:tax_rate) { create(:tax_rate, calculator: Spree::Calculator::DefaultTax.new) } + let!(:adjustment1) { create(:adjustment, adjustable: li1, originator: tax_rate, label: "TR", amount: 123, included_tax: 10.00) } + let!(:adjustment2) { create(:adjustment, adjustable: li1, originator: tax_rate, label: "TR", amount: 123, included_tax: 10.00) } + + before { li1; li2 } + + it "finds line items with tax" do + LineItem.with_tax.should == [li1] + end + + it "finds line items without tax" do + LineItem.without_tax.should == [li2] + end + end end describe "calculating price with adjustments" do @@ -47,5 +63,312 @@ module Spree li.amount_with_adjustments.should == 122.22 end end + + describe "tax" do + let(:li_no_tax) { create(:line_item) } + let(:li_tax) { create(:line_item) } + let(:tax_rate) { create(:tax_rate, calculator: Spree::Calculator::DefaultTax.new) } + let!(:adjustment) { create(:adjustment, adjustable: li_tax, originator: tax_rate, label: "TR", amount: 123, included_tax: 10.00) } + + context "checking if a line item has tax included" do + it "returns true when it does" do + expect(li_tax).to have_tax + end + + it "returns false otherwise" do + expect(li_no_tax).to_not have_tax + end + end + + context "calculating the amount of included tax" do + it "returns the included tax when present" do + expect(li_tax.included_tax).to eq 10.00 + end + + it "returns 0.00 otherwise" do + expect(li_no_tax.included_tax).to eq 0.00 + end + end + end + + describe "unit value/description" do + describe "inheriting units" do + let!(:p) { create(:product, variant_unit: "weight", variant_unit_scale: 1, master: create(:variant, unit_value: 1000 )) } + let!(:v) { p.variants.first } + let!(:o) { create(:order) } + + context "on create" do + context "when no final_weight_volume is set" do + let(:li) { build(:line_item, order: o, variant: v, quantity: 3) } + + it "initializes final_weight_volume from the variant's unit_value" do + expect(li.final_weight_volume).to be nil + li.save + expect(li.final_weight_volume).to eq 3000 + end + end + + context "when a final_weight_volume has been set" do + let(:li) { build(:line_item, order: o, variant: v, quantity: 3, final_weight_volume: 2000) } + + it "uses the changed value" do + expect(li.final_weight_volume).to eq 2000 + li.save + expect(li.final_weight_volume).to eq 2000 + end + end + end + + context "on save" do + let!(:li) { create(:line_item, order: o, variant: v, quantity: 3) } + + before do + expect(li.final_weight_volume).to eq 3000 + end + + context "when final_weight_volume is changed" do + let(:attrs) { { final_weight_volume: 2000 } } + + context "and quantity is not changed" do + before do + li.update_attributes(attrs) + end + + it "uses the value given" do + expect(li.final_weight_volume).to eq 2000 + end + end + + context "and quantity is changed" do + before do + attrs.merge!( quantity: 4 ) + li.update_attributes(attrs) + end + + it "uses the value given" do + expect(li.final_weight_volume).to eq 2000 + end + end + end + + context "when final_weight_volume is not changed" do + let(:attrs) { { price: 3.00 } } + + context "and quantity is not changed" do + before do + li.update_attributes(attrs) + end + + it "does not change final_weight_volume" do + expect(li.final_weight_volume).to eq 3000 + end + end + + context "and quantity is changed" do + context "from > 0" do + context "and a final_weight_volume has been set" do + before do + expect(li.final_weight_volume).to eq 3000 + attrs.merge!( quantity: 4 ) + li.update_attributes(attrs) + end + + it "scales the final_weight_volume based on the change in quantity" do + expect(li.final_weight_volume).to eq 4000 + end + end + + context "and a final_weight_volume has not been set" do + before do + li.update_attributes(final_weight_volume: nil) + attrs.merge!( quantity: 1 ) + li.update_attributes(attrs) + end + + it "calculates a final_weight_volume from the variants unit_value" do + expect(li.final_weight_volume).to eq 1000 + end + end + end + + context "from 0" do + before { li.update_attributes(quantity: 0) } + + context "and a final_weight_volume has been set" do + before do + expect(li.final_weight_volume).to eq 0 + attrs.merge!( quantity: 4 ) + li.update_attributes(attrs) + end + + it "recalculates a final_weight_volume from the variants unit_value" do + expect(li.final_weight_volume).to eq 4000 + end + end + + context "and a final_weight_volume has not been set" do + before do + li.update_attributes(final_weight_volume: nil) + attrs.merge!( quantity: 1 ) + li.update_attributes(attrs) + end + + it "calculates a final_weight_volume from the variants unit_value" do + expect(li.final_weight_volume).to eq 1000 + end + end + end + end + end + end + end + + describe "generating the full name" do + let(:li) { LineItem.new } + + context "when display_name is blank" do + before do + li.stub(:unit_to_display) { 'unit_to_display' } + li.stub(:display_name) { '' } + end + + it "returns unit_to_display" do + li.full_name.should == 'unit_to_display' + end + end + + context "when unit_to_display contains display_name" do + before do + li.stub(:unit_to_display) { '1kg Jar' } + li.stub(:display_name) { '1kg' } + end + + it "returns unit_to_display" do + li.full_name.should == '1kg Jar' + end + end + + context "when display_name contains unit_to_display" do + before do + li.stub(:unit_to_display) { '10kg' } + li.stub(:display_name) { '10kg Box' } + end + + it "returns display_name" do + li.full_name.should == '10kg Box' + end + end + + context "otherwise" do + before do + li.stub(:unit_to_display) { '1 Loaf' } + li.stub(:display_name) { 'Spelt Sourdough' } + end + + it "returns unit_to_display" do + li.full_name.should == 'Spelt Sourdough (1 Loaf)' + end + end + end + + describe "generating the product and variant name" do + let(:li) { LineItem.new } + let(:p) { double(:product, name: 'product') } + before { allow(li).to receive(:product) { p } } + + context "when full_name starts with the product name" do + before { allow(li).to receive(:full_name) { p.name + " - something" } } + + it "does not show the product name twice" do + li.product_and_full_name.should == 'product - something' + end + end + + context "when full_name does not start with the product name" do + before { allow(li).to receive(:full_name) { "display_name (unit)" } } + + it "prepends the product name to the full name" do + li.product_and_full_name.should == 'product - display_name (unit)' + end + end + end + + describe "getting name for display" do + it "returns product name" do + li = create(:line_item, product: create(:product)) + li.name_to_display.should == li.product.name + end + end + + describe "getting unit for display" do + it "returns options_text" do + li = create(:line_item) + li.stub(:options_text).and_return "ponies" + li.unit_to_display.should == "ponies" + end + end + + context "when the line_item already has a final_weight_volume set (and all required option values do not exist)" do + let!(:p0) { create(:simple_product, variant_unit: 'weight', variant_unit_scale: 1) } + let!(:v) { create(:variant, product: p0, unit_value: 10, unit_description: 'bar') } + + let!(:p) { create(:simple_product, variant_unit: 'weight', variant_unit_scale: 1) } + let!(:li) { create(:line_item, product: p, final_weight_volume: 5) } + + it "removes the old option value and assigns the new one" do + ov_orig = li.option_values.last + ov_var = v.option_values.last + allow(li).to receive(:unit_description) { 'foo' } + + expect { + li.update_attribute(:final_weight_volume, 10) + }.to change(Spree::OptionValue, :count).by(1) + + li.option_values.should_not include ov_orig + li.option_values.should_not include ov_var + ov = li.option_values.last + ov.name.should == "10g foo" + end + end + + context "when the variant already has a value set (and all required option values exist)" do + let!(:p0) { create(:simple_product, variant_unit: 'weight', variant_unit_scale: 1) } + let!(:v) { create(:variant, product: p0, unit_value: 10, unit_description: 'bar') } + + let!(:p) { create(:simple_product, variant_unit: 'weight', variant_unit_scale: 1) } + let!(:li) { create(:line_item, product: p, final_weight_volume: 5) } + + it "removes the old option value and assigns the new one" do + ov_orig = li.option_values.last + ov_new = v.option_values.last + allow(li).to receive(:unit_description) { 'bar' } + + expect { + li.update_attribute(:final_weight_volume, 10) + }.to change(Spree::OptionValue, :count).by(0) + + li.option_values.should_not include ov_orig + li.option_values.should include ov_new + end + end + end + + describe "deleting unit option values" do + let!(:p) { create(:simple_product, variant_unit: 'weight', variant_unit_scale: 1) } + let!(:ot) { Spree::OptionType.find_by_name 'unit_weight' } + let!(:li) { create(:line_item, product: p) } + + it "removes option value associations for unit option types" do + expect { + li.delete_unit_option_values + }.to change(li.option_values, :count).by(-1) + end + + it "does not delete option values" do + expect { + li.delete_unit_option_values + }.to change(Spree::OptionValue, :count).by(0) + end + end end end diff --git a/spec/models/spree/order_populator_spec.rb b/spec/models/spree/order_populator_spec.rb index 5602e46da5..7ba59fb47a 100644 --- a/spec/models/spree/order_populator_spec.rb +++ b/spec/models/spree/order_populator_spec.rb @@ -9,11 +9,51 @@ module Spree let(:order_cycle) { double(:order_cycle) } let(:op) { OrderPopulator.new(order, currency) } + context "end-to-end" do + let(:order) { create(:order, distributor: distributor, order_cycle: order_cycle) } + let(:distributor) { create(:distributor_enterprise) } + let(:order_cycle) { create(:simple_order_cycle, distributors: [distributor], variants: [v]) } + let(:op) { OrderPopulator.new(order, nil) } + let(:v) { create(:variant) } + + describe "populate" do + it "adds a variant" do + op.populate({variants: {v.id.to_s => {quantity: '1', max_quantity: '2'}}}, true) + li = order.find_line_item_by_variant(v) + li.should be + li.quantity.should == 1 + li.max_quantity.should == 2 + li.final_weight_volume.should == 1.0 + end + + it "updates a variant's quantity, max quantity and final_weight_volume" do + order.add_variant v, 1, 2 + + op.populate({variants: {v.id.to_s => {quantity: '2', max_quantity: '3'}}}, true) + li = order.find_line_item_by_variant(v) + li.should be + li.quantity.should == 2 + li.max_quantity.should == 3 + li.final_weight_volume.should == 2.0 + end + + it "removes a variant" do + order.add_variant v, 1, 2 + + op.populate({variants: {}}, true) + order.line_items(:reload) + li = order.find_line_item_by_variant(v) + li.should_not be + end + end + end + describe "populate" do before do op.should_receive(:distributor_and_order_cycle). and_return([distributor, order_cycle]) end + it "checks that distribution can supply all products in the cart" do op.should_receive(:distribution_can_supply_products_in_cart). with(distributor, order_cycle).and_return(false) @@ -21,13 +61,6 @@ module Spree op.populate(params).should be_false op.errors.to_a.should == ["That distributor or order cycle can't supply all the products in your cart. Please choose another."] end - - it "empties the order if override is true" do - op.stub(:distribution_can_supply_products_in_cart).and_return true - order.stub(:with_lock).and_yield - order.should_receive(:empty!) - op.populate(params, true) - end it "locks the order" do op.stub(:distribution_can_supply_products_in_cart).and_return(true) @@ -37,20 +70,90 @@ module Spree it "attempts cart add with max_quantity" do op.stub(:distribution_can_supply_products_in_cart).and_return true - order.should_receive(:empty!) - params = {variants: {"1" => {quantity: 1, max_quantity: 2}}} + params = {variants: {"1" => {quantity: 1, max_quantity: 2}}} order.stub(:with_lock).and_yield + op.stub(:varies_from_cart) { true } + op.stub(:variants_removed) { [] } op.should_receive(:attempt_cart_add).with("1", 1, 2).and_return true op.populate(params, true) end end + describe "varies_from_cart" do + let(:variant) { double(:variant, id: 123) } + + it "returns true when item is not in cart and a quantity is specified" do + op.should_receive(:line_item_for_variant_id).with(variant.id).and_return(nil) + op.send(:varies_from_cart, {variant_id: variant.id, quantity: '2'}).should be_true + end + + it "returns true when item is not in cart and a max_quantity is specified" do + op.should_receive(:line_item_for_variant_id).with(variant.id).and_return(nil) + op.send(:varies_from_cart, {variant_id: variant.id, quantity: '0', max_quantity: '2'}).should be_true + end + + it "returns false when item is not in cart and no quantity or max_quantity are specified" do + op.should_receive(:line_item_for_variant_id).with(variant.id).and_return(nil) + op.send(:varies_from_cart, {variant_id: variant.id, quantity: '0'}).should be_false + end + + it "returns true when quantity varies" do + li = double(:line_item, quantity: 1, max_quantity: nil) + op.stub(:line_item_for_variant_id) { li } + + op.send(:varies_from_cart, {variant_id: variant.id, quantity: '2'}).should be_true + end + + it "returns true when max_quantity varies" do + li = double(:line_item, quantity: 1, max_quantity: nil) + op.stub(:line_item_for_variant_id) { li } + + op.send(:varies_from_cart, {variant_id: variant.id, quantity: '1', max_quantity: '3'}).should be_true + end + + it "returns false when max_quantity varies only in nil vs 0" do + li = double(:line_item, quantity: 1, max_quantity: nil) + op.stub(:line_item_for_variant_id) { li } + + op.send(:varies_from_cart, {variant_id: variant.id, quantity: '1'}).should be_false + end + + it "returns false when both are specified and neither varies" do + li = double(:line_item, quantity: 1, max_quantity: 2) + op.stub(:line_item_for_variant_id) { li } + + op.send(:varies_from_cart, {variant_id: variant.id, quantity: '1', max_quantity: '2'}).should be_false + end + end + + describe "variants_removed" do + it "returns the variant ids when one is in the cart but not in those given" do + op.stub(:variant_ids_in_cart) { [123] } + op.send(:variants_removed, []).should == [123] + end + + it "returns nothing when all items in the cart are provided" do + op.stub(:variant_ids_in_cart) { [123] } + op.send(:variants_removed, [{variant_id: '123'}]).should == [] + end + + it "returns nothing when items are added to cart" do + op.stub(:variant_ids_in_cart) { [123] } + op.send(:variants_removed, [{variant_id: '123'}, {variant_id: '456'}]).should == [] + end + + it "does not return duplicates" do + op.stub(:variant_ids_in_cart) { [123, 123] } + op.send(:variants_removed, []).should == [123] + end + end + describe "attempt_cart_add" do it "performs additional validations" do variant = double(:variant) - variant.stub(:scope_to_hub) quantity = 123 Spree::Variant.stub(:find).and_return(variant) + VariantOverride.stub(:for).and_return(nil) op.should_receive(:check_stock_levels).with(variant, quantity).and_return(true) op.should_receive(:check_order_cycle_provided_for).with(variant).and_return(true) diff --git a/spec/models/spree/order_spec.rb b/spec/models/spree/order_spec.rb index ecc19f5493..4d681e01cc 100644 --- a/spec/models/spree/order_spec.rb +++ b/spec/models/spree/order_spec.rb @@ -54,6 +54,7 @@ describe Spree::Order do product_distribution.should_receive(:create_adjustment_for).with(line_item) subject.stub(:product_distribution_for) { product_distribution } + subject.update_distribution_charge! end @@ -329,6 +330,22 @@ describe Spree::Order do end end + describe "removing an item from the order" do + let(:order) { create(:order) } + let(:v1) { create(:variant) } + let(:v2) { create(:variant) } + + before do + order.add_variant v1 + order.add_variant v2 + end + + it "removes the variant's line item" do + order.remove_variant v1 + order.line_items(:reload).map(&:variant).should == [v2] + end + end + describe "emptying the order" do it "removes shipping method" do subject.shipping_method = create(:shipping_method) @@ -504,11 +521,79 @@ describe Spree::Order do end end + describe "checking if an order is an account invoice" do + let(:accounts_distributor) { create(:distributor_enterprise) } + let(:order_account_invoice) { create(:order, distributor: accounts_distributor) } + let(:order_general) { create(:order, distributor: create(:distributor_enterprise)) } + + before do + Spree::Config.accounts_distributor_id = accounts_distributor.id + end + + it "returns true when the order is distributed by the accounts distributor" do + order_account_invoice.should be_account_invoice + end + + it "returns false otherwise" do + order_general.should_not be_account_invoice + end + end + describe "sending confirmation emails" do + let!(:distributor) { create(:distributor_enterprise) } + let!(:order) { create(:order, distributor: distributor) } + it "sends confirmation emails" do expect do - create(:order).deliver_order_confirmation_email + order.deliver_order_confirmation_email end.to enqueue_job ConfirmOrderJob end + + it "does not send confirmation emails when distributor is the accounts_distributor" do + Spree::Config.set({ accounts_distributor_id: distributor.id }) + + expect do + order.deliver_order_confirmation_email + end.to_not enqueue_job ConfirmOrderJob + end + end + + describe "associating a customer" do + let(:user) { create(:user) } + let(:distributor) { create(:distributor_enterprise) } + + context "when a user has been set on the order" do + let!(:order) { create(:order, distributor: distributor, user: user) } + context "and a customer for order.distributor and order.user.email already exists" do + let!(:customer) { create(:customer, enterprise: distributor, email: user.email) } + it "associates the order with the existing customer" do + order.send(:associate_customer) + expect(order.customer).to eq customer + end + end + context "and a customer for order.distributor and order.user.email does not alread exist" do + let!(:customer) { create(:customer, enterprise: distributor, email: 'some-other-email@email.com') } + it "creates a new customer" do + expect{order.send(:associate_customer)}.to change{Customer.count}.by 1 + end + end + end + + context "when a user has not been set on the order" do + let!(:order) { create(:order, distributor: distributor, user: nil) } + context "and a customer for order.distributor and order.email already exists" do + let!(:customer) { create(:customer, enterprise: distributor, email: order.email) } + it "creates a new customer" do + order.send(:associate_customer) + expect(order.customer).to eq customer + end + end + context "and a customer for order.distributor and order.email does not alread exist" do + let!(:customer) { create(:customer, enterprise: distributor, email: 'some-other-email@email.com') } + it "creates a new customer" do + expect{order.send(:associate_customer)}.to change{Customer.count}.by 1 + end + end + end end end diff --git a/spec/models/spree/preferences/file_configuration_spec.rb b/spec/models/spree/preferences/file_configuration_spec.rb new file mode 100644 index 0000000000..eb18b23e12 --- /dev/null +++ b/spec/models/spree/preferences/file_configuration_spec.rb @@ -0,0 +1,57 @@ +require 'spec_helper' + +module Spree + module Preferences + class TestConfiguration < FileConfiguration + preference :name, :string + + include OpenFoodNetwork::Paperclippable + preference :logo, :file + has_attached_file :logo + end + + describe FileConfiguration do + let(:c) { TestConfiguration.new } + + describe "getting preferences" do + it "returns regular preferences" do + c.name = 'foo' + c.get_preference(:name).should == 'foo' + end + + it "returns file preferences" do + c.get_preference(:logo).should be_a Paperclip::Attachment + end + + it "returns regular preferences via []" do + c.name = 'foo' + c[:name].should == 'foo' + end + + it "returns file preferences via []" do + c[:logo].should be_a Paperclip::Attachment + end + end + + describe "getting preference types" do + it "returns regular preference types" do + c.preference_type(:name).should == :string + end + + it "returns file preference types" do + c.preference_type(:logo).should == :file + end + end + + describe "respond_to?" do + it "responds to preference getters" do + c.respond_to?(:name).should be_true + end + + it "responds to preference setters" do + c.respond_to?(:name=).should be_true + end + end + end + end +end diff --git a/spec/models/spree/product_spec.rb b/spec/models/spree/product_spec.rb index 505ac8ee2e..25913f679b 100644 --- a/spec/models/spree/product_spec.rb +++ b/spec/models/spree/product_spec.rb @@ -35,23 +35,23 @@ module Spree it "defaults available_on to now" do Timecop.freeze product = Product.new - product.available_on.should == Time.now + product.available_on.should == Time.zone.now end describe "tax category" do context "when a tax category is required" do - before { Spree::Config.products_require_tax_category = true } - it "is invalid when a tax category is not provided" do - build(:product, tax_category_id: nil).should_not be_valid + with_products_require_tax_category(true) do + build(:product, tax_category_id: nil).should_not be_valid + end end end context "when a tax category is not required" do - before { Spree::Config.products_require_tax_category = false } - it "is valid when a tax category is not provided" do - build(:product, tax_category_id: nil).should be_valid + with_products_require_tax_category(false) do + build(:product, tax_category_id: nil).should be_valid + end end end end @@ -205,7 +205,7 @@ module Spree Product.in_distributor(d1).should == [p1] end - it "doesn't show products listed in the incoming exchange only", :future => true do + it "doesn't show products listed in the incoming exchange only" do s = create(:supplier_enterprise) c = create(:distributor_enterprise) d = create(:distributor_enterprise) diff --git a/spec/models/spree/shipping_method_spec.rb b/spec/models/spree/shipping_method_spec.rb index baea2ac56a..0e738470dd 100644 --- a/spec/models/spree/shipping_method_spec.rb +++ b/spec/models/spree/shipping_method_spec.rb @@ -55,5 +55,32 @@ module Spree sm.should be_available_to_order o end end + + describe "finding services offered by all distributors" do + let!(:d1) { create(:distributor_enterprise) } + let!(:d2) { create(:distributor_enterprise) } + let!(:d3) { create(:distributor_enterprise) } + let!(:d4) { create(:distributor_enterprise) } + let!(:d1_pickup) { create(:shipping_method, require_ship_address: false, distributors: [d1]) } + let!(:d1_delivery) { create(:shipping_method, require_ship_address: true, distributors: [d1]) } + let!(:d2_pickup) { create(:shipping_method, require_ship_address: false, distributors: [d2]) } + let!(:d3_delivery) { create(:shipping_method, require_ship_address: true, distributors: [d3]) } + + it "reports when the services are available" do + ShippingMethod.services[d1.id].should == {pickup: true, delivery: true} + end + + it "reports when only pickup is available" do + ShippingMethod.services[d2.id].should == {pickup: true, delivery: false} + end + + it "reports when only delivery is available" do + ShippingMethod.services[d3.id].should == {pickup: false, delivery: true} + end + + it "returns no entry when no service is available" do + ShippingMethod.services[d4.id].should be_nil + end + end end end diff --git a/spec/models/spree/tax_rate_spec.rb b/spec/models/spree/tax_rate_spec.rb index 25aad986a0..03b2cb39aa 100644 --- a/spec/models/spree/tax_rate_spec.rb +++ b/spec/models/spree/tax_rate_spec.rb @@ -29,5 +29,42 @@ module Spree end end end + + describe "ensuring that tax rate is marked as tax included_in_price" do + let(:tax_rate) { create(:tax_rate, included_in_price: false, calculator: Spree::Calculator::DefaultTax.new) } + + it "sets included_in_price to true" do + tax_rate.send(:with_tax_included_in_price) do + tax_rate.included_in_price.should be_true + end + end + + it "sets the included_in_price value accessible to the calculator to true" do + tax_rate.send(:with_tax_included_in_price) do + tax_rate.calculator.calculable.included_in_price.should be_true + end + end + + it "passes through the return value of the block" do + tax_rate.send(:with_tax_included_in_price) do + 'asdf' + end.should == 'asdf' + end + + it "restores both values to their original afterwards" do + tax_rate.send(:with_tax_included_in_price) {} + tax_rate.included_in_price.should be_false + tax_rate.calculator.calculable.included_in_price.should be_false + end + + it "restores both values when an exception is raised" do + expect do + tax_rate.send(:with_tax_included_in_price) { raise Exception.new 'oops' } + end.to raise_error 'oops' + + tax_rate.included_in_price.should be_false + tax_rate.calculator.calculable.included_in_price.should be_false + end + end end end diff --git a/spec/models/spree/taxon_spec.rb b/spec/models/spree/taxon_spec.rb new file mode 100644 index 0000000000..a0d729c054 --- /dev/null +++ b/spec/models/spree/taxon_spec.rb @@ -0,0 +1,28 @@ +require 'spec_helper' + +module Spree + describe Taxon do + let(:e) { create(:supplier_enterprise) } + let(:t0) { p1.taxons.order('id ASC').first } + let(:t1) { create(:taxon) } + let(:t2) { create(:taxon) } + + describe "finding all supplied taxons" do + let!(:p1) { create(:simple_product, supplier: e, taxons: [t1, t2]) } + + it "finds taxons" do + Taxon.supplied_taxons.should == {e.id => Set.new([t0.id, t1.id, t2.id])} + end + end + + describe "finding all distributed taxons" do + let!(:oc) { create(:simple_order_cycle, distributors: [e], variants: [p1.master]) } + let(:s) { create(:supplier_enterprise) } + let(:p1) { create(:simple_product, supplier: s, taxons: [t1, t2]) } + + it "finds taxons" do + Taxon.distributed_taxons.should == {e.id => Set.new([t0.id, t1.id, t2.id])} + end + end + end +end diff --git a/spec/models/spree/user_spec.rb b/spec/models/spree/user_spec.rb index 848febb9c5..7fefd56afb 100644 --- a/spec/models/spree/user_spec.rb +++ b/spec/models/spree/user_spec.rb @@ -56,9 +56,9 @@ describe Spree.user_class do end describe "known_users" do - let!(:u1) { create_enterprise_user } - let!(:u2) { create_enterprise_user } - let!(:u3) { create_enterprise_user } + let!(:u1) { create(:user) } + let!(:u2) { create(:user) } + let!(:u3) { create(:user) } let!(:e1) { create(:enterprise, owner: u1, users: [u1, u2]) } describe "as an enterprise user" do @@ -73,6 +73,7 @@ describe Spree.user_class do describe "as admin" do let(:admin) { quick_login_as_admin } + it "returns all users" do expect(admin.known_users).to include u1, u2, u3 end diff --git a/spec/models/spree/variant_spec.rb b/spec/models/spree/variant_spec.rb index 73aa8900d1..cd6bfc1bd3 100644 --- a/spec/models/spree/variant_spec.rb +++ b/spec/models/spree/variant_spec.rb @@ -3,10 +3,18 @@ require 'open_food_network/option_value_namer' module Spree describe Variant do + describe "double loading" do + # app/models/spree/variant_decorator.rb may be double-loaded in delayed job environment, + # so we need to be able to do so without error. + it "succeeds without error" do + load "#{Rails.root}/app/models/spree/variant_decorator.rb" + end + end + describe "scopes" do it "finds non-deleted variants" do v_not_deleted = create(:variant) - v_deleted = create(:variant, deleted_at: Time.now) + v_deleted = create(:variant, deleted_at: Time.zone.now) Spree::Variant.not_deleted.should include v_not_deleted Spree::Variant.not_deleted.should_not include v_deleted @@ -104,41 +112,36 @@ module Spree end end - describe "generating the full name" do + describe "indexing variants by id" do + let!(:v1) { create(:variant) } + let!(:v2) { create(:variant) } + let!(:v3) { create(:variant) } + + it "indexes variants by id" do + Variant.where(id: [v1, v2, v3]).indexed.should == + {v1.id => v1, v2.id => v2, v3.id => v3} + end + end + + describe "generating the product and variant name" do let(:v) { Variant.new } + let(:p) { double(:product, name: 'product') } + before { allow(v).to receive(:product) { p } } - before do - v.stub(:display_name) { 'display_name' } - v.stub(:unit_to_display) { 'unit_to_display' } + context "when full_name starts with the product name" do + before { allow(v).to receive(:full_name) { p.name + " - something" } } + + it "does not show the product name twice" do + v.product_and_full_name.should == 'product - something' + end end - it "returns unit_to_display when display_name is blank" do - v.stub(:display_name) { '' } - v.full_name.should == 'unit_to_display' - end + context "when full_name does not start with the product name" do + before { allow(v).to receive(:full_name) { "display_name (unit)" } } - it "returns display_name when it contains unit_to_display" do - v.stub(:display_name) { 'DiSpLaY_name' } - v.stub(:unit_to_display) { 'name' } - v.full_name.should == 'DiSpLaY_name' - end - - it "returns unit_to_display when it contains display_name" do - v.stub(:display_name) { '_to_' } - v.stub(:unit_to_display) { 'unit_TO_display' } - v.full_name.should == 'unit_TO_display' - end - - it "returns a combination otherwise" do - v.stub(:display_name) { 'display_name' } - v.stub(:unit_to_display) { 'unit_to_display' } - v.full_name.should == 'display_name (unit_to_display)' - end - - it "is resilient to regex chars" do - v = Variant.new display_name: ")))" - v.stub(:unit_to_display) { ")))" } - v.full_name.should == ")))" + it "prepends the product name to the full name" do + v.product_and_full_name.should == 'product - display_name (unit)' + end end end @@ -240,6 +243,44 @@ module Spree end describe "unit value/description" do + describe "generating the full name" do + let(:v) { Variant.new } + + before do + v.stub(:display_name) { 'display_name' } + v.stub(:unit_to_display) { 'unit_to_display' } + end + + it "returns unit_to_display when display_name is blank" do + v.stub(:display_name) { '' } + v.full_name.should == 'unit_to_display' + end + + it "returns display_name when it contains unit_to_display" do + v.stub(:display_name) { 'DiSpLaY_name' } + v.stub(:unit_to_display) { 'name' } + v.full_name.should == 'DiSpLaY_name' + end + + it "returns unit_to_display when it contains display_name" do + v.stub(:display_name) { '_to_' } + v.stub(:unit_to_display) { 'unit_TO_display' } + v.full_name.should == 'unit_TO_display' + end + + it "returns a combination otherwise" do + v.stub(:display_name) { 'display_name' } + v.stub(:unit_to_display) { 'unit_to_display' } + v.full_name.should == 'display_name (unit_to_display)' + end + + it "is resilient to regex chars" do + v = Variant.new display_name: ")))" + v.stub(:unit_to_display) { ")))" } + v.full_name.should == ")))" + end + end + describe "getting name for display" do it "returns display_name if present" do v = create(:variant, display_name: "foo") @@ -305,23 +346,18 @@ module Spree end end - context "when the variant already has a value set (and all required option values exist)" do - let!(:p0) { create(:simple_product, variant_unit: 'weight', variant_unit_scale: 1) } - let!(:v0) { create(:variant, product: p0, unit_value: 10, unit_description: 'foo') } - + context "when the variant already has a value set (and all required option values do not exist)" do let!(:p) { create(:simple_product, variant_unit: 'weight', variant_unit_scale: 1) } let!(:v) { create(:variant, product: p, unit_value: 5, unit_description: 'bar') } it "removes the old option value and assigns the new one" do ov_orig = v.option_values.last - ov_new = v0.option_values.last expect { v.update_attributes!(unit_value: 10, unit_description: 'foo') - }.to change(Spree::OptionValue, :count).by(0) + }.to change(Spree::OptionValue, :count).by(1) v.option_values.should_not include ov_orig - v.option_values.should include ov_new end end diff --git a/spec/models/suburb_spec.rb b/spec/models/suburb_spec.rb deleted file mode 100644 index 62a2464cd6..0000000000 --- a/spec/models/suburb_spec.rb +++ /dev/null @@ -1,24 +0,0 @@ -require 'spec_helper' - -describe Suburb do - it { should belong_to(:state) } - it { should delegate(:name).to(:state).with_prefix } - - describe "searching for matching suburbs" do - before(:each) do - Suburb.create(name: "Camberwell", postcode: 3124, latitude: -37.824818, longitude: 145.057957, state_id: Spree::State.first) - end - - it "should find suburb on part of name" do - Suburb.matching("Camb").count.should be > 0 - end - - it "should find suburb on part of postcode" do - Suburb.matching(312).count.should be > 0 - end - - it "should find nothing where part doesn't match" do - Suburb.matching("blahblah1234#!!!").count.should_not be > 0 - end - end -end diff --git a/spec/models/variant_override_spec.rb b/spec/models/variant_override_spec.rb index 3a6bd9b1df..ed2f2c00f4 100644 --- a/spec/models/variant_override_spec.rb +++ b/spec/models/variant_override_spec.rb @@ -14,8 +14,16 @@ describe VariantOverride do it "finds variant overrides for a set of hubs" do VariantOverride.for_hubs([hub1, hub2]).should match_array [vo1, vo2] end + + describe "fetching variant overrides indexed by variant" do + it "gets indexed variant overrides for one hub" do + VariantOverride.indexed(hub1).should == {v => vo1} + VariantOverride.indexed(hub2).should == {v => vo2} + end + end end + describe "looking up prices" do it "returns the numeric price when present" do VariantOverride.create!(variant: variant, hub: hub, price: 12.34) @@ -41,16 +49,16 @@ describe VariantOverride do describe "checking if stock levels have been overriden" do it "returns true when stock level has been overridden" do create(:variant_override, variant: variant, hub: hub, count_on_hand: 12) - VariantOverride.stock_overridden?(hub, variant).should be_true + VariantOverride.stock_overridden?(hub, variant).should be true end it "returns false when the override has no stock level" do create(:variant_override, variant: variant, hub: hub, count_on_hand: nil) - VariantOverride.stock_overridden?(hub, variant).should be_false + VariantOverride.stock_overridden?(hub, variant).should be false end it "returns false when there is no override for the hub/variant" do - VariantOverride.stock_overridden?(hub, variant).should be_false + VariantOverride.stock_overridden?(hub, variant).should be false end end @@ -61,16 +69,40 @@ describe VariantOverride do vo.reload.count_on_hand.should == 10 end - it "silently logs an error if the variant override doesn't have a stock level" do - vo = create(:variant_override, variant: variant, hub: hub, count_on_hand: nil) - Bugsnag.should_receive(:notify) - VariantOverride.decrement_stock! hub, variant, 2 - vo.reload.count_on_hand.should be_nil - end - it "silently logs an error if the variant override does not exist" do Bugsnag.should_receive(:notify) VariantOverride.decrement_stock! hub, variant, 2 end end + + describe "checking default stock value is present" do + it "returns true when a default stock level has been set" do + vo = create(:variant_override, variant: variant, hub: hub, count_on_hand: 12, default_stock: 20) + vo.default_stock?.should be true + end + + it "returns false when the override has no default stock level" do + vo = create(:variant_override, variant: variant, hub: hub, count_on_hand: 12, default_stock:nil) + vo.default_stock?.should be false + end + end + + describe "resetting stock levels" do + it "resets the on hand level to the value in the default_stock field" do + vo = create(:variant_override, variant: variant, hub: hub, count_on_hand: 12, default_stock: 20, resettable: true) + vo.reset_stock! + vo.reload.count_on_hand.should == 20 + end + it "silently logs an error if the variant override doesn't have a default stock level" do + vo = create(:variant_override, variant: variant, hub: hub, count_on_hand: 12, default_stock:nil, resettable: true) + Bugsnag.should_receive(:notify) + vo.reset_stock! + vo.reload.count_on_hand.should == 12 + end + it "doesn't reset the level if the behaviour is disabled" do + vo = create(:variant_override, variant: variant, hub: hub, count_on_hand: 12, default_stock: 10, resettable: false) + vo.reset_stock! + vo.reload.count_on_hand.should == 12 + end + end end diff --git a/spec/performance/injection_helper_spec.rb b/spec/performance/injection_helper_spec.rb new file mode 100644 index 0000000000..ef8d937ce1 --- /dev/null +++ b/spec/performance/injection_helper_spec.rb @@ -0,0 +1,29 @@ +require 'spec_helper' + +describe InjectionHelper, type: :helper, performance: true do + let(:oc) { create(:simple_order_cycle) } + let(:relative_supplier) { create(:supplier_enterprise) } + let(:relative_distributor) { create(:distributor_enterprise) } + + before do + 50.times do + e = create(:enterprise) + oc.distributors << e + create(:enterprise_relationship, parent: e, child: relative_supplier) + create(:enterprise_relationship, parent: e, child: relative_distributor) + end + end + + it "is performant in injecting enterprises" do + results = [] + 4.times do |i| + ActiveRecord::Base.connection.query_cache.clear + Rails.cache.clear + result = Benchmark.measure { helper.inject_enterprises } + results << result.total if i > 0 + puts result + end + + puts (results.sum / results.count * 1000).round 0 + end +end diff --git a/spec/performance/orders_controller_spec.rb b/spec/performance/orders_controller_spec.rb new file mode 100644 index 0000000000..e151e4f506 --- /dev/null +++ b/spec/performance/orders_controller_spec.rb @@ -0,0 +1,33 @@ +require 'spec_helper' + +describe Spree::OrdersController, type: :controller, performance: true do + let(:distributor) { create(:distributor_enterprise) } + let(:order_cycle) { create(:simple_order_cycle, distributors: [distributor], variants: products.map { |p| p.variants.first }) } + let(:products) { (0...num_products).map { create(:product) } } + let(:order) { subject.current_order(true) } + let(:num_products) { 20 } + + before do + order.set_distribution! distributor, order_cycle + controller.stub(:current_order) { order } + + Spree::Config.currency = 'AUD' + end + + describe "adding products to cart" do + it "adds products to cart" do + puts "Pre-populating first product" + spree_post :populate, variants: {products[0].variants.first.id => 1} + + result = Benchmark.measure do + (1..num_products).each do |num_products| + puts "Populating #{num_products} products" + variants = Hash[ products.map { |p| [p.variants.first.id, 1] }.first(num_products) ] + spree_post :populate, variants: variants + end + end + + puts result + end + end +end diff --git a/spec/performance/shop_controller_spec.rb b/spec/performance/shop_controller_spec.rb new file mode 100644 index 0000000000..bfe49cd178 --- /dev/null +++ b/spec/performance/shop_controller_spec.rb @@ -0,0 +1,37 @@ +require 'spec_helper' + +describe ShopController, type: :controller, performance: true do + let(:d) { create(:distributor_enterprise) } + let(:enterprise_fee) { create(:enterprise_fee) } + let(:order_cycle) { create(:simple_order_cycle, distributors: [d], coordinator_fees: [enterprise_fee]) } + + before do + controller.stub(:current_distributor) { d } + controller.stub(:current_order_cycle) { order_cycle } + Spree::Config.currency = 'AUD' + end + + describe "fetching products" do + let(:exchange) { order_cycle.exchanges.to_enterprises(d).outgoing.first } + let(:image) { File.open(File.expand_path('../../../app/assets/images/logo-white.png', __FILE__)) } + + before do + 11.times do + p = create(:simple_product) + p.set_property 'Organic Certified', 'NASAA 12345' + v1 = create(:variant, product: p) + v2 = create(:variant, product: p) + Spree::Image.create! viewable_id: p.master.id, viewable_type: 'Spree::Variant', attachment: image + + exchange.variants << [v1, v2] + end + end + + it "returns products via json" do + results = multi_benchmark(3) do + xhr :get, :products + response.should be_success + end + end + end +end diff --git a/spec/requests/large_request_spec.rb b/spec/requests/large_request_spec.rb new file mode 100644 index 0000000000..6e389d5ecb --- /dev/null +++ b/spec/requests/large_request_spec.rb @@ -0,0 +1,13 @@ +# Large requests can fail if Devise tries to store the URL in the session cookie. +# +# http://daniel.fone.net.nz/blog/2014/11/28/actiondispatch-cookies-cookieoverflow-via-devise-s-user_return_to/ +require 'spec_helper' + +RSpec.describe 'A very large request', type: :request do + it 'should not overflow cookies' do + get '/admin', foo: 'x' * ActionDispatch::Cookies::SignedCookieJar::MAX_COOKIE_SIZE + expect(response.status).to eq(302) # HTTP status 302 - Found + ## Use the newer syntax if rspec gets upgraded + # expect(response).to have_http_status(:redirect) + end +end diff --git a/spec/requests/shop_spec.rb b/spec/requests/shop_spec.rb index cc8838220c..9f34573e8d 100644 --- a/spec/requests/shop_spec.rb +++ b/spec/requests/shop_spec.rb @@ -32,7 +32,7 @@ describe "Shop API" do v51.update_attribute(:count_on_hand, 1) v52.update_attribute(:count_on_hand, 0) v71.update_attribute(:count_on_hand, 1) - v71.update_attribute(:deleted_at, Time.now) + v71.update_attribute(:deleted_at, Time.zone.now) exchange = Exchange.find(oc1.exchanges.to_enterprises(distributor).outgoing.first.id) exchange.update_attribute :pickup_time, "frogs" exchange.variants << v61 diff --git a/spec/serializers/admin/index_enterprise_serializer_spec.rb b/spec/serializers/admin/index_enterprise_serializer_spec.rb new file mode 100644 index 0000000000..3651f53f8d --- /dev/null +++ b/spec/serializers/admin/index_enterprise_serializer_spec.rb @@ -0,0 +1,34 @@ +describe Api::Admin::IndexEnterpriseSerializer do + include AuthenticationWorkflow + + let(:enterprise) { create(:distributor_enterprise) } + context "when spree_current_user is a manager" do + let(:user) { create_enterprise_user } + before do + user.enterprise_roles.create(enterprise: enterprise) + end + + it "sets 'owned' to false" do + serializer = Api::Admin::IndexEnterpriseSerializer.new enterprise, spree_current_user: user + serializer.to_json.should match "\"owned\":false" + end + end + + context "when spree_current_user is " do + let(:user) { enterprise.owner } + + it "sets 'owned' to true" do + serializer = Api::Admin::IndexEnterpriseSerializer.new enterprise, spree_current_user: user + serializer.to_json.should match "\"owned\":true" + end + end + + context "when spree_current_user is the owner" do + let(:user) { create(:admin_user) } + + it "sets 'owned' to true" do + serializer = Api::Admin::IndexEnterpriseSerializer.new enterprise, spree_current_user: user + serializer.to_json.should match "\"owned\":true" + end + end +end diff --git a/spec/serializers/enterprise_serializer_spec.rb b/spec/serializers/enterprise_serializer_spec.rb index 1063a042e7..f70852d973 100644 --- a/spec/serializers/enterprise_serializer_spec.rb +++ b/spec/serializers/enterprise_serializer_spec.rb @@ -1,21 +1,31 @@ #require 'spec_helper' describe Api::EnterpriseSerializer do + let(:serializer) { Api::EnterpriseSerializer.new enterprise, data: data } let(:enterprise) { create(:distributor_enterprise) } let(:taxon) { create(:taxon) } + let(:data) { OpenStruct.new(earliest_closing_times: {}, + active_distributors: [], + distributed_taxons: {enterprise.id => [123]}, + supplied_taxons: {enterprise.id => [456]}, + shipping_method_services: {}, + relatives: {enterprise.id => {producers: [123], distributors: [456]}}) } + it "serializes an enterprise" do - serializer = Api::EnterpriseSerializer.new enterprise serializer.to_json.should match enterprise.name end - it "includes distributed taxons" do - enterprise.stub(:distributed_taxons).and_return [taxon] - serializer = Api::EnterpriseSerializer.new enterprise - serializer.to_json.should match taxon.id.to_s + it "serializes taxons as ids only" do + serializer.serializable_hash[:taxons].should == [{id: 123}] + serializer.serializable_hash[:supplied_taxons].should == [{id: 456}] end - it "will render urls" do - serializer = Api::EnterpriseSerializer.new enterprise + it "serializes producers and hubs as ids only" do + serializer.serializable_hash[:producers].should == [{id: 123}] + serializer.serializable_hash[:hubs].should == [{id: 456}] + end + + it "serializes icons" do serializer.to_json.should match "map_005-hub.svg" end end diff --git a/spec/serializers/product_serializer_spec.rb b/spec/serializers/product_serializer_spec.rb deleted file mode 100644 index 0091668dbb..0000000000 --- a/spec/serializers/product_serializer_spec.rb +++ /dev/null @@ -1,14 +0,0 @@ -describe Api::ProductSerializer do - let(:hub) { create(:distributor_enterprise) } - let(:oc) { create(:simple_order_cycle, distributors: [hub], variants: [v1]) } - let(:p) { create(:simple_product) } - let!(:v1) { create(:variant, product: p, unit_value: 3) } - let!(:v2) { create(:variant, product: p, unit_value: 5) } - - it "scopes variants to distribution" do - s = Api::ProductSerializer.new p, current_distributor: hub, current_order_cycle: oc - json = s.to_json - json.should include v1.options_text - json.should_not include v2.options_text - end -end diff --git a/spec/spec_helper.rb b/spec/spec_helper.rb index a911fe7742..80d7c2eeb7 100644 --- a/spec/spec_helper.rb +++ b/spec/spec_helper.rb @@ -1,17 +1,18 @@ -require 'simplecov' -SimpleCov.start - - require 'rubygems' # Require pry when we're not inside Travis-CI -require 'pry' unless ENV['HAS_JOSH_K_SEAL_OF_APPROVAL'] +require 'pry' unless ENV['CI'] + +require 'knapsack' +Knapsack::Adapters::RSpecAdapter.bind ENV["RAILS_ENV"] ||= 'test' require File.expand_path("../../config/environment", __FILE__) require 'rspec/rails' require 'capybara' require 'database_cleaner' +require 'rspec/retry' +require 'paper_trail/frameworks/rspec' # Allow connections to phantomjs/selenium whilst raising errors # when connecting to external sites @@ -33,7 +34,7 @@ require 'capybara/poltergeist' Capybara.javascript_driver = :poltergeist Capybara.register_driver :poltergeist do |app| - options = {phantomjs_options: ['--load-images=no'], window_size: [1280, 800], timeout: 1.minute} + options = {phantomjs_options: ['--load-images=no'], window_size: [1280, 800], timeout: 2.minutes} # Extend poltergeist's timeout to allow ample time to use pry in browser thread #options.merge! {timeout: 5.minutes} # Enable the remote inspector: Use page.driver.debug to open a remote debugger in chrome @@ -41,7 +42,7 @@ Capybara.register_driver :poltergeist do |app| Capybara::Poltergeist::Driver.new(app, options) end -Capybara.default_wait_time = 30 +Capybara.default_max_wait_time = 30 require "paperclip/matchers" @@ -70,16 +71,27 @@ RSpec.configure do |config| # Filters config.filter_run_excluding :skip => true, :future => true, :to_figure_out => true + # Retry + config.verbose_retry = true + # DatabaseCleaner config.before(:suite) { DatabaseCleaner.clean_with :deletion, {except: ['spree_countries', 'spree_states']} } config.before(:each) { DatabaseCleaner.strategy = :transaction } config.before(:each, js: true) { DatabaseCleaner.strategy = :deletion, {except: ['spree_countries', 'spree_states']} } config.before(:each) { DatabaseCleaner.start } config.after(:each) { DatabaseCleaner.clean } + config.after(:each, js:true) do + Capybara.reset_sessions! + RackRequestBlocker.wait_for_requests_complete + DatabaseCleaner.clean + end # Geocoding config.before(:each) { Spree::Address.any_instance.stub(:geocode).and_return([1,1]) } + # Ensure we start with consistent config settings + config.before(:each) { Spree::Config.products_require_tax_category = false } + # Helpers config.include Rails.application.routes.url_helpers config.include Spree::UrlHelpers @@ -91,10 +103,12 @@ RSpec.configure do |config| config.include OpenFoodNetwork::ControllerHelper, :type => :controller config.include OpenFoodNetwork::FeatureToggleHelper config.include OpenFoodNetwork::EnterpriseGroupsHelper + config.include OpenFoodNetwork::ProductsHelper config.include OpenFoodNetwork::DistributionHelper config.include OpenFoodNetwork::HtmlHelper config.include ActionView::Helpers::DateHelper config.include OpenFoodNetwork::DelayedJobHelper + config.include OpenFoodNetwork::PerformanceHelper # FactoryGirl require 'factory_girl_rails' diff --git a/spec/support/html_helper.rb b/spec/support/html_helper.rb index 54c6ffe1f1..ffa261fc39 100644 --- a/spec/support/html_helper.rb +++ b/spec/support/html_helper.rb @@ -1,6 +1,6 @@ module OpenFoodNetwork module HtmlHelper - def save_and_open(html) + def html_save_and_open(html) require "launchy" file = Tempfile.new('html') file.write html diff --git a/spec/support/matchers/select2_matchers.rb b/spec/support/matchers/select2_matchers.rb index 67cfcd81d0..6ab4fd5788 100644 --- a/spec/support/matchers/select2_matchers.rb +++ b/spec/support/matchers/select2_matchers.rb @@ -21,21 +21,50 @@ RSpec::Matchers.define :have_select2 do |id, options={}| if results.all? results << all_options_present(from, options[:with_options]) if options.key? :with_options results << exact_options_present(from, options[:options]) if options.key? :options + results << no_options_present(from, options[:without_options]) if options.key? :without_options end results.all? end failure_message_for_should do |actual| - message = "expected to find select2 ##{@id}" + message = "expected to find select2 ##{@id}" message += " with #{@options.inspect}" if @options.any? message end match_for_should_not do |node| - raise "Not yet implemented" + @id, @options, @node = id, options, node + + #id = find_label_by_text(locator) + from = "#s2id_#{id}" + + results = [] + + results << node.has_no_selector?(from, wait: 1) + + # if results.all? + # results << selected_option_is(from, options[:selected]) if options.key? :selected + # end + + if results.none? + results << all_options_absent(from, options[:with_options]) if options.key? :with_options + #results << exact_options_present(from, options[:options]) if options.key? :options + #results << no_options_present(from, options[:without_options]) if options.key? :without_options + end + + if (options.keys & %i(selected options without_options)).any? + raise "Not yet implemented" + end + + results.any? end + failure_message_for_should_not do |actual| + message = "expected not to find select2 ##{@id}" + message += " with #{@options.inspect}" if @options.any? + message + end def all_options_present(from, options) with_select2_open(from) do @@ -45,12 +74,28 @@ RSpec::Matchers.define :have_select2 do |id, options={}| end end + def all_options_absent(from, options) + with_select2_open(from) do + options.all? do |option| + @node.has_no_selector? "div.select2-drop-active ul.select2-results li", text: option + end + end + end + def exact_options_present(from, options) with_select2_open(from) do @node.all("div.select2-drop-active ul.select2-results li").map(&:text) == options end end + def no_options_present(from, options) + with_select2_open(from) do + options.none? do |option| + @node.has_selector? "div.select2-drop-active ul.select2-results li", text: option + end + end + end + def selected_option_is(from, text) within find(from) do find("a.select2-choice").text == text @@ -58,9 +103,9 @@ RSpec::Matchers.define :have_select2 do |id, options={}| end def with_select2_open(from) - find(from).click + open_select2 from r = yield - find(from).click + close_select2 from r end end diff --git a/spec/support/matchers/table_matchers.rb b/spec/support/matchers/table_matchers.rb index 053562b9e4..411b0b646f 100644 --- a/spec/support/matchers/table_matchers.rb +++ b/spec/support/matchers/table_matchers.rb @@ -26,3 +26,44 @@ RSpec::Matchers.define :have_table_row do |row| node.all('tr').map { |tr| tr.all('th, td').map(&:text) } end end + + + +# find("#my-table").should match_table [[...]] +RSpec::Matchers.define :match_table do |expected_table| + + match_for_should do |node| + rows = node. + all("tr"). + map { |r| r.all("th,td").map { |c| c.text.strip } } + + if rows.count != expected_table.count + @failure_message = "found table with #{rows.count} rows, expected #{expected_table.count}" + + else + rows.each_with_index do |row, i| + expected_row = expected_table[i] + if row.count != expected_row.count + @failure_message = "row #{i} has #{row.count} columns, expected #{expected_row.count}" + break + + elsif row != expected_row + row.each_with_index do |cell, j| + if cell != expected_row[j] + @failure_message = "cell [#{i}, #{j}] has content '#{cell}', expected '#{expected_row[j]}'" + break + end + end + break if @failure_message + end + end + end + + @failure_message.nil? + end + + failure_message_for_should do |text| + @failure_message + end + +end diff --git a/spec/support/performance_helper.rb b/spec/support/performance_helper.rb new file mode 100644 index 0000000000..a2d4fe3630 --- /dev/null +++ b/spec/support/performance_helper.rb @@ -0,0 +1,20 @@ +module OpenFoodNetwork + module PerformanceHelper + def multi_benchmark(num_samples) + results = (0..num_samples).map do |i| + ActiveRecord::Base.connection.query_cache.clear + Rails.cache.clear + + result = Benchmark.measure { yield } + + puts result + + result.total + end.drop(1) # Do not return the first sample + + puts (results.sum / results.count * 1000).round 0 + + results + end + end +end diff --git a/spec/support/products_helper.rb b/spec/support/products_helper.rb new file mode 100644 index 0000000000..8e9ebc1c98 --- /dev/null +++ b/spec/support/products_helper.rb @@ -0,0 +1,12 @@ +module OpenFoodNetwork + module ProductsHelper + def with_products_require_tax_category(value) + original_value = Spree::Config.products_require_tax_category + + Spree::Config.products_require_tax_category = value + yield + ensure + Spree::Config.products_require_tax_category = original_value + end + end +end diff --git a/spec/support/request/authentication_workflow.rb b/spec/support/request/authentication_workflow.rb index e71c0dd21a..79b6d81452 100644 --- a/spec/support/request/authentication_workflow.rb +++ b/spec/support/request/authentication_workflow.rb @@ -70,7 +70,7 @@ module AuthenticationWorkflow visit spree.login_path fill_in 'email', :with => 'someone@ofn.org' fill_in 'password', :with => 'passw0rd' - click_button 'Log in' + click_button 'Login' end end diff --git a/spec/support/request/ui_component_helper.rb b/spec/support/request/ui_component_helper.rb index d9f01b447b..43872aee27 100644 --- a/spec/support/request/ui_component_helper.rb +++ b/spec/support/request/ui_component_helper.rb @@ -9,7 +9,7 @@ module UIComponentHelper end def click_login_button - click_button "Log in" + click_button "Login" end def click_signup_button @@ -28,7 +28,7 @@ module UIComponentHelper end def open_login_modal - find("a", text: "Log in").click + find("a", text: "Login").click end def open_off_canvas @@ -36,7 +36,7 @@ module UIComponentHelper end def have_login_modal - have_selector ".login-modal" + have_selector ".login-modal" end def open_product_modal(product) @@ -69,7 +69,7 @@ module UIComponentHelper end def cart_dirty - page.find("span.cart-span")[:class].include? 'dirty' + page.find("span.cart-span")[:class].include? 'pure-dirty' end def wait_for_ajax @@ -100,7 +100,7 @@ module UIComponentHelper def expand_active_table_node(name) find(".active_table_node", text: name).click end - + def follow_active_table_node(name) expand_active_table_node(name) find(".active_table_node a", text: "#{name}").click diff --git a/spec/support/request/web_helper.rb b/spec/support/request/web_helper.rb index db8b3af51c..30ce677c6c 100644 --- a/spec/support/request/web_helper.rb +++ b/spec/support/request/web_helper.rb @@ -93,19 +93,11 @@ module WebHelper errors.map(&:text) end - def handle_js_confirm(accept=true, debug=false) + def handle_js_confirm(accept=true) page.evaluate_script "window.confirm = function(msg) { return #{!!accept }; }" yield end - def handle_webdriver_random_failure(retry_times = 3) - begin - yield - rescue Selenium::WebDriver::Error::InvalidSelectorError => e - e.message =~ /nsIDOMXPathEvaluator.createNSResolver/ ? (retry if (retry_times -= 1 ) > 0) : raise - end - end - def click_dialog_button(button_content) page.find(:xpath, "//div[@class=\"ui-dialog-buttonset\"]//span[contains(text(),\"#{button_content}\")]").click end @@ -127,7 +119,7 @@ module WebHelper # Do not use this without good reason. Capybara's built-in waiting is very effective. def wait_until(secs=nil) require "timeout" - Timeout.timeout(secs || Capybara.default_wait_time) do + Timeout.timeout(secs || Capybara.default_max_wait_time) do sleep(0.1) until value = yield value end @@ -152,10 +144,16 @@ module WebHelper have_selector "div.select2-result-label", text: value end + def open_select2(selector) + page.evaluate_script "jQuery('#{selector}').select2('open');" + end + + def close_select2(selector) + page.evaluate_script "jQuery('#{selector}').select2('close');" + end private def wait_for_ajax wait_until { page.evaluate_script("$.active") == 0 } end end -