Contoh Integrasi Odoo dengan Aplikasi Mobile Android dan iOS untuk Form Survey

Integrasi Odoo dengan aplikasi mobile semakin banyak diminati perusahaan untuk semakin membuat proses bisnis semakin efisien dan kemudahan dalam operasional.

Kali ini saya kan memberikan contoh integrasi Odoo dengan aplikasi mobile Android dan iOS yang kami bangun menggunakan Flutter.

Aplikasi ini merupakan permintaan dari salah satu customer kami yang menggunakan Odoo untuk sistem patroli dan menggunakan modul survey untuk mencatat hasil patroli termasuk mengupload photo dan juga untuk mencatat lokasi (latitude dan longitude).

Di artikel ini saya akan membagikan beberapa kode Flutter yang bisa menginspirasi teman-teman yang mungkin sedang mengerjakan proyek serupa.

Hasil akhir dari aplikasi mobilenya seperti ini :

Modul Survey Odoo di Android

Aplikasi mobile tersebut mengambil data dati modul survey yang ada di Odoo dan menampilkan secara native di aplikasi mobile. Form yang ada di Odoo yang seperti ini :

Integrasi Odoo dengan Aplikasi Android dan iOS

Form yang ada di modul survey Odoo akan diisi melalui aplikasi mobile dan hasilnya menjadi seperti ini

Integrasi Odoo dengan Aplikasi Mobile

Termasuk juga modifikasi module Odoo supaya bisa menerima upload photo dari aplikasi mobile dan menampilkannya di web. Hasilnya seperti ini :

Upload Foto Odoo dari Aplikasi Mobile

Dalam case ini juga diperlukan titik lokasi patroli yang berupa latitude dan longitude yang dikirim oleh aplikasi ke Odoo

Code untuk mengirim latitude dan longitude dari Flutter ke Odoo sebagai berikut

// Dapatkan lokasi pengguna
    Position position = await Geolocator.getCurrentPosition(
        desiredAccuracy: LocationAccuracy.high);

Dan berikut ini adalah code untuk mengkonversi form survey yang ada di modul Odoo ke aplikasi Android dan iOS dengan Flutter

// Model untuk Question
class Question {
  final int id;
  final String title;
  final String type;
  final bool is_patrol_note;
  final bool is_mandatory;
  final List<Answer> answers;

  Question({
    required this.id,
    required this.title,
    required this.type,
    required this.is_patrol_note,
    required this.is_mandatory,
    required this.answers,
  });

  factory Question.fromJson(Map<String, dynamic> json) {
    var answersList = json['answers'] as List;
    List<Answer> _answers =
        answersList.map((answer) => Answer.fromJson(answer)).toList();

    return Question(
      id: json['id'],
      title: json['title'],
      type: json['type'],
      is_patrol_note: json['is_patrol_note'],
      is_mandatory: json['is_mandatory'],
      answers: _answers,
    );
  }
}

// Model untuk Survey
class Survey {
  final int id;
  final String title;
  final List<Question> questions;

  Survey({required this.id, required this.title, required this.questions});

  factory Survey.fromJson(Map<String, dynamic> json) {
    var questionsList = json['questions'] as List;
    List<Question> _questions =
        questionsList.map((question) => Question.fromJson(question)).toList();

    return Survey(
      id: json['id'],
      title: json['title'],
      questions: _questions,
    );
  }
}

// Widget untuk Form Survey
class SurveyForm extends StatefulWidget {
  final Survey survey;
  final OdooClient odooClient;
  final String jadwalName;
  final int patrolReportId;
  final int checkPointId;
  final int checkPointReportId;

  SurveyForm(
      {required this.survey,
      required this.odooClient,
      required this.jadwalName,
      required this.patrolReportId,
      required this.checkPointId,
      required this.checkPointReportId});

  @override
  _SurveyFormState createState() => _SurveyFormState();
}

Dan berikut ini adalah kelas untuk membuat tampilan questionnya

  Widget buildQuestionWidget(Question question) {
    switch (question.type) {
      case 'simple_choice':
        return buildSimpleChoiceQuestion(question);
      case 'char_box':
        return buildCharBoxQuestion(question);
      case 'numerical_box':
        return buildNumericalBoxQuestion(question);
      default:
        return Container();
    }
  }

  Widget buildSimpleChoiceQuestion(Question question) {
    return Column(
      crossAxisAlignment: CrossAxisAlignment.start,
      children: [
        Padding(
          padding: const EdgeInsets.symmetric(vertical: 8.0),
          child: Text(
            question.title,
            style: TextStyle(fontSize: 18, fontWeight: FontWeight.bold),
          ),
        ),
        Column(
          children: question.answers.map((answer) {
            return Column(
              children: [
                RadioListTile<int>(
                  title: Text(answer.value),
                  value: answer.id,
                  groupValue: answers[question.id],
                  onChanged: (value) {
                    setState(() {
                      answers[question.id] = value;
                      photo_required[question.id] = answer.photo_required;
                    });
                  },
                ),
                if (answer.photo_required == true &&
                    photo_required[question.id] == true)
                  Column(
                    children: [
                      ElevatedButton(
                        onPressed: () => _pickImage(question.id),
                        child: Text('Upload Foto'),
                      ),
                      if (images[question.id] != null)
                        GestureDetector(
                          onTap: () {
                            showDialog(
                              context: context,
                              builder: (BuildContext context) {
                                return Dialog(
                                  shape: RoundedRectangleBorder(
                                    borderRadius: BorderRadius
                                        .zero, // Menghilangkan rounded corners
                                  ),
                                  child: Container(
                                    width: 400,
                                    child: Image.memory(
                                      base64Decode(images[question.id]!),
                                      fit: BoxFit.contain,
                                    ),
                                  ),
                                );
                              },
                            );
                          },
                          child: Image.memory(
                            base64Decode(images[question.id]!),
                            width: 40,
                            height: 40,
                          ),
                        ),
                    ],
                  ),
              ],
            );
          }).toList(),
        ),
      ],
    );
  }

  Widget buildCharBoxQuestion(Question question) {
    return Column(
      crossAxisAlignment: CrossAxisAlignment.start,
      children: [
        Padding(
          padding: const EdgeInsets.symmetric(vertical: 8.0),
          child: Text(
            question.title,
            style: TextStyle(fontSize: 18, fontWeight: FontWeight.bold),
          ),
        ),
        TextField(
          onChanged: (value) {
            answers[question.id] = value;
          },
          decoration: InputDecoration(
            hintText: 'Masukkan teks',
          ),
        ),
      ],
    );
  }

  Widget buildNumericalBoxQuestion(Question question) {
    return Column(
      crossAxisAlignment: CrossAxisAlignment.start,
      children: [
        Padding(
          padding: const EdgeInsets.symmetric(vertical: 8.0),
          child: Text(
            question.title,
            style: TextStyle(fontSize: 18, fontWeight: FontWeight.bold),
          ),
        ),
        TextField(
          keyboardType: TextInputType.number,
          onChanged: (value) {
            answers[question.id] = int.tryParse(value);
          },
          decoration: InputDecoration(
            hintText: 'Masukkan angka',
          ),
        ),
      ],
    );
  }

Catatan : Odoo yang dipakai versi 17 dengan Flutter versi 3.24.2

Leave a Comment

Your email address will not be published. Required fields are marked *

1