diff --git a/.gitlab/CODEOWNERS b/.gitlab/CODEOWNERS
new file mode 100644
index 0000000000000000000000000000000000000000..a69023229f36573edb56148d0e187d8e397ce110
--- /dev/null
+++ b/.gitlab/CODEOWNERS
@@ -0,0 +1,4 @@
+# Code owners file
+
+## Changes to these file(s) require approval from the teaching team
+sonar-project.properties @addianto @hafiyyan94
\ No newline at end of file
diff --git a/README.md b/README.md
index 3020546098563953b463aa84d24bb2958f22f589..3734ba0a5f9c94947bcea0145748b6214280794a 100755
--- a/README.md
+++ b/README.md
@@ -2,6 +2,13 @@
 
 > Internship matchmaking platform for students and companies.
 
+[![pipeline status](https://gitlab.cs.ui.ac.id/pmpl/class-project/kape/badges/master/pipeline.svg)](https://gitlab.cs.ui.ac.id/pmpl/class-project/kape/commits/master)
+[![coverage report](https://gitlab.cs.ui.ac.id/pmpl/class-project/kape/badges/master/coverage.svg)](https://gitlab.cs.ui.ac.id/pmpl/class-project/kape/commits/master)
+
+[![Quality Gate Status](https://pmpl.cs.ui.ac.id/sonarqube/api/project_badges/measure?project=id.ac.ui.cs.foss%3Akape&metric=alert_status)](https://pmpl.cs.ui.ac.id/sonarqube/dashboard?id=id.ac.ui.cs.foss%3Akape)
+[![Bugs](https://pmpl.cs.ui.ac.id/sonarqube/api/project_badges/measure?project=id.ac.ui.cs.foss%3Akape&metric=bugs)](https://pmpl.cs.ui.ac.id/sonarqube/dashboard?id=id.ac.ui.cs.foss%3Akape)
+[![Code Smells](https://pmpl.cs.ui.ac.id/sonarqube/api/project_badges/measure?project=id.ac.ui.cs.foss%3Akape&metric=code_smells)](https://pmpl.cs.ui.ac.id/sonarqube/dashboard?id=id.ac.ui.cs.foss%3Akape)
+
 ## Table of Contents
 
 - [Install](#install)
diff --git a/core/tests/test_create_vacancies.py b/core/tests/test_create_vacancies.py
new file mode 100644
index 0000000000000000000000000000000000000000..3a8fd4ade24f5d20a3b1a62552ea8ebdea9c82b6
--- /dev/null
+++ b/core/tests/test_create_vacancies.py
@@ -0,0 +1,66 @@
+from datetime import datetime
+
+import requests_mock
+from django.contrib.auth.models import User
+from rest_framework import status
+from rest_framework.test import APITestCase
+
+from core.models.accounts import Company, Student, Supervisor
+from core.models.vacancies import Vacancy, Application
+
+class CreateAndUpdateVacancyTest(APITestCase):
+    def test_new_vacancy_success(self):
+        superuser = User.objects.create_superuser('dummy.company', 'dummy.company@company.com', 'lalala123')
+        new_company = Company.objects.create(user=superuser, description="lalalaz", status=Company.VERIFIED, logo=None,
+                                             address=None)
+        self.client.force_authenticate(user=superuser)
+
+        url = '/api/vacancies/'
+        response = self.client.post(url, {'company': new_company.pk, 'open_time': datetime.fromtimestamp(0),
+        	 'close_time': datetime.today(), 'name': 'new_vacancy', 'description': 'new_vacancy	'}, format='json')
+        self.assertEqual(response.status_code, status.HTTP_200_OK)
+
+        vacancies = Vacancy.objects.count()
+        self.assertEqual(vacancies,1)
+
+    def test_new_vacancy_failed(self):
+        superuser = User.objects.create_superuser('dummy.company', 'dummy.company@company.com', 'lalala123')
+        new_company = Company.objects.create(user=superuser, description="lalalaz", status=Company.VERIFIED, logo=None,
+                                             address=None)
+        self.client.force_authenticate(user=superuser)
+
+        url = '/api/vacancies/'
+        response = self.client.post(url, {'company': new_company.pk, 'open_time': datetime.today(),
+        	 'close_time': datetime.fromtimestamp(0), 'name': 'new_vacancy', 'description': 'new_vacancy'}, format='json')
+        self.assertEqual(response.status_code, status.HTTP_400_BAD_REQUEST)
+
+        vacancies = Vacancy.objects.count()
+        self.assertEqual(vacancies,0)
+
+    def test_update_vacancy_success(self):
+        superuser = User.objects.create_superuser('dummy.company', 'dummy.company@company.com', 'lalala123')
+        new_company = Company.objects.create(user=superuser, description="lalalaz", status=Company.VERIFIED, logo=None,
+                                             address=None)
+        self.client.force_authenticate(user=superuser)
+
+        new_vacancy = Vacancy.objects.create(company=new_company, verified=False, open_time=datetime.fromtimestamp(0),
+                                              description="lalala", close_time=datetime.today(), name='new_company')
+
+        url = '/api/vacancies/' + str(new_vacancy.pk) + '/'
+        response = self.client.patch(url, {'open_time': datetime.fromtimestamp(0), 'close_time': datetime.today(),
+        	'name': 'new_vacancy2', 'description': 'new_vacancy2'}, format='json')
+        self.assertEqual(response.status_code, status.HTTP_200_OK)
+
+    def test_update_vacancy_failed(self):
+        superuser = User.objects.create_superuser('dummy.company', 'dummy.company@company.com', 'lalala123')
+        new_company = Company.objects.create(user=superuser, description="lalalaz", status=Company.VERIFIED, logo=None,
+                                             address=None)
+        self.client.force_authenticate(user=superuser)
+
+        new_vacancy = Vacancy.objects.create(company=new_company, verified=False, open_time=datetime.fromtimestamp(0),
+                                              description="lalala", close_time=datetime.today(), name='new_company')
+
+        url = '/api/vacancies/' + str(new_vacancy.pk) + '/'
+        response = self.client.patch(url, {'open_time': datetime.today(), 'close_time': datetime.fromtimestamp(0),
+        	'name': 'new_vacancy2', 'description': 'new_vacancy2'}, format='json')
+        self.assertEqual(response.status_code, status.HTTP_400_BAD_REQUEST)
\ No newline at end of file
diff --git a/core/views/vacancies.py b/core/views/vacancies.py
index e4b8b989272617ceaa00475a79293ee2eb266d9a..39f7422e70863a92c50a6f623b391ed8e4d8209b 100644
--- a/core/views/vacancies.py
+++ b/core/views/vacancies.py
@@ -21,9 +21,9 @@ from core.views.accounts import StudentViewSet
 class VacancyViewSet(MultiSerializerViewSetMixin, viewsets.ModelViewSet):
     queryset = Vacancy.objects.all()
     serializer_class = VacancySerializer
-    serializer_action_classes = {
-        'create': PostVacancySerializer
-    }
+    # serializer_action_classes = {
+    #     'create': PostVacancySerializer
+    # }
     permission_classes = [IsAdminOrCompany]
     pagination_class = PageNumberPagination
 
@@ -44,6 +44,38 @@ class VacancyViewSet(MultiSerializerViewSetMixin, viewsets.ModelViewSet):
             return self.get_paginated_response(VacancySerializer(page, many=True, context={'request': request}).data)
         return Response(VacancySerializer(vacancies, many=True, context={'request': request}).data)
 
+    def create(self, request):
+        data = request.data
+        company_set = Company.objects.filter(id=data['company'])
+        if len(company_set) == 0:
+            raise ValidationError('no company')
+        company = company_set[0]
+        open_time = data['open_time']
+        close_time = data['close_time']
+        name = data['name']
+        description = data['description']
+        if close_time < open_time:
+            raise ValidationError('Waktu tutup lowongan harus lebih dari waktu buka lowongan!')
+        vacancy = Vacancy(company=company, open_time=open_time, close_time=close_time, name=name, description=description)
+        vacancy.save()
+        return Response(status=status.HTTP_200_OK)
+
+    def partial_update(self, request, pk):
+        data = request.data
+        vacancy = Vacancy.objects.get(pk=pk)
+        open_time = data['open_time']
+        close_time = data['close_time']
+        name = data['name']
+        description = data['description']
+        if close_time < open_time:
+            raise ValidationError('Waktu tutup lowongan harus lebih dari waktu buka lowongan!')
+        vacancy.open_time = open_time
+        vacancy.close_time = close_time
+        vacancy.name = name
+        vacancy.description = description
+        vacancy.save()
+        return Response(status=status.HTTP_200_OK)
+
     @detail_route(permission_classes=[IsAdminOrCompany])
     def count(self, request, pk=None):
         vacancy = self.get_object()
diff --git a/sonar-project.properties b/sonar-project.properties
index 48a6c493c8cf0d1ead4cb49babe175535621ae67..c3db74cddc82e00230c7f6527111959ce3a27b3d 100644
--- a/sonar-project.properties
+++ b/sonar-project.properties
@@ -7,6 +7,7 @@
 sonar.projectKey=id.ac.ui.cs.foss:kape
 sonar.exclusions=/.devcontainer/,/.gitlab/,*.config.js,/.tmp/,
 sonar.scm.provider=git
+sonar.projectVersion=1.0.0
 
 ## Authentication
 ### sonar.login=[pass token via CLI/CI]